Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EVM] [DO NOT MERGE] Add Ethereum stackification #714

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8694758
[EVM] Fix creation of bundles with terminators
PavelKopyl Nov 16, 2024
dac8052
[EVM] Fix ordering of ARGUMENT instructions
PavelKopyl Nov 15, 2024
9d1228e
[EVM] Add split critical edges pass
PavelKopyl Nov 15, 2024
0225e69
[EVM] Re-enable inlining
PavelKopyl Nov 15, 2024
4ce37f7
[EVM] Add backward propagation (BP) stackification
PavelKopyl Nov 15, 2024
7e5cde9
Handle EVM::LINKERSYMBOL after rebasing on '[EVM] Add libraries support'
akiramenai Jan 27, 2025
eedad7d
[EVM] Support commutable operations in BP stackification algorithm
PavelKopyl Nov 15, 2024
18be4d0
[EVM] Remove FunctionInfo struct (#742)
vladimirradosavljevic Nov 27, 2024
071bfc5
[EVM] Introduce pseudo jumps, call and ret instructions
vladimirradosavljevic Dec 4, 2024
de60fba
[EVM] Move generation of JUMPDEST to the AsmPrinter phase
vladimirradosavljevic Dec 5, 2024
c07f184
[EVM] Refactor EVMOptimizedCodeTransform
vladimirradosavljevic Dec 13, 2024
151a427
[EVM] Refactor EVMAssembly
vladimirradosavljevic Dec 13, 2024
c8459a1
[EVM] Merge EVMOptimizedCodeTransform and EVMAssembly into one file
vladimirradosavljevic Dec 12, 2024
d4ec2f1
[EVM] Address comments
vladimirradosavljevic Dec 18, 2024
28ff37c
[EVM] Refactor EVMControlFlowGraph/EVMControlFlowGraphBuilder
PavelKopyl Dec 20, 2024
5a91e2d
[EVM] Replace std::containers/algorithms with the llvm's counterparts
PavelKopyl Dec 20, 2024
4774ca5
[EVM] Refactor EVMStackLayoutGenerator to use LLVM API more broadly
PavelKopyl Jan 16, 2025
af527df
[EVM][CMake] Fix unittests shared libs build
akiramenai Jan 13, 2025
b8df3c6
[EVM] Refactor StackSlot std::variant -> LLVM style RTTI
akiramenai Jan 16, 2025
ffd4a21
[EVM] Update assert in EVMStackifyCodeEmitter::CodeEmitter::emitSymbol
vladimirradosavljevic Jan 28, 2025
0aeb883
[EVM] Improve Operation implementation
akiramenai Jan 21, 2025
0ea80f9
[EVM] Support immutables in new stackification algorithm
vladimirradosavljevic Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions llvm/lib/Target/EVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,27 @@ add_llvm_target(EVMCodeGen
EVMAllocaHoisting.cpp
EVMArgumentMove.cpp
EVMAsmPrinter.cpp
EVMBackwardPropagationStackification.cpp
EVMCodegenPrepare.cpp
EVMFrameLowering.cpp
EVMISelDAGToDAG.cpp
EVMISelLowering.cpp
EVMInstrInfo.cpp
EVMLinkRuntime.cpp
EVMLowerIntrinsics.cpp
EVMMachineCFGInfo.cpp
EVMMachineFunctionInfo.cpp
EVMMCInstLower.cpp
EVMOptimizeLiveIntervals.cpp
EVMRegColoring.cpp
EVMRegisterInfo.cpp
EVMSingleUseExpression.cpp
EVMSplitCriticalEdges.cpp
EVMStackDebug.cpp
EVMStackLayoutGenerator.cpp
EVMStackModel.cpp
EVMStackify.cpp
EVMStackifyCodeEmitter.cpp
EVMSubtarget.cpp
EVMTargetMachine.cpp
EVMTargetTransformInfo.cpp
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/EVM/EVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ ModulePass *createEVMLinkRuntimePass();
FunctionPass *createEVMOptimizeLiveIntervals();
FunctionPass *createEVMRegColoring();
FunctionPass *createEVMSingleUseExpression();
FunctionPass *createEVMSplitCriticalEdges();
FunctionPass *createEVMStackify();
FunctionPass *createEVMBPStackification();

// PassRegistry initialization declarations.
void initializeEVMCodegenPreparePass(PassRegistry &);
Expand All @@ -61,7 +63,9 @@ void initializeEVMLinkRuntimePass(PassRegistry &);
void initializeEVMOptimizeLiveIntervalsPass(PassRegistry &);
void initializeEVMRegColoringPass(PassRegistry &);
void initializeEVMSingleUseExpressionPass(PassRegistry &);
void initializeEVMSplitCriticalEdgesPass(PassRegistry &);
void initializeEVMStackifyPass(PassRegistry &);
void initializeEVMBPStackificationPass(PassRegistry &);

struct EVMLinkRuntimePass : PassInfoMixin<EVMLinkRuntimePass> {
EVMLinkRuntimePass() = default;
Expand Down
32 changes: 18 additions & 14 deletions llvm/lib/Target/EVM/EVMArgumentMove.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
//
//===----------------------------------------------------------------------===//
//
// This file moves ARGUMENT instructions after ScheduleDAG scheduling.
// This file moves and orders ARGUMENT instructions after ScheduleDAG
// scheduling.
//
// Arguments are really live-in registers, however, since we use virtual
// registers and LLVM doesn't support live-in virtual registers, we're
Expand Down Expand Up @@ -67,21 +68,24 @@ bool EVMArgumentMove::runOnMachineFunction(MachineFunction &MF) {

bool Changed = false;
MachineBasicBlock &EntryMBB = MF.front();
SmallVector<MachineInstr *> Args;
for (MachineInstr &MI : EntryMBB) {
if (EVM::ARGUMENT == MI.getOpcode())
Args.push_back(&MI);
}

// Look for the first NonArg instruction.
const auto InsertPt =
std::find_if_not(EntryMBB.begin(), EntryMBB.end(), [](auto &MI) {
return EVM::ARGUMENT == MI.getOpcode();
});
// Sort ARGUMENT instructions in ascending order of their arguments.
std::sort(Args.begin(), Args.end(),
[](const MachineInstr *MI1, const MachineInstr *MI2) {
int64_t Arg1Idx = MI1->getOperand(1).getImm();
int64_t Arg2Idx = MI2->getOperand(1).getImm();
return Arg1Idx < Arg2Idx;
});

// Now move any argument instructions later in the block
// to before our first NonArg instruction.
for (MachineInstr &MI : llvm::make_range(InsertPt, EntryMBB.end())) {
if (EVM::ARGUMENT == MI.getOpcode()) {
EntryMBB.insert(InsertPt, MI.removeFromParent());
Changed = true;
}
for (MachineInstr *MI : reverse(Args)) {
MachineInstr *Arg = MI->removeFromParent();
EntryMBB.insert(EntryMBB.begin(), Arg);
Changed = true;
}

return Changed;
}
118 changes: 82 additions & 36 deletions llvm/lib/Target/EVM/EVMAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

using namespace llvm;

extern cl::opt<bool> EVMKeepRegisters;

#define DEBUG_TYPE "asm-printer"

namespace {
Expand All @@ -52,43 +54,22 @@ class EVMAsmPrinter : public AsmPrinter {

StringRef getPassName() const override { return "EVM Assembly "; }

void SetupMachineFunction(MachineFunction &MF) override;

void emitInstruction(const MachineInstr *MI) override;

void emitFunctionEntryLabel() override;
void emitBasicBlockStart(const MachineBasicBlock &MBB) override;

/// Return true if the basic block has exactly one predecessor and the control
/// transfer mechanism between the predecessor and this block is a
/// fall-through.
bool isBlockOnlyReachableByFallthrough(
const MachineBasicBlock *MBB) const override;
void emitFunctionEntryLabel() override;

void emitEndOfAsmFile(Module &) override;

private:
void emitAssemblySymbol(const MachineInstr *MI);
void emitWideRelocatableSymbol(const MachineInstr *MI);
void emitLoadImmutableLabel(const MachineInstr *MI);
void emitJumpDest();
};
} // end of anonymous namespace

void EVMAsmPrinter::SetupMachineFunction(MachineFunction &MF) {
// Unbundle <push_label, jump> bundles.
for (MachineBasicBlock &MBB : MF) {
MachineBasicBlock::instr_iterator I = MBB.instr_begin(),
E = MBB.instr_end();
for (; I != E; ++I) {
if (I->isBundledWithPred()) {
assert(I->isConditionalBranch() || I->isUnconditionalBranch());
I->unbundleFromPred();
}
}
}

AsmPrinter::SetupMachineFunction(MF);
}

void EVMAsmPrinter::emitFunctionEntryLabel() {
AsmPrinter::emitFunctionEntryLabel();

Expand All @@ -111,19 +92,84 @@ void EVMAsmPrinter::emitFunctionEntryLabel() {
}
}

void EVMAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) {
AsmPrinter::emitBasicBlockStart(MBB);

// Emit JUMPDEST instruction at the beginning of the basic block, if
// this is not a block that is only reachable by fallthrough.
if (!EVMKeepRegisters && !AsmPrinter::isBlockOnlyReachableByFallthrough(&MBB))
emitJumpDest();
}

void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) {
EVMMCInstLower MCInstLowering(OutContext, *this, VRegMapping,
MF->getRegInfo());
unsigned Opc = MI->getOpcode();
if (Opc == EVM::DATASIZE_S || Opc == EVM::DATAOFFSET_S) {
emitAssemblySymbol(MI);

switch (MI->getOpcode()) {
default:
break;
case EVM::PseudoCALL: {
// Generate push instruction with the address of a function.
MCInst Push;
Push.setOpcode(EVM::PUSH4_S);
assert(MI->getOperand(0).isGlobal() &&
"The first operand of PseudoCALL should be a GlobalValue.");

// TODO: #745: Refactor EVMMCInstLower::Lower so we could use lowerOperand
// instead of creating a MCOperand directly.
MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create(
getSymbol(MI->getOperand(0).getGlobal()), OutContext));
Push.addOperand(MCOp);
EmitToStreamer(*OutStreamer, Push);

// Jump to a function.
MCInst Jump;
Jump.setOpcode(EVM::JUMP_S);
EmitToStreamer(*OutStreamer, Jump);

// In case a function has a return label, emit it, and also
// emit a JUMPDEST instruction.
if (MI->getNumExplicitOperands() > 1) {
assert(MI->getOperand(1).isMCSymbol() &&
"The second operand of PseudoCALL should be a MCSymbol.");
OutStreamer->emitLabel(MI->getOperand(1).getMCSymbol());
emitJumpDest();
}
return;
}
if (Opc == EVM::LINKERSYMBOL_S) {
emitWideRelocatableSymbol(MI);
case EVM::PseudoRET: {
// TODO: #746: Use PseudoInstExpansion and do this expansion in tblgen.
MCInst Jump;
Jump.setOpcode(EVM::JUMP_S);
EmitToStreamer(*OutStreamer, Jump);
return;
}
if (Opc == EVM::LOADIMMUTABLE_S) {
case EVM::PseudoJUMP:
case EVM::PseudoJUMPI: {
MCInst Push;
Push.setOpcode(EVM::PUSH4_S);

// TODO: #745: Refactor EVMMCInstLower::Lower so we could use lowerOperand
// instead of creating a MCOperand directly.
MCOperand MCOp = MCOperand::createExpr(MCSymbolRefExpr::create(
MI->getOperand(0).getMBB()->getSymbol(), OutContext));
Push.addOperand(MCOp);
EmitToStreamer(*OutStreamer, Push);

MCInst Jump;
Jump.setOpcode(MI->getOpcode() == EVM::PseudoJUMP ? EVM::JUMP_S
: EVM::JUMPI_S);
EmitToStreamer(*OutStreamer, Jump);
return;
}
case EVM::LINKERSYMBOL_S:
emitWideRelocatableSymbol(MI);
return;
case EVM::DATASIZE_S:
case EVM::DATAOFFSET_S:
emitAssemblySymbol(MI);
return;
case EVM::LOADIMMUTABLE_S:
emitLoadImmutableLabel(MI);
return;
}
Expand All @@ -133,12 +179,6 @@ void EVMAsmPrinter::emitInstruction(const MachineInstr *MI) {
EmitToStreamer(*OutStreamer, TmpInst);
}

bool EVMAsmPrinter::isBlockOnlyReachableByFallthrough(
const MachineBasicBlock *MBB) const {
// For simplicity, always emit BB labels.
return false;
}

// Lowers LOADIMMUTABLE_S as show below:
// LOADIMMUTABLE_S @immutable_id
// ->
Expand Down Expand Up @@ -247,6 +287,12 @@ void EVMAsmPrinter::emitEndOfAsmFile(Module &) {
ImmutablesMap.clear();
}

void EVMAsmPrinter::emitJumpDest() {
MCInst JumpDest;
JumpDest.setOpcode(EVM::JUMPDEST_S);
EmitToStreamer(*OutStreamer, JumpDest);
}

extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeEVMAsmPrinter() {
const RegisterAsmPrinter<EVMAsmPrinter> X(getTheEVMTarget());
}
100 changes: 100 additions & 0 deletions llvm/lib/Target/EVM/EVMBackwardPropagationStackification.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//===----- EVMBPStackification.cpp - BP stackification ---------*- C++ -*--===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file implements backward propagation (BP) stackification.
// Original idea was taken from the Ethereum's compiler (solc) stackification
// algorithm.
// The algorithm is broken into following components:
// - CFG (Control Flow Graph) and CFG builder. Stackification CFG has similar
// structure to LLVM CFG one, but employs wider notion of instruction.
// - Stack layout generator. Contains information about the stack layout at
// entry and exit of each CFG::BasicBlock. It also contains input/output
// stack layout for each operation.
// - Code transformation into stakified form. This component uses both CFG
// and the stack layout information to get stackified LLVM MIR.
// - Stack shuffler. Finds optimal (locally) transformation between two stack
// layouts using three primitives: POP, PUSHn, DUPn. The stack shuffler
// is used by the components above.
//
//===----------------------------------------------------------------------===//

#include "EVM.h"
#include "EVMStackifyCodeEmitter.h"
#include "EVMSubtarget.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

#define DEBUG_TYPE "evm-ethereum-stackify"

namespace {
class EVMBPStackification final : public MachineFunctionPass {
public:
static char ID; // Pass identification, replacement for typeid

EVMBPStackification() : MachineFunctionPass(ID) {}

private:
StringRef getPassName() const override {
return "EVM Ethereum stackification";
}

void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<LiveIntervals>();
AU.addRequired<MachineLoopInfo>();
MachineFunctionPass::getAnalysisUsage(AU);
}

bool runOnMachineFunction(MachineFunction &MF) override;

MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::TracksLiveness);
}
};
} // end anonymous namespace

char EVMBPStackification::ID = 0;

INITIALIZE_PASS_BEGIN(EVMBPStackification, DEBUG_TYPE,
"Backward propagation stackification", false, false)
INITIALIZE_PASS_DEPENDENCY(MachineLoopInfo)
INITIALIZE_PASS_END(EVMBPStackification, DEBUG_TYPE,
"Backward propagation stackification", false, false)

FunctionPass *llvm::createEVMBPStackification() {
return new EVMBPStackification();
}

bool EVMBPStackification::runOnMachineFunction(MachineFunction &MF) {
LLVM_DEBUG({
dbgs() << "********** Backward propagation stackification **********\n"
<< "********** Function: " << MF.getName() << '\n';
});

MachineRegisterInfo &MRI = MF.getRegInfo();
auto &LIS = getAnalysis<LiveIntervals>();
MachineLoopInfo *MLI = &getAnalysis<MachineLoopInfo>();

// We don't preserve SSA form.
MRI.leaveSSA();

assert(MRI.tracksLiveness() && "Stackification expects liveness");
EVMMachineCFGInfo CFGInfo(MF, MLI);
EVMStackModel StackModel(MF, LIS);
std::unique_ptr<EVMStackLayout> Layout =
EVMStackLayoutGenerator(MF, MLI, StackModel, CFGInfo).run();
EVMStackifyCodeEmitter(*Layout, StackModel, CFGInfo, MF).run();
return true;
}
7 changes: 4 additions & 3 deletions llvm/lib/Target/EVM/EVMInstrFormats.td
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class NI<dag oops, dag iops, list<dag> pattern, bit stack,
let Opc = inst;
let Inst{7-0} = Opc;
let GasCost = cost;
let Defs = !if(stack, [], [ARGUMENTS]);
}

// Generates both register and stack based versions of one actual instruction.
Expand All @@ -61,7 +62,7 @@ multiclass I<dag oops_r, dag iops_r, list<dag> pattern_r,
int cost = 0, dag oops_s = (outs), dag iops_s = (ins), string argstr_s = ""> {
let isCodeGenOnly = 1 in
def "" : NI<oops_r, iops_r, pattern_r, false, opcstr#argstr_r, inst, cost>;
let BaseName = NAME in
let BaseName = NAME, Defs = []<Register> in
def _S : NI<oops_s, iops_s, [], true, opcstr#argstr_s, inst, cost>;
}

Expand All @@ -73,8 +74,8 @@ class NRI<dag oops, dag iops, list<dag> pattern, string asmstr>
: NI<oops, iops, pattern, false, asmstr> {
}

class EVMPseudo<dag oops, dag iops, list<dag> pattern>
: NI<oops, iops, pattern, false> {
class EVMPseudo<dag oops, dag iops, list<dag> pattern, bit stack = 0>
: NI<oops, iops, pattern, stack> {
let isPseudo = 1;
let isCodeGenOnly = 1;
}
Loading
Loading