1 //===---------- speculation.cpp - Utilities for Speculation ----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/ExecutionEngine/Orc/Speculation.h"
10 #include "llvm/IR/BasicBlock.h"
11 #include "llvm/IR/Function.h"
12 #include "llvm/IR/IRBuilder.h"
13 #include "llvm/IR/Instruction.h"
14 #include "llvm/IR/Instructions.h"
15 #include "llvm/IR/LLVMContext.h"
16 #include "llvm/IR/Module.h"
17 #include "llvm/IR/Type.h"
18 #include "llvm/IR/Verifier.h"
19 #include "llvm/Support/Debug.h"
20 
21 #include <vector>
22 
23 namespace llvm {
24 
25 namespace orc {
26 
27 // ImplSymbolMap methods
28 void ImplSymbolMap::trackImpls(SymbolAliasMap ImplMaps, JITDylib *SrcJD) {
29   assert(SrcJD && "Tracking on Null Source .impl dylib");
30   std::lock_guard<std::mutex> Lockit(ConcurrentAccess);
31   for (auto &I : ImplMaps) {
32     auto It = Maps.insert({I.first, {I.second.Aliasee, SrcJD}});
33     // check rationale when independent dylibs have same symbol name?
34     assert(It.second && "ImplSymbols are already tracked for this Symbol?");
35     (void)(It);
36   }
37 }
38 
39 // Trigger Speculative Compiles.
40 void Speculator::speculateForEntryPoint(Speculator *Ptr, uint64_t StubId) {
41   assert(Ptr && " Null Address Received in orc_speculate_for ");
42   Ptr->speculateFor(StubId);
43 }
44 
45 Error Speculator::addSpeculationRuntime(JITDylib &JD,
46                                         MangleAndInterner &Mangle) {
47   JITEvaluatedSymbol ThisPtr(pointerToJITTargetAddress(this),
48                              JITSymbolFlags::Exported);
49   JITEvaluatedSymbol SpeculateForEntryPtr(
50       pointerToJITTargetAddress(&speculateForEntryPoint),
51       JITSymbolFlags::Exported);
52   return JD.define(absoluteSymbols({
53       {Mangle("__orc_speculator"), ThisPtr},                // Data Symbol
54       {Mangle("__orc_speculate_for"), SpeculateForEntryPtr} // Callable Symbol
55   }));
56 }
57 
58 // If two modules, share the same LLVMContext, different threads must
59 // not access them concurrently without locking the associated LLVMContext
60 // this implementation follows this contract.
61 void IRSpeculationLayer::emit(MaterializationResponsibility R,
62                               ThreadSafeModule TSM) {
63 
64   assert(TSM && "Speculation Layer received Null Module ?");
65   assert(TSM.getContext().getContext() != nullptr &&
66          "Module with null LLVMContext?");
67 
68   // Instrumentation of runtime calls, lock the Module
69   TSM.withModuleDo([this, &R](Module &M) {
70     auto &MContext = M.getContext();
71     auto SpeculatorVTy = StructType::create(MContext, "Class.Speculator");
72     auto RuntimeCallTy = FunctionType::get(
73         Type::getVoidTy(MContext),
74         {SpeculatorVTy->getPointerTo(), Type::getInt64Ty(MContext)}, false);
75     auto RuntimeCall =
76         Function::Create(RuntimeCallTy, Function::LinkageTypes::ExternalLinkage,
77                          "__orc_speculate_for", &M);
78     auto SpeclAddr = new GlobalVariable(
79         M, SpeculatorVTy, false, GlobalValue::LinkageTypes::ExternalLinkage,
80         nullptr, "__orc_speculator");
81 
82     IRBuilder<> Mutator(MContext);
83 
84     // QueryAnalysis allowed to transform the IR source, one such example is
85     // Simplify CFG helps the static branch prediction heuristics!
86     for (auto &Fn : M.getFunctionList()) {
87       if (!Fn.isDeclaration()) {
88 
89         auto IRNames = QueryAnalysis(Fn);
90         // Instrument and register if Query has result
91         if (IRNames.hasValue()) {
92 
93           // Emit globals for each function.
94           auto LoadValueTy = Type::getInt8Ty(MContext);
95           auto SpeculatorGuard = new GlobalVariable(
96               M, LoadValueTy, false, GlobalValue::LinkageTypes::InternalLinkage,
97               ConstantInt::get(LoadValueTy, 0),
98               "__orc_speculate.guard.for." + Fn.getName());
99           SpeculatorGuard->setAlignment(Align::None());
100           SpeculatorGuard->setUnnamedAddr(GlobalValue::UnnamedAddr::Local);
101 
102           BasicBlock &ProgramEntry = Fn.getEntryBlock();
103           // Create BasicBlocks before the program's entry basicblock
104           BasicBlock *SpeculateBlock = BasicBlock::Create(
105               MContext, "__orc_speculate.block", &Fn, &ProgramEntry);
106           BasicBlock *SpeculateDecisionBlock = BasicBlock::Create(
107               MContext, "__orc_speculate.decision.block", &Fn, SpeculateBlock);
108 
109           assert(SpeculateDecisionBlock == &Fn.getEntryBlock() &&
110                  "SpeculateDecisionBlock not updated?");
111           Mutator.SetInsertPoint(SpeculateDecisionBlock);
112 
113           auto LoadGuard =
114               Mutator.CreateLoad(LoadValueTy, SpeculatorGuard, "guard.value");
115           // if just loaded value equal to 0,return true.
116           auto CanSpeculate =
117               Mutator.CreateICmpEQ(LoadGuard, ConstantInt::get(LoadValueTy, 0),
118                                    "compare.to.speculate");
119           Mutator.CreateCondBr(CanSpeculate, SpeculateBlock, &ProgramEntry);
120 
121           Mutator.SetInsertPoint(SpeculateBlock);
122           auto ImplAddrToUint =
123               Mutator.CreatePtrToInt(&Fn, Type::getInt64Ty(MContext));
124           Mutator.CreateCall(RuntimeCallTy, RuntimeCall,
125                              {SpeclAddr, ImplAddrToUint});
126           Mutator.CreateStore(ConstantInt::get(LoadValueTy, 1),
127                               SpeculatorGuard);
128           Mutator.CreateBr(&ProgramEntry);
129 
130           assert(Mutator.GetInsertBlock()->getParent() == &Fn &&
131                  "IR builder association mismatch?");
132           S.registerSymbols(internToJITSymbols(IRNames.getValue()),
133                             &R.getTargetJITDylib());
134         }
135       }
136     }
137   });
138 
139   assert(!TSM.withModuleDo([](const Module &M) { return verifyModule(M); }) &&
140          "Speculation Instrumentation breaks IR?");
141 
142   NextLayer.emit(std::move(R), std::move(TSM));
143 }
144 
145 } // namespace orc
146 } // namespace llvm
147