/src/WasmEdge/lib/llvm/llvm.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | #include "llvm.h" |
5 | | |
6 | | #include <llvm/IR/GlobalValue.h> |
7 | | #include <llvm/Object/ObjectFile.h> |
8 | | #include <llvm/Transforms/Utils/BasicBlockUtils.h> |
9 | | #if LLVM_VERSION_MAJOR < 12 || WASMEDGE_OS_WINDOWS |
10 | | #include <llvm/ExecutionEngine/Orc/Core.h> |
11 | | #endif |
12 | | #if LLVM_VERSION_MAJOR < 13 |
13 | | #include <llvm/ExecutionEngine/Orc/LLJIT.h> |
14 | | #include <llvm/Support/CBindingWrapping.h> |
15 | | #include <llvm/Support/Error.h> |
16 | | #endif |
17 | | #if LLVM_VERSION_MAJOR < 17 |
18 | | #include <llvm/IR/Instructions.h> |
19 | | #include <llvm/Support/CBindingWrapping.h> |
20 | | #endif |
21 | | |
22 | | #if WASMEDGE_OS_WINDOWS |
23 | | #include "common/spdlog.h" |
24 | | #include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h> |
25 | | #include <llvm/ExecutionEngine/SectionMemoryManager.h> |
26 | | #include <llvm/Support/Process.h> |
27 | | #include <system/winapi.h> |
28 | | #endif |
29 | | |
30 | | namespace llvm { |
31 | | #if WASMEDGE_OS_WINDOWS |
32 | | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ExecutionSession, |
33 | | LLVMOrcExecutionSessionRef) |
34 | | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ObjectLayer, LLVMOrcObjectLayerRef) |
35 | | #endif |
36 | | #if LLVM_VERSION_MAJOR < 12 |
37 | | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::LLJITBuilder, LLVMOrcLLJITBuilderRef) |
38 | | #endif |
39 | | #if LLVM_VERSION_MAJOR < 13 |
40 | | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::ThreadSafeModule, |
41 | | LLVMOrcThreadSafeModuleRef) |
42 | | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::IRTransformLayer, |
43 | | LLVMOrcIRTransformLayerRef) |
44 | 0 | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::MaterializationResponsibility, |
45 | 0 | LLVMOrcMaterializationResponsibilityRef) |
46 | 0 | DEFINE_SIMPLE_CONVERSION_FUNCTIONS(orc::LLJIT, LLVMOrcLLJITRef) |
47 | 0 | #endif |
48 | 0 | } // namespace llvm |
49 | 0 |
|
50 | 0 | #if LLVM_VERSION_MAJOR < 17 |
51 | 0 | LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef Call) { |
52 | 0 | return static_cast<LLVMTailCallKind>( |
53 | 0 | llvm::unwrap<llvm::CallInst>(Call)->getTailCallKind()); |
54 | 0 | } |
55 | | |
56 | 233 | void LLVMSetTailCallKind(LLVMValueRef Call, LLVMTailCallKind kind) { |
57 | 233 | llvm::unwrap<llvm::CallInst>(Call)->setTailCallKind( |
58 | 233 | static_cast<llvm::CallInst::TailCallKind>(kind)); |
59 | 233 | } |
60 | | #endif |
61 | | |
62 | | namespace WasmEdge::LLVM { |
63 | | |
64 | 22.1k | void Value::setDSOLocal(bool Local) noexcept { |
65 | 22.1k | llvm::cast<llvm::GlobalValue>(reinterpret_cast<llvm::Value *>(Ref)) |
66 | 22.1k | ->setDSOLocal(Local); |
67 | 22.1k | } |
68 | | |
69 | 10.5k | void Value::eliminateUnreachableBlocks() noexcept { |
70 | 10.5k | llvm::EliminateUnreachableBlocks( |
71 | 10.5k | *llvm::cast<llvm::Function>(reinterpret_cast<llvm::Value *>(Ref))); |
72 | 10.5k | } |
73 | | |
74 | 57.1k | bool SectionIterator::isText() const noexcept { |
75 | 57.1k | auto *S = reinterpret_cast<const llvm::object::section_iterator *>(Ref); |
76 | 57.1k | return (*S)->isText(); |
77 | 57.1k | } |
78 | | |
79 | 53.0k | bool SectionIterator::isData() const noexcept { |
80 | 53.0k | auto *S = reinterpret_cast<const llvm::object::section_iterator *>(Ref); |
81 | 53.0k | return (*S)->isData(); |
82 | 53.0k | } |
83 | | |
84 | 44.4k | bool SectionIterator::isBSS() const noexcept { |
85 | 44.4k | auto *S = reinterpret_cast<const llvm::object::section_iterator *>(Ref); |
86 | 44.4k | return (*S)->isBSS(); |
87 | 44.4k | } |
88 | | |
89 | 57.1k | bool SectionIterator::isPData() const noexcept { |
90 | | #if WASMEDGE_OS_WINDOWS |
91 | | using namespace std::literals; |
92 | | return ".pdata"sv == getName(); |
93 | | #else |
94 | 57.1k | return false; |
95 | 57.1k | #endif |
96 | 57.1k | } |
97 | | |
98 | 61.3k | bool SectionIterator::isEHFrame() const noexcept { |
99 | 61.3k | #if WASMEDGE_OS_LINUX |
100 | 61.3k | using namespace std::literals; |
101 | 61.3k | return ".eh_frame"sv == getName(); |
102 | | #elif WASMEDGE_OS_MACOS |
103 | | using namespace std::literals; |
104 | | return "__eh_frame"sv == getName(); |
105 | | #else |
106 | | return false; |
107 | | #endif |
108 | 61.3k | } |
109 | | |
110 | 30.6k | bool SectionIterator::isVirtual() const noexcept { |
111 | 30.6k | auto *S = reinterpret_cast<const llvm::object::section_iterator *>(Ref); |
112 | 30.6k | return (*S)->isVirtual(); |
113 | 30.6k | } |
114 | | |
115 | | #if WASMEDGE_OS_WINDOWS |
116 | | namespace { |
117 | | class DefaultMMapper final : public llvm::SectionMemoryManager::MemoryMapper { |
118 | | public: |
119 | | llvm::sys::MemoryBlock allocateMappedMemory( |
120 | | llvm::SectionMemoryManager::AllocationPurpose /*Purpose*/, |
121 | | size_t NumBytes, const llvm::sys::MemoryBlock *const NearBlock, |
122 | | unsigned Flags, std::error_code &EC) override { |
123 | | return llvm::sys::Memory::allocateMappedMemory(NumBytes, NearBlock, Flags, |
124 | | EC); |
125 | | } |
126 | | std::error_code protectMappedMemory(const llvm::sys::MemoryBlock &Block, |
127 | | unsigned Flags) override { |
128 | | return llvm::sys::Memory::protectMappedMemory(Block, Flags); |
129 | | } |
130 | | |
131 | | std::error_code releaseMappedMemory(llvm::sys::MemoryBlock &M) override { |
132 | | return llvm::sys::Memory::releaseMappedMemory(M); |
133 | | } |
134 | | }; |
135 | | |
136 | | class ContiguousSectionMemoryManager : public llvm::RTDyldMemoryManager { |
137 | | public: |
138 | | explicit ContiguousSectionMemoryManager( |
139 | | llvm::SectionMemoryManager::MemoryMapper *UnownedMM = nullptr) |
140 | | : MMapper(UnownedMM), OwnedMMapper(nullptr) { |
141 | | if (!MMapper) { |
142 | | OwnedMMapper = std::make_unique<DefaultMMapper>(); |
143 | | MMapper = OwnedMMapper.get(); |
144 | | } |
145 | | } |
146 | | |
147 | | ~ContiguousSectionMemoryManager() noexcept override { |
148 | | using namespace std::literals; |
149 | | if (Preallocated.allocatedSize() != 0) { |
150 | | auto EC = MMapper->releaseMappedMemory(Preallocated); |
151 | | if (EC) { |
152 | | spdlog::error("releaseMappedMemory failed with error: {}"sv, |
153 | | EC.message()); |
154 | | } |
155 | | } |
156 | | } |
157 | | |
158 | | bool needsToReserveAllocationSpace() override { return true; } |
159 | | |
160 | | void reserveAllocationSpace(uintptr_t CodeSize, llvm::Align CodeAlign, |
161 | | uintptr_t RODataSize, llvm::Align RODataAlign, |
162 | | uintptr_t RWDataSize, |
163 | | llvm::Align RWDataAlign) override { |
164 | | using namespace std::literals; |
165 | | assuming(Preallocated.allocatedSize() == 0); |
166 | | |
167 | | static const size_t PageSize = llvm::sys::Process::getPageSizeEstimate(); |
168 | | assuming(CodeAlign.value() <= PageSize); |
169 | | assuming(RODataAlign.value() <= PageSize); |
170 | | assuming(RWDataAlign.value() <= PageSize); |
171 | | CodeSize = roundUpTo(CodeSize + CodeAlign.value(), PageSize); |
172 | | RODataSize = roundUpTo(RODataSize + RODataAlign.value(), PageSize); |
173 | | RWDataSize = roundUpTo(RWDataSize + RWDataAlign.value(), PageSize); |
174 | | const uintptr_t TotalSize = |
175 | | CodeSize + RODataSize + RWDataSize + PageSize * 3; |
176 | | |
177 | | std::error_code EC; |
178 | | Preallocated = MMapper->allocateMappedMemory( |
179 | | llvm::SectionMemoryManager::AllocationPurpose::Code, TotalSize, nullptr, |
180 | | llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE, EC); |
181 | | if (EC) { |
182 | | spdlog::error("allocateMappedMemory failed with error: {}"sv, |
183 | | EC.message()); |
184 | | return; |
185 | | } |
186 | | |
187 | | auto base = reinterpret_cast<std::uintptr_t>(Preallocated.base()); |
188 | | CodeMem = CodeFree = |
189 | | llvm::sys::MemoryBlock(reinterpret_cast<void *>(base), CodeSize); |
190 | | base += CodeSize; |
191 | | RODataMem = RODataFree = |
192 | | llvm::sys::MemoryBlock(reinterpret_cast<void *>(base), RODataSize); |
193 | | base += RODataSize; |
194 | | RWDataMem = RWDataFree = |
195 | | llvm::sys::MemoryBlock(reinterpret_cast<void *>(base), RWDataSize); |
196 | | } |
197 | | |
198 | | uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, |
199 | | unsigned /*SectionID*/, |
200 | | llvm::StringRef /*SectionName*/, |
201 | | bool IsReadOnly) override { |
202 | | if (IsReadOnly) { |
203 | | return Allocate(RODataFree, Size, Alignment); |
204 | | } else { |
205 | | return Allocate(RWDataFree, Size, Alignment); |
206 | | } |
207 | | } |
208 | | |
209 | | uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, |
210 | | unsigned /*SectionID*/, |
211 | | llvm::StringRef /*SectionName*/) override { |
212 | | return Allocate(CodeFree, Size, Alignment); |
213 | | } |
214 | | |
215 | | bool finalizeMemory(std::string *ErrMsg) override { |
216 | | std::error_code EC; |
217 | | |
218 | | EC = MMapper->protectMappedMemory(CodeMem, llvm::sys::Memory::MF_READ | |
219 | | llvm::sys::Memory::MF_EXEC); |
220 | | if (EC) { |
221 | | if (ErrMsg) { |
222 | | *ErrMsg = EC.message(); |
223 | | } |
224 | | return true; |
225 | | } |
226 | | EC = MMapper->protectMappedMemory(RODataMem, llvm::sys::Memory::MF_READ); |
227 | | if (EC) { |
228 | | if (ErrMsg) { |
229 | | *ErrMsg = EC.message(); |
230 | | } |
231 | | return true; |
232 | | } |
233 | | |
234 | | llvm::sys::Memory::InvalidateInstructionCache(CodeMem.base(), |
235 | | CodeMem.allocatedSize()); |
236 | | return false; |
237 | | } |
238 | | |
239 | | private: |
240 | | llvm::sys::MemoryBlock Preallocated; |
241 | | |
242 | | // Sections must be in the order code < rodata < rwdata. |
243 | | llvm::sys::MemoryBlock CodeMem; |
244 | | llvm::sys::MemoryBlock RODataMem; |
245 | | llvm::sys::MemoryBlock RWDataMem; |
246 | | |
247 | | llvm::sys::MemoryBlock CodeFree; |
248 | | llvm::sys::MemoryBlock RODataFree; |
249 | | llvm::sys::MemoryBlock RWDataFree; |
250 | | |
251 | | llvm::SectionMemoryManager::MemoryMapper *MMapper; |
252 | | std::unique_ptr<llvm::SectionMemoryManager::MemoryMapper> OwnedMMapper; |
253 | | |
254 | | uint8_t *Allocate(llvm::sys::MemoryBlock &FreeBlock, std::uintptr_t Size, |
255 | | unsigned alignment) { |
256 | | using namespace std::literals; |
257 | | const auto Base = reinterpret_cast<uintptr_t>(FreeBlock.base()); |
258 | | const auto Start = roundUpTo(Base, alignment); |
259 | | const uintptr_t PaddedSize = (Start - Base) + Size; |
260 | | if (PaddedSize > FreeBlock.allocatedSize()) { |
261 | | spdlog::error("Failed to satisfy suballocation request for {}"sv, Size); |
262 | | return nullptr; |
263 | | } |
264 | | FreeBlock = |
265 | | llvm::sys::MemoryBlock(reinterpret_cast<void *>(Base + PaddedSize), |
266 | | FreeBlock.allocatedSize() - PaddedSize); |
267 | | return reinterpret_cast<uint8_t *>(Start); |
268 | | } |
269 | | |
270 | | static uintptr_t roundUpTo(uintptr_t Value, uintptr_t Divisor) noexcept { |
271 | | return ((Value + (Divisor - 1)) / Divisor) * Divisor; |
272 | | } |
273 | | }; |
274 | | |
275 | | // Register stack unwind info for JIT functions |
276 | | class Win64EHManager : public ContiguousSectionMemoryManager { |
277 | | using Base = ContiguousSectionMemoryManager; |
278 | | uint64_t CodeAddress = 0; |
279 | | |
280 | | public: |
281 | | ~Win64EHManager() noexcept override {} |
282 | | |
283 | | uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, |
284 | | unsigned SectionID, |
285 | | llvm::StringRef SectionName) override { |
286 | | using namespace std::literals; |
287 | | const auto Allocated = |
288 | | Base::allocateCodeSection(Size, Alignment, SectionID, SectionName); |
289 | | if (SectionName == llvm::StringRef(".text"sv)) { |
290 | | CodeAddress = reinterpret_cast<uint64_t>(Allocated); |
291 | | } |
292 | | return Allocated; |
293 | | } |
294 | | |
295 | | void registerEHFrames(uint8_t *Addr, uint64_t /*LoadAddr*/, |
296 | | size_t Size) noexcept override { |
297 | | using namespace std::literals; |
298 | | winapi::RUNTIME_FUNCTION_ *const FunctionTable = |
299 | | reinterpret_cast<winapi::RUNTIME_FUNCTION_ *>(Addr); |
300 | | const uint32_t EntryCount = |
301 | | static_cast<uint32_t>(Size / sizeof(winapi::RUNTIME_FUNCTION_)); |
302 | | if (EntryCount == 0) |
303 | | return; |
304 | | // Calculate the object image base address by assuming that the address of |
305 | | // the first function is equal to the address of the code section. |
306 | | const auto ImageBase = CodeAddress - FunctionTable[0].BeginAddress; |
307 | | winapi::RtlAddFunctionTable(FunctionTable, EntryCount, ImageBase); |
308 | | EHFrames.push_back({Addr, Size}); |
309 | | } |
310 | | void deregisterEHFrames() noexcept override { |
311 | | using namespace std::literals; |
312 | | for (auto &Frame : EHFrames) { |
313 | | winapi::RtlDeleteFunctionTable( |
314 | | reinterpret_cast<winapi::RUNTIME_FUNCTION_ *>(Frame.Addr)); |
315 | | } |
316 | | EHFrames.clear(); |
317 | | } |
318 | | }; |
319 | | } // namespace |
320 | | |
321 | | LLVMOrcLLJITBuilderRef OrcLLJIT::getBuilder() noexcept { |
322 | | using llvm::unwrap; |
323 | | using llvm::wrap; |
324 | | const LLVMOrcLLJITBuilderRef Builder = LLVMOrcCreateLLJITBuilder(); |
325 | | LLVMOrcLLJITBuilderSetObjectLinkingLayerCreator( |
326 | | Builder, |
327 | | [](void *, LLVMOrcExecutionSessionRef ES, const char *) noexcept { |
328 | | auto Layer = std::make_unique<llvm::orc::RTDyldObjectLinkingLayer>( |
329 | | *unwrap(ES), []( |
330 | | #if LLVM_VERSION_MAJOR >= 21 |
331 | | const llvm::MemoryBuffer & |
332 | | #endif |
333 | | ) { return std::make_unique<Win64EHManager>(); }); |
334 | | Layer->setOverrideObjectFlagsWithResponsibilityFlags(true); |
335 | | Layer->setAutoClaimResponsibilityForObjectSymbols(true); |
336 | | return wrap(static_cast<llvm::orc::ObjectLayer *>(Layer.release())); |
337 | | }, |
338 | | nullptr); |
339 | | return Builder; |
340 | | } |
341 | | #else |
342 | 0 | LLVMOrcLLJITBuilderRef OrcLLJIT::getBuilder() noexcept { return nullptr; } |
343 | | #endif |
344 | | |
345 | | } // namespace WasmEdge::LLVM |
346 | | |
347 | | #if LLVM_VERSION_MAJOR < 12 && WASMEDGE_OS_WINDOWS |
348 | | void LLVMOrcLLJITBuilderSetObjectLinkingLayerCreator( |
349 | | LLVMOrcLLJITBuilderRef Builder, |
350 | | LLVMOrcLLJITBuilderObjectLinkingLayerCreatorFunction F, |
351 | | void *Ctx) noexcept { |
352 | | using llvm::unwrap; |
353 | | using llvm::wrap; |
354 | | unwrap(Builder)->setObjectLinkingLayerCreator( |
355 | | [=](llvm::orc::ExecutionSession &ES, const llvm::Triple &TT) { |
356 | | auto TTStr = TT.str(); |
357 | | return std::unique_ptr<llvm::orc::ObjectLayer>( |
358 | | unwrap(F(Ctx, wrap(&ES), TTStr.c_str()))); |
359 | | }); |
360 | | } |
361 | | #endif |
362 | | #if LLVM_VERSION_MAJOR < 13 |
363 | | LLVMOrcIRTransformLayerRef |
364 | 0 | LLVMOrcLLJITGetIRTransformLayer(LLVMOrcLLJITRef J) noexcept { |
365 | 0 | using llvm::unwrap; |
366 | 0 | using llvm::wrap; |
367 | 0 | return wrap(&(unwrap(J)->getIRTransformLayer())); |
368 | 0 | } |
369 | | void LLVMOrcIRTransformLayerSetTransform( |
370 | | LLVMOrcIRTransformLayerRef IRTransformLayer, |
371 | | LLVMOrcIRTransformLayerTransformFunction TransformFunction, |
372 | 0 | void *Ctx) noexcept { |
373 | 0 | using llvm::unwrap; |
374 | 0 | using llvm::wrap; |
375 | 0 | unwrap(IRTransformLayer) |
376 | 0 | ->setTransform([=](llvm::orc::ThreadSafeModule TSM, |
377 | 0 | llvm::orc::MaterializationResponsibility &R) |
378 | 0 | -> llvm::Expected<llvm::orc::ThreadSafeModule> { |
379 | 0 | LLVMOrcThreadSafeModuleRef TSMRef = |
380 | 0 | wrap(new llvm::orc::ThreadSafeModule(std::move(TSM))); |
381 | 0 | if (LLVMErrorRef Err = TransformFunction(Ctx, &TSMRef, wrap(&R))) { |
382 | 0 | return unwrap(Err); |
383 | 0 | } |
384 | 0 | return std::move(*unwrap(TSMRef)); |
385 | 0 | }); |
386 | 0 | } |
387 | | |
388 | | LLVMErrorRef |
389 | | LLVMOrcThreadSafeModuleWithModuleDo(LLVMOrcThreadSafeModuleRef TSM, |
390 | | LLVMOrcGenericIRModuleOperationFunction F, |
391 | 0 | void *Ctx) noexcept { |
392 | 0 | using llvm::unwrap; |
393 | 0 | using llvm::wrap; |
394 | 0 | return wrap(unwrap(TSM)->withModuleDo( |
395 | 0 | [&](llvm::Module &M) { return unwrap(F(Ctx, wrap(&M))); })); |
396 | 0 | } |
397 | | #endif |