/// \file ValueMaterializer.cpp // // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/Support/GenericDomTreeConstruction.h" #include "revng/Support/Debug.h" #include "revng/ValueMaterializer/ValueMaterializer.h" void ValueMaterializer::run() { revng_log(ValueMaterializerLogger, "Evaluating " << getName(V) << " using " << getName(Context) << " as context"); DataFlowGraph = DataFlowGraph::fromValue(V, TheLimits); revng_log(ValueMaterializerLogger, "The data-flow graph has " << DataFlowGraph.size() << " nodes"); DFGSizeStatitistics.push(DataFlowGraph.size()); // // Prepare the data-flow graph // // Drop all the SCCs: we want to have a DAG. // Note that dropping part of the data flow graph does not affect // correctness. DataFlowGraph.removeCycles(); DataFlowGraph.enforceLimits(TheLimits); DataFlowGraph.purgeUnreachable(); computeOracleConstraints(); applyOracleResultsToDataFlowGraph(); computeSizeLowerBound(); electMaterializationStartingPoints(); Values = DataFlowGraph.materialize(DataFlowGraph.getEntryNode(), MO); } void ValueMaterializer::computeOracleConstraints() { using namespace llvm; switch (Oracle) { case Oracle::None: break; case Oracle::LazyValueInfo: for (DataFlowGraph::Node *Node : DataFlowGraph.nodes()) if (auto *I = dyn_cast(V)) if (I->getType()->isIntegerTy()) OracleConstraints[I] = LVI.getConstantRange(I, Context); break; case Oracle::AdvancedValueInfo: std::tie(OracleConstraints, CFEG, MFIResults) = runAVI(DataFlowGraph, Context, DT, LVI, true); break; default: revng_abort(); } } void ValueMaterializer::applyOracleResultsToDataFlowGraph() { using namespace llvm; const DataLayout &DL = getModule(Context)->getDataLayout(); for (DataFlowGraph::Node *Node : DataFlowGraph.nodes()) { auto *V = Node->Value; auto *CE = dyn_cast(V); if (auto *C = dyn_cast(V)) { auto Lower = C->getValue(); auto Upper = Lower + APInt(V->getType()->getIntegerBitWidth(), 1, false); Node->OracleRange = ConstantRange(Lower, Upper); revng_assert(Lower != Upper); revng_assert(Node->OracleRange->size() != 0); } else if (isa(V) or isa(V)) { // TODO: should we set a range of 0 for ConstantPointerNull unsigned BitWidth = 0; auto *ValueType = V->getType(); if (isa(ValueType)) { BitWidth = DL.getPointerTypeSizeInBits(ValueType); } else if (isa(ValueType)) { BitWidth = V->getType()->getIntegerBitWidth(); } else { revng_abort(); } Node->OracleRange = ConstantRange(BitWidth, false); } else if (auto *I = dyn_cast(V)) { // Query the oracle auto It = OracleConstraints.find(I); if (It != OracleConstraints.end()) Node->OracleRange = It->second; } else if (CE != nullptr and (CE->getOpcode() == Instruction::IntToPtr or CE->getOpcode() == Instruction::PtrToInt)) { // Ignore } else if (isa(V)) { // Ignore } else { revng_abort(); } } } void ValueMaterializer::computeSizeLowerBound() { using namespace llvm; // SizeLowerBound is the size of the range we expect *in absence of // collisions*. An example of collisions is when you have [0, 10) * 0. You'd // expect 10 elements, but you'll get only 1. for (DataFlowGraph::Node *Node : post_order(&DataFlowGraph)) { auto *V = Node->Value; unsigned Opcode = 0; if (auto *I = dyn_cast(V)) Opcode = I->getOpcode(); auto SuccessorsCount = Node->successorCount(); Node->SizeLowerBound = DataFlowGraph::Node::MaxSizeLowerBound; if (SuccessorsCount) { // Is this an instruction whose SizeLowerBound should be the max of the // successors? // If not, we'll just set SizeLowerBound to 1. bool UseMaxOfSuccessors = (isPhiLike(V) or Opcode == Instruction::Add or Opcode == Instruction::Sub or Opcode == Instruction::Shl); if (UseMaxOfSuccessors) { // Use max of successors revng_assert(SuccessorsCount > 0); Node->SizeLowerBound = (*Node->successors().begin())->SizeLowerBound; for (auto *Successor : skip_front(Node->successors())) { if (Successor->SizeLowerBound > Node->SizeLowerBound) Node->SizeLowerBound = Successor->SizeLowerBound; } } else { // Compute the proudct of successors if (SuccessorsCount > 0) { OverflowSafeInt Product = 1; for (auto *Successor : Node->successors()) Product *= Successor->SizeLowerBound; if (Product) Node->SizeLowerBound = *Product; } } } // SizeLowerBound cannot exceed the size of the OracleRange if (Node->OracleRange.has_value()) { auto OracleRangeSize = Node->OracleRange->size(); if (OracleRangeSize.ult(Node->SizeLowerBound)) Node->SizeLowerBound = OracleRangeSize.getLimitedValue(); } } } void ValueMaterializer::electMaterializationStartingPoints() { using namespace llvm; // // Choose initial materialization candidates // using Node = DataFlowGraph::Node; for (Node *N : DataFlowGraph.nodes()) { if (N->OracleRange.has_value()) { auto OracleRangeSize = N->OracleRange->size(); revng_assert(OracleRangeSize.uge(N->SizeLowerBound)); if (N->SizeLowerBound == OracleRangeSize) { revng_log(ValueMaterializerLogger, "Setting UseOracle for " << N->valueToString() << " since SizeLowerBound == " "OracleRangeSize (" << N->SizeLowerBound << ")"); N->UseOracle = true; } } } // // Finalize materialization entry points // { // // Exclude candidates that do not dominate all the leaves they reach // and compute final set of candidates // // Prepare dominator tree DominatorTreeBase DFGDT; DFGDT.recalculate(DataFlowGraph); using NodeSet = SmallPtrSet; struct NodeData { NodeSet ReachableLeaves; NodeSet ToMaterialize; }; std::map NodesData; // Initialize data structure for leaves for (Node *N : DataFlowGraph.nodes()) { if (not N->hasSuccessors()) { NodesData[N].ReachableLeaves = { N }; NodesData[N].ToMaterialize = { N }; } } auto MergeFromSuccessors = [&NodesData](const Node *N, auto &&Getter) { auto &CurrentNodeData = NodesData[N]; for (const Node *Successor : N->successors()) { const auto &SuccessorNodeData = NodesData.at(Successor); Getter(CurrentNodeData) ->insert(Getter(SuccessorNodeData)->begin(), Getter(SuccessorNodeData)->end()); } }; for (Node *N : post_order(&DataFlowGraph)) { auto &CurrentNodeData = NodesData[N]; // Compute the set of reachable leaves by merging the set of leaves of // the successors MergeFromSuccessors(N, [](auto &Node) { return &Node.ReachableLeaves; }); if (N->UseOracle) { // Check if it dominates all the reachable leaves, if not, do not use // the oracle for (const Node *ReachableLeaf : CurrentNodeData.ReachableLeaves) { if (not DFGDT.dominates(N, ReachableLeaf)) { N->UseOracle = false; break; } } } // Update the results: either replace results with self or merge results // from inputs if (N->UseOracle) { CurrentNodeData.ToMaterialize = { N }; } else { // Merge results of successors MergeFromSuccessors(N, [](auto &Node) { return &Node.ToMaterialize; }); } } // Update UseOracle using ToMaterialize of the analysis associated to the // root node const auto &RootData = NodesData.at(DataFlowGraph.getEntryNode()); for (Node *N : DataFlowGraph.nodes()) N->UseOracle = RootData.ToMaterialize.contains(N); } }