Skip to content

Commit 668b215

Browse files
authored
Merge pull request rust-lang#105 from ptersilie/nomoreentryblockcalls
Add pass disallowing calls in entry blocks.
2 parents 881577b + 10bf29b commit 668b215

File tree

6 files changed

+169
-8
lines changed

6 files changed

+169
-8
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef LLVM_TRANSFORMS_YK_NOCALLSINENTRYBLOCKS_H
2+
#define LLVM_TRANSFORMS_YK_NOCALLSINENTRYBLOCKS_H
3+
4+
#include "llvm/Pass.h"
5+
6+
namespace llvm {
7+
ModulePass *createYkNoCallsInEntryBlocksPass();
8+
} // namespace llvm
9+
10+
#endif

llvm/lib/CodeGen/StackMaps.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,22 @@ StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI,
305305
// each one of them. Note, that the stackmap may track either of %rbx or
306306
// %rcx, resulting in different ways below to retrieve the mappings.
307307
int ExtraReg = 0;
308+
Register R = MOI->getReg();
308309
if (MOI->isReg()) {
309-
Register R = MOI->getReg();
310+
if (MOI->isKill()) {
311+
// There's no point in tracking a killed register. Instead, try and
312+
// find a copy of the register and use that to increase the likelyhood
313+
// of us tracking that value.
314+
// YKFIXME: This is likely only a temporary fix. In the future we might
315+
// want to allow stackmaps to track arbitrarily many locations per live
316+
// variable. Currently, we can only track two.
317+
for (auto I : SpillOffsets) {
318+
if (I.second == R) {
319+
R = I.first;
320+
break;
321+
}
322+
}
323+
}
310324
if (SpillOffsets.count(R) > 0) {
311325
RHS = SpillOffsets[R];
312326
assert(SpillOffsets[R] != 0);
@@ -333,9 +347,9 @@ StackMaps::parseOperand(MachineInstr::const_mop_iterator MOI,
333347
}
334348
}
335349

336-
unsigned DwarfRegNum = getDwarfRegNum(MOI->getReg(), TRI);
350+
unsigned DwarfRegNum = getDwarfRegNum(R, TRI);
337351
unsigned LLVMRegNum = *TRI->getLLVMRegNum(DwarfRegNum, false);
338-
unsigned SubRegIdx = TRI->getSubRegIndex(LLVMRegNum, MOI->getReg());
352+
unsigned SubRegIdx = TRI->getSubRegIndex(LLVMRegNum, R);
339353
if (SubRegIdx)
340354
Offset = TRI->getSubRegIdxOffset(SubRegIdx);
341355

llvm/lib/CodeGen/TargetPassConfig.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include "llvm/Transforms/Yk/Linkage.h"
5454
#include "llvm/Transforms/Yk/ShadowStack.h"
5555
#include "llvm/Transforms/Yk/Stackmaps.h"
56+
#include "llvm/Transforms/Yk/NoCallsInEntryBlocks.h"
5657
#include <cassert>
5758
#include <optional>
5859
#include <string>
@@ -282,6 +283,10 @@ static cl::opt<bool>
282283
YkInsertStackMaps("yk-insert-stackmaps", cl::init(false), cl::NotHidden,
283284
cl::desc("Insert stackmaps for JIT deoptimisation"));
284285

286+
static cl::opt<bool>
287+
YkNoCallsInEntryBlocks("yk-no-calls-in-entryblocks", cl::init(false), cl::NotHidden,
288+
cl::desc("Ensure there are no calls in the entryblock."));
289+
285290
/// Allow standard passes to be disabled by command line options. This supports
286291
/// simple binary flags that either suppress the pass or do nothing.
287292
/// i.e. -disable-mypass=false has no effect.
@@ -1126,6 +1131,15 @@ bool TargetPassConfig::addISelPasses() {
11261131
if (YkBlockDisambiguate)
11271132
addPass(createYkBlockDisambiguatePass());
11281133

1134+
if (YkNoCallsInEntryBlocks) {
1135+
// Make sure this pass runs before the shadowstack pass, so that we don't
1136+
// split the entry block after that pass inserted a `malloc` into `main`.
1137+
// This would otherwise result in allocas being moved outside the entry
1138+
// block which makes them dynamic allocas, and results in stackmaps not
1139+
// being able to record the functions stack size.
1140+
addPass(createYkNoCallsInEntryBlocksPass());
1141+
}
1142+
11291143
if (YkShadowStack) {
11301144
addPass(createYkShadowStackPass());
11311145
}

llvm/lib/Target/X86/X86AsmPrinter.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,21 @@ void clearRhs(Register Reg, std::map<Register, int64_t> &SpillMap) {
6565
auto I = SpillMap.begin();
6666
while (I != SpillMap.end()) {
6767
if (I->second == Reg) {
68-
I = SpillMap.erase(I);
68+
// If there's a mapping A => B, where B has been reassigned and has a
69+
// mapping B => C, then transitively apply the mapping so that A => C.
70+
// This makes sure we don't remove mappings (especially to stack slots)
71+
// when a register is reassigned. Example:
72+
// store $r13 in [$rbp - 8]
73+
// $rcx = $r13
74+
// $r13 = $rbx
75+
// Upon assigning `$r13 = $rbx`, we apply `$r13`'s previous mapping to
76+
// `$rcx` so that `SpillMap[$rcx] = -8`.
77+
if(SpillMap.count(Reg) > 0) {
78+
SpillMap[I->first] = SpillMap[Reg];
79+
++I;
80+
} else {
81+
I = SpillMap.erase(I);
82+
}
6983
} else {
7084
++I;
7185
}
@@ -99,13 +113,14 @@ void processInstructions(
99113
const MachineOperand Rhs = Instr.getOperand(1);
100114
assert(Lhs.isReg() && "Is register.");
101115
assert(Rhs.isReg() && "Is register.");
116+
// Reassigning a new value to LHS means any mappings to Lhs are now void
117+
// and need to be removed. We need to do this before updating the
118+
// mapping, so the transitive property of the SpillMap isn't violated
119+
// (see `clearRhs` for more info).
120+
clearRhs(Lhs.getReg(), SpillMap);
102121
SpillMap[Lhs.getReg()] = Rhs.getReg();
103122
// YKFIXME: If the `mov` instruction has a killed-flag, remove the
104123
// register from the map.
105-
106-
// Reassigning a new value to Lhs means any mappings to Lhs are now void
107-
// and need to be removed.
108-
clearRhs(Lhs.getReg(), SpillMap);
109124
continue;
110125
}
111126

llvm/lib/Transforms/Yk/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_llvm_component_library(LLVMYkPasses
55
LivenessAnalysis.cpp
66
StackMaps.cpp
77
ShadowStack.cpp
8+
NoCallsInEntryBlocks.cpp
89

910
DEPENDS
1011
intrinsics_gen
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// NoCallsInEntryBlocks.cpp - Ensure there are no calls in the first block.
2+
//
3+
// This pass splits up the entry block so that there are no calls inside it
4+
// (one exception being the malloc call inserted by the shadowstack pass which
5+
// will be applied later). This allows us to detect call backs from external
6+
// functions more easily, which in turn simplifies our JITModBuilder
7+
// implementation and fixes the soundness issue that existed in the old
8+
// StackAdjust approach.
9+
//
10+
// As a simplified example, imagine this function:
11+
//
12+
// void foo(int argc) {
13+
// if (argc == 0) {
14+
// extfunc(foo)
15+
// }
16+
// }
17+
//
18+
// where `extfunc` may or may not call back to `foo`. Previously, this might
19+
// result in a trace like
20+
//
21+
// foo.0
22+
// extfunc
23+
// foo.0
24+
// ...
25+
//
26+
// Upon seeing the second `foo.0` we are unable to determine whether we are
27+
// observing a callback to `foo` or are returning from `extfunc` which hasn't
28+
// called foo. When running this pass we force `extfunc` not to be in the entry
29+
// block, resulting in a trace like
30+
//
31+
// foo.0
32+
// foo.1
33+
// extfunc
34+
// foo.1
35+
// ...
36+
//
37+
// Here it is clear that `extfunc` has not called `foo` as we are seeing block
38+
// `foo.1` next. If `extfunc` had called `foo`, the next block would be
39+
// `foo.0`.
40+
41+
#include "llvm/Transforms/Yk/NoCallsInEntryBlocks.h"
42+
#include "llvm/IR/BasicBlock.h"
43+
#include "llvm/IR/Function.h"
44+
#include "llvm/IR/IRBuilder.h"
45+
#include "llvm/IR/Instructions.h"
46+
#include "llvm/IR/Module.h"
47+
#include "llvm/IR/Verifier.h"
48+
#include "llvm/InitializePasses.h"
49+
#include "llvm/Pass.h"
50+
#include "llvm/Transforms/Yk/LivenessAnalysis.h"
51+
52+
#define DEBUG_TYPE "yk-no-more-entryblock-calls"
53+
54+
using namespace llvm;
55+
56+
namespace llvm {
57+
void initializeYkNoCallsInEntryBlocksPass(PassRegistry &);
58+
} // namespace llvm
59+
60+
namespace {
61+
62+
class YkNoCallsInEntryBlocks : public ModulePass {
63+
public:
64+
static char ID;
65+
YkNoCallsInEntryBlocks() : ModulePass(ID) {
66+
initializeYkNoCallsInEntryBlocksPass(*PassRegistry::getPassRegistry());
67+
}
68+
69+
bool runOnModule(Module &M) override {
70+
LLVMContext &Context = M.getContext();
71+
72+
// split block at first function call
73+
std::set<Instruction *> Calls;
74+
for (Function &F : M) {
75+
if (F.empty()) // skip declarations.
76+
continue;
77+
BasicBlock &BB = F.getEntryBlock();
78+
auto CurrFuncName = F.getName();
79+
for (Instruction &I : BB) {
80+
if (isa<CallInst>(I)) {
81+
Function *F = cast<CallInst>(I).getCalledFunction();
82+
BB.splitBasicBlock(&I);
83+
break;
84+
}
85+
}
86+
}
87+
88+
#ifndef NDEBUG
89+
// Our pass runs after LLVM normally does its verify pass. In debug builds
90+
// we run it again to check that our pass is generating valid IR.
91+
if (verifyModule(M, &errs())) {
92+
Context.emitError("Stackmap insertion pass generated invalid IR!");
93+
return false;
94+
}
95+
#endif
96+
return true;
97+
}
98+
};
99+
} // namespace
100+
101+
char YkNoCallsInEntryBlocks::ID = 0;
102+
INITIALIZE_PASS(YkNoCallsInEntryBlocks, DEBUG_TYPE, "no more entry block calls",
103+
false, false)
104+
105+
ModulePass *llvm::createYkNoCallsInEntryBlocksPass() {
106+
return new YkNoCallsInEntryBlocks();
107+
}

0 commit comments

Comments
 (0)