diff --git a/CMakeLists.txt b/CMakeLists.txt index e29922df0..2133f916b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,7 +276,7 @@ macro(add_decompilation_pipeline_test CATEGORY INPUT_FILE CONFIGURATION OUTPUT NAME decompile_string_based_${TARGET_NAME} COMMAND revng --prefix=${CMAKE_BINARY_DIR} opt -S -restructure-cfg - -add-ir-serialization-markers -beautify-ghast --helpers-to-header + -add-assignment-markers -beautify-ghast --helpers-to-header --helpers-header-name=${OUTFOLDER}/decompiled_stringbased/helpers.h -model-to-header --types-header-name=${OUTFOLDER}/decompiled_stringbased/revng-types.h diff --git a/include/revng-c/Support/FunctionTags.h b/include/revng-c/Support/FunctionTags.h index a6620d068..c9e691965 100644 --- a/include/revng-c/Support/FunctionTags.h +++ b/include/revng-c/Support/FunctionTags.h @@ -21,7 +21,7 @@ extern Tag MallocLike; extern Tag IsRef; extern Tag AddressOf; extern Tag ModelGEP; -extern Tag SerializationMarker; +extern Tag AssignmentMarker; extern Tag OpaqueExtractValue; } // namespace FunctionTags @@ -38,7 +38,7 @@ void initAddressOfPool(OpaqueFunctionsPool &Pool); llvm::Function * getModelGEP(llvm::Module &M, llvm::Type *RetTy, llvm::Type *BaseAddressTy); -llvm::Function *getSerializationMarker(llvm::Module &M, llvm::Type *T); +llvm::Function *getAssignmentMarker(llvm::Module &M, llvm::Type *T); /// Derive the function type of the corresponding OpaqueExtractValue() function /// from an ExtractValue instruction. OpaqueExtractValue() basically wraps an diff --git a/lib/Backend/DecompileFunction.cpp b/lib/Backend/DecompileFunction.cpp index 828a0c0dd..dd4a692f7 100644 --- a/lib/Backend/DecompileFunction.cpp +++ b/lib/Backend/DecompileFunction.cpp @@ -377,10 +377,10 @@ private: void emitBasicBlock(const BasicBlock *BB); private: - /// Serialize an instruction, if it is marked for serialization, or - /// add a string containing its expression to the TokenMap. Conditional - /// branch instructions are associated to their condition's token, and are - /// never emitted directly (they are handled during the GHAST visit) + /// Emit an assignment for an instruction, if it is marked for assignemnt, + /// otherwise add a string containing its expression to the TokenMap. + /// Control-flow instructions are associated to their condition's token, and + /// are never emitted directly (they are handled during the GHAST visit) StringToken buildExpression(const llvm::Instruction &I); /// Assign a string token and a QualifiedType to an Instruction @@ -650,7 +650,7 @@ StringToken CCodeGenerator::handleSpecialFunction(const llvm::CallInst *Call) { // Second argument is the value being addressed const llvm::Value *Arg = Call->getArgOperand(1); Expression = buildAddressExpr(TokenMap.at(Arg)); - } else if (FunctionTags::SerializationMarker.isTagOf(CalledFunc)) { + } else if (FunctionTags::AssignmentMarker.isTagOf(CalledFunc)) { const llvm::Value *Arg = Call->getArgOperand(0); // If a local variable has already been declared for the first argument, @@ -662,7 +662,7 @@ StringToken CCodeGenerator::handleSpecialFunction(const llvm::CallInst *Call) { if (TopScopeVariables.contains(Call)) { // If an entry in the TokenMap exists for this value, we have already // declared it, hence we just need to emit an assignment it here. - revng_log(Log, "Already declared! Serializing " << Expression.str()); + revng_log(Log, "Already declared! Assigning " << Expression.str()); Out << buildAssignmentExpr(TypeMap.at(Call), TokenMap.at(Call), TypeMap.at(Arg), @@ -672,7 +672,7 @@ StringToken CCodeGenerator::handleSpecialFunction(const llvm::CallInst *Call) { Expression = TokenMap.at(Call); } else { - revng_log(Log, "\tSerializing " << Expression.str()); + revng_log(Log, "\tAssigning " << Expression.str()); const StringToken VarName = NameGenerator.nextVarName(Call); revng_log(Log, "Declaring new local var for " << Expression.str()); Out << buildAssignmentExpr(TypeMap.at(Call), @@ -752,7 +752,7 @@ StringToken CCodeGenerator::handleSpecialFunction(const llvm::CallInst *Call) { // If this call returns an aggregate type, we have to serialize the call // immediately. This is needed because the name of the type returned by this // function is not in the model: its name is derived from the called - // function. If we wait for `SerializationMarker` to emit a declaration for + // function. If we wait for `AssignmentMarker` to emit a declaration for // it, we will loose information on which is the type of the returned // struct. if (Call->getType()->isAggregateType()) { @@ -841,7 +841,7 @@ StringToken CCodeGenerator::buildExpression(const llvm::Instruction &I) { // returned values from the callee function, and emit the call // immediately. // If we were to postpone the emission to the next - // `SerializationMarker`, we would loose information on the name of the + // `AssignmentMarker`, we would loose information on the name of the // return struct. if (Call->getType()->isAggregateType()) { StringToken VarName = NameGenerator.nextVarName(Call); @@ -854,7 +854,7 @@ StringToken CCodeGenerator::buildExpression(const llvm::Instruction &I) { } else if (auto *CPrototype = dyn_cast(Prototype)) { // CABIFunctionTypes are allowed to return arrays, which get enclosed in // a wrapper whose type name is derive by the callee. If we were to - // postpone the emission to the next `SerializationMarker`, we would + // postpone the emission to the next `AssignmentMarker`, we would // loose information on the name of the return struct. if (CPrototype->ReturnType.isArray()) { StringToken VarName = NameGenerator.nextVarName(Call); diff --git a/lib/Backend/VariableScopeAnalysisPass.cpp b/lib/Backend/VariableScopeAnalysisPass.cpp index c12f4cc1f..aa3775183 100644 --- a/lib/Backend/VariableScopeAnalysisPass.cpp +++ b/lib/Backend/VariableScopeAnalysisPass.cpp @@ -135,7 +135,7 @@ static ValuePtrSet collectLocalVariables(const Function &F) { llvm::Function *CalledFunction = Call->getCalledFunction(); if (not CalledFunction) continue; - if (not FunctionTags::SerializationMarker.isTagOf(CalledFunction)) + if (not FunctionTags::AssignmentMarker.isTagOf(CalledFunction)) continue; // If an instruction's uses are all inside the same basic block, we are diff --git a/lib/BeautifyGHAST/BeautifyGHAST.cpp b/lib/BeautifyGHAST/BeautifyGHAST.cpp index 458d57315..a95e538b0 100644 --- a/lib/BeautifyGHAST/BeautifyGHAST.cpp +++ b/lib/BeautifyGHAST/BeautifyGHAST.cpp @@ -101,10 +101,10 @@ static bool hasSideEffects(IfNode *If) { return true; if (auto *Call = llvm::dyn_cast(&I)) { - // If it's a call to a serialization marker, look at the second + // If it's a call to an assignment marker, look at the second // argument. If it's a true constant, than it has side effects. auto *Callee = Call->getCalledFunction(); - if (Callee and FunctionTags::SerializationMarker.isTagOf(Callee)) { + if (Callee and FunctionTags::AssignmentMarker.isTagOf(Callee)) { auto *Arg1 = Call->getArgOperand(1); auto *HasSideEffects = llvm::cast(Arg1); return HasSideEffects->isOne(); @@ -754,7 +754,7 @@ computeCumulativeNodeWeight(ASTNode *Node, // TODO: At the moment we use the BasicBlock size to assign a weight to the // code nodes. In future, we would want to use the number of statement // emitted in the decompiled code as weight (and use - // `SerializationMarker`s to do that). + // `AssignmentMarker`s to do that). CodeNode *Code = llvm::cast(Node); llvm::BasicBlock *BB = Code->getBB(); rc_return BB->size(); diff --git a/lib/HeadersGeneration/HelpersToHeader.cpp b/lib/HeadersGeneration/HelpersToHeader.cpp index d50148969..b11f48c4e 100644 --- a/lib/HeadersGeneration/HelpersToHeader.cpp +++ b/lib/HeadersGeneration/HelpersToHeader.cpp @@ -71,7 +71,7 @@ bool dumpHelpersToHeader(const llvm::Module &M, llvm::raw_ostream &Header) { auto FTags = FunctionTags::TagsSet::from(&F); if (FTags.contains(FunctionTags::Isolated) or FTags.contains(FunctionTags::ModelGEP) - or FTags.contains(FunctionTags::SerializationMarker) + or FTags.contains(FunctionTags::AssignmentMarker) or FTags.contains(FunctionTags::AddressOf) or FTags.contains(FunctionTags::Marker)) continue; diff --git a/lib/IRCanonicalization/CMakeLists.txt b/lib/IRCanonicalization/CMakeLists.txt index c42fbec23..a75944933 100644 --- a/lib/IRCanonicalization/CMakeLists.txt +++ b/lib/IRCanonicalization/CMakeLists.txt @@ -2,20 +2,17 @@ # Copyright rev.ng Labs Srl. See LICENSE.md for details. # +add_subdirectory(MarkAssignments) + revng_add_analyses_library( - revngcIRCanonicalization - revngc - AddIRSerializationMarkerPass.cpp - ExitSSAPass.cpp - LivenessAnalysis.cpp - MakeModelGEPPass.cpp + revngcIRCanonicalization revngc ExitSSAPass.cpp MakeModelGEPPass.cpp TwosComplementArithmeticNormalizationPass.cpp) target_link_libraries( revngcIRCanonicalization + revngcInitModelTypes revngcSupport revngcTargetFunctionOption revng::revngModel revng::revngSupport - revngcInitModelTypes ${LLVM_LIBRARIES}) diff --git a/lib/IRCanonicalization/AddIRSerializationMarkerPass.cpp b/lib/IRCanonicalization/MarkAssignments/AddAssignmentMarkerPass.cpp similarity index 62% rename from lib/IRCanonicalization/AddIRSerializationMarkerPass.cpp rename to lib/IRCanonicalization/MarkAssignments/AddAssignmentMarkerPass.cpp index 47d1e8a66..337d05112 100644 --- a/lib/IRCanonicalization/AddIRSerializationMarkerPass.cpp +++ b/lib/IRCanonicalization/MarkAssignments/AddAssignmentMarkerPass.cpp @@ -2,8 +2,9 @@ // Copyright rev.ng Labs Srl. See LICENSE.md for details. // -/// Pass that wraps Instructions in LLVM IR that must be serialized in -/// special marker calls. +/// Pass that detects Instructions in a Functions for which we have to generate +/// a variable assignment when decompiling to C, and wraps them in special +/// marker calls. #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Function.h" @@ -18,19 +19,17 @@ #include "revng/Support/FunctionTags.h" #include "revng/Support/IRHelpers.h" +#include "revng-c/Support/FunctionTags.h" #include "revng-c/Support/Mangling.h" #include "revng-c/TargetFunctionOption/TargetFunctionOption.h" -#include "MarkAnalysis.h" -#include "MarkForSerializationFlags.h" +#include "MarkAssignments.h" -Logger<> MarkLog{ "mark-serialization" }; - -struct AddIRSerializationMarkersPass : public llvm::FunctionPass { +struct AddAssignmentMarkersPass : public llvm::FunctionPass { public: static char ID; - AddIRSerializationMarkersPass() : llvm::FunctionPass(ID) {} + AddAssignmentMarkersPass() : llvm::FunctionPass(ID) {} void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { AU.setPreservesCFG(); @@ -39,7 +38,7 @@ public: bool runOnFunction(llvm::Function &F) override; }; -bool AddIRSerializationMarkersPass::runOnFunction(llvm::Function &F) { +bool AddAssignmentMarkersPass::runOnFunction(llvm::Function &F) { // Skip non-isolated functions auto FTags = FunctionTags::TagsSet::from(&F); @@ -52,16 +51,13 @@ bool AddIRSerializationMarkersPass::runOnFunction(llvm::Function &F) { if (not F.getName().equals(TargetFunction.c_str())) return false; - // Mark instructions for serialization, and write the results in ToSerialize - SerializationMap ToSerialize = {}; - MarkAnalysis::Analysis Mark(F, ToSerialize); - Mark.initialize(); - Mark.run(); + MarkAssignments::AssignmentMap + Assignments = MarkAssignments::selectAssignments(F); llvm::Module *M = F.getParent(); llvm::IRBuilder<> Builder(M->getContext()); bool Changed = false; - for (const auto &[I, Flag] : ToSerialize) { + for (auto &[I, Flag] : Assignments) { auto *IType = I->getType(); // We cannot wrap void-typed things into wrappers. @@ -71,7 +67,7 @@ bool AddIRSerializationMarkersPass::runOnFunction(llvm::Function &F) { if (bool(Flag)) { - auto *MarkerF = getSerializationMarker(*M, IType); + auto *MarkerF = getAssignmentMarker(*M, IType); // Insert a call to the SCEV barrier right after I. For now the call to // barrier has an undef argument, that will be fixed later. @@ -81,16 +77,12 @@ bool AddIRSerializationMarkersPass::runOnFunction(llvm::Function &F) { // later on. auto *Undef = llvm::UndefValue::get(IType); - // The second arg operand needs to be true if the serialization is + // The second arg operand needs to be true if the assignment is // required because of side effects. auto *BoolType = MarkerF->getArg(1)->getType(); - llvm::Constant *MarkSideEffects = nullptr; - if (Flag.isSet(SerializationReason::HasSideEffects) - or Flag.isSet(SerializationReason::HasInterferingSideEffects)) { - MarkSideEffects = llvm::ConstantInt::getAllOnesValue(BoolType); - } else { - MarkSideEffects = llvm::ConstantInt::getNullValue(BoolType); - } + auto *MarkSideEffects = Flag.hasSideEffects() ? + llvm::ConstantInt::getAllOnesValue(BoolType) : + llvm::ConstantInt::getNullValue(BoolType); auto *Call = Builder.CreateCall(MarkerF, { Undef, MarkSideEffects }); @@ -106,10 +98,10 @@ bool AddIRSerializationMarkersPass::runOnFunction(llvm::Function &F) { return true; } -char AddIRSerializationMarkersPass::ID = 0; +char AddAssignmentMarkersPass::ID = 0; -using Register = llvm::RegisterPass; -static Register X("add-ir-serialization-markers", - "Pass that adds serialization markers to the IR", +using Register = llvm::RegisterPass; +static Register X("add-assignment-markers", + "Pass that adds assignment markers to the IR", false, false); diff --git a/lib/IRCanonicalization/MarkAssignments/CMakeLists.txt b/lib/IRCanonicalization/MarkAssignments/CMakeLists.txt new file mode 100644 index 000000000..c7a84a45b --- /dev/null +++ b/lib/IRCanonicalization/MarkAssignments/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# Copyright rev.ng Labs Srl. See LICENSE.md for details. +# + +revng_add_analyses_library( + revngcMarkAssignments revngc AddAssignmentMarkerPass.cpp LivenessAnalysis.cpp + MarkAssignments.cpp) + +target_link_libraries( + revngcMarkAssignments revngcSupport revngcTargetFunctionOption + revng::revngModel revng::revngSupport ${LLVM_LIBRARIES}) diff --git a/lib/IRCanonicalization/LivenessAnalysis.cpp b/lib/IRCanonicalization/MarkAssignments/LivenessAnalysis.cpp similarity index 100% rename from lib/IRCanonicalization/LivenessAnalysis.cpp rename to lib/IRCanonicalization/MarkAssignments/LivenessAnalysis.cpp diff --git a/lib/IRCanonicalization/LivenessAnalysis.h b/lib/IRCanonicalization/MarkAssignments/LivenessAnalysis.h similarity index 100% rename from lib/IRCanonicalization/LivenessAnalysis.h rename to lib/IRCanonicalization/MarkAssignments/LivenessAnalysis.h diff --git a/lib/IRCanonicalization/MarkAnalysis.h b/lib/IRCanonicalization/MarkAssignments/MarkAssignments.cpp similarity index 84% rename from lib/IRCanonicalization/MarkAnalysis.h rename to lib/IRCanonicalization/MarkAssignments/MarkAssignments.cpp index 60e8cd0b3..8c0369c44 100644 --- a/lib/IRCanonicalization/MarkAnalysis.h +++ b/lib/IRCanonicalization/MarkAssignments/MarkAssignments.cpp @@ -1,10 +1,9 @@ -#pragma once - // // Copyright (c) rev.ng Labs Srl. See LICENSE.md for details. // -/// Analysis that marks instructions to be serialized in C +/// Analysis that detects which Instructions in a Function need an assignment in +/// decompilation to C. #include @@ -19,7 +18,7 @@ #include "revng-c/Support/FunctionTags.h" #include "LivenessAnalysis.h" -#include "MarkForSerializationFlags.h" +#include "MarkAssignments.h" namespace llvm { @@ -29,11 +28,11 @@ class Instruction; } // end namespace llvm -extern Logger<> MarkLog; +Logger<> MarkLog{ "mark-assignments" }; -namespace MarkAnalysis { +namespace MarkAssignments { -inline bool +static bool haveInterferingSideEffects(const llvm::Instruction & /*InstrWithSideEffects*/, const llvm::Instruction &Other) { // Calls to pure functions never have interfering side effects. @@ -43,8 +42,6 @@ haveInterferingSideEffects(const llvm::Instruction & /*InstrWithSideEffects*/, return true; } -using DuplicationMap = std::map; - class IntersectionMonotoneSetWithTaint; using LatticeElement = IntersectionMonotoneSetWithTaint; @@ -202,7 +199,7 @@ class Analysis : public MonotoneFramework { private: llvm::Function &F; - SerializationMap &ToSerialize; + AssignmentMap Assignments; LivenessAnalysis::LivenessMap LiveIn; public: @@ -214,16 +211,25 @@ public: using InterruptType = typename Base::InterruptType; +public: + Analysis(llvm::Function &F) : + Base(&F.getEntryBlock()), F(F), Assignments(), LiveIn() { + Base::registerExtremal(&F.getEntryBlock()); + } + + void initialize() { + Base::initialize(); + LiveIn = computeLiveness(F); + } + + AssignmentMap &&takeAssignments() { return std::move(Assignments); } + +public: void assertLowerThanOrEqual(const LatticeElement &A, const LatticeElement &B) const { revng_assert(A.lowerThanOrEqual(B)); } - Analysis(llvm::Function &F, SerializationMap &ToSerialize) : - Base(&F.getEntryBlock()), F(F), ToSerialize(ToSerialize), LiveIn() { - Base::registerExtremal(&F.getEntryBlock()); - } - [[noreturn]] void dumpFinalState() const { revng_abort(); } SuccVector successors(llvm::BasicBlock *BB, InterruptType &) const { @@ -269,11 +275,6 @@ public: return LatticeElement::top(); } - void initialize() { - Base::initialize(); - LiveIn = computeLiveness(F); - } - InterruptType transfer(llvm::BasicBlock *BB) { using namespace llvm; revng_log(MarkLog, @@ -312,21 +313,17 @@ public: } } - // PHINodes are never serialized directly in the BB they are. - if (isa(I)) - continue; + // After the new redesign of IRCanonicalization PHINodes shouldn't even + // reach this stage. + revng_assert(not isa(I)); - // Skip branching instructions. - // Branch instructions are never serialized directly, because it's only - // after building an AST and matching ifs, loops, switches and others that - // we really know what kind of C statement we want to emit for a given - // branch. + // Skip branching instructions, since they never generate assignments. if (isa(I) or isa(I)) continue; if (isa(&I) or (isa(&I) and not isCallToPure(I))) { // StoreInst and CallInst that are not pure always have side effects. - ToSerialize[&I].set(HasSideEffects); + Assignments[&I].set(Reasons::HasSideEffects); revng_log(MarkLog, "Instr HasSideEffects"); // Also, force calls to revng_stack_frame to behave like if they had @@ -334,7 +331,7 @@ public: if (auto *Call = dyn_cast(&I)) { auto *Callee = Call->getCalledFunction(); if (Callee and FunctionTags::AllocatesLocalVariable.isTagOf(Callee)) { - ToSerialize[&I].set(HasManyUses); + Assignments[&I].set(Reasons::HasManyUses); revng_log(MarkLog, "Instr HasManyUses"); } } @@ -343,30 +340,33 @@ public: switch (I.getNumUses()) { case 1: { - // Instructions with a single used do not necessarily need to be - // serialized. + // Instructions with a single used do not necessarily need to generate + // an assignment. } break; case 0: { - // Force unused instructions to be serialized. This is done to ease + // Force unused instructions to be assigned. This is done to ease // debugging, and could potentially be dropped in the future. - ToSerialize[&I].set(AlwaysSerialize); - revng_log(MarkLog, "Instr AlwaysSerialize"); + if (not I.getType()->isVoidTy()) { + Assignments[&I].set(Reasons::AlwaysAssign); + revng_log(MarkLog, "Instr AlwaysAssign"); + } } break; default: { - // Instructions with more than one use are always serialized. - ToSerialize[&I].set(HasManyUses); + // Instructions with more than one use are always assigned, so all the + // users can re-use the assigned variable. + Assignments[&I].set(Reasons::HasManyUses); revng_log(MarkLog, "Instr HasManyUses: " << I.getNumUses()); } break; } - auto SerIt = ToSerialize.find(&I); - if (SerIt != ToSerialize.end() - and (SerializationFlags::hasSideEffects(SerIt->second) - or SerIt->second.isSet(SerializationReason::AlwaysSerialize))) { - revng_log(MarkLog, "Serialize Pending"); - // We also have to serialize all the instructions that are still pending + auto SerIt = Assignments.find(&I); + if (SerIt != Assignments.end() + and (SerIt->second.hasSideEffects() + or SerIt->second.isSet(Reasons::AlwaysAssign))) { + revng_log(MarkLog, "Assign Pending"); + // We also have to assign all the instructions that are still pending // and have interfering side effects. for (auto PendingIt = Pending.begin(); PendingIt != Pending.end();) { auto *PendingInstr = PendingIt->first; @@ -374,7 +374,7 @@ public: "Pending: '" << PendingInstr << "': " << dumpToString(PendingInstr)); if (haveInterferingSideEffects(I, *PendingInstr)) { - ToSerialize[PendingInstr].set(HasInterferingSideEffects); + Assignments[PendingInstr].set(Reasons::HasInterferingSideEffects); revng_log(MarkLog, "HasInterferingSideEffects"); PendingIt = Pending.erase(PendingIt); @@ -393,4 +393,11 @@ public: } }; -} // namespace MarkAnalysis +AssignmentMap selectAssignments(llvm::Function &F) { + MarkAssignments::Analysis Mark(F); + Mark.initialize(); + Mark.run(); + return Mark.takeAssignments(); +} + +} // end namespace MarkAssignments diff --git a/lib/IRCanonicalization/MarkAssignments/MarkAssignments.h b/lib/IRCanonicalization/MarkAssignments/MarkAssignments.h new file mode 100644 index 000000000..cd66ee100 --- /dev/null +++ b/lib/IRCanonicalization/MarkAssignments/MarkAssignments.h @@ -0,0 +1,126 @@ +#pragma once + +// +// Copyright (c) rev.ng Labs Srl. See LICENSE.md for details. +// + +#include "revng/Support/Debug.h" + +extern Logger<> MarkLog; + +namespace MarkAssignments { + +namespace Reasons { + +/// Bitmask that represents reasons why an Instruction needs an assignment. +enum Values { + None = 0, + + // ==== Semantic Reasons ==== + // If an Instruction needs an assignment for one of this reasons, it means + // that not emitting an assignment for it would affect semantics because of + // side effects + + // The Instruction has side effects + HasSideEffects = 1 << 0, + // The Instruction does not have side effects per-se, but it might see the + // side effects of other Instructions that do. + // As an example, a LoadInst does not have side-effects when emitted in C, + // but it could read a memory location where a previous StoreInst has written. + HasInterferingSideEffects = 1 << 1, + + // ==== Cosmetic Reasons ==== + // If an Instruction needs an assignments for one of this reasons, it can + // safely be ignored, without affecting semantics. + + // Failsafe flag to forcibly assign an Instruction with no uses to a variable. + // This is useful for debug purposes, because it makes dead Instructions show + // up in decompiled code. + AlwaysAssign = 1 << 2, + // The Instruction has many uses, so we assign it to a variable, so that all + // uses can reference the variable instead of embedding the whole expression + // that represents the computation. + HasManyUses = 1 << 3, +}; + +} // end namespace Reasons + +} // end namespace MarkAssignments + +inline MarkAssignments::Reasons::Values +operator|(MarkAssignments::Reasons::Values A, + MarkAssignments::Reasons::Values B) { + using EnumT = decltype(A); + using IntT = std::underlying_type_t; + return static_cast(static_cast(A) | static_cast(B)); +} + +inline MarkAssignments::Reasons::Values +operator&(MarkAssignments::Reasons::Values A, + MarkAssignments::Reasons::Values B) { + using EnumT = decltype(A); + using IntT = std::underlying_type_t; + return static_cast(static_cast(A) & static_cast(B)); +} + +inline MarkAssignments::Reasons::Values +operator~(MarkAssignments::Reasons::Values A) { + using EnumT = decltype(A); + using IntT = std::underlying_type_t; + return static_cast(~static_cast(A)); +} + +namespace MarkAssignments { + +/// Wrapper class for MarkAssignments::Reasons::Values +class Flags { +private: + MarkAssignments::Reasons::Values TheFlags = Reasons::None; + +public: + Flags(MarkAssignments::Reasons::Values X) : TheFlags(X){}; + + Flags() = default; + ~Flags() = default; + + Flags(const Flags &) = default; + Flags &operator=(const Flags &) = default; + + Flags(Flags &&) = default; + Flags &operator=(Flags &&) = default; + + void set(Reasons::Values Flag) { + TheFlags = static_cast(TheFlags | Flag); + } + + bool isSet(Reasons::Values Flag) const { return TheFlags & Flag; } + + bool hasSideEffects() const { + // Either HasSideEffects or HasInterferingSideEffects imply side effects. + return isSet(Reasons::HasSideEffects) + or isSet(Reasons::HasInterferingSideEffects); + } + + Reasons::Values value() const { return TheFlags; } + + explicit operator bool() const { return TheFlags != Reasons::None; } +}; + +} // end namespace MarkAssignments + +namespace llvm { + +class Instruction; +class Function; + +} // namespace llvm + +namespace MarkAssignments { + +using AssignmentMap = std::map; + +/// Selects in F the Instructions that need an assignment when decompiling to C. +/// Retuns a map with the selected Instruction, and the reasons of selection. +AssignmentMap selectAssignments(llvm::Function &); + +} // end namespace MarkAssignments diff --git a/lib/IRCanonicalization/MarkForSerializationFlags.h b/lib/IRCanonicalization/MarkForSerializationFlags.h deleted file mode 100644 index 27469b92e..000000000 --- a/lib/IRCanonicalization/MarkForSerializationFlags.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -// -// Copyright (c) rev.ng Labs Srl. See LICENSE.md for details. -// - -namespace llvm { - -class Instruction; - -} // namespace llvm - -/// bitmask that represents reasons why an Instruction muse be serialized -enum SerializationReason { - None = 0, - AlwaysSerialize = 1 << 0, - HasSideEffects = 1 << 1, - HasInterferingSideEffects = 1 << 2, - HasManyUses = 1 << 3, -}; - -inline SerializationReason -operator|(SerializationReason a, SerializationReason b) { - using underlying_t = std::underlying_type_t; - return static_cast(static_cast(a) - | static_cast(b)); -} - -inline SerializationReason -operator&(SerializationReason a, SerializationReason b) { - using underlying_t = std::underlying_type_t; - return static_cast(static_cast(a) - & static_cast(b)); -} - -inline SerializationReason operator~(SerializationReason a) { - using underlying_t = std::underlying_type_t; - return static_cast(~static_cast(a)); -} - -/// Wrapper class for SerializationReason -class SerializationFlags { -public: - SerializationFlags() = default; - ~SerializationFlags() = default; - SerializationFlags(SerializationReason X) : Flags(X){}; - SerializationFlags(const SerializationFlags &) = default; - SerializationFlags(SerializationFlags &&) = default; - SerializationFlags &operator=(const SerializationFlags &) = default; - SerializationFlags &operator=(SerializationFlags &&) = default; - - void set(SerializationReason Flag) { - Flags = static_cast(Flags | Flag); - } - - bool isSet(SerializationReason Flag) const { return Flags & Flag; } - - SerializationReason value() const { return Flags; } - - /// Returns true if the Instruction associated with \F is affected by - /// side effects. - static bool hasSideEffects(const SerializationFlags &F) { - // HasSideEffects, and HasInterferingSideEffects imply side effects. - return F.Flags & (HasSideEffects | HasInterferingSideEffects); - } - - explicit operator bool() const { return Flags != None; } - -private: - SerializationReason Flags = None; -}; - -using SerializationMap = std::map; diff --git a/lib/InitModelTypes/InitModelTypes.cpp b/lib/InitModelTypes/InitModelTypes.cpp index 41cf5751a..176bde3af 100644 --- a/lib/InitModelTypes/InitModelTypes.cpp +++ b/lib/InitModelTypes/InitModelTypes.cpp @@ -262,14 +262,14 @@ static TypeVector getReturnTypes(const llvm::CallInst *Call, addPointerQualifier(PointedType, Model); ReturnTypes.push_back(PointedType); - } else if (FunctionTags::SerializationMarker.isTagOf(CalledFunc)) { + } else if (FunctionTags::AssignmentMarker.isTagOf(CalledFunc)) { const llvm::Value *Arg = Call->getArgOperand(0); // Structs are handled on their own if (Arg->getType()->isStructTy()) return {}; - // SerializationMarker is transparent + // AssignmentMarker is transparent auto It = TypeMap.find(Arg); if (It != TypeMap.end()) { ReturnTypes.push_back(It->second); diff --git a/lib/Support/FunctionTags.cpp b/lib/Support/FunctionTags.cpp index ed85e70a3..67f314af4 100644 --- a/lib/Support/FunctionTags.cpp +++ b/lib/Support/FunctionTags.cpp @@ -14,7 +14,7 @@ #include "revng-c/Support/Mangling.h" static constexpr const char *const ModelGEPName = "ModelGEP"; -static constexpr const char *const MarkerName = "SerializationMarker"; +static constexpr const char *const MarkerName = "AssignmentMarker"; namespace FunctionTags { Tag AllocatesLocalVariable("AllocatesLocalVariable"); @@ -22,7 +22,7 @@ Tag MallocLike("MallocLike"); Tag IsRef("IsRef"); Tag AddressOf("AddressOf"); Tag ModelGEP(ModelGEPName); -Tag SerializationMarker(MarkerName); +Tag AssignmentMarker(MarkerName); Tag OpaqueExtractValue("OpaqueExtractvalue"); } // namespace FunctionTags @@ -119,7 +119,7 @@ getModelGEP(llvm::Module &M, llvm::Type *RetTy, llvm::Type *BaseAddressTy) { return ModelGEPFunction; } -llvm::Function *getSerializationMarker(llvm::Module &M, llvm::Type *T) { +llvm::Function *getAssignmentMarker(llvm::Module &M, llvm::Type *T) { using namespace llvm; // Create a function, with T as return type, and 2 arguments. @@ -136,7 +136,7 @@ llvm::Function *getSerializationMarker(llvm::Module &M, llvm::Type *T) { MarkerF->addFnAttr(llvm::Attribute::NoUnwind); MarkerF->addFnAttr(llvm::Attribute::WillReturn); MarkerF->addFnAttr(llvm::Attribute::ReadNone); - FunctionTags::SerializationMarker.addTo(MarkerF); + FunctionTags::AssignmentMarker.addTo(MarkerF); FunctionTags::Marker.addTo(MarkerF); return MarkerF; diff --git a/tests/unit/MarkAssignmentsTest.cpp b/tests/unit/MarkAssignmentsTest.cpp new file mode 100644 index 000000000..0a817d8bd --- /dev/null +++ b/tests/unit/MarkAssignmentsTest.cpp @@ -0,0 +1,506 @@ +// +// Copyright rev.ng Labs Srl. See LICENSE.md for details. +// + +#include +#include +#include +#include +#include + +#define BOOST_TEST_MODULE MarkAssignmentsTest +bool init_unit_test(); +#include "boost/test/unit_test.hpp" + +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" + +#include "revng/Support/IRHelpers.h" +#include "revng/UnitTestHelpers/LLVMTestHelpers.h" + +#include "lib/IRCanonicalization/MarkAssignments/MarkAssignments.h" + +using namespace MarkAssignments; +using namespace MarkAssignments::Reasons; + +struct BBAssignmentFlags { + std::string BBName; + std::vector InstrFlags; +}; + +using ExpectedFlagsType = std::vector; + +static void runTestOnFunctionWithExpected(const char *Body, + const ExpectedFlagsType &Expected) { + MarkLog.enable(); + revng_log(MarkLog, "Running Test On Function: " << Body); + + llvm::LLVMContext C; + std::unique_ptr M = loadModule(C, Body); + revng_check(not llvm::verifyModule(*M, &llvm::dbgs())); + + // main is present and has a body + llvm::Function *F = M->getFunction("main"); + revng_check(nullptr != F); + revng_check(not F->empty()); + + auto Results = selectAssignments(*F); + + // We expect a vector of results for each BasicBlock in F, otherwise the + // Expected results are not well formed. + revng_check(F->size() == Expected.size()); + + for (const auto &[BB, BBFlags] : llvm::zip_first(*F, Expected)) { + const auto &[BBName, InstrFlagVec] = BBFlags; + // Name matches + revng_check(BB.hasName() and BB.getName().str() == BBName); + // Same number of instructions + revng_check(BB.size() == InstrFlagVec.size()); + for (const auto &[I, ExpectedFlag] : llvm::zip_first(BB, InstrFlagVec)) { + // Check that the expected flags match the computed one. + // Using Results[] may create a None flag if non was computed but that's + // not a problem. + revng_log(MarkLog, dumpToString(&I)); + revng_log(MarkLog, + "Results[&I].value() = " << Results[&I].value() + << " ExpectedFlag = " << ExpectedFlag); + revng_check(Results[&I].value() == ExpectedFlag); + } + } +} + +using InstrNameToLoadNamesMap = std::map>; + +BOOST_AUTO_TEST_CASE(AlwaysAssignNoUses) { + + const char *EmptyBody = R"LLVM( + %unused = inttoptr i64 4294967296 to i64* + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ "initial_block", { AlwaysAssign, None } }, + }; + + runTestOnFunctionWithExpected(EmptyBody, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(RetNone) { + + const char *Body = R"LLVM( + ret void + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ "initial_block", { None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(CallSideEffectsAndNoUses) { + + const char *Body = R"LLVM( + %bswapped = call i64 @llvm.bswap.i64(i64 1231231231) + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ "initial_block", + { HasSideEffects | AlwaysAssign, None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(StoreSideEffects) { + + const char *Body = R"LLVM( + store i64 123, i64 * inttoptr (i64 4294967296 to i64*) + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ "initial_block", { HasSideEffects, None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(ManyUses) { + + const char *Body = R"LLVM( + %pointer = inttoptr i64 4294967296 to i64* + %loaded = load i64, i64 * %pointer + %reloaded = load i64, i64 * %pointer + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ "initial_block", + { HasManyUses, AlwaysAssign, AlwaysAssign, None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(BranchNone) { + + const char *Body = R"LLVM( + br label %next + + next: + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ "initial_block", { None } }, + BBAssignmentFlags{ "next", { None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(Interfering) { + + const char *Body = R"LLVM( + %pointer = inttoptr i64 4294967296 to i64* + %loaded = load i64, i64 * %pointer + store i64 undef, i64 * %pointer + store i64 %loaded, i64 * %pointer + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ "initial_block", + { HasManyUses, + HasInterferingSideEffects, + HasSideEffects, + HasSideEffects, + None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(NonInterfering) { + + const char *Body = R"LLVM( + %pointer = inttoptr i64 4294967296 to i64* + %loaded = load i64, i64 * %pointer + store i64 %loaded, i64 * %pointer + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ "initial_block", + { HasManyUses, None, HasSideEffects, None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(ComplexNonInterfering) { + + const char *Body = R"LLVM( + %pointer = inttoptr i64 4294967296 to i64* + %loaded = load i64, i64 * %pointer + %sum1 = add i64 %loaded, 4 + %sum2 = add i64 %sum1, 4 + store i64 %sum2, i64 * %pointer + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ /*.BBName =*/"initial_block", + /*.InstrFlags =*/{ HasManyUses, + None, + None, + None, + HasSideEffects, + None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(DoubleIndirectionWithoutSideEffects) { + + const char *Body = R"LLVM( + %ptrtoptr = inttoptr i64 4294967296 to i64** + %ptr = load i64*, i64 **%ptrtoptr + %value = load i64, i64 *%ptr + %call = call i64 @llvm.bswap.i64(i64 %value) + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ /*.BBName =*/"initial_block", + /*.InstrFlags =*/{ None, + None, + None, + AlwaysAssign | HasSideEffects, + None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(DoubleIndirectionWithSideEffects) { + + const char *Body = R"LLVM( + %ptrtoptr = inttoptr i64 4294967296 to i64** + %ptr = load i64*, i64 **%ptrtoptr + %call = call i64 @llvm.bswap.i64(i64 1032143324) + %value = load i64, i64 *%ptr + %othercall = call i64 @llvm.bswap.i64(i64 %value) + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ /*.BBName =*/"initial_block", + /*.InstrFlags =*/{ None, + HasInterferingSideEffects, + AlwaysAssign | HasSideEffects, + None, + AlwaysAssign | HasSideEffects, + None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(ConditionalIsNotAssigned1) { + const char *Body = R"LLVM( + %pointer0 = inttoptr i64 4294967000 to i64* + %value0 = load i64, i64 *%pointer0 + %pointer1 = inttoptr i64 4294967000 to i64* + %value1 = load i64, i64 *%pointer1 + %pointer2 = inttoptr i64 4294967200 to i64* + %value2 = load i64, i64 *%pointer2 + %cmp = icmp ult i64 %value1, %value2 + br i1 %cmp, label %smaller, label %greater + + smaller: + br label %end + + greater: + br label %end + + end: + store i64 %value0, i64 * %pointer0 + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ /*.BBName =*/"initial_block", + /*.InstrFlags =*/ + { + HasManyUses, + None, + None, + None, + None, + None, + None, + None, + } }, + BBAssignmentFlags{ /*.BBName =*/"smaller", + /*.InstrFlags =*/{ None } }, + BBAssignmentFlags{ /*.BBName =*/"greater", + /*.InstrFlags =*/{ None } }, + BBAssignmentFlags{ /*.BBName =*/"end", + /*.InstrFlags =*/{ HasSideEffects, None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(ConditionalIsNotAssigned2) { + const char *Body = R"LLVM( + %pointer0 = inttoptr i64 4294967000 to i64* + %value0 = load i64, i64 *%pointer0 + %pointer1 = inttoptr i64 4294967000 to i64* + %value1 = load i64, i64 *%pointer1 + %pointer2 = inttoptr i64 4294967200 to i64* + %value2 = load i64, i64 *%pointer2 + %cmp = icmp ult i64 %value1, %value2 + br i1 %cmp, label %smaller, label %greater + + smaller: + br label %end + + greater: + store i64 %value0, i64 * %pointer0 + br label %end + + end: + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ /*.BBName =*/"initial_block", + /*.InstrFlags =*/ + { + HasManyUses, + None, + None, + None, + None, + None, + None, + None, + } }, + BBAssignmentFlags{ /*.BBName =*/"smaller", + /*.InstrFlags =*/{ None } }, + BBAssignmentFlags{ /*.BBName =*/"greater", + /*.InstrFlags =*/{ HasSideEffects, None } }, + BBAssignmentFlags{ /*.BBName =*/"end", + /*.InstrFlags =*/{ None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(AssignPendingInThenBranch) { + const char *Body = R"LLVM( + %pointer0 = inttoptr i64 4294967000 to i64* + %value0 = load i64, i64 *%pointer0 + %pointer1 = inttoptr i64 4294967000 to i64* + %value1 = load i64, i64 *%pointer1 + %pointer2 = inttoptr i64 4294967200 to i64* + %value2 = load i64, i64 *%pointer2 + %cmp = icmp ult i64 %value1, %value2 + br i1 %cmp, label %smaller, label %greater + + smaller: + store i64 undef, i64 *undef + br label %end + + greater: + br label %end + + end: + %constant = add i64 %value0, 0 + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ /*.BBName =*/"initial_block", + /*.InstrFlags =*/ + { + None, + HasInterferingSideEffects, + None, + None, + None, + None, + None, + None, + } }, + BBAssignmentFlags{ /*.BBName =*/"smaller", + /*.InstrFlags =*/{ HasSideEffects, None } }, + BBAssignmentFlags{ /*.BBName =*/"greater", + /*.InstrFlags =*/{ None } }, + BBAssignmentFlags{ /*.BBName =*/"end", + /*.InstrFlags =*/{ AlwaysAssign, None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(AssignPendingInElseBranch) { + const char *Body = R"LLVM( + %pointer0 = inttoptr i64 4294967000 to i64* + %value0 = load i64, i64 *%pointer0 + %pointer1 = inttoptr i64 4294967000 to i64* + %value1 = load i64, i64 *%pointer1 + %pointer2 = inttoptr i64 4294967200 to i64* + %value2 = load i64, i64 *%pointer2 + %cmp = icmp ult i64 %value1, %value2 + br i1 %cmp, label %smaller, label %greater + + smaller: + br label %end + + greater: + store i64 undef, i64 *undef + br label %end + + end: + %constant = add i64 %value0, 0 + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ /*.BBName =*/"initial_block", + /*.InstrFlags =*/ + { + None, + HasInterferingSideEffects, + None, + None, + None, + None, + None, + None, + } }, + BBAssignmentFlags{ /*.BBName =*/"smaller", + /*.InstrFlags =*/{ None } }, + BBAssignmentFlags{ /*.BBName =*/"greater", + /*.InstrFlags =*/{ HasSideEffects, None } }, + BBAssignmentFlags{ /*.BBName =*/"end", + /*.InstrFlags =*/{ AlwaysAssign, None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} + +BOOST_AUTO_TEST_CASE(Loop) { + const char *Body = R"LLVM( + %thealloca = alloca i64, align 8 + %initptr = inttoptr i64 4294967000 to i64* + %initval = load i64, i64 *%initptr + store i64 %initval, i64 *%thealloca + br label %head + + head: + %loopingvar = load i64, i64 *%thealloca + br label %tail + + tail: + %loopptr = inttoptr i64 4294967200 to i64* + %loopval = load i64, i64 *%loopptr + %cmp = icmp ult i64 undef, %loopingvar + store i64 %loopval, i64* %thealloca + br i1 %cmp, label %head, label %end + + end: + unreachable + )LLVM"; + + ExpectedFlagsType ExpectedFlags{ + BBAssignmentFlags{ /*.BBName =*/"initial_block", + /*.InstrFlags =*/ + { + HasManyUses, + None, + None, + HasSideEffects, + None, + } }, + BBAssignmentFlags{ /*.BBName =*/"head", + /*.InstrFlags =*/{ None, None } }, + BBAssignmentFlags{ /*.BBName =*/"tail", + /*.InstrFlags =*/{ None, + None, + HasInterferingSideEffects, + HasSideEffects, + None } }, + BBAssignmentFlags{ /*.BBName =*/"end", + /*.InstrFlags =*/{ None } }, + }; + + runTestOnFunctionWithExpected(Body, ExpectedFlags); +} diff --git a/tests/unit/MarkForSerializationTest.cpp b/tests/unit/MarkForSerializationTest.cpp deleted file mode 100644 index de56c82c2..000000000 --- a/tests/unit/MarkForSerializationTest.cpp +++ /dev/null @@ -1,545 +0,0 @@ -/// \file MarkForSerializationTest.cpp -/// Test for MarkForSerialization - -// -// Copyright rev.ng Labs Srl. See LICENSE.md for details. -// - -#include -#include -#include -#include -#include - -#define BOOST_TEST_MODULE MarkForSerializationTest -bool init_unit_test(); -#include "boost/test/unit_test.hpp" - -#include "llvm/IR/Function.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Verifier.h" - -#include "revng/Support/IRHelpers.h" -#include "revng/UnitTestHelpers/LLVMTestHelpers.h" - -#include "lib/IRCanonicalization/MarkAnalysis.h" - -struct BBSerializationFlags { - std::string BBName; - std::vector InstrFlags; -}; - -using ExpectedDuplicatesType = std::vector; -using ExpectedFlagsType = std::vector; - -static void -runTestOnFunctionWithExpected(const char *Body, - const ExpectedDuplicatesType &ExpectedDups, - const ExpectedFlagsType &Expected) { - MarkLog.enable(); - revng_log(MarkLog, "Running Test On Function: " << Body); - - llvm::LLVMContext C; - std::unique_ptr M = loadModule(C, Body); - revng_check(not llvm::verifyModule(*M, &llvm::dbgs())); - - // main is present and has a body - llvm::Function *F = M->getFunction("main"); - revng_check(nullptr != F); - revng_check(not F->empty()); - - SerializationMap Results; - MarkAnalysis::Analysis Mark(*F, Results); - Mark.initialize(); - Mark.run(); - - // We expect a vector of results for each BasicBlock in F, otherwise the - // Expected results are not well formed. - revng_check(F->size() == Expected.size()); - - for (const auto &[BB, BBFlags] : llvm::zip_first(*F, Expected)) { - const auto &[BBName, InstrFlagVec] = BBFlags; - // Name matches - revng_check(BB.hasName() and BB.getName().str() == BBName); - // Same number of instructions - revng_check(BB.size() == InstrFlagVec.size()); - for (const auto &[I, ExpectedFlag] : llvm::zip_first(BB, InstrFlagVec)) { - // Check that the expected flags match the computed one. - // Using Results[] may create a None flag if non was computed but that's - // not a problem. - revng_log(MarkLog, dumpToString(&I)); - revng_log(MarkLog, - "Results[&I].value() = " << Results[&I].value() - << " ExpectedFlag = " << ExpectedFlag); - revng_check(Results[&I].value() == ExpectedFlag); - } - } -} - -using InstrNameToLoadNamesMap = std::map>; - -BOOST_AUTO_TEST_CASE(AlwaysSerializeNoUses) { - - const char *EmptyBody = R"LLVM( - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ "initial_block", { AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(EmptyBody, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(Ret) { - - const char *Body = R"LLVM( - ret void - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ "initial_block", { AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(CallSideEffects) { - - const char *Body = R"LLVM( - %bswapped = call i64 @llvm.bswap.i64(i64 1231231231) - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ - "initial_block", { HasSideEffects | AlwaysSerialize, AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(StoreSideEffects) { - - const char *Body = R"LLVM( - store i64 123, i64 * inttoptr (i64 4294967296 to i64*) - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ - "initial_block", { HasSideEffects | AlwaysSerialize, AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(ManyUses) { - - const char *Body = R"LLVM( - %pointer = inttoptr i64 4294967296 to i64* - %loaded = load i64, i64 * %pointer - %reloaded = load i64, i64 * %pointer - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ - "initial_block", - { HasManyUses, AlwaysSerialize, AlwaysSerialize, AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(BranchNone) { - - const char *Body = R"LLVM( - br label %next - - next: - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1, 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ "initial_block", { None } }, - BBSerializationFlags{ "next", { AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(Interfering) { - - const char *Body = R"LLVM( - %pointer = inttoptr i64 4294967296 to i64* - %loaded = load i64, i64 * %pointer - store i64 undef, i64 * %pointer - store i64 %loaded, i64 * %pointer - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ "initial_block", - { HasManyUses, - HasInterferingSideEffects, - AlwaysSerialize | HasSideEffects, - AlwaysSerialize | HasSideEffects, - AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(NonInterfering) { - - const char *Body = R"LLVM( - %pointer = inttoptr i64 4294967296 to i64* - %loaded = load i64, i64 * %pointer - store i64 %loaded, i64 * %pointer - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ "initial_block", - { HasManyUses, - None, - HasSideEffects | AlwaysSerialize, - AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(ComplexNonInterfering) { - - const char *Body = R"LLVM( - %pointer = inttoptr i64 4294967296 to i64* - %loaded = load i64, i64 * %pointer - %sum1 = add i64 %loaded, 4 - %sum2 = add i64 %sum1, 4 - store i64 %sum2, i64 * %pointer - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ /*.BBName =*/"initial_block", - /*.InstrFlags =*/{ HasManyUses, - None, - None, - None, - AlwaysSerialize | HasSideEffects, - AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(DoubleIndirectionWithoutSideEffects) { - - const char *Body = R"LLVM( - %ptrtoptr = inttoptr i64 4294967296 to i64** - %ptr = load i64*, i64 **%ptrtoptr - %value = load i64, i64 *%ptr - %call = call i64 @llvm.bswap.i64(i64 %value) - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ /*.BBName =*/"initial_block", - /*.InstrFlags =*/{ None, - None, - None, - AlwaysSerialize | HasSideEffects, - AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(DoubleIndirectionWithSideEffects) { - - const char *Body = R"LLVM( - %ptrtoptr = inttoptr i64 4294967296 to i64** - %ptr = load i64*, i64 **%ptrtoptr - %call = call i64 @llvm.bswap.i64(i64 1032143324) - %value = load i64, i64 *%ptr - %othercall = call i64 @llvm.bswap.i64(i64 %value) - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ /*.BBName =*/"initial_block", - /*.InstrFlags =*/{ None, - HasInterferingSideEffects, - AlwaysSerialize | HasSideEffects, - None, - AlwaysSerialize | HasSideEffects, - AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(ConditionalIsNotSerialized1) { - const char *Body = R"LLVM( - %pointer0 = inttoptr i64 4294967000 to i64* - %value0 = load i64, i64 *%pointer0 - %pointer1 = inttoptr i64 4294967000 to i64* - %value1 = load i64, i64 *%pointer1 - %pointer2 = inttoptr i64 4294967200 to i64* - %value2 = load i64, i64 *%pointer2 - %cmp = icmp ult i64 %value1, %value2 - br i1 %cmp, label %smaller, label %greater - - smaller: - br label %end - - greater: - br label %end - - end: - store i64 %value0, i64 * %pointer0 - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1, 1, 1, 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ /*.BBName =*/"initial_block", - /*.InstrFlags =*/ - { - HasManyUses, - None, - None, - None, - None, - None, - None, - None, - } }, - BBSerializationFlags{ /*.BBName =*/"smaller", - /*.InstrFlags =*/{ None } }, - BBSerializationFlags{ /*.BBName =*/"greater", - /*.InstrFlags =*/{ None } }, - BBSerializationFlags{ - /*.BBName =*/"end", - /*.InstrFlags =*/{ HasSideEffects | AlwaysSerialize, AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(ConditionalIsNotSerialized2) { - const char *Body = R"LLVM( - %pointer0 = inttoptr i64 4294967000 to i64* - %value0 = load i64, i64 *%pointer0 - %pointer1 = inttoptr i64 4294967000 to i64* - %value1 = load i64, i64 *%pointer1 - %pointer2 = inttoptr i64 4294967200 to i64* - %value2 = load i64, i64 *%pointer2 - %cmp = icmp ult i64 %value1, %value2 - br i1 %cmp, label %smaller, label %greater - - smaller: - br label %end - - greater: - store i64 %value0, i64 * %pointer0 - br label %end - - end: - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1, 1, 1, 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ /*.BBName =*/"initial_block", - /*.InstrFlags =*/ - { - HasManyUses, - None, - None, - None, - None, - None, - None, - None, - } }, - BBSerializationFlags{ /*.BBName =*/"smaller", - /*.InstrFlags =*/{ None } }, - BBSerializationFlags{ - /*.BBName =*/"greater", - /*.InstrFlags =*/{ HasSideEffects | AlwaysSerialize, None } }, - BBSerializationFlags{ /*.BBName =*/"end", - /*.InstrFlags =*/{ AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(SerializePendingInThenBranch) { - const char *Body = R"LLVM( - %pointer0 = inttoptr i64 4294967000 to i64* - %value0 = load i64, i64 *%pointer0 - %pointer1 = inttoptr i64 4294967000 to i64* - %value1 = load i64, i64 *%pointer1 - %pointer2 = inttoptr i64 4294967200 to i64* - %value2 = load i64, i64 *%pointer2 - %cmp = icmp ult i64 %value1, %value2 - br i1 %cmp, label %smaller, label %greater - - smaller: - store i64 undef, i64 *undef - br label %end - - greater: - br label %end - - end: - %constant = add i64 %value0, 0 - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1, 1, 1, 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ /*.BBName =*/"initial_block", - /*.InstrFlags =*/ - { - None, - HasInterferingSideEffects, - None, - None, - None, - None, - None, - None, - } }, - BBSerializationFlags{ - /*.BBName =*/"smaller", - /*.InstrFlags =*/{ HasSideEffects | AlwaysSerialize, None } }, - BBSerializationFlags{ /*.BBName =*/"greater", - /*.InstrFlags =*/{ None } }, - BBSerializationFlags{ - /*.BBName =*/"end", - /*.InstrFlags =*/{ AlwaysSerialize, AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(SerializePendingInElseBranch) { - const char *Body = R"LLVM( - %pointer0 = inttoptr i64 4294967000 to i64* - %value0 = load i64, i64 *%pointer0 - %pointer1 = inttoptr i64 4294967000 to i64* - %value1 = load i64, i64 *%pointer1 - %pointer2 = inttoptr i64 4294967200 to i64* - %value2 = load i64, i64 *%pointer2 - %cmp = icmp ult i64 %value1, %value2 - br i1 %cmp, label %smaller, label %greater - - smaller: - br label %end - - greater: - store i64 undef, i64 *undef - br label %end - - end: - %constant = add i64 %value0, 0 - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1, 1, 1, 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ /*.BBName =*/"initial_block", - /*.InstrFlags =*/ - { - None, - HasInterferingSideEffects, - None, - None, - None, - None, - None, - None, - } }, - BBSerializationFlags{ /*.BBName =*/"smaller", - /*.InstrFlags =*/{ None } }, - BBSerializationFlags{ - /*.BBName =*/"greater", - /*.InstrFlags =*/{ HasSideEffects | AlwaysSerialize, None } }, - BBSerializationFlags{ - /*.BBName =*/"end", - /*.InstrFlags =*/{ AlwaysSerialize, AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} - -BOOST_AUTO_TEST_CASE(Loop) { - const char *Body = R"LLVM( - %initptr = inttoptr i64 4294967000 to i64* - %initval = load i64, i64 *%initptr - br label %head - - head: - %loopingvar = phi i64 [ %initval, %initial_block ], [ %loopval, %tail ] - br label %tail - - tail: - %loopptr = inttoptr i64 4294967200 to i64* - %loopval = load i64, i64 *%loopptr - %cmp = icmp ult i64 undef, %loopingvar - br i1 %cmp, label %head, label %end - - end: - unreachable - )LLVM"; - - ExpectedDuplicatesType ExpectedDups{ 1, 1, 1, 1 }; - - ExpectedFlagsType ExpectedFlags{ - BBSerializationFlags{ /*.BBName =*/"initial_block", - /*.InstrFlags =*/ - { - None, - None, - None, - } }, - BBSerializationFlags{ /*.BBName =*/"head", - /*.InstrFlags =*/{ None, None } }, - BBSerializationFlags{ /*.BBName =*/"tail", - /*.InstrFlags =*/{ None, None, None, None } }, - BBSerializationFlags{ /*.BBName =*/"end", - /*.InstrFlags =*/{ AlwaysSerialize } }, - }; - - runTestOnFunctionWithExpected(Body, ExpectedDups, ExpectedFlags); -} diff --git a/tests/unit/UnitTests.cmake b/tests/unit/UnitTests.cmake index 7fd46560a..3d4c19ff8 100644 --- a/tests/unit/UnitTests.cmake +++ b/tests/unit/UnitTests.cmake @@ -82,26 +82,23 @@ target_link_libraries( add_test(NAME dla_step_manager COMMAND ./dla_step_manager) # -# MarkForSerializationTest +# MarkAssignmentsTest # -revng_add_test_executable(MarkForSerializationTest - "${SRC}/MarkForSerializationTest.cpp") -target_compile_definitions(MarkForSerializationTest - PRIVATE "BOOST_TEST_DYN_LINK=1") -target_include_directories( - MarkForSerializationTest PRIVATE "${CMAKE_SOURCE_DIR}" - "${Boost_INCLUDE_DIRS}") +revng_add_test_executable(MarkAssignmentsTest "${SRC}/MarkAssignmentsTest.cpp") +target_compile_definitions(MarkAssignmentsTest PRIVATE "BOOST_TEST_DYN_LINK=1") +target_include_directories(MarkAssignmentsTest PRIVATE "${CMAKE_SOURCE_DIR}" + "${Boost_INCLUDE_DIRS}") target_link_libraries( - MarkForSerializationTest - revngcIRCanonicalization + MarkAssignmentsTest + revngcMarkAssignments revngcSupport revng::revngModel revng::revngSupport revng::revngUnitTestHelpers Boost::unit_test_framework ${LLVM_LIBRARIES}) -add_test(NAME MarkForSerializationTest COMMAND ./MarkForSerializationTest) +add_test(NAME MarkAssignmentsTest COMMAND ./MarkAssignmentsTest) # # DLASteps