Coverage Report

Created: 2026-07-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/llvm/compiler.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "llvm/compiler.h"
5
6
#include "compiler/context.h"
7
#include "compiler/function_compiler.h"
8
9
#include "common/spdlog.h"
10
#include "data.h"
11
#include "llvm.h"
12
13
#include <algorithm>
14
#include <cstdint>
15
#include <string>
16
17
namespace LLVM = WasmEdge::LLVM;
18
using namespace std::literals;
19
20
namespace {
21
22
struct RAIICleanup {
23
  RAIICleanup(LLVM::Compiler::CompileContext *&ContextRef,
24
              LLVM::Compiler::CompileContext *NewContext)
25
2.23k
      : Context(ContextRef) {
26
2.23k
    Context = NewContext;
27
2.23k
  }
28
2.23k
  ~RAIICleanup() { Context = nullptr; }
29
  LLVM::Compiler::CompileContext *&Context;
30
};
31
32
// Translate Compiler::OptimizationLevel to llvm::PassBuilder version
33
#if LLVM_VERSION_MAJOR >= 13
34
static inline const char *
35
toLLVMLevel(WasmEdge::CompilerConfigure::OptimizationLevel Level) noexcept {
36
  using OL = WasmEdge::CompilerConfigure::OptimizationLevel;
37
  switch (Level) {
38
  case OL::O0:
39
    return "default<O0>,function(tailcallelim)";
40
  case OL::O1:
41
    return "default<O1>,function(tailcallelim)";
42
  case OL::O2:
43
    return "default<O2>";
44
  case OL::O3:
45
    return "default<O3>";
46
  case OL::Os:
47
    return "default<Os>";
48
  case OL::Oz:
49
    return "default<Oz>";
50
  default:
51
    assumingUnreachable();
52
  }
53
}
54
#else
55
static inline std::pair<unsigned int, unsigned int>
56
2.22k
toLLVMLevel(WasmEdge::CompilerConfigure::OptimizationLevel Level) noexcept {
57
2.22k
  using OL = WasmEdge::CompilerConfigure::OptimizationLevel;
58
2.22k
  switch (Level) {
59
0
  case OL::O0:
60
0
    return {0, 0};
61
0
  case OL::O1:
62
0
    return {1, 0};
63
0
  case OL::O2:
64
0
    return {2, 0};
65
2.22k
  case OL::O3:
66
2.22k
    return {3, 0};
67
0
  case OL::Os:
68
0
    return {2, 1};
69
0
  case OL::Oz:
70
0
    return {2, 2};
71
0
  default:
72
0
    assumingUnreachable();
73
2.22k
  }
74
2.22k
}
75
#endif
76
77
static inline LLVMCodeGenOptLevel toLLVMCodeGenLevel(
78
2.22k
    WasmEdge::CompilerConfigure::OptimizationLevel Level) noexcept {
79
2.22k
  using OL = WasmEdge::CompilerConfigure::OptimizationLevel;
80
2.22k
  switch (Level) {
81
0
  case OL::O0:
82
0
    return LLVMCodeGenLevelNone;
83
0
  case OL::O1:
84
0
    return LLVMCodeGenLevelLess;
85
0
  case OL::O2:
86
0
    return LLVMCodeGenLevelDefault;
87
2.22k
  case OL::O3:
88
2.22k
    return LLVMCodeGenLevelAggressive;
89
0
  case OL::Os:
90
0
    return LLVMCodeGenLevelDefault;
91
0
  case OL::Oz:
92
0
    return LLVMCodeGenLevelDefault;
93
0
  default:
94
0
    assumingUnreachable();
95
2.22k
  }
96
2.22k
}
97
} // namespace
98
99
namespace WasmEdge {
100
namespace LLVM {
101
102
2.23k
Expect<void> Compiler::checkConfigure() noexcept {
103
  // Note: Although the Exception Handling and Memory64 proposals are not
104
  // implemented in AOT yet, we should not trap here because the default
105
  // configuration has become WASM 3.0, which contains these proposals.
106
2.23k
  if (Conf.hasProposal(Proposal::ExceptionHandling)) {
107
2.23k
    spdlog::warn("Proposal Exception Handling is not yet supported in WasmEdge "
108
2.23k
                 "AOT/JIT. The compilation will be trapped when related data "
109
2.23k
                 "structure or instructions found in WASM."sv);
110
2.23k
  }
111
2.23k
  if (Conf.hasProposal(Proposal::Annotations)) {
112
0
    spdlog::error(ErrCode::Value::InvalidAOTConfigure);
113
0
    spdlog::error("    Proposal Custom Annotation Syntax is not yet supported "
114
0
                  "in WasmEdge AOT/JIT."sv);
115
0
    return Unexpect(ErrCode::Value::InvalidAOTConfigure);
116
0
  }
117
2.23k
  return {};
118
2.23k
}
119
120
Expect<void> Compiler::optimize(LLVM::Module &LLModule,
121
2.22k
                                LLVM::TargetMachine &TM) noexcept {
122
2.22k
  spdlog::info("optimize start"sv);
123
2.22k
  auto Triple = LLModule.getTarget();
124
2.22k
  auto [TheTarget, ErrorMessage] = LLVM::Target::getFromTriple(Triple);
125
2.22k
  if (ErrorMessage) {
126
0
    spdlog::error("getFromTriple failed:{}"sv, ErrorMessage.string_view());
127
0
    return Unexpect(ErrCode::Value::IllegalPath);
128
0
  }
129
130
2.22k
  std::string CPUName;
131
#if defined(__riscv) && __riscv_xlen == 64
132
  CPUName = "generic-rv64"s;
133
#else
134
2.22k
  if (!Conf.getCompilerConfigure().isGenericBinary()) {
135
2.22k
    CPUName = LLVM::getHostCPUName().string_view();
136
2.22k
  } else {
137
0
    CPUName = "generic"s;
138
0
  }
139
2.22k
#endif
140
141
  // On RISC-V we use generic-rv64 as the CPU, so also use default
142
  // features; host features under QEMU can be inconsistent (e.g.
143
  // zvl*b without v) which LLVM >= 20 rejects.
144
2.22k
  TM = LLVM::TargetMachine::create(
145
2.22k
      TheTarget, Triple, CPUName.c_str(),
146
#if defined(__riscv) && __riscv_xlen == 64
147
      "",
148
#else
149
2.22k
      LLVM::getHostCPUFeatures().unwrap(),
150
2.22k
#endif
151
2.22k
      toLLVMCodeGenLevel(Conf.getCompilerConfigure().getOptimizationLevel()),
152
2.22k
      LLVMRelocPIC, LLVMCodeModelDefault);
153
154
#if LLVM_VERSION_MAJOR >= 13
155
  auto PBO = LLVM::PassBuilderOptions::create();
156
  if (auto Error = PBO.runPasses(
157
          LLModule,
158
          toLLVMLevel(Conf.getCompilerConfigure().getOptimizationLevel()),
159
          TM)) {
160
    spdlog::error("{}"sv, Error.message().string_view());
161
  }
162
#else
163
2.22k
  auto FP = LLVM::PassManager::createForModule(LLModule);
164
2.22k
  auto MP = LLVM::PassManager::create();
165
166
2.22k
  TM.addAnalysisPasses(MP);
167
2.22k
  TM.addAnalysisPasses(FP);
168
2.22k
  {
169
2.22k
    auto PMB = LLVM::PassManagerBuilder::create();
170
2.22k
    auto [OptLevel, SizeLevel] =
171
2.22k
        toLLVMLevel(Conf.getCompilerConfigure().getOptimizationLevel());
172
2.22k
    PMB.setOptLevel(OptLevel);
173
2.22k
    PMB.setSizeLevel(SizeLevel);
174
2.22k
    PMB.populateFunctionPassManager(FP);
175
2.22k
    PMB.populateModulePassManager(MP);
176
2.22k
  }
177
2.22k
  switch (Conf.getCompilerConfigure().getOptimizationLevel()) {
178
0
  case CompilerConfigure::OptimizationLevel::O0:
179
0
  case CompilerConfigure::OptimizationLevel::O1:
180
0
    FP.addTailCallEliminationPass();
181
0
    break;
182
2.22k
  default:
183
2.22k
    break;
184
2.22k
  }
185
186
2.22k
  FP.initializeFunctionPassManager();
187
24.3k
  for (auto Fn = LLModule.getFirstFunction(); Fn; Fn = Fn.getNextFunction()) {
188
22.1k
    FP.runFunctionPassManager(Fn);
189
22.1k
  }
190
2.22k
  FP.finalizeFunctionPassManager();
191
2.22k
  MP.runPassManager(LLModule);
192
2.22k
#endif
193
194
2.22k
  spdlog::info("optimize done"sv);
195
2.22k
  return {};
196
2.22k
}
197
198
// Initialize the LLVM module held by the data for compilation: set the
199
// target triple and the PIC level, and return the LLVM context.
200
2.23k
static LLVM::Context initLLVMModule(LLVM::Data &D) noexcept {
201
2.23k
  auto LLContext = D.extract().getLLContext();
202
2.23k
  LLVM::Core::init(LLContext.unwrap());
203
2.23k
  auto &LLModule = D.extract().LLModule;
204
2.23k
  LLModule.setTarget(LLVM::getDefaultTargetTriple().unwrap());
205
2.23k
  LLModule.addFlag(LLVMModuleFlagBehaviorError, "PIC Level"sv, 2);
206
2.23k
  return LLContext;
207
2.23k
}
208
209
2.23k
Expect<Data> Compiler::compile(const AST::Module &Module) noexcept {
210
  // Check that the module is validated.
211
2.23k
  if (unlikely(!Module.getIsValidated())) {
212
0
    spdlog::error(ErrCode::Value::NotValidated);
213
0
    return Unexpect(ErrCode::Value::NotValidated);
214
0
  }
215
216
2.23k
  std::unique_lock Lock(Mutex);
217
2.23k
  spdlog::info("compile start"sv);
218
219
2.23k
  LLVM::Data D;
220
2.23k
  auto LLContext = initLLVMModule(D);
221
2.23k
  auto &LLModule = D.extract().LLModule;
222
223
2.23k
  CompileContext NewContext(LLContext, LLModule,
224
2.23k
                            Conf.getCompilerConfigure().isGenericBinary());
225
2.23k
  RAIICleanup Cleanup(Context, &NewContext);
226
2.23k
  Context->addVersionGlobal();
227
228
  // Compile all sections and the function declarations.
229
2.23k
  compileSections(Module, false);
230
  // Compile all function bodies.
231
2.23k
  const auto DefinedCount = Module.getDefinedFuncCount();
232
12.7k
  for (uint32_t I = 0; I < DefinedCount; ++I) {
233
10.5k
    EXPECTED_TRY(compileFunctionBody(I));
234
10.5k
  }
235
  // Compile ExportSection.
236
2.22k
  compile(Module.getExportSection());
237
  // StartSection is not required for compilation.
238
239
2.22k
  spdlog::info("verify start"sv);
240
2.22k
  LLModule.verify(LLVMPrintMessageAction);
241
242
2.22k
  auto &TM = D.extract().TM;
243
2.22k
  EXPECTED_TRY(optimize(LLModule, TM));
244
245
  // Set initializer for constant value
246
2.22k
  Context->finalizeIntrinsicsTable();
247
2.22k
  return Expect<Data>{std::move(D)};
248
2.22k
}
249
250
void Compiler::compile(const AST::TypeSection &TypeSec,
251
2.23k
                       bool DeclarationsOnly) noexcept {
252
2.23k
  auto WrapperTy =
253
2.23k
      LLVM::Type::getFunctionType(Context->VoidTy,
254
2.23k
                                  {Context->ExecCtxPtrTy, Context->Int8PtrTy,
255
2.23k
                                   Context->Int8PtrTy, Context->Int8PtrTy},
256
2.23k
                                  false);
257
2.23k
  auto SubTypes = TypeSec.getContent();
258
2.23k
  const auto Size = SubTypes.size();
259
2.23k
  if (Size == 0) {
260
143
    return;
261
143
  }
262
2.08k
  Context->CompositeTypes.reserve(Size);
263
2.08k
  Context->FunctionWrappers.reserve(Size);
264
265
4.44k
  auto SetFuncAttributes = [&](auto FDecl) {
266
4.44k
    FDecl.setVisibility(LLVMProtectedVisibility);
267
4.44k
    FDecl.setDSOLocal(true);
268
4.44k
    FDecl.setDLLStorageClass(LLVMDLLExportStorageClass);
269
4.44k
    FDecl.addFnAttr(Context->NoStackArgProbe);
270
4.44k
    FDecl.addFnAttr(Context->StrictFP);
271
4.44k
    FDecl.addFnAttr(Context->UWTable);
272
4.44k
    FDecl.addParamAttr(0, Context->ReadOnly);
273
4.44k
    FDecl.addParamAttr(0, Context->NoAlias);
274
4.44k
    FDecl.addParamAttr(1, Context->NoAlias);
275
4.44k
    FDecl.addParamAttr(2, Context->NoAlias);
276
4.44k
    FDecl.addParamAttr(3, Context->NoAlias);
277
4.44k
  };
278
279
  // Iterate and compile types.
280
6.72k
  for (size_t I = 0; I < Size; ++I) {
281
4.63k
    const auto &CompType = SubTypes[I].getCompositeType();
282
4.63k
    const auto Name = fmt::format("t{}"sv, Context->CompositeTypes.size());
283
4.63k
    if (CompType.isFunc()) {
284
      // Check that the function type is unique.
285
4.48k
      {
286
4.48k
        bool Unique = true;
287
17.7k
        for (size_t J = 0; J < I; ++J) {
288
13.4k
          if (Context->CompositeTypes[J] &&
289
13.4k
              Context->CompositeTypes[J]->isFunc()) {
290
13.1k
            const auto &OldFuncType = Context->CompositeTypes[J]->getFuncType();
291
13.1k
            if (OldFuncType == CompType.getFuncType()) {
292
186
              Unique = false;
293
186
              Context->CompositeTypes.push_back(Context->CompositeTypes[J]);
294
186
              if (DeclarationsOnly) {
295
0
                auto FDecl = Context->LLModule.get().addFunction(
296
0
                    WrapperTy, LLVMExternalLinkage, Name.c_str());
297
0
                SetFuncAttributes(FDecl);
298
0
                Context->FunctionWrappers.push_back(FDecl);
299
186
              } else {
300
186
                auto F = Context->FunctionWrappers[J];
301
186
                Context->FunctionWrappers.push_back(F);
302
186
                auto A = Context->LLModule.get().addAlias(WrapperTy, F,
303
186
                                                          Name.c_str());
304
186
                A.setLinkage(LLVMExternalLinkage);
305
186
                A.setVisibility(LLVMProtectedVisibility);
306
186
                A.setDSOLocal(true);
307
186
                A.setDLLStorageClass(LLVMDLLExportStorageClass);
308
186
              }
309
186
              break;
310
186
            }
311
13.1k
          }
312
13.4k
        }
313
4.48k
        if (!Unique) {
314
186
          continue;
315
186
        }
316
4.48k
      }
317
318
      // Create Wrapper
319
4.29k
      auto F = Context->LLModule.get().addFunction(
320
4.29k
          WrapperTy, LLVMExternalLinkage, Name.c_str());
321
4.29k
      {
322
4.29k
        SetFuncAttributes(F);
323
324
4.29k
        if (!DeclarationsOnly) {
325
4.29k
          LLVM::Builder Builder(Context->LLContext);
326
4.29k
          Builder.positionAtEnd(
327
4.29k
              LLVM::BasicBlock::create(Context->LLContext, F, "entry"));
328
329
4.29k
          auto FTy = toLLVMType(Context->LLContext, Context->ExecCtxPtrTy,
330
4.29k
                                CompType.getFuncType());
331
4.29k
          auto RTy = FTy.getReturnType();
332
4.29k
          std::vector<LLVM::Type> FPTy(FTy.getNumParams());
333
4.29k
          FTy.getParamTypes(FPTy);
334
335
4.29k
          const size_t ArgCount = FPTy.size() - 1;
336
4.29k
          auto ExecCtxPtr = F.getFirstParam();
337
4.29k
          auto RawFunc = LLVM::FunctionCallee{
338
4.29k
              FTy, Builder.createBitCast(ExecCtxPtr.getNextParam(),
339
4.29k
                                         FTy.getPointerTo())};
340
4.29k
          auto RawArgs = ExecCtxPtr.getNextParam().getNextParam();
341
4.29k
          auto RawRets = RawArgs.getNextParam();
342
343
4.29k
          std::vector<LLVM::Value> Args;
344
4.29k
          Args.reserve(FTy.getNumParams());
345
4.29k
          Args.push_back(ExecCtxPtr);
346
8.97k
          for (size_t J = 0; J < ArgCount; ++J) {
347
4.68k
            Args.push_back(Builder.createValuePtrLoad(
348
4.68k
                FPTy[J + 1], RawArgs, Context->Int8Ty, J * LLVM::kValSize));
349
4.68k
          }
350
351
4.29k
          auto Ret = Builder.createCall(RawFunc, Args);
352
4.29k
          if (RTy.isVoidTy()) {
353
            // nothing to do
354
2.79k
          } else if (RTy.isStructTy()) {
355
327
            auto Rets = unpackStruct(Builder, Ret);
356
327
            Builder.createArrayPtrStore(Rets, RawRets, Context->Int8Ty,
357
327
                                        LLVM::kValSize);
358
2.46k
          } else {
359
2.46k
            Builder.createValuePtrStore(Ret, RawRets, Context->Int8Ty);
360
2.46k
          }
361
4.29k
          Builder.createRetVoid();
362
4.29k
        }
363
4.29k
      }
364
      // Copy wrapper, param and return lists to module instance.
365
4.29k
      Context->FunctionWrappers.push_back(F);
366
4.29k
    } else {
367
      // Non function type case. Create empty wrapper.
368
153
      auto F = Context->LLModule.get().addFunction(
369
153
          WrapperTy, LLVMExternalLinkage, Name.c_str());
370
153
      {
371
153
        SetFuncAttributes(F);
372
373
153
        if (!DeclarationsOnly) {
374
153
          LLVM::Builder Builder(Context->LLContext);
375
153
          Builder.positionAtEnd(
376
153
              LLVM::BasicBlock::create(Context->LLContext, F, "entry"));
377
153
          Builder.createRetVoid();
378
153
        }
379
153
      }
380
153
      Context->FunctionWrappers.push_back(F);
381
153
    }
382
4.44k
    Context->CompositeTypes.push_back(&CompType);
383
4.44k
  }
384
2.08k
}
385
386
2.23k
void Compiler::compile(const AST::ImportSection &ImportSec) noexcept {
387
  // Iterate and compile import descriptions.
388
2.23k
  for (const auto &ImpDesc : ImportSec.getContent()) {
389
    // Get data from import description.
390
449
    const auto &ExtType = ImpDesc.getExternalType();
391
392
    // Add the imports to the module instance.
393
449
    switch (ExtType) {
394
311
    case ExternalType::Function: // Function type index
395
311
    {
396
311
      const auto FuncID = static_cast<uint32_t>(Context->Functions.size());
397
      // Get the function type index in module.
398
311
      uint32_t TypeIdx = ImpDesc.getExternalFuncTypeIdx();
399
311
      assuming(TypeIdx < Context->CompositeTypes.size());
400
311
      assuming(Context->CompositeTypes[TypeIdx]->isFunc());
401
311
      const auto &FuncType = Context->CompositeTypes[TypeIdx]->getFuncType();
402
311
      auto FTy =
403
311
          toLLVMType(Context->LLContext, Context->ExecCtxPtrTy, FuncType);
404
311
      auto RTy = FTy.getReturnType();
405
311
      auto F =
406
311
          LLVM::FunctionCallee{FTy, Context->LLModule.get().addFunction(
407
311
                                        FTy, LLVMInternalLinkage,
408
311
                                        fmt::format("f{}"sv, FuncID).c_str())};
409
311
      F.Fn.setDSOLocal(true);
410
311
      F.Fn.addFnAttr(Context->NoStackArgProbe);
411
311
      F.Fn.addFnAttr(Context->StrictFP);
412
311
      F.Fn.addFnAttr(Context->UWTable);
413
311
      F.Fn.addParamAttr(0, Context->ReadOnly);
414
311
      F.Fn.addParamAttr(0, Context->NoAlias);
415
416
311
      LLVM::Builder Builder(Context->LLContext);
417
311
      Builder.positionAtEnd(
418
311
          LLVM::BasicBlock::create(Context->LLContext, F.Fn, "entry"));
419
420
311
      const auto ArgSize = FuncType.getParamTypes().size();
421
311
      const auto RetSize =
422
311
          RTy.isVoidTy() ? 0 : FuncType.getReturnTypes().size();
423
424
311
      LLVM::Value Args = Builder.createArray(ArgSize, LLVM::kValSize);
425
311
      LLVM::Value Rets = Builder.createArray(RetSize, LLVM::kValSize);
426
427
311
      auto Arg = F.Fn.getFirstParam();
428
495
      for (unsigned I = 0; I < ArgSize; ++I) {
429
184
        Arg = Arg.getNextParam();
430
184
        Builder.createValuePtrStore(Arg, Args, Context->Int8Ty,
431
184
                                    I * LLVM::kValSize);
432
184
      }
433
434
311
      Builder.createCall(
435
311
          Context->getIntrinsic(
436
311
              Builder, Executable::Intrinsics::kCall,
437
311
              LLVM::Type::getFunctionType(
438
311
                  Context->VoidTy,
439
311
                  {Context->Int32Ty, Context->Int8PtrTy, Context->Int8PtrTy},
440
311
                  false)),
441
311
          {Context->LLContext.getInt32(FuncID), Args, Rets});
442
443
311
      if (RetSize == 0) {
444
190
        Builder.createRetVoid();
445
190
      } else if (RetSize == 1) {
446
86
        Builder.createRet(
447
86
            Builder.createValuePtrLoad(RTy, Rets, Context->Int8Ty));
448
86
      } else {
449
35
        Builder.createAggregateRet(Builder.createArrayPtrLoad(
450
35
            RetSize, RTy, Rets, Context->Int8Ty, LLVM::kValSize));
451
35
      }
452
453
311
      Context->Functions.emplace_back(TypeIdx, F, nullptr);
454
311
      Context->ImportCount++;
455
311
      break;
456
311
    }
457
43
    case ExternalType::Table: // Table type
458
43
    {
459
      // Get table address type. External type checked in validation.
460
43
      const auto &TabType = ImpDesc.getExternalTableType();
461
43
      const auto AddrType = TabType.getLimit().getAddrType();
462
43
      auto Type = toLLVMType(Context->LLContext, AddrType);
463
43
      Context->TableAddrTypes.push_back(Type);
464
43
      break;
465
311
    }
466
49
    case ExternalType::Memory: // Memory type
467
49
    {
468
      // Get memory address type. External type checked in validation.
469
49
      const auto &MemType = ImpDesc.getExternalMemoryType();
470
49
      const auto AddrType = MemType.getLimit().getAddrType();
471
49
      auto Type = toLLVMType(Context->LLContext, AddrType);
472
49
      Context->MemoryAddrTypes.push_back(Type);
473
49
      break;
474
311
    }
475
40
    case ExternalType::Global: // Global type
476
40
    {
477
      // Get global type. External type checked in validation.
478
40
      const auto &GlobType = ImpDesc.getExternalGlobalType();
479
40
      const auto &ValType = GlobType.getValType();
480
40
      auto Type = toLLVMType(Context->LLContext, ValType);
481
40
      Context->Globals.push_back(Type);
482
40
      break;
483
311
    }
484
6
    case ExternalType::Tag: // Tag type
485
6
    {
486
      // TODO: EXCEPTION - implement the AOT.
487
6
      break;
488
311
    }
489
0
    default:
490
0
      assumingUnreachable();
491
449
    }
492
449
  }
493
2.23k
}
494
495
2.22k
void Compiler::compile(const AST::ExportSection &) noexcept {}
496
497
2.23k
void Compiler::compile(const AST::GlobalSection &GlobalSec) noexcept {
498
2.23k
  for (const auto &GlobalSeg : GlobalSec.getContent()) {
499
159
    const auto &ValType = GlobalSeg.getGlobalType().getValType();
500
159
    auto Type = toLLVMType(Context->LLContext, ValType);
501
159
    Context->Globals.push_back(Type);
502
159
  }
503
2.23k
}
504
505
void Compiler::compile(const AST::MemorySection &MemorySec,
506
2.23k
                       const AST::DataSection &) noexcept {
507
2.23k
  for (const auto &MemType : MemorySec.getContent()) {
508
863
    const auto AddrType = MemType.getLimit().getAddrType();
509
863
    auto Type = toLLVMType(Context->LLContext, AddrType);
510
863
    Context->MemoryAddrTypes.push_back(Type);
511
863
  }
512
2.23k
}
513
514
void Compiler::compile(const AST::TableSection &TableSec,
515
2.23k
                       const AST::ElementSection &) noexcept {
516
2.23k
  for (const auto &TableSeg : TableSec.getContent()) {
517
206
    const auto AddrType = TableSeg.getTableType().getLimit().getAddrType();
518
206
    auto Type = toLLVMType(Context->LLContext, AddrType);
519
206
    Context->TableAddrTypes.push_back(Type);
520
206
  }
521
2.23k
}
522
523
void Compiler::compileSections(const AST::Module &Module,
524
2.23k
                               bool DeclarationsOnly) noexcept {
525
  // Compile Function Types
526
2.23k
  compile(Module.getTypeSection(), DeclarationsOnly);
527
  // Compile ImportSection
528
2.23k
  compile(Module.getImportSection());
529
  // Compile GlobalSection
530
2.23k
  compile(Module.getGlobalSection());
531
  // Compile MemorySection (MemorySec, DataSec)
532
2.23k
  compile(Module.getMemorySection(), Module.getDataSection());
533
  // Compile TableSection (TableSec, ElemSec)
534
2.23k
  compile(Module.getTableSection(), Module.getElementSection());
535
  // Create function declarations without compiling bodies. (FunctionSec,
536
  // CodeSec)
537
2.23k
  compileFunctionDeclarations(Module.getFunctionSection(),
538
2.23k
                              Module.getCodeSection());
539
2.23k
}
540
541
void Compiler::compileFunctionDeclarations(
542
    const AST::FunctionSection &FunctionSec,
543
2.23k
    const AST::CodeSection &CodeSec) noexcept {
544
2.23k
  const auto &TypeIdxs = FunctionSec.getContent();
545
2.23k
  const auto &CodeSegs = CodeSec.getContent();
546
2.23k
  assuming(TypeIdxs.size() == CodeSegs.size());
547
548
12.7k
  for (size_t I = 0; I < CodeSegs.size(); ++I) {
549
10.5k
    const auto &TypeIdx = TypeIdxs[I];
550
10.5k
    const auto &Code = CodeSegs[I];
551
10.5k
    assuming(TypeIdx < Context->CompositeTypes.size());
552
10.5k
    assuming(Context->CompositeTypes[TypeIdx]->isFunc());
553
10.5k
    const auto &FuncType = Context->CompositeTypes[TypeIdx]->getFuncType();
554
10.5k
    const auto FuncID = Context->Functions.size();
555
10.5k
    auto FTy = toLLVMType(Context->LLContext, Context->ExecCtxPtrTy, FuncType);
556
10.5k
    LLVM::FunctionCallee F = {FTy, Context->LLModule.get().addFunction(
557
10.5k
                                       FTy, LLVMExternalLinkage,
558
10.5k
                                       fmt::format("f{}"sv, FuncID).c_str())};
559
10.5k
    F.Fn.setVisibility(LLVMProtectedVisibility);
560
10.5k
    F.Fn.setDSOLocal(true);
561
10.5k
    F.Fn.setDLLStorageClass(LLVMDLLExportStorageClass);
562
10.5k
    F.Fn.addFnAttr(Context->NoStackArgProbe);
563
10.5k
    F.Fn.addFnAttr(Context->StrictFP);
564
10.5k
    F.Fn.addFnAttr(Context->UWTable);
565
10.5k
    F.Fn.addParamAttr(0, Context->ReadOnly);
566
10.5k
    F.Fn.addParamAttr(0, Context->NoAlias);
567
568
10.5k
    Context->Functions.emplace_back(TypeIdx, F, &Code);
569
10.5k
  }
570
2.23k
}
571
572
10.5k
Expect<void> Compiler::compileFunctionBody(uint32_t LocalFuncIndex) noexcept {
573
  // Find the function in the Functions list
574
  // LocalFuncIndex is relative to the defined functions (not imports)
575
10.5k
  uint32_t GlobalFuncIndex = Context->ImportCount + LocalFuncIndex;
576
10.5k
  if (GlobalFuncIndex >= Context->Functions.size()) {
577
0
    spdlog::error("[lazy-jit]: function index {} out of range"sv,
578
0
                  LocalFuncIndex);
579
0
    return Unexpect(ErrCode::Value::IllegalPath);
580
0
  }
581
582
10.5k
  auto &[T, F, Code] = Context->Functions[GlobalFuncIndex];
583
10.5k
  if (!Code) {
584
0
    spdlog::error("[lazy-jit]: cannot compile import function {}"sv,
585
0
                  LocalFuncIndex);
586
0
    return Unexpect(ErrCode::Value::IllegalPath);
587
0
  }
588
589
  // Check if already compiled (function has basic blocks)
590
10.5k
  if (F.Fn.countBasicBlocks() > 0) {
591
0
    spdlog::debug("[lazy-jit]: function {} already compiled"sv, LocalFuncIndex);
592
0
    return {};
593
0
  }
594
595
10.5k
  spdlog::debug("[lazy-jit]: compiling function {}"sv, LocalFuncIndex);
596
597
10.5k
  std::vector<ValType> Locals;
598
10.5k
  for (const auto &Local : Code->getLocals()) {
599
666k
    for (unsigned I = 0; I < Local.first; ++I) {
600
665k
      Locals.push_back(Local.second);
601
665k
    }
602
1.64k
  }
603
604
10.5k
  FunctionCompiler FC(
605
10.5k
      *Context, F, Locals, Conf.getCompilerConfigure().isInterruptible(),
606
10.5k
      Conf.getStatisticsConfigure().isInstructionCounting(),
607
10.5k
      Conf.getStatisticsConfigure().isCostMeasuring(),
608
10.5k
      Conf.getRuntimeConfigure().getRunMode() == RunMode::LazyJIT);
609
10.5k
  auto Type = Context->resolveBlockType(T);
610
10.5k
  EXPECTED_TRY(FC.compile(*Code, std::move(Type)));
611
10.5k
  F.Fn.eliminateUnreachableBlocks();
612
613
10.5k
  return {};
614
10.5k
}
615
616
Expect<LLVM::Data>
617
0
LLVM::Compiler::compileInfrastructure(const AST::Module &Module) noexcept {
618
  // Check the module is validated.
619
0
  if (unlikely(!Module.getIsValidated())) {
620
0
    spdlog::error(ErrCode::Value::NotValidated);
621
0
    return Unexpect(ErrCode::Value::NotValidated);
622
0
  }
623
624
0
  std::unique_lock Lock(Mutex);
625
0
  spdlog::info("[lazy-jit]: compile infrastructure start"sv);
626
627
0
  Data D;
628
0
  auto LLContext = initLLVMModule(D);
629
0
  auto &LLModule = D.extract().LLModule;
630
631
0
  CompileContext NewContext(LLContext, LLModule,
632
0
                            Conf.getCompilerConfigure().isGenericBinary());
633
0
  RAIICleanup Cleanup(Context, &NewContext);
634
0
  Context->addVersionGlobal();
635
636
  // Compile all sections and the function declarations without bodies.
637
0
  compileSections(Module, false);
638
  // Compile ExportSection
639
0
  compile(Module.getExportSection());
640
641
  // Set initializer for constant value
642
0
  Context->finalizeIntrinsicsTable();
643
0
  LLModule.verify(LLVMPrintMessageAction);
644
645
0
  spdlog::info("[lazy-jit]: infrastructure compilation done"sv);
646
647
0
  return Expect<Data>{std::move(D)};
648
0
}
649
650
Expect<LLVM::Data>
651
Compiler::compileFunctions(Data &&LLData, const AST::Module &Module,
652
0
                           Span<const uint32_t> LocalFuncIndices) noexcept {
653
0
  if (unlikely(!Module.getIsValidated())) {
654
0
    spdlog::error(ErrCode::Value::NotValidated);
655
0
    return Unexpect(ErrCode::Value::NotValidated);
656
0
  }
657
0
  if (unlikely(LocalFuncIndices.empty())) {
658
0
    spdlog::error("[lazy-jit]: compileFunctions with empty index list"sv);
659
0
    return Unexpect(ErrCode::Value::IllegalPath);
660
0
  }
661
662
0
  std::unique_lock Lock(Mutex);
663
0
  std::vector<uint32_t> Sorted(LocalFuncIndices.begin(),
664
0
                               LocalFuncIndices.end());
665
0
  std::sort(Sorted.begin(), Sorted.end());
666
0
  Sorted.erase(std::unique(Sorted.begin(), Sorted.end()), Sorted.end());
667
668
0
  spdlog::debug("[lazy-jit]: compile functions batch ({}) start"sv,
669
0
                Sorted.size());
670
671
  // Each batch starts from a fresh module sharing the same thread-safe
672
  // context: on success the previous batch module was consumed by the JIT,
673
  // and after a failed batch the leftover module must be discarded so its
674
  // declarations are not re-added on top of themselves.
675
0
  LLData.extract().resetModule();
676
0
  auto LLContext = initLLVMModule(LLData);
677
0
  auto &LLModule = LLData.extract().LLModule;
678
679
0
  CompileContext NewContext(LLContext, LLModule,
680
0
                            Conf.getCompilerConfigure().isGenericBinary());
681
0
  RAIICleanup Cleanup(Context, &NewContext);
682
683
  // Emit the type wrappers as external declarations resolved against the
684
  // infrastructure module, then declare the functions and compile the
685
  // requested bodies.
686
0
  compileSections(Module, true);
687
688
0
  for (uint32_t FuncIndex : Sorted) {
689
0
    EXPECTED_TRY(compileFunctionBody(FuncIndex));
690
0
  }
691
692
0
  spdlog::info("[lazy-jit]: verify batch ({} funcs) start"sv, Sorted.size());
693
0
  LLModule.verify(LLVMPrintMessageAction);
694
0
  spdlog::info("[lazy-jit]: verify batch ({} funcs) done"sv, Sorted.size());
695
696
0
  auto &TM = LLData.extract().TM;
697
0
  EXPECTED_TRY(optimize(LLModule, TM));
698
699
0
  spdlog::debug("[lazy-jit]: compile functions batch ({}) done"sv,
700
0
                Sorted.size());
701
0
  return Expect<Data>{std::move(LLData)};
702
0
}
703
704
} // namespace LLVM
705
} // namespace WasmEdge