Coverage Report

Created: 2026-08-14 06:41

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