diff --git a/llvm/lib/Target/EVM/EVMControlFlowGraph.h b/llvm/lib/Target/EVM/EVMControlFlowGraph.h index 86b7e2720560..dacff6f8f87e 100644 --- a/llvm/lib/Target/EVM/EVMControlFlowGraph.h +++ b/llvm/lib/Target/EVM/EVMControlFlowGraph.h @@ -167,7 +167,7 @@ inline bool canBeFreelyGenerated(StackSlot const &Slot) { /// Control flow graph consisting of 'CFG::BasicBlock`s' connected by control /// flow. struct CFG { - explicit CFG() {} + explicit CFG(const MachineFunction &MF) : MF(MF) {} CFG(CFG const &) = delete; CFG(CFG &&) = delete; CFG &operator=(CFG const &) = delete; @@ -204,7 +204,6 @@ struct CFG { std::variant Operation; }; - struct FunctionInfo; /// A basic control flow block containing 'Operation`s' acting on the stack. /// Maintains a list of entry blocks and a typed exit. struct BasicBlock { @@ -230,7 +229,6 @@ struct CFG { struct FunctionReturn { Stack RetValues; - CFG::FunctionInfo *Info = nullptr; }; struct Terminated {}; @@ -254,21 +252,13 @@ struct CFG { Exit = InvalidExit{}; }; - struct FunctionInfo { - MachineFunction *MF = nullptr; - BasicBlock *Entry = nullptr; - std::vector Parameters; - std::vector Exits; - bool CanContinue = true; - }; - - FunctionInfo FuncInfo; - /// Container for blocks for explicit ownership. std::list Blocks; DenseMap MachineBBToBB; + std::vector Parameters; + const MachineFunction &MF; - BasicBlock &getBlock(const MachineBasicBlock *MBB) { + BasicBlock &getBlock(const MachineBasicBlock *MBB) const { auto It = MachineBBToBB.find(MBB); assert(It != MachineBBToBB.end()); return *It->second; diff --git a/llvm/lib/Target/EVM/EVMControlFlowGraphBuilder.cpp b/llvm/lib/Target/EVM/EVMControlFlowGraphBuilder.cpp index cb072b24de5c..323a55051390 100644 --- a/llvm/lib/Target/EVM/EVMControlFlowGraphBuilder.cpp +++ b/llvm/lib/Target/EVM/EVMControlFlowGraphBuilder.cpp @@ -30,13 +30,12 @@ using namespace llvm; /// Marks each block that needs to maintain a clean stack. That is each block /// that has an outgoing path to a function return. -static void markNeedsCleanStack(CFG &Cfg) { - for (CFG::BasicBlock *Exit : Cfg.FuncInfo.Exits) +static void markNeedsCleanStack(std::vector &Exits) { + for (CFG::BasicBlock *Exit : Exits) EVMUtils::BreadthFirstSearch{{Exit}}.run( [&](CFG::BasicBlock *Block, auto AddChild) { Block->NeedsCleanStack = true; - // TODO: it seems this is not needed, as the return block has - // no childs. + // Traverse over predecessors to mark them as well. for (CFG::BasicBlock *Entry : Block->Entries) AddChild(Entry); }); @@ -45,8 +44,7 @@ static void markNeedsCleanStack(CFG &Cfg) { /// Marks each cut-vertex in the CFG, i.e. each block that begins a disconnected /// sub-graph of the CFG. Entering such a block means that control flow will /// never return to a previously visited block. -static void markStartsOfSubGraphs(CFG &Cfg) { - CFG::BasicBlock *Entry = Cfg.FuncInfo.Entry; +static void markStartsOfSubGraphs(CFG::BasicBlock *Entry) { /** * Detect bridges following Algorithm 1 in * https://arxiv.org/pdf/2108.07346.pdf and mark the bridge targets as starts @@ -112,27 +110,19 @@ static void markStartsOfSubGraphs(CFG &Cfg) { std::unique_ptr ControlFlowGraphBuilder::build(MachineFunction &MF, const LiveIntervals &LIS, MachineLoopInfo *MLI) { - auto Result = std::make_unique(); + auto Result = std::make_unique(MF); ControlFlowGraphBuilder Builder(*Result, LIS, MLI); for (MachineBasicBlock &MBB : MF) Result->createBlock(&MBB); - Result->FuncInfo.MF = &MF; - Result->FuncInfo.Entry = &Result->getBlock(&MF.front()); - const Function &F = MF.getFunction(); - if (F.hasFnAttribute(Attribute::NoReturn)) - Result->FuncInfo.CanContinue = false; - // Handle function parameters auto *MFI = MF.getInfo(); - Result->FuncInfo.Parameters = - std::vector(MFI->getNumParams(), JunkSlot{}); + Result->Parameters = std::vector(MFI->getNumParams(), JunkSlot{}); for (const MachineInstr &MI : MF.front()) { if (MI.getOpcode() == EVM::ARGUMENT) { int64_t ArgIdx = MI.getOperand(1).getImm(); - Result->FuncInfo.Parameters[ArgIdx] = - VariableSlot{MI.getOperand(0).getReg()}; + Result->Parameters[ArgIdx] = VariableSlot{MI.getOperand(0).getReg()}; } } @@ -142,12 +132,12 @@ std::unique_ptr ControlFlowGraphBuilder::build(MachineFunction &MF, for (MachineBasicBlock &MBB : MF) Builder.handleBasicBlockSuccessors(MBB); - markStartsOfSubGraphs(*Result); - markNeedsCleanStack(*Result); + markStartsOfSubGraphs(&Result->getBlock(&MF.front())); + markNeedsCleanStack(Builder.Exits); LLVM_DEBUG({ dbgs() << "************* CFG *************\n"; - ControlFlowGraphPrinter P(dbgs()); + ControlFlowGraphPrinter P(dbgs(), MF); P(*Result); }); @@ -328,7 +318,7 @@ void ControlFlowGraphBuilder::handleFunctionCall(const MachineInstr &MI) { } void ControlFlowGraphBuilder::handleReturn(const MachineInstr &MI) { - Cfg.FuncInfo.Exits.emplace_back(CurrentBlock); + Exits.emplace_back(CurrentBlock); Stack Input; collectInstrOperands(MI, &Input, nullptr); // We need to reverse input operands to restore original ordering, @@ -336,8 +326,7 @@ void ControlFlowGraphBuilder::handleReturn(const MachineInstr &MI) { // Calling convention: return values are passed in stack such that the // last one specified in the RET instruction is passed on the stack TOP. std::reverse(Input.begin(), Input.end()); - CurrentBlock->Exit = - CFG::BasicBlock::FunctionReturn{std::move(Input), &Cfg.FuncInfo}; + CurrentBlock->Exit = CFG::BasicBlock::FunctionReturn{std::move(Input)}; } static std::pair diff --git a/llvm/lib/Target/EVM/EVMControlFlowGraphBuilder.h b/llvm/lib/Target/EVM/EVMControlFlowGraphBuilder.h index efe9b97313d0..df56def2ccee 100644 --- a/llvm/lib/Target/EVM/EVMControlFlowGraphBuilder.h +++ b/llvm/lib/Target/EVM/EVMControlFlowGraphBuilder.h @@ -49,6 +49,7 @@ class ControlFlowGraphBuilder { const LiveIntervals &LIS; MachineLoopInfo *MLI = nullptr; CFG::BasicBlock *CurrentBlock = nullptr; + std::vector Exits; }; } // namespace llvm diff --git a/llvm/lib/Target/EVM/EVMOptimizedCodeTransform.cpp b/llvm/lib/Target/EVM/EVMOptimizedCodeTransform.cpp index 3025675257f2..9b79e094b971 100644 --- a/llvm/lib/Target/EVM/EVMOptimizedCodeTransform.cpp +++ b/llvm/lib/Target/EVM/EVMOptimizedCodeTransform.cpp @@ -121,7 +121,8 @@ EVMOptimizedCodeTransform::EVMOptimizedCodeTransform(EVMAssembly &Assembly, CFG const &Cfg, StackLayout const &Layout, MachineFunction &MF) - : Assembly(Assembly), Layout(Layout), FuncInfo(&Cfg.FuncInfo), MF(MF) {} + : Assembly(Assembly), Layout(Layout), MF(MF), + EntryBB(Cfg.getBlock(&MF.front())), Parameters(Cfg.Parameters) {} bool EVMOptimizedCodeTransform::AreLayoutsCompatible(Stack const &SourceStack, Stack const &TargetStack) { @@ -185,7 +186,7 @@ void EVMOptimizedCodeTransform::createStackLayout(Stack TargetStack) { " slots in " + stackToString(CurrentStack)) .str(); - report_fatal_error(FuncInfo->MF->getName() + Twine(": ") + Msg); + report_fatal_error(MF.getName() + Twine(": ") + Msg); } }, // Push or dup callback. @@ -310,7 +311,7 @@ void EVMOptimizedCodeTransform::createOperationEntryLayout( void EVMOptimizedCodeTransform::operator()(const CFG::BasicBlock &Block) { // Current location for the entry BB was set up in operator()(). - if (&Block != FuncInfo->Entry) + if (&Block != &EntryBB) Assembly.setCurrentLocation(Block.MBB); // Assert that this is the first visit of the block and mark as generated. @@ -322,7 +323,7 @@ void EVMOptimizedCodeTransform::operator()(const CFG::BasicBlock &Block) { // Assert that the stack is valid for entering the block. The entry layout // of the function entry block should is fully determined by the first // instruction, so we can ignore 'BlockInfo.entryLayout'. - if (&Block != FuncInfo->Entry) { + if (&Block != &EntryBB) { assert(AreLayoutsCompatible(CurrentStack, BlockInfo.entryLayout)); // Might set some slots to junk, if not required by the block. CurrentStack = BlockInfo.entryLayout; @@ -431,14 +432,13 @@ void EVMOptimizedCodeTransform::operator()(const CFG::BasicBlock &Block) { (*this)(*CondJump.NonZero); }, [&](CFG::BasicBlock::FunctionReturn const &FuncReturn) { - assert(FuncInfo->CanContinue); + assert(!MF.getFunction().hasFnAttribute(Attribute::NoReturn)); // Construct the function return layout, which is fully determined // by the function signature. Stack ExitStack = FuncReturn.RetValues; - ExitStack.emplace_back( - FunctionReturnLabelSlot{FuncReturn.Info->MF}); + ExitStack.emplace_back(FunctionReturnLabelSlot{&MF}); // Create the function return layout and jump. createStackLayout(ExitStack); @@ -471,24 +471,24 @@ void EVMOptimizedCodeTransform::operator()(const CFG::BasicBlock &Block) { void EVMOptimizedCodeTransform::operator()() { assert(CurrentStack.empty() && Assembly.getStackHeight() == 0); - Assembly.setCurrentLocation(FuncInfo->Entry->MBB); + Assembly.setCurrentLocation(EntryBB.MBB); - assert(!BlockLabels.count(FuncInfo->Entry)); + assert(!BlockLabels.count(&EntryBB)); // Create function entry layout in CurrentStack. - if (FuncInfo->CanContinue) - CurrentStack.emplace_back(FunctionReturnLabelSlot{FuncInfo->MF}); + if (!MF.getFunction().hasFnAttribute(Attribute::NoReturn)) + CurrentStack.emplace_back(FunctionReturnLabelSlot{&MF}); // Calling convention: input arguments are passed in stack such that the // first one specified in the function declaration is passed on the stack TOP. - for (auto const &Param : reverse(FuncInfo->Parameters)) + for (auto const &Param : reverse(Parameters)) CurrentStack.emplace_back(Param); Assembly.setStackHeight(static_cast(CurrentStack.size())); Assembly.appendLabel(); // Visit the function entry block. - (*this)(*FuncInfo->Entry); + (*this)(EntryBB); Assembly.finalize(); } diff --git a/llvm/lib/Target/EVM/EVMOptimizedCodeTransform.h b/llvm/lib/Target/EVM/EVMOptimizedCodeTransform.h index 2382c68a80b3..aa4f08ee74af 100644 --- a/llvm/lib/Target/EVM/EVMOptimizedCodeTransform.h +++ b/llvm/lib/Target/EVM/EVMOptimizedCodeTransform.h @@ -74,8 +74,9 @@ class EVMOptimizedCodeTransform { EVMAssembly &Assembly; StackLayout const &Layout; - CFG::FunctionInfo const *FuncInfo = nullptr; - MachineFunction &MF; + const MachineFunction &MF; + const CFG::BasicBlock &EntryBB; + const std::vector &Parameters; Stack CurrentStack; DenseMap CallToReturnMCSymbol; diff --git a/llvm/lib/Target/EVM/EVMStackDebug.cpp b/llvm/lib/Target/EVM/EVMStackDebug.cpp index e3792e0151a5..e9bb24e6bb38 100644 --- a/llvm/lib/Target/EVM/EVMStackDebug.cpp +++ b/llvm/lib/Target/EVM/EVMStackDebug.cpp @@ -83,19 +83,22 @@ std::string llvm::stackSlotToString(const StackSlot &Slot) { #ifndef NDEBUG void ControlFlowGraphPrinter::operator()(const CFG &Cfg) { - (*this)(Cfg.FuncInfo); + (*this)(); for (const auto &Block : Cfg.Blocks) printBlock(Block); } -void ControlFlowGraphPrinter::operator()(CFG::FunctionInfo const &Info) { - OS << "Function: " << Info.MF->getName() << '\n'; - OS << "Entry block: " << getBlockId(*Info.Entry) << ";\n"; +void ControlFlowGraphPrinter::operator()() { + OS << "Function: " << MF.getName() << '\n'; + OS << "Entry block: " << getBlockId(MF.front()) << ";\n"; } std::string ControlFlowGraphPrinter::getBlockId(CFG::BasicBlock const &Block) { - return std::to_string(Block.MBB->getNumber()) + "." + - std::string(Block.MBB->getName()); + return getBlockId(*Block.MBB); +} + +std::string ControlFlowGraphPrinter::getBlockId(const MachineBasicBlock &MBB) { + return std::to_string(MBB.getNumber()) + "." + std::string(MBB.getName()); } void ControlFlowGraphPrinter::printBlock(CFG::BasicBlock const &Block) { @@ -159,8 +162,8 @@ void ControlFlowGraphPrinter::printBlock(CFG::BasicBlock const &Block) { }, [&](const CFG::BasicBlock::FunctionReturn &Return) { OS << "Block" << getBlockId(Block) - << "Exit [label=\"FunctionReturn[" - << Return.Info->MF->getName() << "]\"];\n"; + << "Exit [label=\"FunctionReturn[" << MF.getName() + << "]\"];\n"; OS << "Return values: " << stackToString(Return.RetValues); }, [&](const CFG::BasicBlock::Terminated &) { @@ -191,9 +194,10 @@ void StackLayoutPrinter::operator()(CFG::BasicBlock const &Block, } } -void StackLayoutPrinter::operator()(CFG::FunctionInfo const &Info) { - OS << "Function: " << Info.MF->getName() << "("; - for (const StackSlot &ParamSlot : Info.Parameters) { +void StackLayoutPrinter::operator()(CFG::BasicBlock const &EntryBB, + const std::vector &Parameters) { + OS << "Function: " << MF.getName() << "("; + for (const StackSlot &ParamSlot : Parameters) { if (const auto *Slot = std::get_if(&ParamSlot)) OS << printReg(Slot->VirtualReg, nullptr, 0, nullptr) << ' '; else if (std::holds_alternative(ParamSlot)) @@ -202,8 +206,9 @@ void StackLayoutPrinter::operator()(CFG::FunctionInfo const &Info) { llvm_unreachable("Unexpected stack slot"); } OS << ");\n"; - OS << "FunctionEntry " << " -> Block" << getBlockId(*Info.Entry) << ";\n"; - (*this)(*Info.Entry, false); + OS << "FunctionEntry " + << " -> Block" << getBlockId(EntryBB) << ";\n"; + (*this)(EntryBB, false); } void StackLayoutPrinter::printBlock(CFG::BasicBlock const &Block) { @@ -283,8 +288,8 @@ void StackLayoutPrinter::printBlock(CFG::BasicBlock const &Block) { }, [&](CFG::BasicBlock::FunctionReturn const &Return) { OS << "Block" << getBlockId(Block) - << "Exit [label=\"FunctionReturn[" - << Return.Info->MF->getName() << "]\"];\n"; + << "Exit [label=\"FunctionReturn[" << MF.getName() + << "]\"];\n"; }, [&](CFG::BasicBlock::Terminated const &) { OS << "Block" << getBlockId(Block) diff --git a/llvm/lib/Target/EVM/EVMStackDebug.h b/llvm/lib/Target/EVM/EVMStackDebug.h index efe39f20f084..7978fe79c729 100644 --- a/llvm/lib/Target/EVM/EVMStackDebug.h +++ b/llvm/lib/Target/EVM/EVMStackDebug.h @@ -33,24 +33,29 @@ std::string stackToString(Stack const &S); #ifndef NDEBUG class ControlFlowGraphPrinter { public: - ControlFlowGraphPrinter(raw_ostream &OS) : OS(OS) {} + ControlFlowGraphPrinter(raw_ostream &OS, const MachineFunction &MF) + : OS(OS), MF(MF) {} void operator()(const CFG &Cfg); private: - void operator()(const CFG::FunctionInfo &Info); + void operator()(); std::string getBlockId(const CFG::BasicBlock &Block); + std::string getBlockId(const MachineBasicBlock &MBB); void printBlock(const CFG::BasicBlock &Block); raw_ostream &OS; + const MachineFunction &MF; }; class StackLayoutPrinter { public: - StackLayoutPrinter(raw_ostream &OS, const StackLayout &StackLayout) - : OS(OS), Layout(StackLayout) {} + StackLayoutPrinter(raw_ostream &OS, const StackLayout &StackLayout, + const MachineFunction &MF) + : OS(OS), Layout(StackLayout), MF(MF) {} void operator()(CFG::BasicBlock const &Block, bool IsMainEntry = true); - void operator()(CFG::FunctionInfo const &Info); + void operator()(CFG::BasicBlock const &EntryBB, + const std::vector &Parameters); private: void printBlock(CFG::BasicBlock const &Block); @@ -58,6 +63,7 @@ class StackLayoutPrinter { raw_ostream &OS; const StackLayout &Layout; + const MachineFunction &MF; std::map BlockIds; size_t BlockCount = 0; std::list BlocksToPrint; diff --git a/llvm/lib/Target/EVM/EVMStackLayoutGenerator.cpp b/llvm/lib/Target/EVM/EVMStackLayoutGenerator.cpp index 7e739a746379..d596c61e44a2 100644 --- a/llvm/lib/Target/EVM/EVMStackLayoutGenerator.cpp +++ b/llvm/lib/Target/EVM/EVMStackLayoutGenerator.cpp @@ -33,21 +33,24 @@ using namespace llvm; StackLayout StackLayoutGenerator::run(const CFG &Cfg) { StackLayout Layout; - StackLayoutGenerator LayoutGenerator{Layout, &Cfg.FuncInfo}; - LayoutGenerator.processEntryPoint(*Cfg.FuncInfo.Entry, &Cfg.FuncInfo); + StackLayoutGenerator LayoutGenerator{Layout, Cfg.MF, Cfg.Parameters}; + + auto &EntryBB = Cfg.getBlock(&Cfg.MF.front()); + LayoutGenerator.processEntryPoint(EntryBB); LLVM_DEBUG({ dbgs() << "************* Stack Layout *************\n"; - StackLayoutPrinter P(dbgs(), Layout); - P(Cfg.FuncInfo); + StackLayoutPrinter P(dbgs(), Layout, Cfg.MF); + P(EntryBB, Cfg.Parameters); }); return Layout; } StackLayoutGenerator::StackLayoutGenerator( - StackLayout &Layout, CFG::FunctionInfo const *FunctionInfo) - : Layout(Layout), CurrentFunctionInfo(FunctionInfo) {} + StackLayout &Layout, const MachineFunction &MF, + const std::vector &Parameters) + : Layout(Layout), Parameters(Parameters), MF(MF) {} namespace { @@ -317,8 +320,7 @@ Stack StackLayoutGenerator::propagateStackThroughBlock( return CurrentStack; } -void StackLayoutGenerator::processEntryPoint( - CFG::BasicBlock const &Entry, CFG::FunctionInfo const *FunctionInfo) { +void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const &Entry) { std::list ToVisit{&Entry}; std::set Visited; @@ -392,7 +394,7 @@ void StackLayoutGenerator::processEntryPoint( } stitchConditionalJumps(Entry); - fillInJunk(Entry, FunctionInfo); + fillInJunk(Entry); } std::optional StackLayoutGenerator::getExitLayoutOrStageDependencies( @@ -452,10 +454,8 @@ std::optional StackLayoutGenerator::getExitLayoutOrStageDependencies( -> std::optional { // A function return needs the return variables and the function // return label slot on stack. - assert(FunctionReturn.Info); Stack ReturnStack = FunctionReturn.RetValues; - ReturnStack.emplace_back( - FunctionReturnLabelSlot{FunctionReturn.Info->MF}); + ReturnStack.emplace_back(FunctionReturnLabelSlot{&MF}); return ReturnStack; }, [&](CFG::BasicBlock::Terminated const &) -> std::optional { @@ -730,8 +730,7 @@ size_t llvm::EvaluateStackTransform(Stack Source, Stack const &Target) { return OpGas; } -void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const &Block, - CFG::FunctionInfo const *FunctionInfo) { +void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const &Block) { /// Recursively adds junk to the subgraph starting on \p Entry. /// Since it is only called on cut-vertices, the full subgraph retains proper /// stack balance. @@ -789,9 +788,10 @@ void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const &Block, return BestNumJunk; }; - if (FunctionInfo && !FunctionInfo->CanContinue && Block.AllowsJunk()) { + if (MF.getFunction().hasFnAttribute(Attribute::NoReturn) && + Block.AllowsJunk()) { Stack Params; - for (const auto &Param : FunctionInfo->Parameters) + for (const auto &Param : Parameters) Params.emplace_back(Param); std::reverse(Params.begin(), Params.end()); size_t BestNumJunk = diff --git a/llvm/lib/Target/EVM/EVMStackLayoutGenerator.h b/llvm/lib/Target/EVM/EVMStackLayoutGenerator.h index ff675b88f30d..aed7a5b3d7ba 100644 --- a/llvm/lib/Target/EVM/EVMStackLayoutGenerator.h +++ b/llvm/lib/Target/EVM/EVMStackLayoutGenerator.h @@ -57,8 +57,8 @@ class StackLayoutGenerator { static std::vector reportStackTooDeep(CFG const &Cfg); private: - StackLayoutGenerator(StackLayout &Context, - CFG::FunctionInfo const *FunctionInfo); + StackLayoutGenerator(StackLayout &Layout, const MachineFunction &MF, + const std::vector &Parameters); /// Returns the optimal entry stack layout, s.t. \p Operation can be applied /// to it and the result can be transformed to \p ExitStack with minimal stack @@ -77,8 +77,7 @@ class StackLayoutGenerator { /// Main algorithm walking the graph from entry to exit and propagating back /// the stack layouts to the entries. Iteratively reruns itself along /// backwards jumps until the layout is stabilized. - void processEntryPoint(CFG::BasicBlock const &Entry, - CFG::FunctionInfo const *FunctionInfo = nullptr); + void processEntryPoint(CFG::BasicBlock const &Entry); /// Returns the best known exit layout of \p Block, if all dependencies are /// already \p Visited. If not, adds the dependencies to \p DependencyList and @@ -118,11 +117,11 @@ class StackLayoutGenerator { /// Fills in junk when entering branches that do not need a clean stack in /// case the result is cheaper. - void fillInJunk(CFG::BasicBlock const &Block, - CFG::FunctionInfo const *FunctionInfo = nullptr); + void fillInJunk(CFG::BasicBlock const &Block); StackLayout &Layout; - CFG::FunctionInfo const *CurrentFunctionInfo = nullptr; + const std::vector &Parameters; + const MachineFunction &MF; }; } // end namespace llvm