LLVM: lib/ExecutionEngine/Orc/EPCIndirectionUtils.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

10

13

14#include

15

16using namespace llvm;

18

19namespace llvm {

20namespace orc {

21

23public:

26

29 return EPCIU.getIndirectStubs(NumStubs);

30 };

31};

32

33}

34}

35

36namespace {

37

39public:

40 EPCTrampolinePool(EPCIndirectionUtils &EPCIU);

41 Error deallocatePool();

42

43protected:

44 Error grow() override;

45

46 using FinalizedAlloc = jitlink::JITLinkMemoryManager::FinalizedAlloc;

47

48 EPCIndirectionUtils &EPCIU;

49 unsigned TrampolineSize = 0;

50 unsigned TrampolinesPerPage = 0;

51 std::vector TrampolineBlocks;

52};

53

56public:

57 EPCIndirectStubsManager(EPCIndirectionUtils &EPCIU) : EPCIU(EPCIU) {}

58

59 Error deallocateStubs();

60

61 Error createStub(StringRef StubName, ExecutorAddr StubAddr,

62 JITSymbolFlags StubFlags) override;

63

64 Error createStubs(const StubInitsMap &StubInits) override;

65

66 ExecutorSymbolDef findStub(StringRef Name, bool ExportedStubsOnly) override;

67

68 ExecutorSymbolDef findPointer(StringRef Name) override;

69

70 Error updatePointer(StringRef Name, ExecutorAddr NewAddr) override;

71

72private:

73 using StubInfo = std::pair<IndirectStubInfo, JITSymbolFlags>;

74

75 std::mutex ISMMutex;

76 EPCIndirectionUtils &EPCIU;

77 StringMap StubInfos;

78};

79

81 : EPCIU(EPCIU) {

84

85 TrampolineSize = ABI.getTrampolineSize();

86 TrampolinesPerPage =

87 (EPC.getPageSize() - ABI.getPointerSize()) / TrampolineSize;

88}

89

90Error EPCTrampolinePool::deallocatePool() {

91 std::promise DeallocResultP;

92 auto DeallocResultF = DeallocResultP.get_future();

93

95 std::move(TrampolineBlocks),

96 [&](Error Err) { DeallocResultP.set_value(std::move(Err)); });

97

98 return DeallocResultF.get();

99}

100

101Error EPCTrampolinePool::grow() {

102 using namespace jitlink;

103

104 assert(AvailableTrampolines.empty() &&

105 "Grow called with trampolines still available");

106

108 assert(ResolverAddress && "Resolver address can not be null");

109

112 auto Alloc = SimpleSegmentAlloc::Create(

113 EPC.getMemMgr(), EPC.getSymbolStringPool(), EPC.getTargetTriple(),

114 nullptr, {{MemProt::Read | MemProt::Exec, {PageSize, Align(PageSize)}}});

116 return Alloc.takeError();

117

118 unsigned NumTrampolines = TrampolinesPerPage;

119

122 SegInfo.WorkingMem.data(), SegInfo.Addr, ResolverAddress, NumTrampolines);

123 for (unsigned I = 0; I < NumTrampolines; ++I)

124 AvailableTrampolines.push_back(SegInfo.Addr + (I * TrampolineSize));

125

126 auto FA = Alloc->finalize();

127 if (!FA)

128 return FA.takeError();

129

130 TrampolineBlocks.push_back(std::move(*FA));

131

133}

134

135Error EPCIndirectStubsManager::createStub(StringRef StubName,

137 JITSymbolFlags StubFlags) {

138 StubInitsMap SIM;

139 SIM[StubName] = std::make_pair(StubAddr, StubFlags);

140 return createStubs(SIM);

141}

142

143Error EPCIndirectStubsManager::createStubs(const StubInitsMap &StubInits) {

144 auto AvailableStubInfos = getIndirectStubs(EPCIU, StubInits.size());

145 if (!AvailableStubInfos)

146 return AvailableStubInfos.takeError();

147

148 {

149 std::lock_guardstd::mutex Lock(ISMMutex);

150 unsigned ASIdx = 0;

151 for (auto &SI : StubInits) {

152 auto &A = (*AvailableStubInfos)[ASIdx++];

153 StubInfos[SI.first()] = std::make_pair(A, SI.second.second);

154 }

155 }

156

159 case 4: {

160 unsigned ASIdx = 0;

161 std::vectortpctypes::UInt32Write PtrUpdates;

162 for (auto &SI : StubInits)

163 PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,

164 static_cast<uint32_t>(SI.second.first.getValue())});

165 return MemAccess.writeUInt32s(PtrUpdates);

166 }

167 case 8: {

168 unsigned ASIdx = 0;

169 std::vectortpctypes::UInt64Write PtrUpdates;

170 for (auto &SI : StubInits)

171 PtrUpdates.push_back({(*AvailableStubInfos)[ASIdx++].PointerAddress,

172 SI.second.first.getValue()});

173 return MemAccess.writeUInt64s(PtrUpdates);

174 }

175 default:

178 }

179}

180

181ExecutorSymbolDef EPCIndirectStubsManager::findStub(StringRef Name,

182 bool ExportedStubsOnly) {

183 std::lock_guardstd::mutex Lock(ISMMutex);

184 auto I = StubInfos.find(Name);

185 if (I == StubInfos.end())

187 return {I->second.first.StubAddress, I->second.second};

188}

189

190ExecutorSymbolDef EPCIndirectStubsManager::findPointer(StringRef Name) {

191 std::lock_guardstd::mutex Lock(ISMMutex);

192 auto I = StubInfos.find(Name);

193 if (I == StubInfos.end())

195 return {I->second.first.PointerAddress, I->second.second};

196}

197

198Error EPCIndirectStubsManager::updatePointer(StringRef Name,

200

202 {

203 std::lock_guardstd::mutex Lock(ISMMutex);

204 auto I = StubInfos.find(Name);

205 if (I == StubInfos.end())

208 PtrAddr = I->second.first.PointerAddress;

209 }

210

213 case 4: {

215 return MemAccess.writeUInt32s(PUpdate);

216 }

217 case 8: {

219 return MemAccess.writeUInt64s(PUpdate);

220 }

221 default:

224 }

225}

226

227}

228

229namespace llvm {

230namespace orc {

231

233

234Expected<std::unique_ptr>

236 const auto &TT = EPC.getTargetTriple();

237 switch (TT.getArch()) {

238 default:

240 std::string("No EPCIndirectionUtils available for ") + TT.str(),

245

248

251

254

257

261

264

268 else

270 }

271}

272

274

275 auto &MemMgr = EPC.getMemMgr();

276 auto Err = MemMgr.deallocate(std::move(IndirectStubAllocs));

277

278 if (TP)

280 static_cast<EPCTrampolinePool &>(*TP).deallocatePool());

281

282 if (ResolverBlock)

283 Err =

284 joinErrors(std::move(Err), MemMgr.deallocate(std::move(ResolverBlock)));

285

286 return Err;

287}

288

293

294 assert(ABI && "ABI can not be null");

295 auto ResolverSize = ABI->getResolverCodeSize();

296

298 SimpleSegmentAlloc::Create(EPC.getMemMgr(), EPC.getSymbolStringPool(),

299 EPC.getTargetTriple(), nullptr,

300 {{MemProt::Read | MemProt::Exec,

301 {ResolverSize, Align(EPC.getPageSize())}}});

302

304 return Alloc.takeError();

305

307 ResolverBlockAddr = SegInfo.Addr;

308 ABI->writeResolverCode(SegInfo.WorkingMem.data(), ResolverBlockAddr,

309 ReentryFnAddr, ReentryCtxAddr);

310

311 auto FA = Alloc->finalize();

312 if (!FA)

313 return FA.takeError();

314

315 ResolverBlock = std::move(*FA);

316 return ResolverBlockAddr;

317}

318

319std::unique_ptr

321 return std::make_unique(*this);

322}

323

325 if (!TP)

326 TP = std::make_unique(*this);

327 return *TP;

328}

329

333 "createLazyCallThroughManager can not have been called before");

334 LCTM = std::make_unique(ES, ErrorHandlerAddr,

336 return *LCTM;

337}

338

340 std::unique_ptr ABI)

341 : EPC(EPC), ABI(std::move(ABI)) {

342 assert(this->ABI && "ABI can not be null");

343

345 "Stubs larger than one page are not supported");

346}

347

349EPCIndirectionUtils::getIndirectStubs(unsigned NumStubs) {

351

352 std::lock_guardstd::mutex Lock(EPCUIMutex);

353

354

355 if (NumStubs > AvailableIndirectStubs.size()) {

356 auto NumStubsToAllocate = NumStubs;

358 auto StubBytes = alignTo(NumStubsToAllocate * ABI->getStubSize(), PageSize);

359 NumStubsToAllocate = StubBytes / ABI->getStubSize();

360 auto PtrBytes =

361 alignTo(NumStubsToAllocate * ABI->getPointerSize(), PageSize);

362

365

366 auto Alloc = SimpleSegmentAlloc::Create(

368 nullptr,

369 {{StubProt, {static_cast<size_t>(StubBytes), Align(PageSize)}},

370 {PtrProt, {static_cast<size_t>(PtrBytes), Align(PageSize)}}});

371

373 return Alloc.takeError();

374

375 auto StubSeg = Alloc->getSegInfo(StubProt);

376 auto PtrSeg = Alloc->getSegInfo(PtrProt);

377

378 ABI->writeIndirectStubsBlock(StubSeg.WorkingMem.data(), StubSeg.Addr,

379 PtrSeg.Addr, NumStubsToAllocate);

380

381 auto FA = Alloc->finalize();

382 if (!FA)

383 return FA.takeError();

384

385 IndirectStubAllocs.push_back(std::move(*FA));

386

387 auto StubExecutorAddr = StubSeg.Addr;

388 auto PtrExecutorAddr = PtrSeg.Addr;

389 for (unsigned I = 0; I != NumStubsToAllocate; ++I) {

390 AvailableIndirectStubs.push_back(

391 IndirectStubInfo(StubExecutorAddr, PtrExecutorAddr));

392 StubExecutorAddr += ABI->getStubSize();

393 PtrExecutorAddr += ABI->getPointerSize();

394 }

395 }

396

397 assert(NumStubs <= AvailableIndirectStubs.size() &&

398 "Sufficient stubs should have been allocated above");

399

400 IndirectStubInfoVector Result;

401 while (NumStubs--) {

402 Result.push_back(AvailableIndirectStubs.back());

403 AvailableIndirectStubs.pop_back();

404 }

405

406 return std::move(Result);

407}

408

412 std::promise LandingAddrP;

413 auto LandingAddrF = LandingAddrP.get_future();

414 LCTM.resolveTrampolineLandingAddress(

416 [&](ExecutorAddr Addr) { LandingAddrP.set_value(Addr); });

417 return LandingAddrF.get().getValue();

418}

419

422 return EPCIU

425 .takeError();

426}

427

428}

429}

assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")

static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")

static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)

Lightweight error class with error context and mandatory checking.

static ErrorSuccess success()

Create a success value.

Tagged union holding either a T or a Error.

virtual void deallocate(std::vector< FinalizedAlloc > Allocs, OnDeallocatedFunction OnDeallocated)=0

Deallocate a list of allocation objects.

EPCIndirectionUtils::IndirectStubInfo IndirectStubInfo

Definition EPCIndirectionUtils.cpp:24

static Expected< IndirectStubInfoVector > getIndirectStubs(EPCIndirectionUtils &EPCIU, unsigned NumStubs)

Definition EPCIndirectionUtils.cpp:28

EPCIndirectionUtils::IndirectStubInfoVector IndirectStubInfoVector

Definition EPCIndirectionUtils.cpp:25

virtual void writeTrampolines(char *TrampolineBlockWorkingMem, ExecutorAddr TrampolineBlockTragetAddr, ExecutorAddr ResolverAddr, unsigned NumTrampolines) const =0

unsigned getPointerSize() const

Provides ExecutorProcessControl based indirect stubs, trampoline pool and lazy call through manager.

LLVM_ABI std::unique_ptr< IndirectStubsManager > createIndirectStubsManager()

Create an IndirectStubsManager for the executor process.

Definition EPCIndirectionUtils.cpp:320

LLVM_ABI Expected< ExecutorAddr > writeResolverBlock(ExecutorAddr ReentryFnAddr, ExecutorAddr ReentryCtxAddr)

Write resolver code to the executor process and return its address.

Definition EPCIndirectionUtils.cpp:290

LazyCallThroughManager & getLazyCallThroughManager()

Create a LazyCallThroughManager for the executor process.

static std::unique_ptr< EPCIndirectionUtils > CreateWithABI(ExecutorProcessControl &EPC)

Create using the given ABI class.

ExecutorProcessControl & getExecutorProcessControl() const

Return a reference to the ExecutorProcessControl object.

LLVM_ABI LazyCallThroughManager & createLazyCallThroughManager(ExecutionSession &ES, ExecutorAddr ErrorHandlerAddr)

Create a LazyCallThroughManager.

Definition EPCIndirectionUtils.cpp:330

LLVM_ABI Error cleanup()

Release memory for resources held by this instance.

Definition EPCIndirectionUtils.cpp:273

LLVM_ABI TrampolinePool & getTrampolinePool()

Create a TrampolinePool for the executor process.

Definition EPCIndirectionUtils.cpp:324

static LLVM_ABI Expected< std::unique_ptr< EPCIndirectionUtils > > Create(ExecutorProcessControl &EPC)

Create based on the ExecutorProcessControl triple.

Definition EPCIndirectionUtils.cpp:235

ABISupport & getABISupport() const

Return a reference to the ABISupport object for this instance.

ExecutorAddr getResolverBlockAddress() const

Returns the address of the Resolver block.

An ExecutionSession represents a running JIT program.

Represents an address in the executor process.

uint64_t getValue() const

static ExecutorAddr fromPtr(T *Ptr, UnwrapFn &&Unwrap=UnwrapFn())

Create an ExecutorAddr from the given pointer.

ExecutorProcessControl supports interaction with a JIT target process.

jitlink::JITLinkMemoryManager & getMemMgr() const

Return a JITLinkMemoryManager for the target process.

const Triple & getTargetTriple() const

Return the Triple for the target process.

std::shared_ptr< SymbolStringPool > getSymbolStringPool() const

Return a shared pointer to the SymbolStringPool for this instance.

unsigned getPageSize() const

Get the page size for the target process.

MemoryAccess & getMemoryAccess() const

Return a MemoryAccess object for the target process.

Represents a defining location for a JIT symbol.

Base class for managing collections of named indirect stubs.

Manages a set of 'lazy call-through' trampolines.

Base class for pools of compiler re-entry trampolines.

UIntWrite< uint64_t > UInt64Write

Describes a write to a uint64_t.

UIntWrite< uint32_t > UInt32Write

Describes a write to a uint32_t.

LLVM_ABI Error setUpInProcessLCTMReentryViaEPCIU(EPCIndirectionUtils &EPCIU)

This will call writeResolver on the given EPCIndirectionUtils instance to set up re-entry via a funct...

Definition EPCIndirectionUtils.cpp:420

static JITTargetAddress reentry(JITTargetAddress LCTMAddr, JITTargetAddress TrampolineAddr)

Definition EPCIndirectionUtils.cpp:409

This is an optimization pass for GlobalISel generic memory operations.

LLVM_ABI std::error_code inconvertibleErrorCode()

The value returned by this function can be returned from convertToErrorCode for Error values where no...

T jitTargetAddressToPointer(JITTargetAddress Addr)

Convert a JITTargetAddress to a pointer.

Error joinErrors(Error E1, Error E2)

Concatenate errors.

uint64_t JITTargetAddress

Represents an address in the target process's address space.

Error make_error(ArgTs &&... Args)

Make a Error instance representing failure using the given error info type.

uint64_t alignTo(uint64_t Size, Align A)

Returns a multiple of A needed to store Size bytes.

OutputIt move(R &&Range, OutputIt Out)

Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.

Implement std::hash so that hash_code can be used in STL containers.