Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions llvm/lib/Target/EVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_llvm_target(EVMCodeGen
EVMAsmPrinter.cpp
EVMBackwardPropagationStackification.cpp
EVMCalculateModuleSize.cpp
EVMConstantSpiller.cpp
EVMConstantUnfolding.cpp
EVMCodegenPrepare.cpp
EVMFinalizeStackFrames.cpp
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/EVM/EVMCalculateModuleSize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ static unsigned getInstSize(const MachineInstr &MI,
// already exceeds the cap, so the push width is moot.
Size += TII->get(EVM::PUSH2_S).getSize() + TII->get(EVM::JUMP_S).getSize();
break;
case EVM::PUSH_FRAME:
// Typical frame index offsets can be encoded in a single byte, but to
// be conservative, let’s assume 2 bytes per offset.
LLVM_FALLTHROUGH;
case EVM::PUSH_LABEL:
// We emit PUSH4_S here. The linker usually relaxes it to PUSH2_S,
// since a 16-bit immediate covers the 24,576-byte EVM runtime code cap
Expand Down
100 changes: 100 additions & 0 deletions llvm/lib/Target/EVM/EVMConstantSpiller.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//===----- EVMConstantSpiller.cpp - Spill constants to memory --*- 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 identifies IMM_RELOAD instructions representing spilled constants
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why IMM_RELOAD if term constant is also use and constant is the correct term?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to CONSTANT_RELOAD.

// throughout the module. It spills constants at the start of the entry function
// and replaces IMM_RELOAD with the corresponding reload instructions.
//
//===----------------------------------------------------------------------===//

#include "EVMConstantSpiller.h"
#include "EVMInstrInfo.h"
#include "EVMSubtarget.h"
#include "MCTargetDesc/EVMMCTargetDesc.h"
#include "TargetInfo/EVMTargetInfo.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"

using namespace llvm;

#define DEBUG_TYPE "evm-spill-constants"

constexpr uint64_t SpillSlotSize = 32;

static MachineInstr *emitPush(MachineBasicBlock &MBB,
MachineBasicBlock::iterator InsertBefore,
const EVMInstrInfo *TII, const APInt &Imm,
LLVMContext &Ctx, const DebugLoc &DL) {
unsigned Opc = EVM::getPUSHOpcode(Imm);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainability: Similar logic already exists in EVMConstantUnfolding.cpp and probably other EVM::getPUSHOpcode call sites. Can we unify it?

auto MI = BuildMI(MBB, InsertBefore, DL, TII->get(EVM::getStackOpcode(Opc)));
if (Opc != EVM::PUSH0)
MI.addCImm(ConstantInt::get(Ctx, Imm));
return MI;
}

uint64_t EVMConstantSpiller::getSpillSize() const {
return ConstantToUseCount.size() * SpillSlotSize;
}

void EVMConstantSpiller::emitSpills(uint64_t SpillOffset,
MachineFunction &EntryMF) {
LLVMContext &Ctx = EntryMF.getFunction().getContext();
const EVMInstrInfo *TII = EntryMF.getSubtarget<EVMSubtarget>().getInstrInfo();

DenseMap<APInt, uint64_t> ConstantToSpillOffset;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainability: Spill offset == Num of const in ConstantToUseCount map * 32. Do we need a separate map for it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's convenient to have a map with final offsets because, as we refer to this info twice. The second time if when we walk over reloads.

for (const auto &KV : ConstantToUseCount) {
ConstantToSpillOffset[KV.first] = SpillOffset;
SpillOffset += SpillSlotSize;
}

// Emit constant stores in prologue of the entry function.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably elaborate this with a call graph and CFG analysis to make the pass more practical. Can we create a follow-up ticket + TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, there could be several strategies. For example, we could compute the frequency of constants and perform spills at the machine function (MF) level.
However, in optimized builds (e.g., with -O3), many functions are inlined into the entry function, so inserting the constant-spilling code at the beginning of __entry seems like a reasonable approach.

MachineBasicBlock &SpillMBB = EntryMF.front();
for (const auto &[Imm, Offset] : ConstantToSpillOffset) {
LLVM_DEBUG({
dbgs() << "Spilling constant: " << Imm
<< ", number of uses: " << ConstantToUseCount.at(Imm)
<< ", at offset: " << Offset << '\n';
});

BuildMI(SpillMBB, SpillMBB.begin(), DebugLoc(), TII->get(EVM::MSTORE_S));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainability: Reverse order complicates comprehension.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, fixed.

emitPush(SpillMBB, SpillMBB.begin(), TII, APInt(256, Offset), Ctx,
DebugLoc());
emitPush(SpillMBB, SpillMBB.begin(), TII, Imm, Ctx, DebugLoc());
}

// Reload spilled constants.
for (MachineInstr *MI : Reloads) {
const APInt Imm = MI->getOperand(0).getCImm()->getValue().zext(256);
uint64_t Offset = ConstantToSpillOffset.at(Imm);

MachineBasicBlock *MBB = MI->getParent();
emitPush(*MBB, MI, TII, APInt(256, Offset), Ctx, MI->getDebugLoc());
auto Load = BuildMI(*MBB, MI, MI->getDebugLoc(), TII->get(EVM::MLOAD_S));
Load->setAsmPrinterFlag(MachineInstr::ReloadReuse);
MI->eraseFromParent();
}
}

EVMConstantSpiller::EVMConstantSpiller(SmallVector<MachineFunction *> &MFs) {
for (MachineFunction *MF : MFs) {
for (MachineBasicBlock &MBB : *MF) {
for (MachineInstr &MI : MBB) {
if (MI.getOpcode() != EVM::IMM_RELOAD)
continue;

const APInt Imm = MI.getOperand(0).getCImm()->getValue().zext(256);
ConstantToUseCount[Imm]++;
Reloads.push_back(&MI);
}
}
}
}
50 changes: 50 additions & 0 deletions llvm/lib/Target/EVM/EVMConstantSpiller.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----- EVMConstantSpiller.h - Spill constants to memory ----*- 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 identifies IMM_RELOAD instructions representing spilled constants
// throughout the module. It spills constants at the start of the entry function
// and replaces IMM_RELOAD with the corresponding reload instructions.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_EVM_EVMCONSTANTSPILLER_H
#define LLVM_LIB_TARGET_EVM_EVMCONSTANTSPILLER_H

// #include "llvm/IR/Module.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"

namespace llvm {

class MachineInstr;
class MachineFunction;

class EVMConstantSpiller {
public:
explicit EVMConstantSpiller(SmallVector<MachineFunction *> &MFs);

/// Inserts constant spills into the first basic block of the entry
/// function and replaces IMM_RELOAD with the corresponding reload
/// instructions at their use sites.
void emitSpills(uint64_t SpillOffset, MachineFunction &EntryMF);

/// Return the total size needed for the spill area.
uint64_t getSpillSize() const;

private:
/// Maps each APInt constant to the number of times it appears across all
/// functions in the module
SmallDenseMap<APInt, unsigned> ConstantToUseCount;

/// IMM_RELOAD instructions that need to be converted into actuall reloads.
SmallVector<MachineInstr *> Reloads;
};
} // namespace llvm

#endif // LLVM_LIB_TARGET_EVM_EVMCONSTANTSPILLER_H
Loading
Loading