From 7eeb672ca7771739604e68bb5f1aaf64f27775cf Mon Sep 17 00:00:00 2001 From: Dmitry Borisenkov Date: Thu, 16 Jan 2025 21:59:05 +0200 Subject: [PATCH] [EVM] Refactor StackSlot std::variant -> LLVM style RTTI --- llvm/lib/Target/EVM/EVMStackDebug.cpp | 74 +---- llvm/lib/Target/EVM/EVMStackDebug.h | 6 - .../Target/EVM/EVMStackLayoutGenerator.cpp | 134 ++++----- llvm/lib/Target/EVM/EVMStackModel.cpp | 90 ++++-- llvm/lib/Target/EVM/EVMStackModel.h | 282 ++++++++++++------ llvm/lib/Target/EVM/EVMStackShuffler.h | 86 +++--- .../lib/Target/EVM/EVMStackifyCodeEmitter.cpp | 117 +++----- llvm/unittests/Target/EVM/CMakeLists.txt | 1 + llvm/unittests/Target/EVM/StackModel.cpp | 157 ++++++++++ llvm/unittests/Target/EVM/StackShuffler.cpp | 90 +++--- 10 files changed, 612 insertions(+), 425 deletions(-) create mode 100644 llvm/unittests/Target/EVM/StackModel.cpp diff --git a/llvm/lib/Target/EVM/EVMStackDebug.cpp b/llvm/lib/Target/EVM/EVMStackDebug.cpp index 6e13e145fb52..98702de4fcee 100644 --- a/llvm/lib/Target/EVM/EVMStackDebug.cpp +++ b/llvm/lib/Target/EVM/EVMStackDebug.cpp @@ -24,82 +24,28 @@ template struct Overload : Ts... { }; template Overload(Ts...) -> Overload; -static std::string getInstName(const MachineInstr *MI) { - const MachineFunction *MF = MI->getParent()->getParent(); - const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); - return TII->getName(MI->getOpcode()).str(); -} - -const Function *llvm::getCalledFunction(const MachineInstr &MI) { - for (const MachineOperand &MO : MI.operands()) { - if (!MO.isGlobal()) - continue; - const Function *Func = dyn_cast(MO.getGlobal()); - if (Func != nullptr) - return Func; - } - return nullptr; -} - std::string llvm::stackToString(const Stack &S) { std::string Result("[ "); - for (auto const &Slot : S) - Result += stackSlotToString(Slot) + ' '; + for (const auto *Slot : S) + Result += Slot->toString() + ' '; Result += ']'; return Result; } -std::string llvm::stackSlotToString(const StackSlot &Slot) { - return std::visit( - Overload{ - [](const FunctionCallReturnLabelSlot &Ret) -> std::string { - return "RET[" + - std::string(getCalledFunction(*Ret.Call)->getName()) + "]"; - }, - [](const FunctionReturnLabelSlot &) -> std::string { return "RET"; }, - [](const VariableSlot &Var) -> std::string { - SmallString<64> S; - raw_svector_ostream OS(S); - OS << printReg(Var.VirtualReg, nullptr, 0, nullptr); - return std::string(S); - ; - }, - [](const LiteralSlot &Literal) -> std::string { - SmallString<64> S; - Literal.Value.toStringSigned(S); - return std::string(S); - }, - [](const SymbolSlot &Symbol) -> std::string { - return getInstName(Symbol.MI) + ":" + - std::string(Symbol.Symbol->getName()); - }, - [](const TemporarySlot &Tmp) -> std::string { - SmallString<128> S; - raw_svector_ostream OS(S); - OS << "TMP[" << getInstName(Tmp.MI) << ", "; - OS << std::to_string(Tmp.Index) + "]"; - return std::string(S); - }, - [](const JunkSlot &Junk) -> std::string { return "JUNK"; }}, - Slot); - ; -} - #ifndef NDEBUG void StackLayoutPrinter::operator()() { OS << "Function: " << MF.getName() << "("; - for (const StackSlot &ParamSlot : StackModel.getFunctionParameters()) { - if (const auto *Slot = std::get_if(&ParamSlot)) - OS << printReg(Slot->VirtualReg, nullptr, 0, nullptr) << ' '; - else if (std::holds_alternative(ParamSlot)) + for (const StackSlot *ParamSlot : StackModel.getFunctionParameters()) { + if (const auto *Slot = dyn_cast(ParamSlot)) + OS << printReg(Slot->getReg(), nullptr, 0, nullptr) << ' '; + else if (isa(ParamSlot)) OS << "[unused param] "; else llvm_unreachable("Unexpected stack slot"); } OS << ");\n"; - OS << "FunctionEntry " - << " -> Block" << getBlockId(MF.front()) << ";\n"; + OS << "FunctionEntry " << " -> Block" << getBlockId(MF.front()) << ";\n"; for (const auto &MBB : MF) { printBlock(MBB); @@ -123,8 +69,8 @@ void StackLayoutPrinter::printBlock(MachineBasicBlock const &Block) { }, [&](Assignment const &Assignment) { OS << "Assignment("; - for (const auto &Var : Assignment.Variables) - OS << printReg(Var.VirtualReg, nullptr, 0, nullptr) + for (const auto *Var : Assignment.Variables) + OS << printReg(Var->getReg(), nullptr, 0, nullptr) << ", "; OS << ")"; }}, @@ -153,7 +99,7 @@ void StackLayoutPrinter::printBlock(MachineBasicBlock const &Block) { auto [CondBr, UncondBr, TrueBB, FalseBB, Condition] = TermInfo->getConditionalBranch(); OS << "Block" << getBlockId(Block) << "Exit [label=\"{ "; - OS << stackSlotToString(StackModel.getStackSlot(*Condition)); + OS << StackModel.getStackSlot(*Condition)->toString(); OS << "| { <0> Zero | <1> NonZero }}\"];\n"; OS << "Block" << getBlockId(Block); OS << "Exit:0 -> Block" << getBlockId(*FalseBB) << ";\n"; diff --git a/llvm/lib/Target/EVM/EVMStackDebug.h b/llvm/lib/Target/EVM/EVMStackDebug.h index b4c8737ccae5..615ffd9279c7 100644 --- a/llvm/lib/Target/EVM/EVMStackDebug.h +++ b/llvm/lib/Target/EVM/EVMStackDebug.h @@ -17,18 +17,12 @@ #include "EVMMachineCFGInfo.h" #include "EVMStackModel.h" #include "llvm/CodeGen/MachineFunction.h" -#include #include -#include -#include -#include namespace llvm { class EVMStackLayout; -const Function *getCalledFunction(const MachineInstr &MI); -std::string stackSlotToString(const StackSlot &Slot); std::string stackToString(Stack const &S); #ifndef NDEBUG diff --git a/llvm/lib/Target/EVM/EVMStackLayoutGenerator.cpp b/llvm/lib/Target/EVM/EVMStackLayoutGenerator.cpp index 26afd5114633..29c3ce825870 100644 --- a/llvm/lib/Target/EVM/EVMStackLayoutGenerator.cpp +++ b/llvm/lib/Target/EVM/EVMStackLayoutGenerator.cpp @@ -63,10 +63,10 @@ findStackTooDeep(Stack const &Source, Stack const &Target) { auto getVariableChoices = [](auto &&SlotRange) { SmallVector Result; - for (auto const &Slot : SlotRange) - if (auto const *VarSlot = std::get_if(&Slot)) - if (!is_contained(Result, VarSlot->VirtualReg)) - Result.push_back(VarSlot->VirtualReg); + for (auto const *Slot : SlotRange) + if (auto const *VarSlot = dyn_cast(Slot)) + if (!is_contained(Result, VarSlot->getReg())) + Result.push_back(VarSlot->getReg()); return Result; }; @@ -77,8 +77,8 @@ findStackTooDeep(Stack const &Source, Stack const &Target) { Errors.emplace_back(EVMStackLayoutGenerator::StackTooDeep{ I - 16, getVariableChoices(take_back(CurrentStack, I + 1))}); }, - [&](StackSlot const &Slot) { - if (isRematerializable(Slot)) + [&](const StackSlot *Slot) { + if (Slot->isRematerializable()) return; if (auto Depth = offset(reverse(CurrentStack), Slot); @@ -102,14 +102,14 @@ Stack createIdealLayout(const Stack &OperationOutput, const Stack &Post, struct PreviousSlot { size_t slot; }; - using LayoutT = SmallVector>; + using LayoutT = SmallVector>; // Determine the number of slots that have to be on stack before executing the // operation (excluding the inputs of the operation itself). That is slots // that should not be generated on the fly and are not outputs of the // operation. size_t PreOperationLayoutSize = Post.size(); - for (auto const &Slot : Post) + for (const auto *Slot : Post) if (is_contained(OperationOutput, Slot) || GenerateSlotOnTheFly(Slot)) --PreOperationLayoutSize; @@ -129,54 +129,53 @@ Stack createIdealLayout(const Stack &OperationOutput, const Stack &Post, struct ShuffleOperations { LayoutT &Layout; const Stack &Post; - std::set Outputs; + std::set Outputs; Multiplicity Mult; Callable GenerateSlotOnTheFly; ShuffleOperations(LayoutT &Layout, Stack const &Post, Callable GenerateSlotOnTheFly) : Layout(Layout), Post(Post), GenerateSlotOnTheFly(GenerateSlotOnTheFly) { - for (auto const &LayoutSlot : Layout) - if (const StackSlot *Slot = std::get_if(&LayoutSlot)) + for (const auto &LayoutSlot : Layout) + if (auto Slot = std::get_if(&LayoutSlot)) Outputs.insert(*Slot); - for (auto const &LayoutSlot : Layout) - if (const StackSlot *Slot = std::get_if(&LayoutSlot)) + for (const auto &LayoutSlot : Layout) + if (auto Slot = std::get_if(&LayoutSlot)) --Mult[*Slot]; - for (auto &&Slot : Post) + for (auto *Slot : Post) if (Outputs.count(Slot) || GenerateSlotOnTheFly(Slot)) ++Mult[Slot]; } bool isCompatible(size_t Source, size_t Target) { return Source < Layout.size() && Target < Post.size() && - (std::holds_alternative(Post[Target]) || + (isa(Post[Target]) || std::visit(Overload{[&](const PreviousSlot &) { return !Outputs.count(Post[Target]) && !GenerateSlotOnTheFly(Post[Target]); }, - [&](const StackSlot &S) { + [&](const StackSlot *S) { return S == Post[Target]; }}, Layout[Source])); } bool sourceIsSame(size_t Lhs, size_t Rhs) { - return std::visit( - Overload{ - [&](PreviousSlot const &, PreviousSlot const &) { return true; }, - [&](StackSlot const &Lhs, StackSlot const &Rhs) { - return Lhs == Rhs; - }, - [&](auto const &, auto const &) { return false; }}, - Layout[Lhs], Layout[Rhs]); + if (std::holds_alternative(Layout[Lhs]) && + std::holds_alternative(Layout[Rhs])) + return true; + + auto SlotLHS = std::get_if(&Layout[Lhs]); + auto SlotRHS = std::get_if(&Layout[Rhs]); + return SlotLHS && SlotRHS && *SlotLHS == *SlotRHS; } int sourceMultiplicity(size_t Offset) { return std::visit( Overload{[&](PreviousSlot const &) { return 0; }, - [&](StackSlot const &S) { return Mult.at(S); }}, + [&](const StackSlot *S) { return Mult.at(S); }}, Layout[Offset]); } @@ -187,8 +186,7 @@ Stack createIdealLayout(const Stack &OperationOutput, const Stack &Post, } bool targetIsArbitrary(size_t Offset) { - return Offset < Post.size() && - std::holds_alternative(Post[Offset]); + return Offset < Post.size() && isa(Post[Offset]); } void swap(size_t I) { @@ -217,9 +215,9 @@ Stack createIdealLayout(const Stack &OperationOutput, const Stack &Post, // VariableSlot{"tmp"}, then we want the variable tmp in the slot at offset 2 // in the layout before the operation. assert(Layout.size() == Post.size()); - SmallVector> IdealLayout(Post.size(), std::nullopt); + SmallVector IdealLayout(Post.size(), nullptr); for (unsigned Idx = 0; Idx < std::min(Layout.size(), Post.size()); ++Idx) { - auto &Slot = Post[Idx]; + auto *Slot = Post[Idx]; auto &IdealPosition = Layout[Idx]; if (PreviousSlot *PrevSlot = std::get_if(&IdealPosition)) IdealLayout[PrevSlot->slot] = Slot; @@ -233,9 +231,9 @@ Stack createIdealLayout(const Stack &OperationOutput, const Stack &Post, assert(IdealLayout.size() == PreOperationLayoutSize); Stack Result; - for (const auto &Item : IdealLayout) { + for (auto *Item : IdealLayout) { assert(Item); - Result.emplace_back(*Item); + Result.push_back(Item); } return Result; @@ -271,8 +269,8 @@ Stack EVMStackLayoutGenerator::propagateStackThroughOperation( AggressiveStackCompression = false; // This is a huge tradeoff between code size, gas cost and stack size. - auto generateSlotOnTheFly = [&](StackSlot const &Slot) { - return AggressiveStackCompression && isRematerializable(Slot); + auto generateSlotOnTheFly = [&](const StackSlot *Slot) { + return AggressiveStackCompression && Slot->isRematerializable(); }; // Determine the ideal permutation of the slots in ExitLayout that are not @@ -284,9 +282,9 @@ Stack EVMStackLayoutGenerator::propagateStackThroughOperation( // Make sure the resulting previous slots do not overlap with any assignmed // variables. if (auto const *Assign = std::get_if(&Operation.Operation)) - for (auto &StackSlot : IdealStack) - if (auto const *VarSlot = std::get_if(&StackSlot)) - assert(!is_contained(Assign->Variables, *VarSlot)); + for (auto *StackSlot : IdealStack) + if (const auto *VarSlot = dyn_cast(StackSlot)) + assert(!is_contained(Assign->Variables, VarSlot)); // Since stack+Operation.output can be easily shuffled to ExitLayout, the // desired layout before the operation is stack+Operation.input; @@ -302,7 +300,7 @@ Stack EVMStackLayoutGenerator::propagateStackThroughOperation( // Remove anything from the stack top that can be freely generated or dupped // from deeper on the stack. while (!IdealStack.empty()) { - if (isRematerializable(IdealStack.back())) + if (IdealStack.back()->isRematerializable()) IdealStack.pop_back(); else if (auto Offset = offset(drop_begin(reverse(IdealStack), 1), IdealStack.back())) { @@ -341,7 +339,7 @@ static size_t getOptimalNumberOfJunks(const Stack &EntryLayout, size_t BestNumJunk = 0; size_t MaxJunk = EntryLayout.size(); for (size_t NumJunk = 1; NumJunk <= MaxJunk; ++NumJunk) { - Stack JunkedTarget(NumJunk, JunkSlot{}); + Stack JunkedTarget(NumJunk, EVMStackModel::getJunkSlot()); JunkedTarget.append(TargetLayout); size_t Cost = EvaluateStackTransform(EntryLayout, JunkedTarget); if (Cost < BestCost) { @@ -397,12 +395,11 @@ void EVMStackLayoutGenerator::runPropagation() { } } - // Check which latch blocks still require fixing of their exit layouts. // Revisit these blocks again. for (auto [Latch, Header] : Backedges) { const Stack &HeaderEntryLayout = MBBEntryLayoutMap[Header]; const Stack &LatchExitLayout = MBBExitLayoutMap[Latch]; - if (all_of(HeaderEntryLayout, [LatchExitLayout](const StackSlot &Slot) { + if (all_of(HeaderEntryLayout, [LatchExitLayout](const StackSlot *Slot) { return is_contained(LatchExitLayout, Slot); })) continue; @@ -464,15 +461,15 @@ void EVMStackLayoutGenerator::runPropagation() { Stack NewSuccEntryLayout = ExitLayout; // Whatever the block being jumped to does not actually require, // can be marked as junk. - for (StackSlot &Slot : NewSuccEntryLayout) + for (StackSlot *&Slot : NewSuccEntryLayout) if (!is_contained(SuccEntryLayout, Slot)) - Slot = JunkSlot{}; + Slot = EVMStackModel::getJunkSlot(); #ifndef NDEBUG // Make sure everything the block being jumped to requires is // actually present or can be generated. - for (const StackSlot &Slot : SuccEntryLayout) - assert(isRematerializable(Slot) || + for (const StackSlot *Slot : SuccEntryLayout) + assert(Slot->isRematerializable() || is_contained(NewSuccEntryLayout, Slot)); #endif // NDEBUG @@ -484,7 +481,7 @@ void EVMStackLayoutGenerator::runPropagation() { Stack EntryStack; bool IsNoReturn = MF.getFunction().hasFnAttribute(Attribute::NoReturn); if (!IsNoReturn) - EntryStack.emplace_back(FunctionReturnLabelSlot{&MF}); + EntryStack.push_back(StackModel.getFunctionReturnLabelSlot(&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. @@ -585,19 +582,19 @@ Stack EVMStackLayoutGenerator::combineStack(Stack const &Stack1, Stack CommonPrefix; for (unsigned Idx = 0; Idx < std::min(Stack1.size(), Stack2.size()); ++Idx) { - const StackSlot &Slot1 = Stack1[Idx]; - const StackSlot &Slot2 = Stack2[Idx]; + StackSlot *Slot1 = Stack1[Idx]; + const StackSlot *Slot2 = Stack2[Idx]; if (!(Slot1 == Slot2)) break; - CommonPrefix.emplace_back(Slot1); + CommonPrefix.push_back(Slot1); } Stack Stack1Tail, Stack2Tail; - for (const auto &Slot : drop_begin(Stack1, CommonPrefix.size())) - Stack1Tail.emplace_back(Slot); + for (auto *Slot : drop_begin(Stack1, CommonPrefix.size())) + Stack1Tail.push_back(Slot); - for (const auto &Slot : drop_begin(Stack2, CommonPrefix.size())) - Stack2Tail.emplace_back(Slot); + for (auto *Slot : drop_begin(Stack2, CommonPrefix.size())) + Stack2Tail.push_back(Slot); if (Stack1Tail.empty()) { CommonPrefix.append(compressStack(Stack2Tail)); @@ -610,20 +607,19 @@ Stack EVMStackLayoutGenerator::combineStack(Stack const &Stack1, } Stack Candidate; - for (auto Slot : Stack1Tail) + for (auto *Slot : Stack1Tail) if (!is_contained(Candidate, Slot)) - Candidate.emplace_back(Slot); + Candidate.push_back(Slot); - for (auto Slot : Stack2Tail) + for (auto *Slot : Stack2Tail) if (!is_contained(Candidate, Slot)) - Candidate.emplace_back(Slot); + Candidate.push_back(Slot); { auto RemIt = std::remove_if( - Candidate.begin(), Candidate.end(), [](StackSlot const &Slot) { - return std::holds_alternative(Slot) || - std::holds_alternative(Slot) || - std::holds_alternative(Slot); + Candidate.begin(), Candidate.end(), [](const StackSlot *Slot) { + return isa(Slot) || isa(Slot) || + isa(Slot); }); Candidate.erase(RemIt, Candidate.end()); } @@ -637,8 +633,8 @@ Stack EVMStackLayoutGenerator::combineStack(Stack const &Stack1, NumOps += 1000; }; - auto DupOrPush = [&](StackSlot const &Slot) { - if (isRematerializable(Slot)) + auto DupOrPush = [&](const StackSlot *Slot) { + if (Slot->isRematerializable()) return; Stack Tmp = CommonPrefix; @@ -699,8 +695,8 @@ Stack EVMStackLayoutGenerator::compressStack(Stack CurStack) { auto I = CurStack.rbegin(), E = CurStack.rend(); for (size_t Depth = 0; I < E; ++I, ++Depth) { - StackSlot &Slot = *I; - if (isRematerializable(Slot)) { + StackSlot *Slot = *I; + if (Slot->isRematerializable()) { FirstDupOffset = CurStack.size() - Depth - 1; break; } @@ -728,8 +724,8 @@ size_t llvm::EvaluateStackTransform(Stack Source, Stack const &Target) { OpGas += 3; // SWAP* gas price; }; - auto DupOrPush = [&](StackSlot const &Slot) { - if (isRematerializable(Slot)) + auto DupOrPush = [&](const StackSlot *Slot) { + if (Slot->isRematerializable()) OpGas += 3; else { auto Depth = offset(reverse(Source), Slot); @@ -751,17 +747,17 @@ size_t llvm::EvaluateStackTransform(Stack Source, Stack const &Target) { void EVMStackLayoutGenerator::addJunksToStackBottom( const MachineBasicBlock *Entry, size_t NumJunk) { for (const MachineBasicBlock *MBB : depth_first(Entry)) { - Stack EntryTmp(NumJunk, JunkSlot{}); + Stack EntryTmp(NumJunk, EVMStackModel::getJunkSlot()); EntryTmp.append(MBBEntryLayoutMap.at(MBB)); MBBEntryLayoutMap[MBB] = std::move(EntryTmp); for (const Operation &Operation : StackModel.getOperations(MBB)) { - Stack OpEntryTmp(NumJunk, JunkSlot{}); + Stack OpEntryTmp(NumJunk, EVMStackModel::getJunkSlot()); OpEntryTmp.append(OperationEntryLayoutMap.at(&Operation)); OperationEntryLayoutMap[&Operation] = std::move(OpEntryTmp); } - Stack ExitTmp(NumJunk, JunkSlot{}); + Stack ExitTmp(NumJunk, EVMStackModel::getJunkSlot()); ExitTmp.append(MBBExitLayoutMap.at(MBB)); MBBExitLayoutMap[MBB] = std::move(ExitTmp); } diff --git a/llvm/lib/Target/EVM/EVMStackModel.cpp b/llvm/lib/Target/EVM/EVMStackModel.cpp index e69d11db925d..ca67872d8b30 100644 --- a/llvm/lib/Target/EVM/EVMStackModel.cpp +++ b/llvm/lib/Target/EVM/EVMStackModel.cpp @@ -17,10 +17,38 @@ #include "llvm/CodeGen/MachineFunction.h" #include -#include using namespace llvm; +static const Function *getCalledFunction(const MachineInstr &MI) { + for (const MachineOperand &MO : MI.operands()) { + if (!MO.isGlobal()) + continue; + if (const auto *Func = dyn_cast(MO.getGlobal())) + return Func; + } + return nullptr; +} +// TODO: make it static once Operation gets rid of std::variant. +std::string llvm::getInstName(const MachineInstr *MI) { + const MachineFunction *MF = MI->getParent()->getParent(); + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + return TII->getName(MI->getOpcode()).str(); +} + +std::string SymbolSlot::toString() const { + return getInstName(MI) + ":" + std::string(Symbol->getName()); +} +std::string FunctionCallReturnLabelSlot::toString() const { + return "RET[" + std::string(getCalledFunction(*Call)->getName()) + "]"; +} +std::string TemporarySlot::toString() const { + SmallString<128> S; + raw_svector_ostream OS(S); + OS << "TMP[" << getInstName(MI) << ", " << std::to_string(Index) + "]"; + return std::string(S.str()); +} + EVMStackModel::EVMStackModel(MachineFunction &MF, const LiveIntervals &LIS) : MF(MF), LIS(LIS) { for (MachineBasicBlock &MBB : MF) { @@ -33,37 +61,34 @@ EVMStackModel::EVMStackModel(MachineFunction &MF, const LiveIntervals &LIS) Stack EVMStackModel::getFunctionParameters() const { auto *MFI = MF.getInfo(); - SmallVector Parameters(MFI->getNumParams(), JunkSlot{}); + SmallVector Parameters(MFI->getNumParams(), + EVMStackModel::getJunkSlot()); for (const MachineInstr &MI : MF.front()) { if (MI.getOpcode() == EVM::ARGUMENT) { int64_t ArgIdx = MI.getOperand(1).getImm(); - Parameters[ArgIdx] = VariableSlot{MI.getOperand(0).getReg()}; + Parameters[ArgIdx] = getVariableSlot(MI.getOperand(0).getReg()); } } return Parameters; } -StackSlot EVMStackModel::getStackSlot(const MachineOperand &MO) const { - const MachineInstr *MI = MO.getParent(); - StackSlot Slot = VariableSlot{MO.getReg()}; - SlotIndex Idx = LIS.getInstructionIndex(*MI); - const LiveInterval *LI = &LIS.getInterval(MO.getReg()); - LiveQueryResult LRQ = LI->Query(Idx); - const VNInfo *VNI = LRQ.valueIn(); - assert(VNI && "Use of non-existing value"); +StackSlot *EVMStackModel::getStackSlot(const MachineOperand &MO) const { // If the virtual register defines a constant and this is the only // definition, emit the literal slot as MI's input. + const LiveInterval *LI = &LIS.getInterval(MO.getReg()); if (LI->containsOneValue()) { + SlotIndex Idx = LIS.getInstructionIndex(*MO.getParent()); + const VNInfo *VNI = LI->Query(Idx).valueIn(); + assert(VNI && "Use of non-existing value"); assert(!VNI->isPHIDef()); const MachineInstr *DefMI = LIS.getInstructionFromIndex(VNI->def); assert(DefMI && "Dead valno in interval"); if (DefMI->getOpcode() == EVM::CONST_I256) { const APInt Imm = DefMI->getOperand(1).getCImm()->getValue(); - Slot = LiteralSlot{std::move(Imm)}; + return getLiteralSlot(std::move(Imm)); } } - - return Slot; + return getVariableSlot(MO.getReg()); } Stack EVMStackModel::getInstrInput(const MachineInstr &MI) const { @@ -78,16 +103,15 @@ Stack EVMStackModel::getInstrInput(const MachineInstr &MI) const { if (MO.getReg() == EVM::SP) continue; - In.emplace_back(getStackSlot(MO)); + In.push_back(getStackSlot(MO)); } return In; } Stack EVMStackModel::getInstrOutput(const MachineInstr &MI) const { Stack Out; - unsigned ArgNumber = 0; - for (const auto &MO : MI.defs()) - Out.push_back(TemporarySlot{&MI, MO.getReg(), ArgNumber++}); + for (unsigned I = 0, E = MI.getNumExplicitDefs(); I < E; ++I) + Out.push_back(getTemporarySlot(&MI, I)); return Out; } @@ -111,7 +135,7 @@ void EVMStackModel::createOperation(MachineInstr &MI, assert(Func); IsNoReturn = Func->hasFnAttribute(Attribute::NoReturn); if (!IsNoReturn) - Input.push_back(FunctionCallReturnLabelSlot{&MI}); + Input.push_back(getFunctionCallReturnLabelSlot(&MI)); break; } } @@ -146,42 +170,42 @@ void EVMStackModel::createOperation(MachineInstr &MI, } break; } - // Cretae CFG::Assignment object for the MI. + // Create CFG::Assignment object for the MI. Stack Input, Output; - SmallVector Variables; + SmallVector Variables; switch (MI.getOpcode()) { case EVM::CONST_I256: { const Register DefReg = MI.getOperand(0).getReg(); const APInt Imm = MI.getOperand(1).getCImm()->getValue(); - Input.push_back(LiteralSlot{std::move(Imm)}); - Output.push_back(VariableSlot{DefReg}); - Variables.push_back(VariableSlot{DefReg}); + Input.push_back(getLiteralSlot(std::move(Imm))); + Output.push_back(getVariableSlot(DefReg)); + Variables.push_back(getVariableSlot(DefReg)); } break; case EVM::DATASIZE: case EVM::DATAOFFSET: case EVM::LINKERSYMBOL: { const Register DefReg = MI.getOperand(0).getReg(); MCSymbol *Sym = MI.getOperand(1).getMCSymbol(); - Input.push_back(SymbolSlot{Sym, &MI}); - Output.push_back(VariableSlot{DefReg}); - Variables.push_back(VariableSlot{DefReg}); + Input.push_back(getSymbolSlot(Sym, &MI)); + Output.push_back(getVariableSlot(DefReg)); + Variables.push_back(getVariableSlot(DefReg)); } break; case EVM::COPY_I256: { // Copy instruction corresponds to the assignment operator, so // we do not need to create intermediate TmpSlots. Input = getInstrInput(MI); const Register DefReg = MI.getOperand(0).getReg(); - Output.push_back(VariableSlot{DefReg}); - Variables.push_back(VariableSlot{DefReg}); + Output.push_back(getVariableSlot(DefReg)); + Variables.push_back(getVariableSlot(DefReg)); } break; default: { unsigned ArgsNumber = 0; for (const auto &MO : MI.defs()) { assert(MO.isReg()); const Register Reg = MO.getReg(); - Input.push_back(TemporarySlot{&MI, Reg, ArgsNumber++}); - Output.push_back(VariableSlot{Reg}); - Variables.push_back(VariableSlot{Reg}); + Input.push_back(getTemporarySlot(&MI, ArgsNumber++)); + Output.push_back(getVariableSlot(Reg)); + Variables.push_back(getVariableSlot(Reg)); } } break; } @@ -200,6 +224,6 @@ Stack EVMStackModel::getReturnArguments(const MachineInstr &MI) const { // 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()); - Input.emplace_back(FunctionReturnLabelSlot{&MF}); + Input.push_back(getFunctionReturnLabelSlot(&MF)); return Input; } diff --git a/llvm/lib/Target/EVM/EVMStackModel.h b/llvm/lib/Target/EVM/EVMStackModel.h index 1f5972821ff2..d716b2208431 100644 --- a/llvm/lib/Target/EVM/EVMStackModel.h +++ b/llvm/lib/Target/EVM/EVMStackModel.h @@ -26,6 +26,7 @@ #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineLoopInfo.h" #include "llvm/CodeGen/Register.h" +#include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/MC/MCSymbol.h" #include @@ -35,138 +36,172 @@ namespace llvm { class MachineFunction; class MachineBasicBlock; -/// The following structs describe different kinds of stack slots. -/// Each stack slot is equality- and less-than-comparable and -/// specifies an attribute 'isRematerializable' that is true, -/// if a slot of this kind always has a known value at compile time and -/// therefore can safely be removed from the stack at any time and then -/// regenerated later. +std::string getInstName(const MachineInstr *MI); -/// The label pushed as return label before a function call, i.e. the label the -/// call is supposed to return to. -struct FunctionCallReturnLabelSlot { - const MachineInstr *Call = nullptr; - static constexpr bool isRematerializable = true; +class StackSlot { +public: + enum SlotKind { + SK_Literal, + SK_Variable, + SK_Symbol, + SK_FunctionCallReturnLabel, + SK_FunctionReturnLabel, + SK_Temporary, + SK_Junk, + }; - bool operator==(FunctionCallReturnLabelSlot const &Rhs) const { - return Call == Rhs.Call; - } +private: + const SlotKind KindID; - bool operator<(FunctionCallReturnLabelSlot const &Rhs) const { - return Call < Rhs.Call; - } +protected: + StackSlot(SlotKind KindID) : KindID(KindID) {} + +public: + virtual ~StackSlot() = default; + + unsigned getSlotKind() const { return KindID; } + + // 'isRematerializable()' returns true, if a slot always has a known value + // at compile time and therefore can safely be removed from the stack at any + // time and then regenerated later. + virtual bool isRematerializable() const = 0; + virtual std::string toString() const = 0; }; -/// The return jump target of a function while generating the code of the -/// function body. I.e. the caller of a function pushes a -/// 'FunctionCallReturnLabelSlot' (see above) before jumping to the function -/// and this very slot is viewed as 'FunctionReturnLabelSlot' inside the -/// function body and jumped to when returning from the function. -struct FunctionReturnLabelSlot { - const MachineFunction *MF = nullptr; - static constexpr bool isRematerializable = false; +/// A slot containing a literal value. +class LiteralSlot final : public StackSlot { + APInt Value; - bool operator==(FunctionReturnLabelSlot const &Rhs) const { - // There can never be return label slots of different functions on stack - // simultaneously. - assert(MF == Rhs.MF); - return true; +public: + LiteralSlot(const APInt &V) : StackSlot(SK_Literal), Value(V) {} + const APInt &getValue() const { return Value; } + + bool isRematerializable() const override { return true; } + std::string toString() const override { + SmallString<64> S; + Value.toStringSigned(S); + return std::string(S.str()); } - - bool operator<(FunctionReturnLabelSlot const &Rhs) const { - // There can never be return label slots of different functions on stack - // simultaneously. - assert(MF == Rhs.MF); - return false; + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Literal; } }; /// A slot containing the current value of a particular variable. -struct VariableSlot { +class VariableSlot final : public StackSlot { Register VirtualReg; - static constexpr bool isRematerializable = false; - bool operator==(VariableSlot const &Rhs) const { - return VirtualReg == Rhs.VirtualReg; +public: + VariableSlot(const Register &R) : StackSlot(SK_Variable), VirtualReg(R) {} + const Register &getReg() const { return VirtualReg; } + + bool isRematerializable() const override { return false; } + std::string toString() const override { + SmallString<64> S; + raw_svector_ostream OS(S); + OS << printReg(VirtualReg, nullptr, 0, nullptr); + return std::string(S.str()); } - - bool operator<(VariableSlot const &Rhs) const { - return VirtualReg < Rhs.VirtualReg; + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Variable; } }; -/// A slot containing a literal value. -struct LiteralSlot { - APInt Value; - static constexpr bool isRematerializable = true; +/// A slot containing a MCSymbol. +class SymbolSlot final : public StackSlot { + MCSymbol *Symbol; + const MachineInstr *MI = nullptr; + +public: + SymbolSlot(MCSymbol *S, const MachineInstr *MI) + : StackSlot(SK_Symbol), Symbol(S), MI(MI) {} + const MachineInstr *getMachineInstr() const { return MI; } + MCSymbol *getSymbol() const { return Symbol; } - bool operator==(LiteralSlot const &Rhs) const { return Value == Rhs.Value; } + bool isRematerializable() const override { return true; } + std::string toString() const override; - bool operator<(LiteralSlot const &Rhs) const { return Value.ult(Rhs.Value); } + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Symbol; + } }; -/// A slot containing a MCSymbol. -struct SymbolSlot { - MCSymbol *Symbol; - const MachineInstr *MI = nullptr; - static constexpr bool isRematerializable = true; +/// The label pushed as return label before a function call, i.e. the label the +/// call is supposed to return to. +class FunctionCallReturnLabelSlot final : public StackSlot { + const MachineInstr *Call = nullptr; + +public: + FunctionCallReturnLabelSlot(const MachineInstr *Call) + : StackSlot(SK_FunctionCallReturnLabel), Call(Call) {} + const MachineInstr *getCall() const { return Call; } - bool operator==(SymbolSlot const &Rhs) const { - return Symbol == Rhs.Symbol && MI->getOpcode() == Rhs.MI->getOpcode(); + bool isRematerializable() const override { return true; } + std::string toString() const override; + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_FunctionCallReturnLabel; } +}; + +/// The return jump target of a function while generating the code of the +/// function body. I.e. the caller of a function pushes a +/// 'FunctionCallReturnLabelSlot' (see above) before jumping to the function +/// and this very slot is viewed as 'FunctionReturnLabelSlot' inside the +/// function body and jumped to when returning from the function. +class FunctionReturnLabelSlot final : public StackSlot { + const MachineFunction *MF = nullptr; - bool operator<(SymbolSlot const &Rhs) const { - return std::make_pair(Symbol, MI->getOpcode()) < - std::make_pair(Rhs.Symbol, Rhs.MI->getOpcode()); +public: + FunctionReturnLabelSlot(const MachineFunction *MF) + : StackSlot(SK_FunctionReturnLabel), MF(MF) {} + const MachineFunction *getMachineFunction() { return MF; } + + bool isRematerializable() const override { return false; } + std::string toString() const override { return "RET"; } + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_FunctionReturnLabel; } }; /// A slot containing the index-th return value of a previous call. -struct TemporarySlot { +class TemporarySlot final : public StackSlot { /// The call that returned this slot. const MachineInstr *MI = nullptr; - Register VirtualReg; /// Specifies to which of the values returned by the call this slot refers. /// index == 0 refers to the slot deepest in the stack after the call. size_t Index = 0; - static constexpr bool isRematerializable = false; - bool operator==(TemporarySlot const &Rhs) const { - return MI == Rhs.MI && Index == Rhs.Index; - } +public: + TemporarySlot(const MachineInstr *MI, size_t Idx) + : StackSlot(SK_Temporary), MI(MI), Index(Idx) {} - bool operator<(TemporarySlot const &Rhs) const { - return std::make_pair(MI, Index) < std::make_pair(Rhs.MI, Rhs.Index); + bool isRematerializable() const override { return false; } + std::string toString() const override; + + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Temporary; } }; /// A slot containing an arbitrary value that is always eventually popped and /// never used. Used to maintain stack balance on control flow joins. -struct JunkSlot { - static constexpr bool isRematerializable = true; +class JunkSlot final : public StackSlot { +public: + JunkSlot() : StackSlot(SK_Junk) {} - bool operator==(JunkSlot const &) const { return true; } + bool isRematerializable() const override { return true; } + std::string toString() const override { return "JUNK"; } - bool operator<(JunkSlot const &) const { return false; } + static bool classof(const StackSlot *S) { + return S->getSlotKind() == SK_Junk; + } }; -using StackSlot = - std::variant; - /// The stack top is the last element of the vector. -using Stack = SmallVector; - -/// Returns true if Slot can be materialized on the stack at any time. -inline bool isRematerializable(const StackSlot &Slot) { - return std::visit( - [](auto const &TypedSlot) { - return std::decay_t::isRematerializable; - }, - Slot); -} +using Stack = SmallVector; struct BuiltinCall { MachineInstr *MI = nullptr; @@ -181,7 +216,7 @@ struct Assignment { /// The variables being assigned to also occur as 'Output' in the /// 'Operation' containing the assignment, but are also stored here for /// convenience. - SmallVector Variables; + SmallVector Variables; }; struct Operation { @@ -193,10 +228,29 @@ struct Operation { }; class EVMStackModel { + MachineFunction &MF; + const LiveIntervals &LIS; + DenseMap> OperationsMap; + + // Storage for stack slots. + mutable DenseMap> LiteralStorage; + mutable DenseMap> VariableStorage; + mutable DenseMap, + std::unique_ptr> + SymbolStorage; + mutable DenseMap> + FunctionCallReturnLabelStorage; + mutable DenseMap, + std::unique_ptr> + TemporaryStorage; + + // There should be a single FunctionReturnLabelSlot for the MF. + mutable std::unique_ptr TheFunctionReturnLabelSlot; + public: EVMStackModel(MachineFunction &MF, const LiveIntervals &LIS); Stack getFunctionParameters() const; - StackSlot getStackSlot(const MachineOperand &MO) const; Stack getInstrInput(const MachineInstr &MI) const; Stack getInstrOutput(const MachineInstr &MI) const; Stack getReturnArguments(const MachineInstr &MI) const; @@ -205,14 +259,54 @@ class EVMStackModel { return OperationsMap.at(MBB); } + // Get or create a requested stack slot. + StackSlot *getStackSlot(const MachineOperand &MO) const; + LiteralSlot *getLiteralSlot(const APInt &V) const { + if (LiteralStorage.count(V) == 0) + LiteralStorage[V] = std::make_unique(V); + return LiteralStorage[V].get(); + } + VariableSlot *getVariableSlot(const Register &R) const { + if (VariableStorage.count(R) == 0) + VariableStorage[R] = std::make_unique(R); + return VariableStorage[R].get(); + } + SymbolSlot *getSymbolSlot(MCSymbol *S, const MachineInstr *MI) const { + auto Key = std::make_pair(S, MI); + if (SymbolStorage.count(Key) == 0) + SymbolStorage[Key] = std::make_unique(S, MI); + return SymbolStorage[Key].get(); + } + FunctionCallReturnLabelSlot * + getFunctionCallReturnLabelSlot(const MachineInstr *Call) const { + if (FunctionCallReturnLabelStorage.count(Call) == 0) + FunctionCallReturnLabelStorage[Call] = + std::make_unique(Call); + return FunctionCallReturnLabelStorage[Call].get(); + } + FunctionReturnLabelSlot * + getFunctionReturnLabelSlot(const MachineFunction *MF) const { + if (!TheFunctionReturnLabelSlot) + TheFunctionReturnLabelSlot = + std::make_unique(MF); + assert(MF == TheFunctionReturnLabelSlot->getMachineFunction()); + return TheFunctionReturnLabelSlot.get(); + } + TemporarySlot *getTemporarySlot(const MachineInstr *MI, size_t Idx) const { + auto Key = std::make_pair(MI, Idx); + if (TemporaryStorage.count(Key) == 0) + TemporaryStorage[Key] = std::make_unique(MI, Idx); + return TemporaryStorage[Key].get(); + } + // Junk is always the same slot. + static JunkSlot *getJunkSlot() { + static JunkSlot TheJunkSlot; + return &TheJunkSlot; + } + private: void createOperation(MachineInstr &MI, SmallVector &Ops) const; - - MachineFunction &MF; - const LiveIntervals &LIS; - DenseMap> OperationsMap; }; - } // namespace llvm -#endif // LLVM_LIB_TARGET_EVM_EVMCONTROLFLOWGRAPH_H +#endif // LLVM_LIB_TARGET_EVM_EVMSTACKMODEL_H diff --git a/llvm/lib/Target/EVM/EVMStackShuffler.h b/llvm/lib/Target/EVM/EVMStackShuffler.h index e91a640727b5..e95a9554b0de 100644 --- a/llvm/lib/Target/EVM/EVMStackShuffler.h +++ b/llvm/lib/Target/EVM/EVMStackShuffler.h @@ -395,50 +395,50 @@ class Shuffler { /// A simple optimized map for mapping StackSlot to ints. class Multiplicity { public: - int &operator[](StackSlot const &Slot) { - if (auto *p = std::get_if(&Slot)) - return FunctionCallReturnLabelSlotMultiplicity[*p]; - if (std::holds_alternative(Slot)) + int &operator[](const StackSlot *Slot) { + if (auto *p = dyn_cast(Slot)) + return FunctionCallReturnLabelSlotMultiplicity[p]; + if (isa(Slot)) return FunctionReturnLabelSlotMultiplicity; - if (auto *p = std::get_if(&Slot)) - return VariableSlotMultiplicity[*p]; - if (auto *p = std::get_if(&Slot)) - return LiteralSlotMultiplicity[*p]; - if (auto *p = std::get_if(&Slot)) - return SymbolSlotMultiplicity[*p]; - if (auto *p = std::get_if(&Slot)) - return TemporarySlotMultiplicity[*p]; - - assert(std::holds_alternative(Slot)); + if (auto *p = dyn_cast(Slot)) + return VariableSlotMultiplicity[p]; + if (auto *p = dyn_cast(Slot)) + return LiteralSlotMultiplicity[p]; + if (auto *p = dyn_cast(Slot)) + return SymbolSlotMultiplicity[p]; + if (auto *p = dyn_cast(Slot)) + return TemporarySlotMultiplicity[p]; + + assert(isa(Slot)); return JunkSlotMultiplicity; } - int at(StackSlot const &Slot) const { - if (auto *p = std::get_if(&Slot)) - return FunctionCallReturnLabelSlotMultiplicity.at(*p); - if (std::holds_alternative(Slot)) + int at(const StackSlot *Slot) const { + if (auto *p = dyn_cast(Slot)) + return FunctionCallReturnLabelSlotMultiplicity.at(p); + if (isa(Slot)) return FunctionReturnLabelSlotMultiplicity; - if (auto *p = std::get_if(&Slot)) - return VariableSlotMultiplicity.at(*p); - if (auto *p = std::get_if(&Slot)) - return LiteralSlotMultiplicity.at(*p); - if (auto *p = std::get_if(&Slot)) - return SymbolSlotMultiplicity.at(*p); - if (auto *p = std::get_if(&Slot)) - return TemporarySlotMultiplicity.at(*p); - - assert(std::holds_alternative(Slot)); + if (auto *p = dyn_cast(Slot)) + return VariableSlotMultiplicity.at(p); + if (auto *p = dyn_cast(Slot)) + return LiteralSlotMultiplicity.at(p); + if (auto *p = dyn_cast(Slot)) + return SymbolSlotMultiplicity.at(p); + if (auto *p = dyn_cast(Slot)) + return TemporarySlotMultiplicity.at(p); + + assert(isa(Slot)); return JunkSlotMultiplicity; } private: - std::map + std::map FunctionCallReturnLabelSlotMultiplicity; int FunctionReturnLabelSlotMultiplicity = 0; - std::map VariableSlotMultiplicity; - std::map LiteralSlotMultiplicity; - std::map SymbolSlotMultiplicity; - std::map TemporarySlotMultiplicity; + std::map VariableSlotMultiplicity; + std::map LiteralSlotMultiplicity; + std::map SymbolSlotMultiplicity; + std::map TemporarySlotMultiplicity; int JunkSlotMultiplicity = 0; }; @@ -471,9 +471,8 @@ void createStackLayout(Stack &CurrentStack, Stack const &TargetStack, --multiplicity[slot]; for (unsigned Offset = 0; Offset < targetStack.size(); ++Offset) { - auto &Slot = targetStack[Offset]; - if (std::holds_alternative(Slot) && - Offset < currentStack.size()) + auto *Slot = targetStack[Offset]; + if (isa(Slot) && Offset < currentStack.size()) ++multiplicity[currentStack[Offset]]; else ++multiplicity[Slot]; @@ -482,7 +481,7 @@ void createStackLayout(Stack &CurrentStack, Stack const &TargetStack, bool isCompatible(size_t Source, size_t Target) { return Source < currentStack.size() && Target < targetStack.size() && - (std::holds_alternative(targetStack[Target]) || + (isa(targetStack[Target]) || currentStack[Source] == targetStack[Target]); } @@ -499,8 +498,7 @@ void createStackLayout(Stack &CurrentStack, Stack const &TargetStack, } bool targetIsArbitrary(size_t Offset) { - return Offset < targetStack.size() && - std::holds_alternative(targetStack[Offset]); + return Offset < targetStack.size() && isa(targetStack[Offset]); } void swap(size_t I) { @@ -518,7 +516,7 @@ void createStackLayout(Stack &CurrentStack, Stack const &TargetStack, } void pushOrDupTarget(size_t Offset) { - auto const &targetSlot = targetStack[Offset]; + auto *targetSlot = targetStack[Offset]; pushOrDupCallback(targetSlot); currentStack.push_back(targetSlot); } @@ -529,10 +527,10 @@ void createStackLayout(Stack &CurrentStack, Stack const &TargetStack, assert(CurrentStack.size() == TargetStack.size()); for (unsigned I = 0; I < CurrentStack.size(); ++I) { - auto &Current = CurrentStack[I]; - auto &Target = TargetStack[I]; - if (std::holds_alternative(Target)) - Current = JunkSlot{}; + StackSlot *&Current = CurrentStack[I]; + auto *Target = TargetStack[I]; + if (isa(Target)) + Current = EVMStackModel::getJunkSlot(); else assert(Current == Target); } diff --git a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp index bbf6d822fb95..addad590a46c 100644 --- a/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp +++ b/llvm/lib/Target/EVM/EVMStackifyCodeEmitter.cpp @@ -234,7 +234,7 @@ void EVMStackifyCodeEmitter::adjustStackForInst(const MachineInstr *MI, unsigned Idx = 0; for (const auto &MO : MI->defs()) { assert(MO.isReg()); - CurrentStack.emplace_back(TemporarySlot{MI, MO.getReg(), Idx++}); + CurrentStack.push_back(StackModel.getTemporarySlot(MI, Idx++)); } assert(Emitter.stackHeight() == CurrentStack.size()); } @@ -247,10 +247,10 @@ void EVMStackifyCodeEmitter::visitCall(const FunctionCall &Call) { // Assert that we got the correct return label on stack. if (callWillReturn(Call.MI)) { - [[maybe_unused]] const auto *returnLabelSlot = - std::get_if( - &CurrentStack[CurrentStack.size() - NumArgs]); - assert(returnLabelSlot && returnLabelSlot->Call == Call.MI); + [[maybe_unused]] const auto *ReturnLabelSlot = + dyn_cast( + CurrentStack[CurrentStack.size() - NumArgs]); + assert(ReturnLabelSlot && ReturnLabelSlot->getCall() == Call.MI); } // Emit call. @@ -275,10 +275,10 @@ void EVMStackifyCodeEmitter::visitAssign(const Assignment &Assignment) { assert(Emitter.stackHeight() == CurrentStack.size()); // Invalidate occurrences of the assigned variables. - for (auto &CurrentSlot : CurrentStack) - if (const VariableSlot *VarSlot = std::get_if(&CurrentSlot)) - if (is_contained(Assignment.Variables, *VarSlot)) - CurrentSlot = JunkSlot{}; + for (auto *&CurrentSlot : CurrentStack) + if (const auto *VarSlot = dyn_cast(CurrentSlot)) + if (is_contained(Assignment.Variables, VarSlot)) + CurrentSlot = EVMStackModel::getJunkSlot(); // Assign variables to current stack top. assert(CurrentStack.size() >= Assignment.Variables.size()); @@ -290,30 +290,12 @@ bool EVMStackifyCodeEmitter::areLayoutsCompatible(const Stack &SourceStack, const Stack &TargetStack) { return SourceStack.size() == TargetStack.size() && all_of(zip_equal(SourceStack, TargetStack), [](const auto &Pair) { - const auto &[Src, Tgt] = Pair; - return std::holds_alternative(Tgt) || (Src == Tgt); + const auto [Src, Tgt] = Pair; + return isa(Tgt) || (Src == Tgt); }); } void EVMStackifyCodeEmitter::createStackLayout(const Stack &TargetStack) { - auto SlotVariableName = [](const StackSlot &Slot) { - return std::visit( - Overload{ - [&](const VariableSlot &Var) { - SmallString<1024> StrBuf; - raw_svector_ostream OS(StrBuf); - OS << printReg(Var.VirtualReg, nullptr, 0, nullptr); - return std::string(StrBuf.c_str()); - }, - [&](const FunctionCallReturnLabelSlot &) { return std::string(); }, - [&](const FunctionReturnLabelSlot &) { return std::string(); }, - [&](const LiteralSlot &) { return std::string(); }, - [&](const SymbolSlot &) { return std::string(); }, - [&](const TemporarySlot &) { return std::string(); }, - [&](const JunkSlot &) { return std::string(); }}, - Slot); - }; - assert(Emitter.stackHeight() == CurrentStack.size()); // ::createStackLayout asserts that it has successfully achieved the target // layout. @@ -327,26 +309,23 @@ void EVMStackifyCodeEmitter::createStackLayout(const Stack &TargetStack) { Emitter.emitSWAP(I); } else { int Deficit = static_cast(I) - 16; - const StackSlot &DeepSlot = CurrentStack[CurrentStack.size() - I - 1]; - std::string VarNameDeep = SlotVariableName(DeepSlot); - std::string VarNameTop = SlotVariableName(CurrentStack.back()); + const StackSlot *DeepSlot = CurrentStack[CurrentStack.size() - I - 1]; std::string Msg = (Twine("cannot swap ") + - (VarNameDeep.empty() ? ("slot " + stackSlotToString(DeepSlot)) - : (Twine("variable ") + VarNameDeep)) + - " with " + - (VarNameTop.empty() - ? ("slot " + stackSlotToString(CurrentStack.back())) - : (Twine("variable ") + VarNameTop)) + - ": too deep in the stack by " + std::to_string(Deficit) + - " slots in " + stackToString(CurrentStack)) + (isa(DeepSlot) ? "variable " : "slot ") + + DeepSlot->toString() + " with " + + (isa(CurrentStack.back()) ? "variable " + : "slot ") + + CurrentStack.back()->toString() + ": too deep in the stack by " + + std::to_string(Deficit) + " slots in " + + stackToString(CurrentStack)) .str(); report_fatal_error(MF.getName() + Twine(": ") + Msg); } }, // Push or dup callback. - [&](const StackSlot &Slot) { + [&](const StackSlot *Slot) { assert(CurrentStack.size() == Emitter.stackHeight()); // Dup the slot, if already on stack and reachable. @@ -357,14 +336,11 @@ void EVMStackifyCodeEmitter::createStackLayout(const Stack &TargetStack) { Emitter.emitDUP(static_cast(Depth + 1)); return; } - if (!isRematerializable(Slot)) { - std::string VarName = SlotVariableName(Slot); + if (!Slot->isRematerializable()) { std::string Msg = - ((VarName.empty() ? "slot " + stackSlotToString(Slot) - : Twine("variable ") + VarName) + - " is " + std::to_string(Depth - 15) + - " too deep in the stack " + stackToString(CurrentStack)) - .str(); + (isa(Slot) ? "variable " : "slot ") + + Slot->toString() + " is " + std::to_string(Depth - 15) + + " too deep in the stack " + stackToString(CurrentStack); report_fatal_error(MF.getName() + ": " + Msg); return; @@ -375,32 +351,25 @@ void EVMStackifyCodeEmitter::createStackLayout(const Stack &TargetStack) { // The slot can be freely generated or is an unassigned return variable. // Push it. - std::visit( - Overload{[&](const LiteralSlot &Literal) { - Emitter.emitConstant(Literal.Value); - }, - [&](const SymbolSlot &Symbol) { - Emitter.emitSymbol(Symbol.MI, Symbol.Symbol); - }, - [&](const FunctionReturnLabelSlot &) { - llvm_unreachable("Cannot produce function return label"); - }, - [&](const FunctionCallReturnLabelSlot &ReturnLabel) { - Emitter.emitLabelReference(ReturnLabel.Call); - }, - [&](const VariableSlot &Variable) { - llvm_unreachable("Variable not found on stack"); - }, - [&](const TemporarySlot &) { - llvm_unreachable("Function call result requested, but " - "not found on stack."); - }, - [&](const JunkSlot &) { - // Note: this will always be popped, so we can push - // anything. - Emitter.emitConstant(0); - }}, - Slot); + if (const auto *L = dyn_cast(Slot)) { + Emitter.emitConstant(L->getValue()); + } else if (const auto *S = dyn_cast(Slot)) { + Emitter.emitSymbol(S->getMachineInstr(), S->getSymbol()); + } else if (const auto *CallRet = + dyn_cast(Slot)) { + Emitter.emitLabelReference(CallRet->getCall()); + } else if (isa(Slot)) { + llvm_unreachable("Cannot produce function return label"); + } else if (isa(Slot)) { + llvm_unreachable("Variable not found on stack"); + } else if (isa(Slot)) { + llvm_unreachable("Function call result requested, but " + "not found on stack."); + } else { + assert(isa(Slot)); + // Note: this will always be popped, so we can push anything. + Emitter.emitConstant(0); + } }, // Pop callback. [&]() { Emitter.emitPOP(); }); diff --git a/llvm/unittests/Target/EVM/CMakeLists.txt b/llvm/unittests/Target/EVM/CMakeLists.txt index f5aee1e08f97..315aef1b0650 100644 --- a/llvm/unittests/Target/EVM/CMakeLists.txt +++ b/llvm/unittests/Target/EVM/CMakeLists.txt @@ -19,6 +19,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_target_unittest(EVMTests StackShuffler.cpp + StackModel.cpp ) set_property(TARGET EVMTests PROPERTY FOLDER "Tests/UnitTests/TargetTests") diff --git a/llvm/unittests/Target/EVM/StackModel.cpp b/llvm/unittests/Target/EVM/StackModel.cpp new file mode 100644 index 000000000000..aaf9da9debe7 --- /dev/null +++ b/llvm/unittests/Target/EVM/StackModel.cpp @@ -0,0 +1,157 @@ +//===---------- llvm/unittests/EVM/StackSlotBuilder.cpp -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "EVMStackModel.h" +#include "EVMTargetMachine.h" +#include "MCTargetDesc/EVMMCTargetDesc.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Target/CodeGenCWrappers.h" +#include "llvm/Target/TargetMachine.h" +#include "gtest/gtest.h" + +using namespace llvm; + +#include +static TargetMachine *unwrap(LLVMTargetMachineRef P) { + return reinterpret_cast(P); +} + +class EVMStackModelTest : public testing::Test { + void SetUp() override { + LLVMInitializeEVMTargetInfo(); + LLVMInitializeEVMTarget(); + LLVMInitializeEVMTargetMC(); + + LLVMTargetRef Target = 0; + const char *Triple = "evm"; + char *ErrMsg = 0; + if (LLVMGetTargetFromTriple(Triple, &Target, &ErrMsg)) { + FAIL() << "Failed to create target from the triple (" << Triple + << "): " << ErrMsg; + return; + } + ASSERT_TRUE(Target); + + // Construct a TargetMachine. + TM = + LLVMCreateTargetMachine(Target, Triple, "", "", LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelDefault); + Context = std::make_unique(); + Mod = std::make_unique("TestModule", *Context); + Mod->setDataLayout(unwrap(TM)->createDataLayout()); + const LLVMTargetMachine &LLVMTM = + static_cast(*unwrap(TM)); + MMIWP = std::make_unique(&LLVMTM); + + Type *const ReturnType = Type::getVoidTy(Mod->getContext()); + FunctionType *FunctionType = FunctionType::get(ReturnType, false); + Function *const F = Function::Create( + FunctionType, GlobalValue::InternalLinkage, "TestFunction", Mod.get()); + MF = &MMIWP->getMMI().getOrCreateMachineFunction(*F); + + LIS = std::make_unique(); + StackModel = std::make_unique(*MF, *LIS.get()); + } + + void TearDown() override { LLVMDisposeTargetMachine(TM); } + +public: + LLVMTargetMachineRef TM; + std::unique_ptr Context; + std::unique_ptr MMIWP; + std::unique_ptr Mod; + std::unique_ptr LIS; + std::unique_ptr StackModel; + MachineFunction *MF = nullptr; +}; + +TEST_F(EVMStackModelTest, LiteralSlot) { + APInt Int0 = APInt(32, 0); + APInt Int42 = APInt(32, 42); + + auto *LiteralSlot0 = StackModel->getLiteralSlot(Int0); + auto *LiteralSlot0Copy = StackModel->getLiteralSlot(Int0); + EXPECT_TRUE(LiteralSlot0 == LiteralSlot0Copy); + + auto *LiteralSlot42 = StackModel->getLiteralSlot(Int42); + EXPECT_TRUE(LiteralSlot0 != LiteralSlot42); + EXPECT_TRUE(LiteralSlot0->getValue() != LiteralSlot42->getValue()); +} + +TEST_F(EVMStackModelTest, VariableSlot) { + MachineRegisterInfo &MRI = MF->getRegInfo(); + Register Reg1 = MRI.createVirtualRegister(&EVM::GPRRegClass); + Register Reg2 = MRI.createVirtualRegister(&EVM::GPRRegClass); + + auto *VarSlot1 = StackModel->getVariableSlot(Reg1); + auto *VarSlot1Copy = StackModel->getVariableSlot(Reg1); + EXPECT_TRUE(VarSlot1 == VarSlot1Copy); + + auto *VarSlot2 = StackModel->getVariableSlot(Reg2); + EXPECT_TRUE(VarSlot1 != VarSlot2); + EXPECT_TRUE(VarSlot1->getReg() != VarSlot2->getReg()); +} + +TEST_F(EVMStackModelTest, SymbolSlot) { + MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + auto MI = MF->CreateMachineInstr(MCID, DebugLoc()); + auto MAI = MCAsmInfo(); + auto MC = std::make_unique(Triple("evm"), &MAI, nullptr, nullptr, + nullptr, nullptr, false); + MCSymbol *Sym1 = MC->createTempSymbol("sym1", false); + MCSymbol *Sym2 = MC->createTempSymbol("sym2", false); + + auto *SymSlot1 = StackModel->getSymbolSlot(Sym1, MI); + auto *SymSlot1Copy = StackModel->getSymbolSlot(Sym1, MI); + EXPECT_TRUE(SymSlot1 == SymSlot1Copy); + + auto *SymSlot2 = StackModel->getSymbolSlot(Sym2, MI); + EXPECT_TRUE(SymSlot1 != SymSlot2); + EXPECT_TRUE(SymSlot1->getSymbol() != SymSlot2->getSymbol()); +} + +TEST_F(EVMStackModelTest, FunctionCallReturnLabelSlot) { + const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo(); + auto Call = MF->CreateMachineInstr(TII->get(EVM::FCALL), DebugLoc()); + auto Call2 = MF->CreateMachineInstr(TII->get(EVM::FCALL), DebugLoc()); + + auto *RetSlot1 = StackModel->getFunctionCallReturnLabelSlot(Call); + auto *RetSlot1Copy = StackModel->getFunctionCallReturnLabelSlot(Call); + EXPECT_TRUE(RetSlot1 == RetSlot1Copy); + + auto *RetSlot2 = StackModel->getFunctionCallReturnLabelSlot(Call2); + EXPECT_TRUE(RetSlot1 != RetSlot2); + EXPECT_TRUE(RetSlot1->getCall() != RetSlot2->getCall()); +} + +TEST_F(EVMStackModelTest, FunctionReturnLabelSlot) { + // Be sure the slot for function return label is a single one. + EXPECT_TRUE(StackModel->getFunctionReturnLabelSlot(MF) == + StackModel->getFunctionReturnLabelSlot(MF)); +} + +TEST_F(EVMStackModelTest, TemporarySlot) { + MCInstrDesc MCID = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + auto MI = MF->CreateMachineInstr(MCID, DebugLoc()); + + auto *TempSlot1 = StackModel->getTemporarySlot(MI, 0); + auto *TempSlot1Copy = StackModel->getTemporarySlot(MI, 0); + EXPECT_TRUE(TempSlot1 == TempSlot1Copy); + + auto *TempSlot2 = StackModel->getTemporarySlot(MI, 1); + EXPECT_TRUE(TempSlot1 != TempSlot2); +} + +TEST_F(EVMStackModelTest, JunkSlot) { + // Be sure the JunkSlot is a single one. + EXPECT_TRUE(EVMStackModel::getJunkSlot() == EVMStackModel::getJunkSlot()); +} diff --git a/llvm/unittests/Target/EVM/StackShuffler.cpp b/llvm/unittests/Target/EVM/StackShuffler.cpp index 8324d2730723..2e99bb685ee8 100644 --- a/llvm/unittests/Target/EVM/StackShuffler.cpp +++ b/llvm/unittests/Target/EVM/StackShuffler.cpp @@ -67,6 +67,9 @@ class LLDCTest : public testing::Test { Function *const F = Function::Create( FunctionType, GlobalValue::InternalLinkage, "TestFunction", Mod.get()); MF = &MMIWP->getMMI().getOrCreateMachineFunction(*F); + + LIS = std::make_unique(); + StackModel = std::make_unique(*MF, *LIS.get()); } void TearDown() override { LLVMDisposeTargetMachine(TM); } @@ -77,6 +80,8 @@ class LLDCTest : public testing::Test { std::unique_ptr Context; std::unique_ptr Mod; std::unique_ptr MMIWP; + std::unique_ptr LIS; + std::unique_ptr StackModel; }; TEST_F(LLDCTest, Basic) { @@ -101,46 +106,49 @@ TEST_F(LLDCTest, Basic) { // Create the source stack layout: // [ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET %5 ] - SourceStack.emplace_back(VariableSlot{Instrs[0].second}); - SourceStack.emplace_back(VariableSlot{Instrs[1].second}); - SourceStack.emplace_back(VariableSlot{Instrs[2].second}); - SourceStack.emplace_back(VariableSlot{Instrs[3].second}); - SourceStack.emplace_back(VariableSlot{Instrs[4].second}); - SourceStack.emplace_back(VariableSlot{Instrs[5].second}); - SourceStack.emplace_back(VariableSlot{Instrs[6].second}); - SourceStack.emplace_back(VariableSlot{Instrs[7].second}); - SourceStack.emplace_back(VariableSlot{Instrs[9].second}); - SourceStack.emplace_back(VariableSlot{Instrs[10].second}); - SourceStack.emplace_back(VariableSlot{Instrs[11].second}); - SourceStack.emplace_back(VariableSlot{Instrs[12].second}); - SourceStack.emplace_back(VariableSlot{Instrs[13].second}); - SourceStack.emplace_back(VariableSlot{Instrs[14].second}); - SourceStack.emplace_back(VariableSlot{Instrs[15].second}); - SourceStack.emplace_back(VariableSlot{Instrs[16].second}); - SourceStack.emplace_back(FunctionReturnLabelSlot{MBB->getParent()}); - SourceStack.emplace_back(FunctionReturnLabelSlot{MBB->getParent()}); - SourceStack.emplace_back(VariableSlot{Instrs[5].second}); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[0].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[1].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[2].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[3].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[4].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[5].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[6].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[7].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[9].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[10].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[11].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[12].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[13].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[14].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[15].second)); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[16].second)); + SourceStack.emplace_back( + StackModel->getFunctionReturnLabelSlot(MBB->getParent())); + SourceStack.emplace_back( + StackModel->getFunctionReturnLabelSlot(MBB->getParent())); + SourceStack.emplace_back(StackModel->getVariableSlot(Instrs[5].second)); // [ %1 %0 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET JUNK JUNK ] - TargetStack.emplace_back(VariableSlot{Instrs[1].second}); - TargetStack.emplace_back(VariableSlot{Instrs[0].second}); - TargetStack.emplace_back(VariableSlot{Instrs[2].second}); - TargetStack.emplace_back(VariableSlot{Instrs[3].second}); - TargetStack.emplace_back(VariableSlot{Instrs[4].second}); - TargetStack.emplace_back(VariableSlot{Instrs[5].second}); - TargetStack.emplace_back(VariableSlot{Instrs[6].second}); - TargetStack.emplace_back(VariableSlot{Instrs[7].second}); - TargetStack.emplace_back(VariableSlot{Instrs[9].second}); - TargetStack.emplace_back(VariableSlot{Instrs[10].second}); - TargetStack.emplace_back(VariableSlot{Instrs[11].second}); - TargetStack.emplace_back(VariableSlot{Instrs[12].second}); - TargetStack.emplace_back(VariableSlot{Instrs[13].second}); - TargetStack.emplace_back(VariableSlot{Instrs[14].second}); - TargetStack.emplace_back(VariableSlot{Instrs[15].second}); - TargetStack.emplace_back(VariableSlot{Instrs[16].second}); - TargetStack.emplace_back(FunctionReturnLabelSlot{MBB->getParent()}); - TargetStack.emplace_back(JunkSlot{}); - TargetStack.emplace_back(JunkSlot{}); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[1].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[0].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[2].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[3].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[4].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[5].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[6].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[7].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[9].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[10].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[11].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[12].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[13].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[14].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[15].second)); + TargetStack.emplace_back(StackModel->getVariableSlot(Instrs[16].second)); + TargetStack.emplace_back( + StackModel->getFunctionReturnLabelSlot(MBB->getParent())); + TargetStack.emplace_back(StackModel->getJunkSlot()); + TargetStack.emplace_back(StackModel->getJunkSlot()); StringRef Reference("\ [ %0 %1 %2 %3 %4 %5 %6 %7 %9 %10 %11 %12 %13 %14 %15 %16 RET RET %5 ]\n\ @@ -170,10 +178,10 @@ PUSH JUNK\n\ Output << stackToString(SourceStack) << '\n'; Output << "SWAP" << SwapDepth << '\n'; }, - [&](StackSlot const &Slot) { // dupOrPush + [&](const StackSlot *Slot) { // dupOrPush Output << stackToString(SourceStack) << '\n'; - if (isRematerializable(Slot)) - Output << "PUSH " << stackSlotToString(Slot) << '\n'; + if (Slot->isRematerializable()) + Output << "PUSH " << Slot->toString() << '\n'; else { Stack TmpStack = SourceStack; std::reverse(TmpStack.begin(), TmpStack.end());