mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
1429b526ab
This commit drops libptc in favor of its new form libtcg. It brings several improvements, among which: * The QEMU version we work on has been upgraded. * CPUStateAccessAnalysis has been reimplemented in a way that makes it easier to debug and solves some limitations (e.g., tracking leaking pointers). * Identification of pieces of the CPU state that are read by each helper and fixing access to the CPU state is now performed at build-time. * We no longer mmap the code we need to translate, dropping all the issues related to code that needed to be mapped where something is already present. * We now have two distinct flavors of helper modules: the full one and the "slim" one. The latter contains the definition only of functions we intend to inline. It is used in most of the pipeline, a good thing since we spend less time optimizing code we don't really care about. The full module is only used on the re-compilation branch of the pipeline. * We no longer split the `cpu_loop` function. * We change MetaAddress to rely on architectures from `model::` as opposed to the LLVM ones. * We no longer attach debug info to LLVM IR containing the original assembly. * We now verify that the lifted code only contains code we expect.
265 lines
9.3 KiB
C++
265 lines
9.3 KiB
C++
/// \file FunctionSummaryOracle.cpp
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
|
|
#include "revng/ABI/FunctionType/Layout.h"
|
|
#include "revng/ADT/STLExtras.h"
|
|
#include "revng/EarlyFunctionAnalysis/FunctionSummaryOracle.h"
|
|
|
|
static Logger<> Log("efa-import-model");
|
|
|
|
namespace efa {
|
|
|
|
FunctionSummary
|
|
PrototypeImporter::prototype(const AttributesSet &Attributes,
|
|
const model::TypeDefinition *Prototype) {
|
|
FunctionSummary Summary(Attributes, ABICSVs, {}, {}, {});
|
|
if (Prototype == nullptr)
|
|
return Summary;
|
|
|
|
// Drop known to be preserved registers from `Summary.ClobberedRegisters`.
|
|
auto TransformToCSV = std::views::transform([this](Register Register) {
|
|
return M.getGlobalVariable(model::Register::getCSVName(Register), true);
|
|
});
|
|
auto IgnoreNullptr = std::views::filter([](const auto *Pointer) -> bool {
|
|
return Pointer != nullptr;
|
|
});
|
|
for (auto *CSV : abi::FunctionType::calleeSavedRegisters(*Prototype)
|
|
| TransformToCSV | IgnoreNullptr) {
|
|
Summary.ClobberedRegisters.erase(CSV);
|
|
}
|
|
|
|
// Stop importing prototype here, if `ClobberedRegisters` is the only
|
|
// information callee needs.
|
|
if (Level == PrototypeImportLevel::None)
|
|
return Summary;
|
|
|
|
Summary.ElectedFSO = abi::FunctionType::finalStackOffset(*Prototype);
|
|
|
|
// Stop importing prototype here, if callee also needs final stack offset.
|
|
if (Level == PrototypeImportLevel::Partial)
|
|
return Summary;
|
|
|
|
for (llvm::GlobalVariable *CSV : ABICSVs) {
|
|
Summary.ABIResults.ArgumentsRegisters.erase(CSV);
|
|
Summary.ABIResults.ReturnValuesRegisters.erase(CSV);
|
|
}
|
|
|
|
auto &&[ArgumentRegisters,
|
|
ReturnValueRegisters] = abi::FunctionType::usedRegisters(*Prototype);
|
|
for (Register ArgumentRegister : ArgumentRegisters) {
|
|
auto Name = model::Register::getCSVName(ArgumentRegister);
|
|
if (llvm::GlobalVariable *CSV = M.getGlobalVariable(Name, true))
|
|
Summary.ABIResults.ArgumentsRegisters.insert(CSV);
|
|
}
|
|
|
|
for (Register ReturnValueRegister : ReturnValueRegisters) {
|
|
auto Name = model::Register::getCSVName(ReturnValueRegister);
|
|
if (llvm::GlobalVariable *CSV = M.getGlobalVariable(Name, true))
|
|
Summary.ABIResults.ReturnValuesRegisters.insert(CSV);
|
|
}
|
|
|
|
// This point is only ever reached on the "full" import (like the one
|
|
// DetectABI does) - the proper function summary in extracted.
|
|
return Summary;
|
|
}
|
|
|
|
std::pair<const FunctionSummary *, bool>
|
|
FunctionSummaryOracle::getCallSite(MetaAddress Function,
|
|
BasicBlockID CallerBlockAddress,
|
|
MetaAddress CalledLocalFunction,
|
|
llvm::StringRef CalledSymbol) {
|
|
auto &&[Summary, IsTailCall] = getExactCallSite(Function, CallerBlockAddress);
|
|
if (Summary != nullptr) {
|
|
return { Summary, IsTailCall };
|
|
} else if (not CalledSymbol.empty()) {
|
|
return { &getDynamicFunction(CalledSymbol), false };
|
|
} else if (CalledLocalFunction.isValid()
|
|
and Binary.Functions().contains(CalledLocalFunction)) {
|
|
return { &getLocalFunction(CalledLocalFunction), false };
|
|
} else {
|
|
return { &getDefault(), false };
|
|
}
|
|
}
|
|
|
|
bool FunctionSummaryOracle::registerCallSite(MetaAddress Function,
|
|
BasicBlockID CallSite,
|
|
FunctionSummary &&New,
|
|
bool IsTailCall) {
|
|
revng_assert(Function.isValid());
|
|
revng_assert(CallSite.isValid());
|
|
std::pair<MetaAddress, BasicBlockID> Key = { Function, CallSite };
|
|
auto It = CallSites.find(Key);
|
|
if (It != CallSites.end()) {
|
|
auto &Recorded = It->second.first;
|
|
bool Changed = not New.containedOrEqual(Recorded);
|
|
New.combine(Recorded);
|
|
Recorded = std::move(New);
|
|
return Changed;
|
|
} else {
|
|
CallSiteDescriptor CSD = { std::move(New), IsTailCall };
|
|
CallSites.emplace(Key, std::move(CSD));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool FunctionSummaryOracle::registerLocalFunction(MetaAddress PC,
|
|
FunctionSummary &&New) {
|
|
revng_assert(PC.isValid());
|
|
|
|
if (Log.isEnabled()) {
|
|
Log << "registerLocalFunction " << PC.toString() << " with summary:\n";
|
|
New.dump(Log);
|
|
Log << DoLog;
|
|
}
|
|
|
|
auto It = LocalFunctions.find(PC);
|
|
bool Changed = It == LocalFunctions.end();
|
|
if (not Changed) {
|
|
auto &Recorded = It->second;
|
|
Changed = not New.containedOrEqual(Recorded);
|
|
New.combine(Recorded);
|
|
Recorded = std::move(New);
|
|
} else {
|
|
LocalFunctions.emplace(PC, std::move(New));
|
|
}
|
|
|
|
return Changed;
|
|
}
|
|
|
|
bool FunctionSummaryOracle::registerDynamicFunction(llvm::StringRef Name,
|
|
FunctionSummary &&New) {
|
|
auto It = DynamicFunctions.find(Name.str());
|
|
if (It != DynamicFunctions.end()) {
|
|
auto &Recorded = It->second;
|
|
bool Changed = not New.containedOrEqual(Recorded);
|
|
New.combine(Recorded);
|
|
Recorded = std::move(New);
|
|
return Changed;
|
|
} else {
|
|
DynamicFunctions.emplace(Name, std::move(New));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
const FunctionSummary &FunctionSummaryOracle::getDefault() {
|
|
if (not Default.has_value())
|
|
setDefault(Importer.prototype({}, Binary.defaultPrototype()));
|
|
return Default.value();
|
|
}
|
|
|
|
FunctionSummary &FunctionSummaryOracle::getLocalFunction(MetaAddress PC) {
|
|
if (not LocalFunctions.contains(PC)) {
|
|
const model::Function &Function = Binary.Functions().at(PC);
|
|
AttributesSet Attributes;
|
|
for (auto &ToCopy : Function.Attributes())
|
|
Attributes.insert(ToCopy);
|
|
|
|
const auto *Prototype = Binary.prototypeOrDefault(Function.prototype());
|
|
auto Summary = Importer.prototype(Attributes, Prototype);
|
|
registerLocalFunction(Function.Entry(), std::move(Summary));
|
|
}
|
|
|
|
return LocalFunctions.at(PC);
|
|
}
|
|
|
|
const FunctionSummary &
|
|
FunctionSummaryOracle::getDynamicFunction(llvm::StringRef Name) {
|
|
if (not DynamicFunctions.contains(Name.str())) {
|
|
const auto &DynamicFunction = Binary.ImportedDynamicFunctions()
|
|
.at(Name.str());
|
|
auto *Prototype = Binary.prototypeOrDefault(DynamicFunction.prototype());
|
|
|
|
AttributesSet Attributes;
|
|
for (auto &ToCopy : DynamicFunction.Attributes())
|
|
Attributes.insert(ToCopy);
|
|
|
|
registerDynamicFunction(DynamicFunction.Name(),
|
|
Importer.prototype(Attributes, Prototype));
|
|
}
|
|
return DynamicFunctions.at(Name.str());
|
|
}
|
|
|
|
std::pair<FunctionSummary *, bool>
|
|
FunctionSummaryOracle::getExactCallSite(MetaAddress Entry,
|
|
BasicBlockID CallSiteAddress) {
|
|
auto It = CallSites.find({ Entry, CallSiteAddress });
|
|
if (It == CallSites.end()) {
|
|
// Note: in case of absence we're doing the lookup every time, not super
|
|
// efficient.
|
|
|
|
const model::Function &Function = Binary.Functions().at(Entry);
|
|
|
|
// TODO: should CallSitePrototypes be index by BasicBlockID?
|
|
if (auto *CallSite = Function.CallSitePrototypes()
|
|
.tryGet(CallSiteAddress.start())) {
|
|
|
|
AttributesSet Attributes;
|
|
for (auto &ToCopy : CallSite->Attributes())
|
|
Attributes.insert(ToCopy);
|
|
registerCallSite(Function.Entry(),
|
|
BasicBlockID(CallSite->CallerBlockAddress()),
|
|
Importer.prototype(Attributes, CallSite->prototype()),
|
|
CallSite->IsTailCall());
|
|
}
|
|
}
|
|
It = CallSites.find({ Entry, CallSiteAddress });
|
|
if (It == CallSites.end()) {
|
|
return { nullptr, false };
|
|
} else {
|
|
return { &It->second.first, It->second.second };
|
|
}
|
|
}
|
|
|
|
template<PrototypeImportLevel Level>
|
|
FunctionSummaryOracle importImpl(llvm::Module &M,
|
|
GeneratedCodeBasicInfo &GCBI,
|
|
const model::Binary &Binary) {
|
|
using GV = llvm::GlobalVariable;
|
|
auto RegisterFilter = std::views::filter([SP = GCBI.spReg()](GV *CSV) {
|
|
return CSV != nullptr && CSV != SP;
|
|
});
|
|
PrototypeImporter Importer{
|
|
.Level = Level,
|
|
.M = M,
|
|
.ABICSVs = GCBI.abiRegisters() | RegisterFilter
|
|
| revng::to<std::set<llvm::GlobalVariable *>>()
|
|
};
|
|
|
|
return FunctionSummaryOracle(Binary, std::move(Importer));
|
|
}
|
|
|
|
FunctionSummaryOracle
|
|
FunctionSummaryOracle::importFullPrototypes(llvm::Module &M,
|
|
GeneratedCodeBasicInfo &GCBI,
|
|
const model::Binary &Binary) {
|
|
revng_log(Log,
|
|
"Importing from the model, while taking prototypes into the "
|
|
"account");
|
|
return importImpl<PrototypeImportLevel::Full>(M, GCBI, Binary);
|
|
}
|
|
|
|
FunctionSummaryOracle
|
|
FunctionSummaryOracle::importBasicPrototypeData(llvm::Module &M,
|
|
GeneratedCodeBasicInfo &GCBI,
|
|
const model::Binary &Binary) {
|
|
revng_log(Log,
|
|
"Importing from the model, but ignoring some of the prototype "
|
|
"data");
|
|
return importImpl<PrototypeImportLevel::Partial>(M, GCBI, Binary);
|
|
}
|
|
|
|
FunctionSummaryOracle
|
|
FunctionSummaryOracle::importWithoutPrototypes(llvm::Module &M,
|
|
GeneratedCodeBasicInfo &GCBI,
|
|
const model::Binary &Binary) {
|
|
revng_log(Log, "Importing from the model, but ignoring prototypes");
|
|
return importImpl<PrototypeImportLevel::None>(M, GCBI, Binary);
|
|
}
|
|
|
|
} // namespace efa
|