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 <class... Ts> struct Overload : Ts... {
 };
 template <class... Ts> Overload(Ts...) -> Overload<Ts...>;
 
-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<Function>(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<VariableSlot>(&ParamSlot))
-      OS << printReg(Slot->VirtualReg, nullptr, 0, nullptr) << ' ';
-    else if (std::holds_alternative<JunkSlot>(ParamSlot))
+  for (const StackSlot *ParamSlot : StackModel.getFunctionParameters()) {
+    if (const auto *Slot = dyn_cast<VariableSlot>(ParamSlot))
+      OS << printReg(Slot->getReg(), nullptr, 0, nullptr) << ' ';
+    else if (isa<JunkSlot>(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 <cassert>
 #include <map>
-#include <numeric>
-#include <set>
-#include <variant>
 
 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<Register> Result;
-    for (auto const &Slot : SlotRange)
-      if (auto const *VarSlot = std::get_if<VariableSlot>(&Slot))
-        if (!is_contained(Result, VarSlot->VirtualReg))
-          Result.push_back(VarSlot->VirtualReg);
+    for (auto const *Slot : SlotRange)
+      if (auto const *VarSlot = dyn_cast<VariableSlot>(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<std::variant<PreviousSlot, StackSlot>>;
+  using LayoutT = SmallVector<std::variant<PreviousSlot, StackSlot *>>;
 
   // 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<StackSlot> Outputs;
+    std::set<StackSlot *> 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<StackSlot>(&LayoutSlot))
+      for (const auto &LayoutSlot : Layout)
+        if (auto Slot = std::get_if<StackSlot *>(&LayoutSlot))
           Outputs.insert(*Slot);
 
-      for (auto const &LayoutSlot : Layout)
-        if (const StackSlot *Slot = std::get_if<StackSlot>(&LayoutSlot))
+      for (const auto &LayoutSlot : Layout)
+        if (auto Slot = std::get_if<StackSlot *>(&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<JunkSlot>(Post[Target]) ||
+             (isa<JunkSlot>(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<PreviousSlot>(Layout[Lhs]) &&
+          std::holds_alternative<PreviousSlot>(Layout[Rhs]))
+        return true;
+
+      auto SlotLHS = std::get_if<StackSlot *>(&Layout[Lhs]);
+      auto SlotRHS = std::get_if<StackSlot *>(&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<JunkSlot>(Post[Offset]);
+      return Offset < Post.size() && isa<JunkSlot>(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<std::optional<StackSlot>> IdealLayout(Post.size(), std::nullopt);
+  SmallVector<StackSlot *> 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<PreviousSlot>(&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<Assignment>(&Operation.Operation))
-    for (auto &StackSlot : IdealStack)
-      if (auto const *VarSlot = std::get_if<VariableSlot>(&StackSlot))
-        assert(!is_contained(Assign->Variables, *VarSlot));
+    for (auto *StackSlot : IdealStack)
+      if (const auto *VarSlot = dyn_cast<VariableSlot>(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<LiteralSlot>(Slot) ||
-                 std::holds_alternative<SymbolSlot>(Slot) ||
-                 std::holds_alternative<FunctionCallReturnLabelSlot>(Slot);
+        Candidate.begin(), Candidate.end(), [](const StackSlot *Slot) {
+          return isa<LiteralSlot>(Slot) || isa<SymbolSlot>(Slot) ||
+                 isa<FunctionCallReturnLabelSlot>(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 <ostream>
-#include <variant>
 
 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<Function>(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<EVMMachineFunctionInfo>();
-  SmallVector<StackSlot> Parameters(MFI->getNumParams(), JunkSlot{});
+  SmallVector<StackSlot *> 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<VariableSlot> Variables;
+  SmallVector<VariableSlot *> 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 <variant>
@@ -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<FunctionCallReturnLabelSlot, FunctionReturnLabelSlot,
-                 VariableSlot, LiteralSlot, SymbolSlot, TemporarySlot,
-                 JunkSlot>;
-
 /// The stack top is the last element of the vector.
-using Stack = SmallVector<StackSlot>;
-
-/// 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<decltype(TypedSlot)>::isRematerializable;
-      },
-      Slot);
-}
+using Stack = SmallVector<StackSlot *>;
 
 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<VariableSlot> Variables;
+  SmallVector<VariableSlot *> Variables;
 };
 
 struct Operation {
@@ -193,10 +228,29 @@ struct Operation {
 };
 
 class EVMStackModel {
+  MachineFunction &MF;
+  const LiveIntervals &LIS;
+  DenseMap<const MachineBasicBlock *, SmallVector<Operation>> OperationsMap;
+
+  // Storage for stack slots.
+  mutable DenseMap<APInt, std::unique_ptr<LiteralSlot>> LiteralStorage;
+  mutable DenseMap<Register, std::unique_ptr<VariableSlot>> VariableStorage;
+  mutable DenseMap<std::pair<MCSymbol *, const MachineInstr *>,
+                   std::unique_ptr<SymbolSlot>>
+      SymbolStorage;
+  mutable DenseMap<const MachineInstr *,
+                   std::unique_ptr<FunctionCallReturnLabelSlot>>
+      FunctionCallReturnLabelStorage;
+  mutable DenseMap<std::pair<const MachineInstr *, size_t>,
+                   std::unique_ptr<TemporarySlot>>
+      TemporaryStorage;
+
+  // There should be a single FunctionReturnLabelSlot for the MF.
+  mutable std::unique_ptr<FunctionReturnLabelSlot> 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<LiteralSlot>(V);
+    return LiteralStorage[V].get();
+  }
+  VariableSlot *getVariableSlot(const Register &R) const {
+    if (VariableStorage.count(R) == 0)
+      VariableStorage[R] = std::make_unique<VariableSlot>(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<SymbolSlot>(S, MI);
+    return SymbolStorage[Key].get();
+  }
+  FunctionCallReturnLabelSlot *
+  getFunctionCallReturnLabelSlot(const MachineInstr *Call) const {
+    if (FunctionCallReturnLabelStorage.count(Call) == 0)
+      FunctionCallReturnLabelStorage[Call] =
+          std::make_unique<FunctionCallReturnLabelSlot>(Call);
+    return FunctionCallReturnLabelStorage[Call].get();
+  }
+  FunctionReturnLabelSlot *
+  getFunctionReturnLabelSlot(const MachineFunction *MF) const {
+    if (!TheFunctionReturnLabelSlot)
+      TheFunctionReturnLabelSlot =
+          std::make_unique<FunctionReturnLabelSlot>(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<TemporarySlot>(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<Operation> &Ops) const;
-
-  MachineFunction &MF;
-  const LiveIntervals &LIS;
-  DenseMap<const MachineBasicBlock *, SmallVector<Operation>> 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<FunctionCallReturnLabelSlot>(&Slot))
-      return FunctionCallReturnLabelSlotMultiplicity[*p];
-    if (std::holds_alternative<FunctionReturnLabelSlot>(Slot))
+  int &operator[](const StackSlot *Slot) {
+    if (auto *p = dyn_cast<FunctionCallReturnLabelSlot>(Slot))
+      return FunctionCallReturnLabelSlotMultiplicity[p];
+    if (isa<FunctionReturnLabelSlot>(Slot))
       return FunctionReturnLabelSlotMultiplicity;
-    if (auto *p = std::get_if<VariableSlot>(&Slot))
-      return VariableSlotMultiplicity[*p];
-    if (auto *p = std::get_if<LiteralSlot>(&Slot))
-      return LiteralSlotMultiplicity[*p];
-    if (auto *p = std::get_if<SymbolSlot>(&Slot))
-      return SymbolSlotMultiplicity[*p];
-    if (auto *p = std::get_if<TemporarySlot>(&Slot))
-      return TemporarySlotMultiplicity[*p];
-
-    assert(std::holds_alternative<JunkSlot>(Slot));
+    if (auto *p = dyn_cast<VariableSlot>(Slot))
+      return VariableSlotMultiplicity[p];
+    if (auto *p = dyn_cast<LiteralSlot>(Slot))
+      return LiteralSlotMultiplicity[p];
+    if (auto *p = dyn_cast<SymbolSlot>(Slot))
+      return SymbolSlotMultiplicity[p];
+    if (auto *p = dyn_cast<TemporarySlot>(Slot))
+      return TemporarySlotMultiplicity[p];
+
+    assert(isa<JunkSlot>(Slot));
     return JunkSlotMultiplicity;
   }
 
-  int at(StackSlot const &Slot) const {
-    if (auto *p = std::get_if<FunctionCallReturnLabelSlot>(&Slot))
-      return FunctionCallReturnLabelSlotMultiplicity.at(*p);
-    if (std::holds_alternative<FunctionReturnLabelSlot>(Slot))
+  int at(const StackSlot *Slot) const {
+    if (auto *p = dyn_cast<FunctionCallReturnLabelSlot>(Slot))
+      return FunctionCallReturnLabelSlotMultiplicity.at(p);
+    if (isa<FunctionReturnLabelSlot>(Slot))
       return FunctionReturnLabelSlotMultiplicity;
-    if (auto *p = std::get_if<VariableSlot>(&Slot))
-      return VariableSlotMultiplicity.at(*p);
-    if (auto *p = std::get_if<LiteralSlot>(&Slot))
-      return LiteralSlotMultiplicity.at(*p);
-    if (auto *p = std::get_if<SymbolSlot>(&Slot))
-      return SymbolSlotMultiplicity.at(*p);
-    if (auto *p = std::get_if<TemporarySlot>(&Slot))
-      return TemporarySlotMultiplicity.at(*p);
-
-    assert(std::holds_alternative<JunkSlot>(Slot));
+    if (auto *p = dyn_cast<VariableSlot>(Slot))
+      return VariableSlotMultiplicity.at(p);
+    if (auto *p = dyn_cast<LiteralSlot>(Slot))
+      return LiteralSlotMultiplicity.at(p);
+    if (auto *p = dyn_cast<SymbolSlot>(Slot))
+      return SymbolSlotMultiplicity.at(p);
+    if (auto *p = dyn_cast<TemporarySlot>(Slot))
+      return TemporarySlotMultiplicity.at(p);
+
+    assert(isa<JunkSlot>(Slot));
     return JunkSlotMultiplicity;
   }
 
 private:
-  std::map<FunctionCallReturnLabelSlot, int>
+  std::map<const FunctionCallReturnLabelSlot *, int>
       FunctionCallReturnLabelSlotMultiplicity;
   int FunctionReturnLabelSlotMultiplicity = 0;
-  std::map<VariableSlot, int> VariableSlotMultiplicity;
-  std::map<LiteralSlot, int> LiteralSlotMultiplicity;
-  std::map<SymbolSlot, int> SymbolSlotMultiplicity;
-  std::map<TemporarySlot, int> TemporarySlotMultiplicity;
+  std::map<const VariableSlot *, int> VariableSlotMultiplicity;
+  std::map<const LiteralSlot *, int> LiteralSlotMultiplicity;
+  std::map<const SymbolSlot *, int> SymbolSlotMultiplicity;
+  std::map<const TemporarySlot *, int> 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<JunkSlot>(Slot) &&
-            Offset < currentStack.size())
+        auto *Slot = targetStack[Offset];
+        if (isa<JunkSlot>(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<JunkSlot>(targetStack[Target]) ||
+             (isa<JunkSlot>(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<JunkSlot>(targetStack[Offset]);
+      return Offset < targetStack.size() && isa<JunkSlot>(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<JunkSlot>(Target))
-      Current = JunkSlot{};
+    StackSlot *&Current = CurrentStack[I];
+    auto *Target = TargetStack[I];
+    if (isa<JunkSlot>(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<FunctionCallReturnLabelSlot>(
-            &CurrentStack[CurrentStack.size() - NumArgs]);
-    assert(returnLabelSlot && returnLabelSlot->Call == Call.MI);
+    [[maybe_unused]] const auto *ReturnLabelSlot =
+        dyn_cast<FunctionCallReturnLabelSlot>(
+            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<VariableSlot>(&CurrentSlot))
-      if (is_contained(Assignment.Variables, *VarSlot))
-        CurrentSlot = JunkSlot{};
+  for (auto *&CurrentSlot : CurrentStack)
+    if (const auto *VarSlot = dyn_cast<VariableSlot>(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<JunkSlot>(Tgt) || (Src == Tgt);
+           const auto [Src, Tgt] = Pair;
+           return isa<JunkSlot>(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<int>(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<VariableSlot>(DeepSlot) ? "variable " : "slot ") +
+               DeepSlot->toString() + " with " +
+               (isa<VariableSlot>(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<unsigned>(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<VariableSlot>(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<LiteralSlot>(Slot)) {
+          Emitter.emitConstant(L->getValue());
+        } else if (const auto *S = dyn_cast<SymbolSlot>(Slot)) {
+          Emitter.emitSymbol(S->getMachineInstr(), S->getSymbol());
+        } else if (const auto *CallRet =
+                       dyn_cast<FunctionCallReturnLabelSlot>(Slot)) {
+          Emitter.emitLabelReference(CallRet->getCall());
+        } else if (isa<FunctionReturnLabelSlot>(Slot)) {
+          llvm_unreachable("Cannot produce function return label");
+        } else if (isa<VariableSlot>(Slot)) {
+          llvm_unreachable("Variable not found on stack");
+        } else if (isa<TemporarySlot>(Slot)) {
+          llvm_unreachable("Function call result requested, but "
+                           "not found on stack.");
+        } else {
+          assert(isa<JunkSlot>(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 <iostream>
+static TargetMachine *unwrap(LLVMTargetMachineRef P) {
+  return reinterpret_cast<TargetMachine *>(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<LLVMContext>();
+    Mod = std::make_unique<Module>("TestModule", *Context);
+    Mod->setDataLayout(unwrap(TM)->createDataLayout());
+    const LLVMTargetMachine &LLVMTM =
+        static_cast<const LLVMTargetMachine &>(*unwrap(TM));
+    MMIWP = std::make_unique<MachineModuleInfoWrapperPass>(&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<LiveIntervals>();
+    StackModel = std::make_unique<EVMStackModel>(*MF, *LIS.get());
+  }
+
+  void TearDown() override { LLVMDisposeTargetMachine(TM); }
+
+public:
+  LLVMTargetMachineRef TM;
+  std::unique_ptr<LLVMContext> Context;
+  std::unique_ptr<MachineModuleInfoWrapperPass> MMIWP;
+  std::unique_ptr<Module> Mod;
+  std::unique_ptr<LiveIntervals> LIS;
+  std::unique_ptr<EVMStackModel> 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<MCContext>(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<LiveIntervals>();
+    StackModel = std::make_unique<EVMStackModel>(*MF, *LIS.get());
   }
 
   void TearDown() override { LLVMDisposeTargetMachine(TM); }
@@ -77,6 +80,8 @@ class LLDCTest : public testing::Test {
   std::unique_ptr<LLVMContext> Context;
   std::unique_ptr<Module> Mod;
   std::unique_ptr<MachineModuleInfoWrapperPass> MMIWP;
+  std::unique_ptr<LiveIntervals> LIS;
+  std::unique_ptr<EVMStackModel> 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());