Skip to content

Commit d72a8ea

Browse files
committed
Move the YkControlPoint pass later in the compilation pipeline.
This is required because one of the codegen passes, CodegenPrepare, can mutate the IR and if we run before this pass, then our view of live variables isn't true.
1 parent d1bab36 commit d72a8ea

File tree

4 files changed

+114
-93
lines changed

4 files changed

+114
-93
lines changed

llvm/include/llvm/Transforms/Yk/ControlPoint.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@
1111
#define YK_NEW_CONTROL_POINT "yk_new_control_point"
1212

1313
namespace llvm {
14-
class YkControlPointPass : public PassInfoMixin<YkControlPointPass> {
15-
public:
16-
explicit YkControlPointPass();
17-
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
18-
};
14+
ModulePass *createYkControlPointPass();
1915
} // namespace llvm
2016

2117
#endif

llvm/lib/CodeGen/TargetPassConfig.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#include "llvm/Transforms/Utils.h"
5050
#include "llvm/Transforms/Utils/SymbolRewriter.h"
5151
#include "llvm/Transforms/Yk/BlockDisambiguate.h"
52+
#include "llvm/Transforms/Yk/ControlPoint.h"
5253
#include <cassert>
5354
#include <string>
5455

@@ -243,6 +244,11 @@ static cl::opt<bool> YkBlockDisambiguate(
243244
"yk-block-disambiguate", cl::init(false), cl::NotHidden,
244245
cl::desc("Disambiguate blocks for yk"));
245246

247+
static cl::opt<bool>
248+
YkPatchCtrlPoint("yk-patch-control-point",
249+
cl::init(false), cl::NotHidden,
250+
cl::desc("Patch yk_control_point()"));
251+
246252
/// Allow standard passes to be disabled by command line options. This supports
247253
/// simple binary flags that either suppress the pass or do nothing.
248254
/// i.e. -disable-mypass=false has no effect.
@@ -1067,8 +1073,22 @@ bool TargetPassConfig::addISelPasses() {
10671073
addPassesToHandleExceptions();
10681074
if (YkBlockDisambiguate)
10691075
addPass(createYkBlockDisambiguatePass());
1070-
addISelPrepare();
10711076

1077+
// We insert the yk control point pass as late as possible. It has to run
1078+
// before instruction selection (or the machine IR won't reflect our
1079+
// patching), but after other passes which mutate the IR (e.g.
1080+
// `CodegenPrepare` above).
1081+
//
1082+
// By running the pass late, we also ensure that no IR optimisation passes
1083+
// ever see our patched-in control point function. If optimisations were to
1084+
// be applied then this would be problematic as the interface to the control
1085+
// point could be changed, e.g. the control point's struct argument could be
1086+
// decomposed into scalar arguments. The JIT runtime relies on the interface
1087+
// *not* being changed.
1088+
if (YkPatchCtrlPoint)
1089+
addPass(createYkControlPointPass());
1090+
1091+
addISelPrepare();
10721092
return addCoreISelPasses();
10731093
}
10741094

llvm/lib/LTO/LTOBackend.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,6 @@ static cl::opt<bool> ThinLTOAssumeMerged(
7575
cl::desc("Assume the input has already undergone ThinLTO function "
7676
"importing and the other pre-optimization pipeline changes."));
7777

78-
static cl::opt<bool>
79-
YkPatchCtrlPoint("yk-patch-control-point",
80-
cl::init(false), cl::NotHidden,
81-
cl::desc("Patch yk_control_point()"));
82-
8378
LLVM_ATTRIBUTE_NORETURN static void reportOpenError(StringRef Path, Twine Msg) {
8479
errs() << "failed to open " << Path << ": " << Msg << '\n';
8580
errs().flush();
@@ -306,13 +301,6 @@ static void runNewPMPasses(const Config &Conf, Module &Mod, TargetMachine *TM,
306301
MPM.addPass(PB.buildLTODefaultPipeline(OL, ExportSummary));
307302
}
308303

309-
// We add the yk control point pass late in the pipeline (after all
310-
// optimisation and just before verification and codegen) so that no IR
311-
// optimisation passes have a chance to change the interface to the control
312-
// point. The JIT runtime relies on the signature not being changed.
313-
if (YkPatchCtrlPoint)
314-
MPM.addPass(YkControlPointPass());
315-
316304
if (!Conf.DisableVerify)
317305
MPM.addPass(VerifierPass());
318306

llvm/lib/Transforms/Yk/ControlPoint.cpp

Lines changed: 92 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
#include "llvm/IR/Function.h"
8080
#include "llvm/IR/Instructions.h"
8181
#include "llvm/IR/Module.h"
82+
#include "llvm/InitializePasses.h"
8283
#include "llvm/Pass.h"
8384
#include <llvm/IR/Dominators.h>
8485
#include <llvm/IR/IRBuilder.h>
@@ -266,87 +267,103 @@ std::vector<Value *> getLiveVars(DominatorTree &DT, CallInst *OldCtrlPoint) {
266267
return Vec;
267268
}
268269

269-
YkControlPointPass::YkControlPointPass() {}
270-
271-
PreservedAnalyses YkControlPointPass::run(Module &M,
272-
ModuleAnalysisManager &AM) {
273-
LLVMContext &Context = M.getContext();
270+
namespace llvm {
271+
void initializeYkControlPointPass(PassRegistry &);
272+
}
274273

275-
// Locate the "dummy" control point provided by the user.
276-
CallInst *OldCtrlPointCall = findControlPointCall(M);
277-
if (OldCtrlPointCall == nullptr) {
278-
Context.emitError("ykllvm couldn't find the call to `yk_control_point()`");
279-
return PreservedAnalyses::all();
274+
namespace {
275+
class YkControlPoint : public ModulePass {
276+
public:
277+
static char ID;
278+
YkControlPoint() : ModulePass(ID) {
279+
initializeYkControlPointPass(*PassRegistry::getPassRegistry());
280280
}
281281

282-
// Replace old control point call.
283-
IRBuilder<> Builder(OldCtrlPointCall);
284-
285-
// Get function containing the control point.
286-
Function *Caller = OldCtrlPointCall->getFunction();
287-
288-
// Find all live variables just before the call to the control point.
289-
DominatorTree DT(*Caller);
290-
std::vector<Value *> LiveVals = getLiveVars(DT, OldCtrlPointCall);
291-
if (LiveVals.size() == 0) {
292-
Context.emitError(
293-
"The interpreter loop has no live variables!\n"
294-
"ykllvm doesn't support this scenario, as such an interpreter would "
295-
"make little sense.");
296-
return PreservedAnalyses::all();
297-
}
282+
bool runOnModule(Module &M) override {
283+
LLVMContext &Context = M.getContext();
298284

299-
// Generate the YkCtrlPointVars struct. This struct is used to package up a
300-
// copy of all LLVM variables that are live just before the call to the
301-
// control point. These are passed in to the patched control point so that
302-
// they can be used as inputs and outputs to JITted trace code. The control
303-
// point returns a new YkCtrlPointVars whose members may have been mutated
304-
// by JITted trace code (if a trace was executed).
305-
std::vector<Type *> TypeParams;
306-
for (Value *V : LiveVals) {
307-
TypeParams.push_back(V->getType());
308-
}
309-
StructType *CtrlPointReturnTy =
310-
StructType::create(TypeParams, "YkCtrlPointVars");
285+
// Locate the "dummy" control point provided by the user.
286+
CallInst *OldCtrlPointCall = findControlPointCall(M);
287+
if (OldCtrlPointCall == nullptr) {
288+
Context.emitError(
289+
"ykllvm couldn't find the call to `yk_control_point()`");
290+
return false;
291+
}
311292

312-
// Create the new control point.
313-
Type *YkLocTy = OldCtrlPointCall->getArgOperand(0)->getType();
314-
FunctionType *FType =
315-
FunctionType::get(CtrlPointReturnTy, {YkLocTy, CtrlPointReturnTy}, false);
316-
Function *NF = Function::Create(FType, GlobalVariable::ExternalLinkage,
317-
YK_NEW_CONTROL_POINT, M);
318-
319-
// Instantiate the YkCtrlPointStruct to pass in to the control point.
320-
Value *InputStruct = cast<Value>(Constant::getNullValue(CtrlPointReturnTy));
321-
unsigned LvIdx = 0;
322-
for (Value *LV : LiveVals) {
323-
InputStruct = Builder.CreateInsertValue(InputStruct, LV, LvIdx);
324-
assert(LvIdx != UINT_MAX);
325-
LvIdx++;
326-
}
293+
// Replace old control point call.
294+
IRBuilder<> Builder(OldCtrlPointCall);
295+
296+
// Get function containing the control point.
297+
Function *Caller = OldCtrlPointCall->getFunction();
298+
299+
// Find all live variables just before the call to the control point.
300+
DominatorTree DT(*Caller);
301+
std::vector<Value *> LiveVals = getLiveVars(DT, OldCtrlPointCall);
302+
if (LiveVals.size() == 0) {
303+
Context.emitError(
304+
"The interpreter loop has no live variables!\n"
305+
"ykllvm doesn't support this scenario, as such an interpreter would "
306+
"make little sense.");
307+
return false;
308+
}
327309

328-
// Insert call to the new control point.
329-
CallInst *CtrlPointRet =
330-
Builder.CreateCall(NF, {OldCtrlPointCall->getArgOperand(0), InputStruct});
331-
332-
// Once the control point returns we need to extract the (potentially
333-
// mutated) values from the returned YkCtrlPointStruct and reassign them to
334-
// their corresponding live variables. In LLVM IR we can do this by simply
335-
// replacing all future references with the new values.
336-
LvIdx = 0;
337-
for (Value *LV : LiveVals) {
338-
Value *New = Builder.CreateExtractValue(cast<Value>(CtrlPointRet), LvIdx);
339-
LV->replaceUsesWithIf(
340-
New, [&](Use &U) { return DT.dominates(CtrlPointRet, U); });
341-
assert(LvIdx != UINT_MAX);
342-
LvIdx++;
343-
}
310+
// Generate the YkCtrlPointVars struct. This struct is used to package up a
311+
// copy of all LLVM variables that are live just before the call to the
312+
// control point. These are passed in to the patched control point so that
313+
// they can be used as inputs and outputs to JITted trace code. The control
314+
// point returns a new YkCtrlPointVars whose members may have been mutated
315+
// by JITted trace code (if a trace was executed).
316+
std::vector<Type *> TypeParams;
317+
for (Value *V : LiveVals) {
318+
TypeParams.push_back(V->getType());
319+
}
320+
StructType *CtrlPointReturnTy =
321+
StructType::create(TypeParams, "YkCtrlPointVars");
322+
323+
// Create the new control point.
324+
Type *YkLocTy = OldCtrlPointCall->getArgOperand(0)->getType();
325+
FunctionType *FType = FunctionType::get(
326+
CtrlPointReturnTy, {YkLocTy, CtrlPointReturnTy}, false);
327+
Function *NF = Function::Create(FType, GlobalVariable::ExternalLinkage,
328+
YK_NEW_CONTROL_POINT, M);
329+
330+
// Instantiate the YkCtrlPointStruct to pass in to the control point.
331+
Value *InputStruct = cast<Value>(Constant::getNullValue(CtrlPointReturnTy));
332+
unsigned LvIdx = 0;
333+
for (Value *LV : LiveVals) {
334+
InputStruct = Builder.CreateInsertValue(InputStruct, LV, LvIdx);
335+
assert(LvIdx != UINT_MAX);
336+
LvIdx++;
337+
}
338+
339+
// Insert call to the new control point.
340+
CallInst *CtrlPointRet = Builder.CreateCall(
341+
NF, {OldCtrlPointCall->getArgOperand(0), InputStruct});
342+
343+
// Once the control point returns we need to extract the (potentially
344+
// mutated) values from the returned YkCtrlPointStruct and reassign them to
345+
// their corresponding live variables. In LLVM IR we can do this by simply
346+
// replacing all future references with the new values.
347+
LvIdx = 0;
348+
for (Value *LV : LiveVals) {
349+
Value *New = Builder.CreateExtractValue(cast<Value>(CtrlPointRet), LvIdx);
350+
LV->replaceUsesWithIf(
351+
New, [&](Use &U) { return DT.dominates(CtrlPointRet, U); });
352+
assert(LvIdx != UINT_MAX);
353+
LvIdx++;
354+
}
355+
356+
// Replace the call to the dummy control point.
357+
OldCtrlPointCall->eraseFromParent();
344358

345-
// Replace the call to the dummy control point.
346-
OldCtrlPointCall->eraseFromParent();
359+
// Generate new control point logic.
360+
createControlPoint(M, NF, LiveVals, CtrlPointReturnTy, YkLocTy);
361+
return true;
362+
}
363+
};
364+
} // namespace
347365

348-
// Generate new control point logic.
349-
createControlPoint(M, NF, LiveVals, CtrlPointReturnTy, YkLocTy);
366+
char YkControlPoint::ID = 0;
367+
INITIALIZE_PASS(YkControlPoint, DEBUG_TYPE, "yk control point", false, false)
350368

351-
return PreservedAnalyses::none();
352-
}
369+
ModulePass *llvm::createYkControlPointPass() { return new YkControlPoint(); }

0 commit comments

Comments
 (0)