Files
revng-revng/lib/Lift/ValueMaterializerPass.cpp
2024-03-15 15:24:59 +01:00

161 lines
5.4 KiB
C++

/// \file ValueMaterializerPass.cpp
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include "llvm/Analysis/LazyValueInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/KnownBits.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "revng/Support/Debug.h"
#include "revng/ValueMaterializer/ValueMaterializer.h"
#include "JumpTargetManager.h"
#include "ValueMaterializerPass.h"
using namespace llvm;
cl::list<uint64_t> DumpValueMaterializerAt("dump-vm-at", cl::ZeroOrMore);
cl::opt<bool> DumpValueMaterializer("dump-all-vm");
using SDMO = StaticDataMemoryOracle;
SDMO::StaticDataMemoryOracle(const DataLayout &DL,
JumpTargetManager &JTM,
const MetaAddress::Features &Features) :
JTM(JTM), Features(Features) {
// Read the value using the endianness of the destination architecture,
// since, if there's a mismatch, in the stack we will also have a byteswap
// instruction
IsLittleEndian = DL.isLittleEndian();
}
MaterializedValue StaticDataMemoryOracle::load(uint64_t LoadAddress,
unsigned LoadSize) {
auto Address = MetaAddress::fromGeneric(LoadAddress, Features);
return JTM.readFromPointer(Address, LoadSize, IsLittleEndian);
}
static void demoteOrToAdd(Function &F) {
using namespace llvm;
auto &DL = F.getParent()->getDataLayout();
std::set<Instruction *> ToReplace;
for (BasicBlock &BB : F) {
for (Instruction &I : BB) {
if (I.getOpcode() == Instruction::Or) {
Value *LHS = I.getOperand(0);
Value *RHS = I.getOperand(1);
const APInt &LHSZeros = computeKnownBits(LHS, DL).Zero;
const APInt &RHSZeros = computeKnownBits(RHS, DL).Zero;
if ((~RHSZeros & ~LHSZeros).isNullValue())
ToReplace.insert(&I);
}
}
}
for (Instruction *I : ToReplace) {
I->replaceAllUsesWith(BinaryOperator::Create(Instruction::Add,
I->getOperand(0),
I->getOperand(1),
Twine(),
I));
eraseFromParent(I);
}
}
PreservedAnalyses ValueMaterializerPass::run(Function &F,
FunctionAnalysisManager &FAM) {
using namespace llvm;
llvm::EliminateUnreachableBlocks(F, nullptr, false);
demoteOrToAdd(F);
// Early exit in case nothing was marked
Function *Marker = F.getParent()->getFunction(MarkerName);
if (Marker == nullptr)
return PreservedAnalyses::all();
auto &LVI = FAM.getResult<LazyValueAnalysis>(F);
auto &DT = FAM.getResult<DominatorTreeAnalysis>(F);
BasicBlock *Entry = &F.getEntryBlock();
SwitchInst *Terminator = cast<SwitchInst>(Entry->getTerminator());
BasicBlock *Dispatcher = Terminator->getDefaultDest();
auto GetConstantArgument = [](CallBase *Call, unsigned Index) {
return cast<ConstantInt>(Call->getArgOperand(Index))->getLimitedValue();
};
for (CallBase *Call : callersIn(Marker, &F)) {
// Decode arguments
revng_assert(Call->arg_size() >= 4);
Value *ToTrack = Call->getArgOperand(0);
uint64_t MaxPhiLike = GetConstantArgument(Call, 1);
uint64_t MaxLoad = GetConstantArgument(Call, 2);
auto Oracle = static_cast<Oracle::Values>(GetConstantArgument(Call, 3));
revng_assert(Oracle < Oracle::Count);
auto AddressAsString = extractFromConstantStringPtr(Call->getArgOperand(4));
auto Address = MetaAddress::fromString(AddressAsString);
revng_assert(Address.isValid());
uint64_t CurrentAddress = Address.address();
MaterializedValues Values;
DataFlowGraph::Limits Limits(MaxPhiLike, MaxLoad);
auto Results = ValueMaterializer::getValuesFor(Call,
ToTrack,
MO,
LVI,
DT,
Limits,
Oracle);
if (Results.values())
Values = std::move(*Results.values());
if (DumpValueMaterializer
or count(DumpValueMaterializerAt, CurrentAddress) > 0) {
// User asked to dump information about this address
dbg << "Values produced by ValueMaterializer for " << getName(ToTrack)
<< " at " << Address.toString() << ":\n";
for (const MaterializedValue &V : Values) {
dbg << " ";
V.dump(dbg);
dbg << "\n";
}
dbg << "Dumping graphs\n";
Results.dataFlowGraph().dump();
AdvancedValueInfoMFI::dump(&Results.cfeg(), Results.mfiResult());
}
//
// Create a revng.avi metadata containing the type of instruction and
// all the possible values we identified
//
QuickMetadata QMD(getContext(&F));
std::vector<Metadata *> ValuesMD;
ValuesMD.reserve(Values.size());
for (const MaterializedValue &V : Values) {
// TODO: we are we ignoring those with symbols
auto Offset = V.value();
std::string SymbolName;
if (V.hasSymbol())
SymbolName = V.symbolName();
ValuesMD.push_back(QMD.tuple({ QMD.get(SymbolName), QMD.get(Offset) }));
}
Call->setMetadata("revng.avi", QMD.tuple(ValuesMD));
}
return PreservedAnalyses::all();
}