LLVM 24.0.0git
ReOptimizeLayer.cpp
Go to the documentation of this file.
6
7using namespace llvm;
8using namespace orc;
9
10bool ReOptimizeLayer::ReOptMaterializationUnitState::tryStartReoptimize() {
11 std::unique_lock<std::mutex> Lock(Mutex);
12 if (Reoptimizing)
13 return false;
14
15 Reoptimizing = true;
16 return true;
17}
18
19void ReOptimizeLayer::ReOptMaterializationUnitState::reoptimizeSucceeded() {
20 std::unique_lock<std::mutex> Lock(Mutex);
21 assert(Reoptimizing && "Tried to mark unstarted reoptimization as done");
22 Reoptimizing = false;
23 CurVersion++;
24}
25
26void ReOptimizeLayer::ReOptMaterializationUnitState::reoptimizeFailed() {
27 std::unique_lock<std::mutex> Lock(Mutex);
28 assert(Reoptimizing && "Tried to mark unstarted reoptimization as done");
29 Reoptimizing = false;
30}
31
33 shared::CWrapperFunctionBuffer (*JITDispatch)(void *Ctx, void *Tag,
34 const char *Data,
35 size_t Size),
36 void *JITDispatchCtx, void *Tag, uint64_t MUID, uint32_t CurVersion) {
37 // Serialize the arguments into a WrapperFunctionBuffer and call dispatch.
39 auto ArgBytes =
40 shared::WrapperFunctionBuffer::allocate(SPSArgs::size(MUID, CurVersion));
41 shared::SPSOutputBuffer OB(ArgBytes.data(), ArgBytes.size());
42 if (!SPSArgs::serialize(OB, MUID, CurVersion)) {
43 errs()
44 << "Reoptimization error: could not serialize reoptimization arguments";
45 abort();
46 }
48 JITDispatch(JITDispatchCtx, Tag, ArgBytes.data(), ArgBytes.size())};
49
50 if (const char *ErrMsg = Buf.getOutOfBandError()) {
51 errs() << "Reoptimization error: " << ErrMsg << "\naborting.\n";
52 abort();
53 }
54}
55
57 const DataLayout &DL) {
58 auto Ctx = std::make_unique<LLVMContext>();
59 auto Mod = std::make_unique<Module>("orc-rt-lite-reoptimize.ll", *Ctx);
60 Mod->setDataLayout(DL);
61 Mod->setTargetTriple(ES.getTargetTriple());
62
63 IRBuilder<> Builder(*Mod);
64
65 // Create basic types portably
66 Type *VoidTy = Type::getVoidTy(*Ctx);
67 Type *Int8Ty = Type::getInt8Ty(*Ctx);
68 Type *Int32Ty = Type::getInt32Ty(*Ctx);
69 Type *Int64Ty = Type::getInt64Ty(*Ctx);
70 Type *VoidPtrTy = PointerType::getUnqual(*Ctx);
71
72 // Helper function type: void (void*, void*, void*, uint64_t, uint32_t)
73 FunctionType *HelperFnTy = FunctionType::get(
74 VoidTy, {VoidPtrTy, VoidPtrTy, VoidPtrTy, Int64Ty, Int32Ty}, false);
75
76 // Define ReoptimizeTag with initializer = 0
77 GlobalVariable *ReoptimizeTag = new GlobalVariable(
78 *Mod, Int8Ty, false, GlobalValue::ExternalLinkage,
79 ConstantInt::get(Int8Ty, 0), "__orc_rt_reoptimize_tag");
80
81 // Define orc_rt_lite_reoptimize function: void (uint64_t, uint32_t)
82 FunctionType *ReOptimizeFnTy =
83 FunctionType::get(VoidTy, {Int64Ty, Int32Ty}, false);
84
85 Function *ReOptimizeFn =
87 "__orc_rt_reoptimize", Mod.get());
88
89 // Set parameter names
90 auto ArgIt = ReOptimizeFn->arg_begin();
91 Value *MUID = &*ArgIt++;
92 MUID->setName("MUID");
93 Value *CurVersion = &*ArgIt;
94 CurVersion->setName("CurVersion");
95
96 // Build function body
97 BasicBlock *Entry = BasicBlock::Create(*Ctx, "entry", ReOptimizeFn);
98 Builder.SetInsertPoint(Entry);
99
100 ExecutorAddr JITDispatchSym, JITDispatchCtxSym;
101 if (auto Err =
102 lookupAndApply(ES.getBootstrapJITDylib(),
103 {recordAddr(rt::DispatchName, &JITDispatchSym),
104 recordAddr(rt::DispatchCtxName, &JITDispatchCtxSym)}))
105 return Err;
106
107 Type *IntPtrTy = DL.getIntPtrType(*Ctx);
108 Constant *JITDispatchPtr = ConstantExpr::getIntToPtr(
109 ConstantInt::get(IntPtrTy, JITDispatchSym.getValue()), VoidPtrTy);
110 Constant *JITDispatchCtxPtr = ConstantExpr::getIntToPtr(
111 ConstantInt::get(IntPtrTy, JITDispatchCtxSym.getValue()), VoidPtrTy);
112 Constant *HelperFnAddr = ConstantExpr::getIntToPtr(
113 ConstantInt::get(IntPtrTy, reinterpret_cast<uintptr_t>(
116
117 // Cast ReoptimizeTag to void*
118 Value *ReoptimizeTagPtr = Builder.CreatePointerCast(ReoptimizeTag, VoidPtrTy);
119
120 // Call the helper function
121 Builder.CreateCall(
122 HelperFnTy, HelperFnAddr,
123 {JITDispatchPtr, JITDispatchCtxPtr, ReoptimizeTagPtr, MUID, CurVersion});
124
125 // Return void
126 Builder.CreateRetVoid();
127
128 return BaseLayer.add(PlatformJD,
129 ThreadSafeModule(std::move(Mod), std::move(Ctx)));
130}
131
133 using ReoptimizeSPSSig = shared::SPSError(uint64_t, uint32_t);
134 return ES.registerCallControllerHandlers(
136 SymbolNameSpec::c("__orc_rt_reoptimize_tag"), this,
137 &ReOptimizeLayer::rt_reoptimize));
138}
139
140void ReOptimizeLayer::emit(std::unique_ptr<MaterializationResponsibility> R,
141 ThreadSafeModule TSM) {
142 auto &JD = R->getTargetJITDylib();
143
144 bool HasNonCallable = false;
145 for (auto &KV : R->getSymbols()) {
146 auto &Flags = KV.second;
147 if (!Flags.isCallable())
148 HasNonCallable = true;
149 }
150
151 if (HasNonCallable) {
152 BaseLayer.emit(std::move(R), std::move(TSM));
153 return;
154 }
155
156 auto &MUState = createMaterializationUnitState(TSM);
157
158 if (auto Err = R->withResourceKeyDo([&](ResourceKey Key) {
159 registerMaterializationUnitResource(Key, MUState);
160 })) {
161 ES.reportError(std::move(Err));
162 R->failMaterialization();
163 return;
164 }
165
166 if (auto Err =
167 ProfilerFunc(*this, MUState.getID(), MUState.getCurVersion(), TSM)) {
168 ES.reportError(std::move(Err));
169 R->failMaterialization();
170 return;
171 }
172
173 auto InitialDests =
174 emitMUImplSymbols(MUState, MUState.getCurVersion(), JD, std::move(TSM));
175 if (!InitialDests) {
176 ES.reportError(InitialDests.takeError());
177 R->failMaterialization();
178 return;
179 }
180
181 RSManager.emitRedirectableSymbols(std::move(R), std::move(*InitialDests));
182}
183
186 unsigned CurVersion,
187 ThreadSafeModule &TSM) {
188 return TSM.withModuleDo([&](Module &M) -> Error {
189 Type *I64Ty = Type::getInt64Ty(M.getContext());
190 GlobalVariable *Counter = new GlobalVariable(
191 M, I64Ty, false, GlobalValue::InternalLinkage,
192 Constant::getNullValue(I64Ty), "__orc_reopt_counter");
193 for (auto &F : M) {
194 if (F.isDeclaration())
195 continue;
196 auto &BB = F.getEntryBlock();
197 auto *IP = &*BB.getFirstInsertionPt();
198 IRBuilder<> IRB(IP);
199 Value *Threshold = ConstantInt::get(I64Ty, CallCountThreshold, true);
200 Value *Cnt = IRB.CreateLoad(I64Ty, Counter);
201 // Use EQ to prevent further reoptimize calls.
202 Value *Cmp = IRB.CreateICmpEQ(Cnt, Threshold);
203 Value *Added = IRB.CreateAdd(Cnt, ConstantInt::get(I64Ty, 1));
204 (void)IRB.CreateStore(Added, Counter);
205 Instruction *SplitTerminator = SplitBlockAndInsertIfThen(Cmp, IP, false);
206 createReoptimizeCall(M, *SplitTerminator, MUID, CurVersion);
207 }
208 return Error::success();
209 });
210}
211
213ReOptimizeLayer::emitMUImplSymbols(ReOptMaterializationUnitState &MUState,
215 ThreadSafeModule TSM) {
217 cantFail(TSM.withModuleDo([&](Module &M) -> Error {
218 MangleAndInterner Mangle(ES, M.getDataLayout());
219 for (auto &F : M)
220 if (!F.isDeclaration()) {
221 std::string NewName =
222 (F.getName() + ".__def__." + Twine(Version)).str();
223 RenamedMap[Mangle(F.getName())] = Mangle(NewName);
224 F.setName(NewName);
225 }
226 return Error::success();
227 }));
228
229 auto RT = JD.createResourceTracker();
230 if (auto Err =
231 JD.define(std::make_unique<BasicIRLayerMaterializationUnit>(
232 BaseLayer, *getManglingOptions(), std::move(TSM)),
233 RT))
234 return Err;
235 MUState.setResourceTracker(RT);
236
237 SymbolLookupSet LookupSymbols;
238 for (auto [K, V] : RenamedMap)
239 LookupSymbols.add(V);
240
241 auto ImplSymbols =
242 ES.lookup({{&JD, JITDylibLookupFlags::MatchAllSymbols}}, LookupSymbols,
244 if (auto Err = ImplSymbols.takeError())
245 return Err;
246
248 for (auto [K, V] : RenamedMap)
249 Result[K] = (*ImplSymbols)[V];
250
251 return Result;
252}
253
254void ReOptimizeLayer::rt_reoptimize(SendErrorFn SendResult,
256 uint32_t CurVersion) {
257 auto &MUState = getMaterializationUnitState(MUID);
258 if (CurVersion < MUState.getCurVersion() || !MUState.tryStartReoptimize()) {
259 SendResult(Error::success());
260 return;
261 }
262
263 ThreadSafeModule TSM = cloneToNewContext(MUState.getThreadSafeModule());
264 auto OldRT = MUState.getResourceTracker();
265 auto &JD = OldRT->getJITDylib();
266
267 if (auto Err = ReOptFunc(*this, MUID, CurVersion + 1, OldRT, TSM)) {
268 ES.reportError(std::move(Err));
269 MUState.reoptimizeFailed();
270 SendResult(Error::success());
271 return;
272 }
273
274 auto SymbolDests =
275 emitMUImplSymbols(MUState, CurVersion + 1, JD, std::move(TSM));
276 if (!SymbolDests) {
277 ES.reportError(SymbolDests.takeError());
278 MUState.reoptimizeFailed();
279 SendResult(Error::success());
280 return;
281 }
282
283 if (auto Err = RSManager.redirect(JD, std::move(*SymbolDests))) {
284 ES.reportError(std::move(Err));
285 MUState.reoptimizeFailed();
286 SendResult(Error::success());
287 return;
288 }
289
290 MUState.reoptimizeSucceeded();
291 SendResult(Error::success());
292}
293
296 uint32_t CurVersion) {
297 Type *MUIDTy = IntegerType::get(M.getContext(), 64);
298 Type *VersionTy = IntegerType::get(M.getContext(), 32);
299 Function *ReoptimizeFunc = M.getFunction("__orc_rt_reoptimize");
300 if (!ReoptimizeFunc) {
301 std::vector<Type *> ArgTys = {MUIDTy, VersionTy};
302 FunctionType *FuncTy =
303 FunctionType::get(Type::getVoidTy(M.getContext()), ArgTys, false);
304 ReoptimizeFunc = Function::Create(FuncTy, GlobalValue::ExternalLinkage,
305 "__orc_rt_reoptimize", &M);
306 }
307 Constant *MUIDArg = ConstantInt::get(MUIDTy, MUID, false);
308 Constant *CurVersionArg = ConstantInt::get(VersionTy, CurVersion, false);
309 IRBuilder<> IRB(&IP);
310 (void)IRB.CreateCall(ReoptimizeFunc, {MUIDArg, CurVersionArg});
311}
312
313ReOptimizeLayer::ReOptMaterializationUnitState &
314ReOptimizeLayer::createMaterializationUnitState(const ThreadSafeModule &TSM) {
315 std::unique_lock<std::mutex> Lock(Mutex);
316 ReOptMaterializationUnitID MUID = NextID;
317 MUStates.emplace(MUID,
318 ReOptMaterializationUnitState(MUID, cloneToNewContext(TSM)));
319 ++NextID;
320 return MUStates.at(MUID);
321}
322
323ReOptimizeLayer::ReOptMaterializationUnitState &
324ReOptimizeLayer::getMaterializationUnitState(ReOptMaterializationUnitID MUID) {
325 std::unique_lock<std::mutex> Lock(Mutex);
326 return MUStates.at(MUID);
327}
328
329void ReOptimizeLayer::registerMaterializationUnitResource(
330 ResourceKey Key, ReOptMaterializationUnitState &State) {
331 std::unique_lock<std::mutex> Lock(Mutex);
332 MUResources[Key].insert(State.getID());
333}
334
336 std::unique_lock<std::mutex> Lock(Mutex);
337 for (auto MUID : MUResources[K])
338 MUStates.erase(MUID);
339
340 MUResources.erase(K);
341 return Error::success();
342}
343
345 ResourceKey SrcK) {
346 std::unique_lock<std::mutex> Lock(Mutex);
347 MUResources[DstK].insert_range(MUResources[SrcK]);
348 MUResources.erase(SrcK);
349}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
#define F(x, y, z)
Definition MD5.cpp:54
static void orc_rt_lite_reoptimize_helper(shared::CWrapperFunctionBuffer(*JITDispatch)(void *Ctx, void *Tag, const char *Data, size_t Size), void *JITDispatchCtx, void *Tag, uint64_t MUID, uint32_t CurVersion)
LLVM Basic Block Representation.
Definition BasicBlock.h:62
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
static LLVM_ABI Constant * getIntToPtr(Constant *C, Type *Ty, bool OnlyIfReduced=false)
This is an important base class in LLVM.
Definition Constant.h:43
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Class to represent function types.
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:169
const Function & getFunction() const
Definition Function.h:167
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ ExternalLinkage
Externally visible function.
Definition GlobalValue.h:53
Value * CreateICmpEQ(Value *LHS, Value *RHS, const Twine &Name="")
Definition IRBuilder.h:2391
LoadInst * CreateLoad(Type *Ty, Value *Ptr, const char *Name)
Provided to resolve 'CreateLoad(Ty, Ptr, "...")' correctly, instead of converting the string to 'bool...
Definition IRBuilder.h:1916
StoreInst * CreateStore(Value *Val, Value *Ptr, bool isVolatile=false)
Definition IRBuilder.h:1934
Value * CreateAdd(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition IRBuilder.h:1427
CallInst * CreateCall(FunctionType *FTy, Value *Callee, ArrayRef< Value * > Args={}, const Twine &Name="", MDNode *FPMathTag=nullptr)
Definition IRBuilder.h:2570
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2918
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:338
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI IntegerType * getInt64Ty(LLVMContext &C)
Definition Type.cpp:300
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:272
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
Definition Type.cpp:297
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:394
LLVM_ABI void lookup(LookupKind K, const JITDylibSearchOrder &SearchOrder, SymbolLookupSet Symbols, SymbolState RequiredState, SymbolsResolvedCallback NotifyComplete, RegisterDependenciesFunction RegisterDependencies)
Search the given JITDylibs for the given symbols.
Definition Core.cpp:1784
Represents an address in the executor process.
uint64_t getValue() const
const IRSymbolMapper::ManglingOptions *& getManglingOptions() const
Get the mangling options for this layer.
Definition Layer.h:76
Represents a JIT'd dynamic library.
Definition Core.h:676
Error registerRuntimeFunctions(JITDylib &PlatformJD)
Registers reoptimize runtime dispatch handlers to given PlatformJD.
ReOptimizeLayer(ExecutionSession &ES, DataLayout &DL, IRLayer &BaseLayer, RedirectableSymbolManager &RM)
void emit(std::unique_ptr< MaterializationResponsibility > R, ThreadSafeModule TSM) override
Emits the given module.
static void createReoptimizeCall(Module &M, Instruction &IP, ReOptMaterializationUnitID MUID, unsigned CurVersion)
void handleTransferResources(JITDylib &JD, ResourceKey DstK, ResourceKey SrcK) override
This function will be called inside the session lock.
Error addOrcRTLiteSupport(JITDylib &PlatformJD, const DataLayout &DL)
Add ORC Runtime-lite support for reoptimization to PlatformJD.
static Error reoptimizeIfCallFrequent(ReOptimizeLayer &Parent, ReOptMaterializationUnitID MUID, unsigned CurVersion, ThreadSafeModule &TSM)
Basic AddProfilerFunc that reoptimizes the function when the call count exceeds CallCountThreshold.
Error handleRemoveResources(JITDylib &JD, ResourceKey K) override
This function will be called outside the session lock.
static const uint64_t CallCountThreshold
A set of symbols to look up, each associated with a SymbolLookupFlags value.
SymbolLookupSet & add(SymbolStringPtr Name, SymbolLookupFlags Flags=SymbolLookupFlags::RequiredSymbol)
Add an element to the set.
static constexpr SymbolNameSpec c(StringRef Name)
An LLVM Module together with a shared ThreadSafeContext.
decltype(auto) withModuleDo(Func &&F)
Locks the associated ThreadSafeContext and calls the given function on the contained Module.
A utility class for serializing to a blob from a variadic list.
Output char buffer with overflow check.
C++ wrapper function buffer: Same as CWrapperFunctionBuffer but auto-releases memory.
const char * getOutOfBandError() const
If this value is an out-of-band error then this returns the error message, otherwise returns nullptr.
static WrapperFunctionBuffer allocate(size_t Size)
Create a WrapperFunctionBuffer with the given size and return a pointer to the underlying memory.
ExecutionSession::CallControllerHandlerBinding bindCallControllerHandlerSPS(SymbolNameSpec Name, HandlerT &&Handler, SymbolLookupFlags LF=SymbolLookupFlags::RequiredSymbol)
Bind a call-controller handler that takes concrete argument types (and a sender for a concrete return...
uintptr_t ResourceKey
Definition Core.h:61
LLVM_ABI void lookupAndApply(unique_function< void(Error)> OnApplied, LookupKind K, const JITDylibSearchOrder &SearchOrder, ArrayRef< LookupPrepareFn > PrepareFns)
Resolve the symbols contributed by every prepare function with a single lookup, then let each of thei...
DenseMap< SymbolStringPtr, ExecutorSymbolDef > SymbolMap
A map from symbol names (as SymbolStringPtrs) to JITSymbols (address/flags pairs).
LLVM_ABI ThreadSafeModule cloneToNewContext(const ThreadSafeModule &TSMW, GVPredicate ShouldCloneDef=GVPredicate(), GVModifier UpdateClonedDefSource=GVModifier())
Clones the given module on to a new context.
@ Resolved
Queried, materialization begun.
Definition Core.h:550
SmartMutex< false > Mutex
Mutex - A standard, always enforced mutex.
Definition Mutex.h:66
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
@ Mod
The access may modify the value stored in memory.
Definition ModRef.h:34
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition Error.h:769
IntPtrTy
Definition InstrProf.h:82
LLVM_ABI Instruction * SplitBlockAndInsertIfThen(Value *Cond, BasicBlock::iterator SplitBefore, bool Unreachable, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, BasicBlock *ThenBlock=nullptr)
Split the containing block at the specified instruction - everything before SplitBefore stays in the ...