Skip to content

Commit 46d99a0

Browse files
vladimirradosavljevicakiramenai
authored andcommitted
[EVM] Add support for calculating module size
Signed-off-by: Vladimir Radosavljevic <[email protected]>
1 parent a9e468e commit 46d99a0

File tree

6 files changed

+561
-7
lines changed

6 files changed

+561
-7
lines changed

llvm/lib/Target/EVM/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_llvm_target(EVMCodeGen
2323
EVMArgumentMove.cpp
2424
EVMAsmPrinter.cpp
2525
EVMBackwardPropagationStackification.cpp
26+
EVMCalculateModuleSize.cpp
2627
EVMConstantUnfolding.cpp
2728
EVMCodegenPrepare.cpp
2829
EVMFinalizeStackFrames.cpp
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//===----- EVMCalculateModuleSize.cpp - Calculate module size --*- 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+
// A utility set for determining the overall size of a module.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "EVMCalculateModuleSize.h"
14+
#include "EVMMachineFunctionInfo.h"
15+
#include "EVMSubtarget.h"
16+
#include "MCTargetDesc/EVMMCTargetDesc.h"
17+
#include "llvm/CodeGen/MachineModuleInfo.h"
18+
#include "llvm/CodeGen/TargetInstrInfo.h"
19+
#include "llvm/CodeGen/TargetSubtargetInfo.h"
20+
#include "llvm/IR/Module.h"
21+
22+
using namespace llvm;
23+
24+
// This is the copied from AsmPrinter::isBlockOnlyReachableByFallthrough.
25+
static bool isBlockOnlyReachableByFallthrough(const MachineBasicBlock *MBB) {
26+
// If this is a landing pad, it isn't a fall through. If it has no preds,
27+
// then nothing falls through to it.
28+
if (MBB->isEHPad() || MBB->pred_empty())
29+
return false;
30+
31+
// If there isn't exactly one predecessor, it can't be a fall through.
32+
if (MBB->pred_size() > 1)
33+
return false;
34+
35+
// The predecessor has to be immediately before this block.
36+
MachineBasicBlock *Pred = *MBB->pred_begin();
37+
if (!Pred->isLayoutSuccessor(MBB))
38+
return false;
39+
40+
// If the block is completely empty, then it definitely does fall through.
41+
if (Pred->empty())
42+
return true;
43+
44+
// Check the terminators in the previous blocks
45+
for (const auto &MI : Pred->terminators()) {
46+
// If it is not a simple branch, we are in a table somewhere.
47+
if (!MI.isBranch() || MI.isIndirectBranch())
48+
return false;
49+
50+
// If we are the operands of one of the branches, this is not a fall
51+
// through. Note that targets with delay slots will usually bundle
52+
// terminators with the delay slot instruction.
53+
for (ConstMIBundleOperands OP(MI); OP.isValid(); ++OP) {
54+
if (OP->isJTI())
55+
return false;
56+
if (OP->isMBB() && OP->getMBB() == MBB)
57+
return false;
58+
}
59+
}
60+
61+
return true;
62+
}
63+
64+
// Return the size of an instruction in bytes. For some pseudo instructions,
65+
// don't use the size from the instruction description, since during code
66+
// generation, some instructions will be relaxed to smaller instructions.
67+
static unsigned getInstSize(const MachineInstr &MI,
68+
const TargetInstrInfo *TII) {
69+
// Skip debug instructions.
70+
if (MI.isDebugInstr())
71+
return 0;
72+
73+
unsigned Size = 0;
74+
switch (MI.getOpcode()) {
75+
default:
76+
Size = MI.getDesc().getSize();
77+
break;
78+
case EVM::PseudoCALL:
79+
// In case that function call has a return label, we will emit JUMPDEST,
80+
// so take it into account.
81+
if (MI.getNumExplicitOperands() > 1)
82+
Size = TII->get(EVM::JUMPDEST_S).getSize();
83+
LLVM_FALLTHROUGH;
84+
case EVM::PseudoJUMPI:
85+
case EVM::PseudoJUMP:
86+
// We emit PUSH4_S here. The linker usually relaxes it to PUSH2_S,
87+
// since a 16-bit immediate covers the 24,576-byte EVM runtime code cap
88+
// (EIP-170). If a wider immediate were ever required, the contract
89+
// already exceeds the cap, so the push width is moot.
90+
Size += TII->get(EVM::PUSH2_S).getSize() + TII->get(EVM::JUMP_S).getSize();
91+
break;
92+
case EVM::PUSH_LABEL:
93+
// We emit PUSH4_S here. The linker usually relaxes it to PUSH2_S,
94+
// since a 16-bit immediate covers the 24,576-byte EVM runtime code cap
95+
// (EIP-170). If a wider immediate were ever required, the contract
96+
// already exceeds the cap, so the push width is moot.
97+
Size = TII->get(EVM::PUSH2_S).getSize();
98+
break;
99+
}
100+
return Size;
101+
}
102+
103+
uint64_t llvm::EVM::calculateFunctionCodeSize(const MachineFunction &MF) {
104+
uint64_t Size = 0;
105+
const auto *TII = MF.getSubtarget<EVMSubtarget>().getInstrInfo();
106+
107+
// If the function has a PUSHDEPLOYADDRESS, it starts with a PUSH20.
108+
if (const auto *MFI = MF.getInfo<EVMMachineFunctionInfo>();
109+
MFI->getHasPushDeployAddress())
110+
Size += TII->get(EVM::PUSH20).getSize();
111+
112+
for (const MachineBasicBlock &MBB : MF) {
113+
// If the block is not only reachable by fallthrough, it starts with
114+
// a JUMPDEST instruction.
115+
if (!isBlockOnlyReachableByFallthrough(&MBB))
116+
Size += TII->get(EVM::JUMPDEST_S).getSize();
117+
118+
Size += std::accumulate(MBB.begin(), MBB.end(), 0,
119+
[&TII](unsigned Sum, const MachineInstr &MI) {
120+
return Sum + getInstSize(MI, TII);
121+
});
122+
}
123+
return Size;
124+
}
125+
126+
uint64_t llvm::EVM::calculateModuleCodeSize(Module &M,
127+
const MachineModuleInfo &MMI) {
128+
uint64_t TotalSize = 0;
129+
for (Function &F : M) {
130+
MachineFunction *MF = MMI.getMachineFunction(F);
131+
if (!MF)
132+
continue;
133+
TotalSize += llvm::EVM::calculateFunctionCodeSize(*MF);
134+
}
135+
136+
// Take into account INVALID instruction at the end of the .text section.
137+
TotalSize++;
138+
return TotalSize;
139+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===----- EVMCalculateModuleSize.h - Calculate module size ----*- 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+
// A utility set for determining the overall size of a module.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H
14+
#define LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H
15+
16+
#include <cstdint>
17+
18+
namespace llvm {
19+
class MachineFunction;
20+
class MachineModuleInfo;
21+
class Module;
22+
23+
namespace EVM {
24+
uint64_t calculateFunctionCodeSize(const MachineFunction &MF);
25+
uint64_t calculateModuleCodeSize(Module &M, const MachineModuleInfo &MMI);
26+
} // namespace EVM
27+
} // namespace llvm
28+
29+
#endif // LLVM_LIB_TARGET_EVM_EVMCALCULATEMODULESIZE_H

llvm/lib/Target/EVM/EVMInstrInfo.td

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,10 @@ def FCALL
244244
} // Uses = [SP], isCall = 1
245245

246246
let isCall = 1 in def PseudoCALL
247-
: EVMPseudo<(outs), (ins jmptarget:$callee, variable_ops), [], true>;
247+
: EVMPseudo<(outs), (ins jmptarget:$callee, variable_ops), [], true> {
248+
// PUSH4_S + JUMP_S.
249+
let Size = 6;
250+
}
248251

249252
//===----------------------------------------------------------------------===//
250253
// EVM arithmetic instructions.
@@ -411,13 +414,19 @@ defm JUMPI
411414
: I<(outs), (ins jmptarget:$dst, GPR:$cond), [(brcond GPR:$cond, bb:$dst)],
412415
"JUMPI", "$dst, $cond", 0x57, 10>;
413416
def JUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst, GPR:$cond), []>;
414-
def PseudoJUMPI : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>;
417+
def PseudoJUMPI : EVMPseudo<(outs), (ins jmptarget:$dst), [], true> {
418+
// PUSH4_S + JUMPI_S.
419+
let Size = 6;
420+
}
415421
def PseudoJUMP_UNLESS : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>;
416422

417423
let isBarrier = 1 in {
418424
defm JUMP : I<(outs), (ins jmptarget:$dst), [(br bb:$dst)], "JUMP", "$dst",
419425
0x56, 8>;
420-
def PseudoJUMP : EVMPseudo<(outs), (ins jmptarget:$dst), [], true>;
426+
def PseudoJUMP : EVMPseudo<(outs), (ins jmptarget:$dst), [], true> {
427+
// PUSH4_S + JUMP_S.
428+
let Size = 6;
429+
}
421430
} // isBarrier = 1
422431
} // isBranch = 1, isTerminator = 1
423432

@@ -829,6 +838,8 @@ defm PUSH0 : I<(outs), (ins), [], "PUSH0", "", 0x5F, 2>;
829838

830839
def PUSH_LABEL : NI<(outs), (ins jmptarget:$dst), [], true, "", 0, 0> {
831840
let isCodeGenOnly = 1;
841+
// PUSH4_S.
842+
let Size = 5;
832843
}
833844

834845
def PUSH_FRAME : NI<(outs), (ins i256imm:$imm), [], true, "", 0, 0> {
@@ -1162,22 +1173,34 @@ def PUSH32_S : PushBase<(outs), (ins i256imm:$imm), [], true, "PUSH32$imm",
11621173
let isAsCheapAsAMove = 1, isReMaterializable = 1, isCodeGenOnly = 1, hasSideEffects = 0 in {
11631174
let BaseName = "DATASIZE" in {
11641175
def DATASIZE : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>;
1165-
def DATASIZE_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>;
1176+
def DATASIZE_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0> {
1177+
// PUSH4_S
1178+
let Size = 5;
1179+
}
11661180
}
11671181

11681182
let BaseName = "DATAOFFSET" in {
11691183
def DATAOFFSET : NI<(outs GPR:$dst), (ins jmptarget:$reloc), [], false, "", 0, 0>;
1170-
def DATAOFFSET_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0>;
1184+
def DATAOFFSET_S : NI<(outs), (ins jmptarget:$reloc), [], true, "", 0, 0> {
1185+
// PUSH4_S
1186+
let Size = 5;
1187+
}
11711188
}
11721189

11731190
let BaseName = "LINKERSYMBOL" in {
11741191
def LINKERSYMBOL : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>;
1175-
def LINKERSYMBOL_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0>;
1192+
def LINKERSYMBOL_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0> {
1193+
// PUSH20_S
1194+
let Size = 21;
1195+
}
11761196
}
11771197

11781198
let BaseName = "LOADIMMUTABLE" in {
11791199
def LOADIMMUTABLE : NI<(outs GPR:$dst), (ins jmptarget:$sym), [], false, "", 0, 0>;
1180-
def LOADIMMUTABLE_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0>;
1200+
def LOADIMMUTABLE_S : NI<(outs), (ins jmptarget:$sym), [], true, "", 0, 0> {
1201+
// PUSH32_S
1202+
let Size = 33;
1203+
}
11811204
}
11821205
}
11831206

0 commit comments

Comments
 (0)