Skip to content

Commit f14e6fc

Browse files
committed
[EVM] Support for spilling constants to memory
1 parent 7603797 commit f14e6fc

File tree

6 files changed

+287
-6
lines changed

6 files changed

+287
-6
lines changed

llvm/lib/Target/EVM/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ add_llvm_target(EVMCodeGen
4444
EVMSHA3ConstFolding.cpp
4545
EVMSingleUseExpression.cpp
4646
EVMSplitCriticalEdges.cpp
47+
EVMConstantSpiller.cpp
4748
EVMStackModel.cpp
4849
EVMStackShuffler.cpp
4950
EVMStackSolver.cpp
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//===----- EVMConstantSpiller.cpp - Spill constants to memory --*- C++ -*--===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
#include "EVMConstantSpiller.h"
13+
#include "EVMInstrInfo.h"
14+
#include "EVMSubtarget.h"
15+
#include "MCTargetDesc/EVMMCTargetDesc.h"
16+
#include "TargetInfo/EVMTargetInfo.h"
17+
#include "llvm/CodeGen/MachineModuleInfo.h"
18+
#include "llvm/CodeGen/Passes.h"
19+
#include "llvm/CodeGen/TargetInstrInfo.h"
20+
#include "llvm/CodeGen/TargetSubtargetInfo.h"
21+
#include "llvm/IR/Module.h"
22+
23+
using namespace llvm;
24+
25+
#define DEBUG_TYPE "evm-spill-constants"
26+
27+
constexpr uint64_t SpillSlotSize = 32;
28+
29+
static cl::opt<unsigned> ConstantSpillThreshold(
30+
"evm-constant-spill-threshold", cl::Hidden, cl::init(30),
31+
cl::desc("Minimum number of uses of a constant across the module required "
32+
"before spilling it to memory is considered profitable"));
33+
34+
static MachineInstr *emitPush(MachineBasicBlock &MBB,
35+
MachineBasicBlock::iterator InsertBefore,
36+
const EVMInstrInfo *TII, const APInt &Imm,
37+
LLVMContext &Ctx, const DebugLoc &DL) {
38+
unsigned Opc = EVM::getPUSHOpcode(Imm);
39+
auto MI = BuildMI(MBB, InsertBefore, DL, TII->get(EVM::getStackOpcode(Opc)));
40+
if (Opc != EVM::PUSH0)
41+
MI.addCImm(ConstantInt::get(Ctx, Imm));
42+
return MI;
43+
}
44+
45+
uint64_t EVMConstantSpiller::getSpillSize() const {
46+
return ConstantUseCount.size() * SpillSlotSize;
47+
}
48+
49+
void EVMConstantSpiller::emitConstantSpills(uint64_t SpillOffset,
50+
MachineFunction &EntryMF) {
51+
LLVMContext &Ctx = EntryMF.getFunction().getContext();
52+
const EVMInstrInfo *TII = EntryMF.getSubtarget<EVMSubtarget>().getInstrInfo();
53+
54+
DenseMap<APInt, uint64_t> ConstantToSpillOffset;
55+
for (const auto &KV : ConstantUseCount) {
56+
ConstantToSpillOffset[KV.first] = SpillOffset;
57+
SpillOffset += SpillSlotSize;
58+
}
59+
60+
// Emit constant stores in prologue of the '__entry' function.
61+
MachineBasicBlock &SpillMBB = EntryMF.front();
62+
auto InsertBefore = SpillMBB.begin();
63+
for (const auto &[Imm, Offset] : ConstantToSpillOffset) {
64+
LLVM_DEBUG({
65+
dbgs() << "Spilling constant: " << Imm
66+
<< ", number of uses: " << ConstantUseCount.at(Imm)
67+
<< ", at offset: " << Offset << '\n';
68+
});
69+
70+
BuildMI(SpillMBB, InsertBefore, DebugLoc(), TII->get(EVM::MSTORE_S));
71+
emitPush(SpillMBB, InsertBefore, TII, APInt(256, Offset), Ctx, DebugLoc());
72+
emitPush(SpillMBB, InsertBefore, TII, Imm, Ctx, DebugLoc());
73+
}
74+
75+
// Reload spilled constants.
76+
for (MachineInstr *MI : ReloadCandidates) {
77+
const APInt Imm = MI->getOperand(0).getCImm()->getValue().zext(256);
78+
uint64_t Offset = ConstantToSpillOffset.at(Imm);
79+
80+
MachineBasicBlock *MBB = MI->getParent();
81+
emitPush(*MBB, MI, TII, APInt(256, Offset), Ctx, MI->getDebugLoc());
82+
auto Load = BuildMI(*MBB, MI, MI->getDebugLoc(), TII->get(EVM::MLOAD_S));
83+
Load->setAsmPrinterFlag(MachineInstr::ReloadReuse);
84+
MI->eraseFromParent();
85+
}
86+
}
87+
88+
static bool shouldSkip(const MachineInstr &MI) {
89+
if (!EVMInstrInfo::isPush(&MI) || (MI.getOpcode() == EVM::PUSH0_S))
90+
return true;
91+
92+
const APInt Imm = MI.getOperand(0).getCImm()->getValue();
93+
return Imm.getActiveBits() < 8 * 8;
94+
}
95+
96+
void EVMConstantSpiller::analyzeModule(Module &M, MachineModuleInfo &MMI) {
97+
for (Function &F : M) {
98+
MachineFunction *MF = MMI.getMachineFunction(F);
99+
if (!MF)
100+
continue;
101+
102+
for (MachineBasicBlock &MBB : *MF) {
103+
for (MachineInstr &MI : MBB) {
104+
if (shouldSkip(MI))
105+
continue;
106+
107+
const APInt Imm = MI.getOperand(0).getCImm()->getValue().zext(256);
108+
if (Imm.isAllOnes())
109+
continue;
110+
111+
ConstantUseCount[Imm]++;
112+
ReloadCandidates.push_back(&MI);
113+
}
114+
}
115+
}
116+
}
117+
118+
void EVMConstantSpiller::filterCandidates() {
119+
SmallVector<APInt> ImmToRemove;
120+
for (const auto &[Imm, NumUses] : ConstantUseCount)
121+
if (NumUses < ConstantSpillThreshold)
122+
ImmToRemove.push_back(Imm);
123+
124+
for (const APInt &Imm : ImmToRemove)
125+
ConstantUseCount.erase(Imm);
126+
127+
erase_if(ReloadCandidates, [this](const MachineInstr *MI) {
128+
const APInt &Imm = MI->getOperand(0).getCImm()->getValue().zext(256);
129+
return !ConstantUseCount.contains(Imm);
130+
});
131+
}
132+
133+
EVMConstantSpiller::EVMConstantSpiller(Module &M, MachineModuleInfo &MMI) {
134+
analyzeModule(M, MMI);
135+
filterCandidates();
136+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//===----- EVMConstantSpiller.h - Spill constants to memory ----*- C++ -*--===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file identifies frequently used large constants across the module.
10+
// If a constant’s usage exceeds the threshold and its bit-width is sufficient,
11+
// it is spilled at the beginning of the __entry function and reloaded at use
12+
// sites.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef LLVM_LIB_TARGET_EVM_EVMCONSTANTSPILLER_H
17+
#define LLVM_LIB_TARGET_EVM_EVMCONSTANTSPILLER_H
18+
19+
#include "llvm/CodeGen/MachineModuleInfo.h"
20+
#include "llvm/IR/Module.h"
21+
22+
namespace llvm {
23+
24+
class MachineInstr;
25+
26+
class EVMConstantSpiller {
27+
public:
28+
EVMConstantSpiller(Module &M, MachineModuleInfo &MMI);
29+
30+
/// Return the total size needed for the spill area.
31+
uint64_t getSpillSize() const;
32+
33+
/// Insert constant spills into the first basic block of the __entry
34+
/// function and insert reloads at their use sites.
35+
void emitConstantSpills(uint64_t SpillOffset, MachineFunction &EntryMF);
36+
37+
private:
38+
/// Maps each APInt constant to the number of times it appears across all
39+
/// functions in the module
40+
SmallDenseMap<APInt, unsigned> ConstantUseCount;
41+
/// PUSH instructions that need to be reloaded
42+
SmallVector<MachineInstr *> ReloadCandidates;
43+
44+
/// Filters out constants whose usage count is below the threshold.
45+
void filterCandidates();
46+
47+
void analyzeModule(Module &M, MachineModuleInfo &MMI);
48+
};
49+
} // namespace llvm
50+
51+
#endif // LLVM_LIB_TARGET_EVM_EVMCONSTANTSPILLER_H

llvm/lib/Target/EVM/EVMConstantUnfolding.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
#include "llvm/CodeGen/MachineLoopInfo.h"
3535
#include "llvm/CodeGen/MachineModuleInfo.h"
3636
#include "llvm/IR/Module.h"
37-
#include "llvm/IR/PassInstrumentation.h"
3837
#include "llvm/InitializePasses.h"
3938
#include "llvm/Support/Debug.h"
4039
#include <functional>

llvm/lib/Target/EVM/EVMFinalizeStackFrames.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "EVM.h"
15+
#include "EVMConstantSpiller.h"
1516
#include "MCTargetDesc/EVMMCTargetDesc.h"
1617
#include "TargetInfo/EVMTargetInfo.h"
1718
#include "llvm/CodeGen/MachineFrameInfo.h"
@@ -147,12 +148,26 @@ bool EVMFinalizeStackFrames::runOnModule(Module &M) {
147148
MachineModuleInfo &MMI = getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
148149
SmallVector<std::pair<MachineFunction *, uint64_t>, 8> ToReplaceFI;
149150

150-
// Calculate the stack size for each function.
151-
for (Function &F : M) {
152-
MachineFunction *MF = MMI.getMachineFunction(F);
153-
if (!MF)
154-
continue;
151+
SmallVector<MachineFunction *> MFs;
152+
for_each(M.getFunctionList(), [&MFs, &MMI](Function &F) {
153+
if (MachineFunction *MF = MMI.getMachineFunction(F))
154+
MFs.push_back(MF);
155+
});
156+
157+
if (MFs.size() &&
158+
MFs.front()->getFunction().hasFnAttribute("evm-entry-function")) {
159+
EVMConstantSpiller ConstSpiller(M, MMI);
160+
if (ConstSpiller.getSpillSize()) {
161+
MachineFunction *MF = MFs.front();
162+
uint64_t StackSize = calculateFrameObjectOffsets(*MF);
163+
MF->getFrameInfo().CreateSpillStackObject(ConstSpiller.getSpillSize(),
164+
Align(32));
165+
ConstSpiller.emitConstantSpills(StackSize, *MF);
166+
}
167+
}
155168

169+
// Calculate the stack size for each function.
170+
for (MachineFunction *MF : MFs) {
156171
uint64_t StackSize = calculateFrameObjectOffsets(*MF);
157172
if (StackSize == 0)
158173
continue;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
2+
; RUN: llc -O3 --evm-stack-region-size=32 --evm-stack-region-offset=128 --evm-constant-spill-threshold=2 < %s | FileCheck %s
3+
target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256"
4+
target triple = "evm-unknown-unknown"
5+
6+
declare void @llvm.evm.return(ptr addrspace(1), i256) noreturn
7+
8+
define void @entry() #1 {
9+
; CHECK-LABEL: entry:
10+
; CHECK: ; %bb.0: ; %entry
11+
; CHECK-NEXT: MSTORE
12+
; CHECK-NEXT: PUSH0
13+
; CHECK-NEXT: PUSH4 0x4E487B71
14+
; CHECK-NEXT: PUSH1 0xE0
15+
; CHECK-NEXT: SHL
16+
; CHECK-NEXT: PUSH0
17+
; CHECK-NEXT: MLOAD ; Reload Reuse
18+
; CHECK-NEXT: PUSH0
19+
; CHECK-NEXT: RETURN
20+
entry:
21+
tail call void @llvm.evm.return(ptr addrspace(1) null, i256 35408467139433450592217433187231851964531694900788300625387963629091585785856)
22+
unreachable
23+
}
24+
25+
define void @test_spill() #2 {
26+
; CHECK-LABEL: test_spill:
27+
; CHECK: ; %bb.0: ; %entry
28+
; CHECK-NEXT: JUMPDEST
29+
; CHECK-NEXT: PUSH0
30+
; CHECK-NEXT: MLOAD ; Reload Reuse
31+
; CHECK-NEXT: PUSH0
32+
; CHECK-NEXT: RETURN
33+
entry:
34+
tail call void @llvm.evm.return(ptr addrspace(1) null, i256 35408467139433450592217433187231851964531694900788300625387963629091585785856)
35+
unreachable
36+
}
37+
38+
; Check that -1 constant is not spilled.
39+
define void @test_not_spiller() #2 {
40+
; CHECK-LABEL: test_not_spiller:
41+
; CHECK: ; %bb.0: ; %entry
42+
; CHECK-NEXT: JUMPDEST
43+
; CHECK-NEXT: PUSH0
44+
; CHECK-NEXT: NOT
45+
; CHECK-NEXT: PUSH0
46+
; CHECK-NEXT: RETURN
47+
entry:
48+
tail call void @llvm.evm.return(ptr addrspace(1) null, i256 -1)
49+
unreachable
50+
}
51+
52+
; Check that small constants are not spilled.
53+
define void @test_not_spilled2() #2 {
54+
; CHECK-LABEL: test_not_spilled2:
55+
; CHECK: ; %bb.0: ; %entry
56+
; CHECK-NEXT: JUMPDEST
57+
; CHECK-NEXT: PUSH5 0x100000000
58+
; CHECK-NEXT: PUSH0
59+
; CHECK-NEXT: RETURN
60+
entry:
61+
tail call void @llvm.evm.return(ptr addrspace(1) null, i256 4294967296)
62+
unreachable
63+
}
64+
65+
; Check that small constants are not spilled.
66+
define void @test_not_spilled3() #2 {
67+
; CHECK-LABEL: test_not_spilled3:
68+
; CHECK: ; %bb.0: ; %entry
69+
; CHECK-NEXT: JUMPDEST
70+
; CHECK-NEXT: PUSH5 0x100000000
71+
; CHECK-NEXT: PUSH0
72+
; CHECK-NEXT: RETURN
73+
entry:
74+
tail call void @llvm.evm.return(ptr addrspace(1) null, i256 4294967296)
75+
unreachable
76+
}
77+
78+
attributes #1 = { nofree noreturn nounwind "evm-entry-function" }
79+
attributes #2 = { nofree nounwind noreturn }

0 commit comments

Comments
 (0)