Skip to content
Open
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
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
92 changes: 92 additions & 0 deletions llvm/lib/Target/EVM/EVMConstantSpiller.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//===----- 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 CONSTANT_RELOAD instructions representing spilled
// constants throughout the module. It spills constants at the start of the
// entry function and replaces CONSTANT_RELOAD with the corresponding reload
// instructions.
//
//===----------------------------------------------------------------------===//

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

using namespace llvm;

#define DEBUG_TYPE "evm-spill-constants"

constexpr uint64_t SpillSlotSize = 32;

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

void EVMConstantSpiller::emitSpills(uint64_t SpillOffset,
MachineFunction &EntryMF) {
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.

// TODO: #925, elaborate analysis to determine the most suitable location
// for performing constant spilling.
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';
});

// Push the constant
TII->insertPush(Imm, SpillMBB, SpillMBB.begin(), DebugLoc());
// Push the offset
TII->insertPush(APInt(256, Offset), SpillMBB, std::next(SpillMBB.begin()),
DebugLoc());
BuildMI(SpillMBB, std::next(SpillMBB.begin(), 2), DebugLoc(),
TII->get(EVM::MSTORE_S));
}

// 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();
TII->insertPush(APInt(256, Offset), *MBB, MI, 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::CONSTANT_RELOAD)
continue;

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

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

#endif // LLVM_LIB_TARGET_EVM_EVMCONSTANTSPILLER_H
Loading
Loading