Skip to content

Commit 7873e69

Browse files
committed
Add pass disallowing calls in entry blocks.
The pass splits entry block at the first call instruction, thus ensuring that no entry block has a function call. This makes it easier during trace construction to determine when external functions call back into mappable code.
1 parent 9f5de3b commit 7873e69

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
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/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/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)