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 16 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions llvm/lib/Target/EVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,28 @@ add_llvm_target(EVMCodeGen
EVMAllocaHoisting.cpp
EVMArgumentMove.cpp
EVMAsmPrinter.cpp
EVMBackwardPropagationStackification.cpp
EVMCodegenPrepare.cpp
EVMFrameLowering.cpp
EVMHelperUtilities.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;
}
111 changes: 79 additions & 32 deletions llvm/lib/Target/EVM/EVMAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

using namespace llvm;

extern cl::opt<bool> EVMKeepRegisters;

#define DEBUG_TYPE "asm-printer"

namespace {
Expand All @@ -46,39 +48,18 @@ 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;

private:
void emitLinkerSymbol(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 @@ -101,12 +82,78 @@ 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) {
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;
}
case EVM::PseudoRET: {
// TODO: #746: Use PseudoInstExpansion and do this expansion in tblgen.
MCInst Jump;
Jump.setOpcode(EVM::JUMP_S);
EmitToStreamer(*OutStreamer, Jump);
return;
}
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::DATASIZE_S:
case EVM::DATAOFFSET_S:
emitLinkerSymbol(MI);
return;
}
Expand All @@ -116,12 +163,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;
}

void EVMAsmPrinter::emitLinkerSymbol(const MachineInstr *MI) {
MCSymbol *LinkerSymbol = MI->getOperand(0).getMCSymbol();
StringRef LinkerSymbolName = LinkerSymbol->getName();
Expand All @@ -142,6 +183,12 @@ void EVMAsmPrinter::emitLinkerSymbol(const MachineInstr *MI) {
EmitToStreamer(*OutStreamer, MCI);
}

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, StackModel, CFGInfo).run();
EVMStackifyCodeEmitter(*Layout, StackModel, CFGInfo, MF).run();
return true;
}
27 changes: 27 additions & 0 deletions llvm/lib/Target/EVM/EVMHelperUtilities.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===----------- EVMHelperUtilities.cpp - Helper utilities ------*- 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
//
//===----------------------------------------------------------------------===//
//
//
//===----------------------------------------------------------------------===//

#include "EVMHelperUtilities.h"
#include "MCTargetDesc/EVMMCTargetDesc.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/IR/Function.h"

using namespace llvm;

// Return whether the function of the call instruction will return.
bool EVMUtils::callWillReturn(const MachineInstr *Call) {
assert(Call->getOpcode() == EVM::FCALL && "Unexpected call instruction");
const MachineOperand *FuncOp = Call->explicit_uses().begin();
assert(FuncOp->isGlobal() && "Expected a global value");
const auto *Func = dyn_cast<Function>(FuncOp->getGlobal());
assert(Func && "Expected a function");
return !Func->hasFnAttribute(Attribute::NoReturn);
}
Loading
Loading