2727// size_t pc;
2828// }
2929//
30+ // struct YkCtrlPointStruct cp_vars;
3031// pc = 0;
3132// while (...) {
32- // struct YkCtrlPointStruct cp_in = { pc };
3333// // Now we call the patched control point.
34- // YkCtrlPointStruct cp_out = yk_new_control_point(cp_in);
35- // pc = cp_out.pc;
34+ // cp_vars.pc = pc;
35+ // yk_new_control_point(&cp_vars);
36+ // pc = cp_vars.pc;
3637// bc = program[pc];
3738// switch (bc) {
3839// // bytecode handlers here.
3940// }
4041// }
4142// ```
4243//
43- // The call to the dummy control point must be the first thing that appears in
44- // an interpreter dispatch loop.
45- //
46- // YKFIXME: The control point cannot yet be used in an interpreter using
47- // threaded dispatch.
48- //
49- // YKFIXME: The tracing logic is currently over-simplified. The following items
50- // need to be fixed:
51- //
52- // - The address of `YkLocation` instances are used for identity, but they are
53- // intended to be freely moved by the user.
54- //
55- // - Tracing starts when we encounter a location for which we have no machine
56- // code. A hot counter should be used instead.
57- //
58- // - There can be only one compiled trace for now. There should be a code
59- // cache mapping from JIT locations to their machine code.
60- //
61- // - The interpreter is assumed to be single threaded. We should implement a
62- // synchronisation function in Rust code that synchronises many threads which
63- // are calling the control point concurrently. This function should return a
64- // value that indicates if we should start/stop tracing, or jump to machine
65- // code etc.
66- //
67- // - Guards are currently assumed to abort the program.
68- // https://github.com/ykjit/yk/issues/443
69- //
70- // - The block that performs the call to JITted code branches back to itself
71- // to achieve rudimentary trace stitching. The looping should really be
72- // implemented in the JITted code itself so that it isn't necessary to
73- // repeatedly enter and exit the JITted code.
74- // https://github.com/ykjit/yk/issues/442
75- // ===----------------------------------------------------------------------===//
44+ // Note that this transformation occurs at the LLVM IR level. The above example
45+ // is shown as C code for easy comprehension.
7646
7747#include " llvm/Transforms/Yk/ControlPoint.h"
7848#include " llvm/IR/BasicBlock.h"
8858#define DEBUG_TYPE " yk-control-point"
8959#define JIT_STATE_PREFIX " jit-state: "
9060
91- // These constants mirror `ykrt::mt::JITACTION_*`.
92- const uintptr_t JITActionNop = 1 ;
93- const uintptr_t JITActionStartTracing = 2 ;
94- const uintptr_t JITActionStopTracing = 3 ;
95-
9661using namespace llvm ;
9762
9863// / Find the call to the dummy control point that we want to patch.
@@ -113,124 +78,6 @@ CallInst *findControlPointCall(Module &M) {
11378 return cast<CallInst>(*U);
11479}
11580
116- // / Creates a call for printing debug information inside the control point.
117- void createJITStatePrint (IRBuilder<> &Builder, Module *Mod, std::string Str) {
118- if (std::getenv (" YKD_PRINT_JITSTATE" ) == nullptr )
119- return ;
120- LLVMContext &Context = Mod->getContext ();
121- FunctionCallee Puts = Mod->getOrInsertFunction (
122- " __yk_debug_print" ,
123- FunctionType::get (Type::getVoidTy (Context),
124- PointerType::get (Type::getInt8Ty (Context), 0 ), true ));
125- Value *PutsString =
126- Builder.CreateGlobalStringPtr (StringRef (JIT_STATE_PREFIX + Str));
127- Builder.CreateCall (Puts, PutsString);
128- }
129-
130- // / Generates the new control point, which includes all logic to start/stop
131- // / tracing and to compile/execute traces.
132- void createControlPoint (Module &Mod, Function *F, std::vector<Value *> LiveVars,
133- StructType *YkCtrlPointStruct, Type *YkLocTy) {
134- auto &Context = Mod.getContext ();
135-
136- // Create control point blocks and setup the IRBuilder.
137- BasicBlock *CtrlPointEntry = BasicBlock::Create (Context, " cpentry" , F);
138- BasicBlock *BBExecuteTrace = BasicBlock::Create (Context, " bbhexectrace" , F);
139- BasicBlock *BBStartTracing = BasicBlock::Create (Context, " bbstarttracing" , F);
140- BasicBlock *BBReturn = BasicBlock::Create (Context, " bbreturn" , F);
141- BasicBlock *BBStopTracing = BasicBlock::Create (Context, " bbstoptracing" , F);
142-
143- // Get the type for a pointer-sized integer.
144- DataLayout DL (&Mod);
145- unsigned PtrBitSize = DL.getPointerSize () * 8 ;
146- IntegerType *PtrSizedInteger = IntegerType::getIntNTy (Context, PtrBitSize);
147-
148- // Some frequently used constants.
149- ConstantInt *JActNop = ConstantInt::get (PtrSizedInteger, JITActionNop);
150- ConstantInt *JActStartTracing =
151- ConstantInt::get (PtrSizedInteger, JITActionStartTracing);
152- ConstantInt *JActStopTracing =
153- ConstantInt::get (PtrSizedInteger, JITActionStopTracing);
154-
155- // Add definitions for __yk functions.
156- Function *FuncTransLoc = llvm::Function::Create (
157- FunctionType::get (PtrSizedInteger, {Type::getInt8PtrTy (Context)}, false ),
158- GlobalValue::ExternalLinkage, " __ykrt_transition_location" , Mod);
159-
160- Function *FuncSetCodePtr = llvm::Function::Create (
161- FunctionType::get (
162- Type::getVoidTy (Context),
163- {Type::getInt8PtrTy (Context), Type::getInt8PtrTy (Context)}, false ),
164- GlobalValue::ExternalLinkage, " __ykrt_set_loc_code_ptr" , Mod);
165-
166- Function *FuncStartTracing = llvm::Function::Create (
167- FunctionType::get (Type::getVoidTy (Context), {Type::getInt64Ty (Context)},
168- false ),
169- GlobalValue::ExternalLinkage, " __yktrace_start_tracing" , Mod);
170-
171- Function *FuncStopTracing = llvm::Function::Create (
172- FunctionType::get (Type::getInt8PtrTy (Context), {}, false ),
173- GlobalValue::ExternalLinkage, " __yktrace_stop_tracing" , Mod);
174-
175- Function *FuncCompileTrace = llvm::Function::Create (
176- FunctionType::get (Type::getInt8PtrTy (Context),
177- {Type::getInt8PtrTy (Context)}, false ),
178- GlobalValue::ExternalLinkage, " __yktrace_irtrace_compile" , Mod);
179-
180- // Populate the entry block. This calls `__ykrt_transition_location()` to
181- // decide what to do next.
182- IRBuilder<> Builder (CtrlPointEntry);
183- Value *CastLoc =
184- Builder.CreateBitCast (F->getArg (0 ), Type::getInt8PtrTy (Context));
185- Value *JITAction = Builder.CreateCall (FuncTransLoc->getFunctionType (),
186- FuncTransLoc, {CastLoc});
187- SwitchInst *ActionSw = Builder.CreateSwitch (JITAction, BBExecuteTrace, 3 );
188- ActionSw->addCase (JActNop, BBReturn);
189- ActionSw->addCase (JActStartTracing, BBStartTracing);
190- ActionSw->addCase (JActStopTracing, BBStopTracing);
191-
192- // Populate the block that starts tracing.
193- Builder.SetInsertPoint (BBStartTracing);
194- createJITStatePrint (Builder, &Mod, " start-tracing" );
195- Builder.CreateCall (FuncStartTracing->getFunctionType (), FuncStartTracing,
196- {ConstantInt::get (Context, APInt (64 , 1 ))});
197- Builder.CreateBr (BBReturn);
198-
199- // Populate the block that calls a compiled trace. If execution gets into
200- // this block then `JITAction` is a pointer to a compiled trace.
201- Builder.SetInsertPoint (BBExecuteTrace);
202- std::vector<Type *> TypeParams;
203- for (Value *LV : LiveVars) {
204- TypeParams.push_back (LV->getType ());
205- }
206- FunctionType *FType = FunctionType::get (
207- Type::getVoidTy (Context), {YkCtrlPointStruct->getPointerTo ()}, false );
208- Value *JITActionPtr =
209- Builder.CreateIntToPtr (JITAction, Type::getInt8PtrTy (Context));
210- Value *CastTrace = Builder.CreateBitCast (JITActionPtr, FType->getPointerTo ());
211- createJITStatePrint (Builder, &Mod, " enter-jit-code" );
212- CallInst *CTResult = Builder.CreateCall (FType, CastTrace, F->getArg (1 ));
213- createJITStatePrint (Builder, &Mod, " exit-jit-code" );
214- CTResult->setTailCall (true );
215- Builder.CreateBr (BBExecuteTrace);
216-
217- // Create block that stops tracing, compiles a trace, and stores it in a
218- // global variable.
219- Builder.SetInsertPoint (BBStopTracing);
220- Value *TR =
221- Builder.CreateCall (FuncStopTracing->getFunctionType (), FuncStopTracing);
222- Value *CT = Builder.CreateCall (FuncCompileTrace->getFunctionType (),
223- FuncCompileTrace, {TR});
224- Builder.CreateCall (FuncSetCodePtr->getFunctionType (), FuncSetCodePtr,
225- {CastLoc, CT});
226- createJITStatePrint (Builder, &Mod, " stop-tracing" );
227- Builder.CreateBr (BBReturn);
228-
229- // Populate the return block.
230- Builder.SetInsertPoint (BBReturn);
231- Builder.CreateRetVoid ();
232- }
233-
23481// / Extract all live variables that need to be passed into the control point.
23582std::vector<Value *> getLiveVars (DominatorTree &DT, CallInst *OldCtrlPoint) {
23683 std::vector<Value *> Vec;
@@ -347,7 +194,6 @@ class YkControlPoint : public ModulePass {
347194 OldCtrlPointCall->eraseFromParent ();
348195
349196 // Generate new control point logic.
350- createControlPoint (M, NF, LiveVals, CtrlPointVarsTy, YkLocTy);
351197 return true ;
352198 }
353199};
0 commit comments