Coverage Report

Created: 2026-08-08 06:32

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.32k
      : Context(ContextRef) {
26
2.32k
    Context = NewContext;
27
2.32k
  }
28
2.32k
  ~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.32k
toLLVMLevel(WasmEdge::CompilerConfigure::OptimizationLevel Level) noexcept {
57
2.32k
  using OL = WasmEdge::CompilerConfigure::OptimizationLevel;
58
2.32k
  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.32k
  case OL::O3:
66
2.32k
    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.32k
  }
74
2.32k
}
75
#endif
76
77
static inline LLVMCodeGenOptLevel toLLVMCodeGenLevel(
78
2.32k
    WasmEdge::CompilerConfigure::OptimizationLevel Level) noexcept {
79
2.32k
  using OL = WasmEdge::CompilerConfigure::OptimizationLevel;
80
2.32k
  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.32k
  case OL::O3:
88
2.32k
    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.32k
  }
96
2.32k
}
97
} // namespace
98
99
namespace WasmEdge {
100
namespace LLVM {
101
102
2.32k
Expect<void> Compiler::checkConfigure() noexcept {
103
2.32k
  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.32k
  return {};
110
2.32k
}
111
112
Expect<void> Compiler::optimize(LLVM::Module &LLModule,
113
2.32k
                                LLVM::TargetMachine &TM) noexcept {
114
2.32k
  spdlog::info("optimize start"sv);
115
2.32k
  auto Triple = LLModule.getTarget();
116
2.32k
  auto [TheTarget, ErrorMessage] = LLVM::Target::getFromTriple(Triple);
117
2.32k
  if (ErrorMessage) {
118
0
    spdlog::error("getFromTriple failed:{}"sv, ErrorMessage.string_view());
119
0
    return Unexpect(ErrCode::Value::IllegalPath);
120
0
  }
121
122
2.32k
  std::string CPUName;
123
#if defined(__riscv) && __riscv_xlen == 64
124
  CPUName = "generic-rv64"s;
125
#else
126
2.32k
  if (!Conf.getCompilerConfigure().isGenericBinary()) {
127
2.32k
    CPUName = LLVM::getHostCPUName().string_view();
128
2.32k
  } else {
129
0
    CPUName = "generic"s;
130
0
  }
131
2.32k
#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.32k
  TM = LLVM::TargetMachine::create(
137
2.32k
      TheTarget, Triple, CPUName.c_str(),
138
#if defined(__riscv) && __riscv_xlen == 64
139
      "",
140
#else
141
2.32k
      LLVM::getHostCPUFeatures().unwrap(),
142
2.32k
#endif
143
2.32k
      toLLVMCodeGenLevel(Conf.getCompilerConfigure().getOptimizationLevel()),
144
2.32k
      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.32k
  auto FP = LLVM::PassManager::createForModule(LLModule);
156
2.32k
  auto MP = LLVM::PassManager::create();
157
158
2.32k
  TM.addAnalysisPasses(MP);
159
2.32k
  TM.addAnalysisPasses(FP);
160
2.32k
  {
161
2.32k
    auto PMB = LLVM::PassManagerBuilder::create();
162
2.32k
    auto [OptLevel, SizeLevel] =
163
2.32k
        toLLVMLevel(Conf.getCompilerConfigure().getOptimizationLevel());
164
2.32k
    PMB.setOptLevel(OptLevel);
165
2.32k
    PMB.setSizeLevel(SizeLevel);
166
2.32k
    PMB.populateFunctionPassManager(FP);
167
2.32k
    PMB.populateModulePassManager(MP);
168
2.32k
  }
169
2.32k
  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.32k
  default:
175
2.32k
    break;
176
2.32k
  }
177
178
2.32k
  FP.initializeFunctionPassManager();
179
25.0k
  for (auto Fn = LLModule.getFirstFunction(); Fn; Fn = Fn.getNextFunction()) {
180
22.6k
    FP.runFunctionPassManager(Fn);
181
22.6k
  }
182
2.32k
  FP.finalizeFunctionPassManager();
183
2.32k
  MP.runPassManager(LLModule);
184
2.32k
#endif
185
186
2.32k
  spdlog::info("optimize done"sv);
187
2.32k
  return {};
188
2.32k
}
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.32k
static LLVM::Context initLLVMModule(LLVM::Data &D) noexcept {
193
2.32k
  auto LLContext = D.extract().getLLContext();
194
2.32k
  LLVM::Core::init(LLContext.unwrap());
195
2.32k
  auto &LLModule = D.extract().LLModule;
196
2.32k
  LLModule.setTarget(LLVM::getDefaultTargetTriple().unwrap());
197
2.32k
  LLModule.addFlag(LLVMModuleFlagBehaviorError, "PIC Level"sv, 2);
198
2.32k
  return LLContext;
199
2.32k
}
200
201
2.32k
Expect<Data> Compiler::compile(const AST::Module &Module) noexcept {
202
  // Check that the module is validated.
203
2.32k
  if (unlikely(!Module.getIsValidated())) {
204
0
    spdlog::error(ErrCode::Value::NotValidated);
205
0
    return Unexpect(ErrCode::Value::NotValidated);
206
0
  }
207
208
2.32k
  std::unique_lock Lock(Mutex);
209
2.32k
  spdlog::info("compile start"sv);
210
211
2.32k
  LLVM::Data D;
212
2.32k
  auto LLContext = initLLVMModule(D);
213
2.32k
  auto &LLModule = D.extract().LLModule;
214
215
2.32k
  CompileContext NewContext(LLContext, LLModule,
216
2.32k
                            Conf.getCompilerConfigure().isGenericBinary());
217
2.32k
  RAIICleanup Cleanup(Context, &NewContext);
218
2.32k
  Context->addVersionGlobal();
219
220
  // Compile all sections and the function declarations.
221
2.32k
  compileSections(Module, false);
222
  // Compile all function bodies.
223
2.32k
  const auto DefinedCount = Module.getDefinedFuncCount();
224
12.7k
  for (uint32_t I = 0; I < DefinedCount; ++I) {
225
10.3k
    EXPECTED_TRY(compileFunctionBody(I));
226
10.3k
  }
227
  // Compile ExportSection.
228
2.32k
  compile(Module.getExportSection());
229
  // StartSection is not required for compilation.
230
231
2.32k
  spdlog::info("verify start"sv);
232
2.32k
  LLModule.verify(LLVMPrintMessageAction);
233
234
2.32k
  auto &TM = D.extract().TM;
235
2.32k
  EXPECTED_TRY(optimize(LLModule, TM));
236
237
  // Set initializer for constant value
238
2.32k
  Context->finalizeIntrinsicsTable();
239
2.32k
  return Expect<Data>{std::move(D)};
240
2.32k
}
241
242
void Compiler::compile(const AST::TypeSection &TypeSec,
243
2.32k
                       bool DeclarationsOnly) noexcept {
244
2.32k
  auto WrapperTy =
245
2.32k
      LLVM::Type::getFunctionType(Context->VoidTy,
246
2.32k
                                  {Context->ExecCtxPtrTy, Context->Int8PtrTy,
247
2.32k
                                   Context->Int8PtrTy, Context->Int8PtrTy},
248
2.32k
                                  false);
249
2.32k
  auto SubTypes = TypeSec.getContent();
250
2.32k
  const auto Size = SubTypes.size();
251
2.32k
  if (Size == 0) {
252
151
    return;
253
151
  }
254
2.16k
  Context->CompositeTypes.reserve(Size);
255
2.16k
  Context->FunctionWrappers.reserve(Size);
256
257
4.57k
  auto SetFuncAttributes = [&](auto FDecl) {
258
4.57k
    FDecl.setVisibility(LLVMProtectedVisibility);
259
4.57k
    FDecl.setDSOLocal(true);
260
4.57k
    FDecl.setDLLStorageClass(LLVMDLLExportStorageClass);
261
4.57k
    FDecl.addFnAttr(Context->NoStackArgProbe);
262
4.57k
    FDecl.addFnAttr(Context->StrictFP);
263
4.57k
    FDecl.addFnAttr(Context->UWTable);
264
4.57k
    FDecl.addParamAttr(0, Context->ReadOnly);
265
4.57k
    FDecl.addParamAttr(0, Context->NoAlias);
266
4.57k
    FDecl.addParamAttr(1, Context->NoAlias);
267
4.57k
    FDecl.addParamAttr(2, Context->NoAlias);
268
4.57k
    FDecl.addParamAttr(3, Context->NoAlias);
269
4.57k
  };
270
271
  // Iterate and compile types.
272
6.93k
  for (size_t I = 0; I < Size; ++I) {
273
4.76k
    const auto &CompType = SubTypes[I].getCompositeType();
274
4.76k
    const auto Name = fmt::format("t{}"sv, Context->CompositeTypes.size());
275
4.76k
    if (CompType.isFunc()) {
276
      // Check that the function type is unique.
277
4.58k
      {
278
4.58k
        bool Unique = true;
279
17.4k
        for (size_t J = 0; J < I; ++J) {
280
13.0k
          if (Context->CompositeTypes[J] &&
281
13.0k
              Context->CompositeTypes[J]->isFunc()) {
282
12.7k
            const auto &OldFuncType = Context->CompositeTypes[J]->getFuncType();
283
12.7k
            if (OldFuncType == CompType.getFuncType()) {
284
189
              Unique = false;
285
189
              Context->CompositeTypes.push_back(Context->CompositeTypes[J]);
286
189
              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
189
              } else {
292
189
                auto F = Context->FunctionWrappers[J];
293
189
                Context->FunctionWrappers.push_back(F);
294
189
                auto A = Context->LLModule.get().addAlias(WrapperTy, F,
295
189
                                                          Name.c_str());
296
189
                A.setLinkage(LLVMExternalLinkage);
297
189
                A.setVisibility(LLVMProtectedVisibility);
298
189
                A.setDSOLocal(true);
299
189
                A.setDLLStorageClass(LLVMDLLExportStorageClass);
300
189
              }
301
189
              break;
302
189
            }
303
12.7k
          }
304
13.0k
        }
305
4.58k
        if (!Unique) {
306
189
          continue;
307
189
        }
308
4.58k
      }
309
310
      // Create Wrapper
311
4.39k
      auto F = Context->LLModule.get().addFunction(
312
4.39k
          WrapperTy, LLVMExternalLinkage, Name.c_str());
313
4.39k
      {
314
4.39k
        SetFuncAttributes(F);
315
316
4.39k
        if (!DeclarationsOnly) {
317
4.39k
          LLVM::Builder Builder(Context->LLContext);
318
4.39k
          Builder.positionAtEnd(
319
4.39k
              LLVM::BasicBlock::create(Context->LLContext, F, "entry"));
320
321
4.39k
          auto FTy = toLLVMType(Context->LLContext, Context->ExecCtxPtrTy,
322
4.39k
                                CompType.getFuncType());
323
4.39k
          auto RTy = FTy.getReturnType();
324
4.39k
          std::vector<LLVM::Type> FPTy(FTy.getNumParams());
325
4.39k
          FTy.getParamTypes(FPTy);
326
327
4.39k
          const size_t ArgCount = FPTy.size() - 1;
328
4.39k
          auto ExecCtxPtr = F.getFirstParam();
329
4.39k
          auto RawFunc = LLVM::FunctionCallee{
330
4.39k
              FTy, Builder.createBitCast(ExecCtxPtr.getNextParam(),
331
4.39k
                                         FTy.getPointerTo())};
332
4.39k
          auto RawArgs = ExecCtxPtr.getNextParam().getNextParam();
333
4.39k
          auto RawRets = RawArgs.getNextParam();
334
335
4.39k
          std::vector<LLVM::Value> Args;
336
4.39k
          Args.reserve(FTy.getNumParams());
337
4.39k
          Args.push_back(ExecCtxPtr);
338
9.13k
          for (size_t J = 0; J < ArgCount; ++J) {
339
4.73k
            Args.push_back(Builder.createValuePtrLoad(
340
4.73k
                FPTy[J + 1], RawArgs, Context->Int8Ty, J * LLVM::kValSize));
341
4.73k
          }
342
343
4.39k
          auto Ret = Builder.createCall(RawFunc, Args);
344
4.39k
          if (RTy.isVoidTy()) {
345
            // nothing to do
346
2.83k
          } else if (RTy.isStructTy()) {
347
311
            auto Rets = unpackStruct(Builder, Ret);
348
311
            Builder.createArrayPtrStore(Rets, RawRets, Context->Int8Ty,
349
311
                                        LLVM::kValSize);
350
2.52k
          } else {
351
2.52k
            Builder.createValuePtrStore(Ret, RawRets, Context->Int8Ty);
352
2.52k
          }
353
4.39k
          Builder.createRetVoid();
354
4.39k
        }
355
4.39k
      }
356
      // Copy wrapper, param and return lists to module instance.
357
4.39k
      Context->FunctionWrappers.push_back(F);
358
4.39k
    } else {
359
      // Non function type case. Create empty wrapper.
360
180
      auto F = Context->LLModule.get().addFunction(
361
180
          WrapperTy, LLVMExternalLinkage, Name.c_str());
362
180
      {
363
180
        SetFuncAttributes(F);
364
365
180
        if (!DeclarationsOnly) {
366
180
          LLVM::Builder Builder(Context->LLContext);
367
180
          Builder.positionAtEnd(
368
180
              LLVM::BasicBlock::create(Context->LLContext, F, "entry"));
369
180
          Builder.createRetVoid();
370
180
        }
371
180
      }
372
180
      Context->FunctionWrappers.push_back(F);
373
180
    }
374
4.57k
    Context->CompositeTypes.push_back(&CompType);
375
4.57k
  }
376
2.16k
}
377
378
2.32k
void Compiler::compile(const AST::ImportSection &ImportSec) noexcept {
379
  // Iterate and compile import descriptions.
380
2.32k
  for (const auto &ImpDesc : ImportSec.getContent()) {
381
    // Get data from import description.
382
547
    const auto &ExtType = ImpDesc.getExternalType();
383
384
    // Add the imports to the module instance.
385
547
    switch (ExtType) {
386
370
    case ExternalType::Function: // Function type index
387
370
    {
388
370
      const auto FuncID = static_cast<uint32_t>(Context->Functions.size());
389
      // Get the function type index in module.
390
370
      uint32_t TypeIdx = ImpDesc.getExternalFuncTypeIdx();
391
370
      assuming(TypeIdx < Context->CompositeTypes.size());
392
370
      assuming(Context->CompositeTypes[TypeIdx]->isFunc());
393
370
      const auto &FuncType = Context->CompositeTypes[TypeIdx]->getFuncType();
394
370
      auto FTy =
395
370
          toLLVMType(Context->LLContext, Context->ExecCtxPtrTy, FuncType);
396
370
      auto RTy = FTy.getReturnType();
397
370
      auto F =
398
370
          LLVM::FunctionCallee{FTy, Context->LLModule.get().addFunction(
399
370
                                        FTy, LLVMInternalLinkage,
400
370
                                        fmt::format("f{}"sv, FuncID).c_str())};
401
370
      F.Fn.setDSOLocal(true);
402
370
      F.Fn.addFnAttr(Context->NoStackArgProbe);
403
370
      F.Fn.addFnAttr(Context->StrictFP);
404
370
      F.Fn.addFnAttr(Context->UWTable);
405
370
      F.Fn.addParamAttr(0, Context->ReadOnly);
406
370
      F.Fn.addParamAttr(0, Context->NoAlias);
407
408
370
      LLVM::Builder Builder(Context->LLContext);
409
370
      Builder.positionAtEnd(
410
370
          LLVM::BasicBlock::create(Context->LLContext, F.Fn, "entry"));
411
412
370
      const auto ArgSize = FuncType.getParamTypes().size();
413
370
      const auto RetSize =
414
370
          RTy.isVoidTy() ? 0 : FuncType.getReturnTypes().size();
415
416
370
      LLVM::Value Args = Builder.createArray(ArgSize, LLVM::kValSize);
417
370
      LLVM::Value Rets = Builder.createArray(RetSize, LLVM::kValSize);
418
419
370
      auto Arg = F.Fn.getFirstParam();
420
617
      for (unsigned I = 0; I < ArgSize; ++I) {
421
247
        Arg = Arg.getNextParam();
422
247
        Builder.createValuePtrStore(Arg, Args, Context->Int8Ty,
423
247
                                    I * LLVM::kValSize);
424
247
      }
425
426
370
      Builder.createCall(
427
370
          Context->getIntrinsic(
428
370
              Builder, Executable::Intrinsics::kCall,
429
370
              LLVM::Type::getFunctionType(
430
370
                  Context->VoidTy,
431
370
                  {Context->Int32Ty, Context->Int8PtrTy, Context->Int8PtrTy},
432
370
                  false)),
433
370
          {Context->LLContext.getInt32(FuncID), Args, Rets});
434
435
370
      if (RetSize == 0) {
436
249
        Builder.createRetVoid();
437
249
      } else if (RetSize == 1) {
438
86
        Builder.createRet(
439
86
            Builder.createValuePtrLoad(RTy, Rets, Context->Int8Ty));
440
86
      } else {
441
35
        Builder.createAggregateRet(Builder.createArrayPtrLoad(
442
35
            RetSize, RTy, Rets, Context->Int8Ty, LLVM::kValSize));
443
35
      }
444
445
370
      Context->Functions.emplace_back(TypeIdx, F, nullptr);
446
370
      Context->ImportCount++;
447
370
      break;
448
370
    }
449
62
    case ExternalType::Table: // Table type
450
62
    {
451
      // Get table address type. External type checked in validation.
452
62
      const auto &TabType = ImpDesc.getExternalTableType();
453
62
      const auto AddrType = TabType.getLimit().getAddrType();
454
62
      auto Type = toLLVMType(Context->LLContext, AddrType);
455
62
      Context->TableAddrTypes.push_back(Type);
456
62
      break;
457
370
    }
458
41
    case ExternalType::Memory: // Memory type
459
41
    {
460
      // Get memory address type. External type checked in validation.
461
41
      const auto &MemType = ImpDesc.getExternalMemoryType();
462
41
      const auto AddrType = MemType.getLimit().getAddrType();
463
41
      auto Type = toLLVMType(Context->LLContext, AddrType);
464
41
      Context->MemoryAddrTypes.push_back(Type);
465
41
      break;
466
370
    }
467
42
    case ExternalType::Global: // Global type
468
42
    {
469
      // Get global type. External type checked in validation.
470
42
      const auto &GlobType = ImpDesc.getExternalGlobalType();
471
42
      const auto &ValType = GlobType.getValType();
472
42
      auto Type = toLLVMType(Context->LLContext, ValType);
473
42
      Context->Globals.push_back(Type);
474
42
      break;
475
370
    }
476
32
    case ExternalType::Tag: // Tag type
477
32
    {
478
      // Get the tag type index. External type checked in validation.
479
32
      const auto &TgType = ImpDesc.getExternalTagType();
480
32
      Context->Tags.push_back(TgType.getTypeIdx());
481
32
      break;
482
370
    }
483
0
    default:
484
0
      assumingUnreachable();
485
547
    }
486
547
  }
487
2.32k
}
488
489
2.32k
void Compiler::compile(const AST::ExportSection &) noexcept {}
490
491
2.32k
void Compiler::compile(const AST::GlobalSection &GlobalSec) noexcept {
492
2.32k
  for (const auto &GlobalSeg : GlobalSec.getContent()) {
493
158
    const auto &ValType = GlobalSeg.getGlobalType().getValType();
494
158
    auto Type = toLLVMType(Context->LLContext, ValType);
495
158
    Context->Globals.push_back(Type);
496
158
  }
497
2.32k
}
498
499
void Compiler::compile(const AST::MemorySection &MemorySec,
500
2.32k
                       const AST::DataSection &) noexcept {
501
2.32k
  for (const auto &MemType : MemorySec.getContent()) {
502
1.13k
    const auto AddrType = MemType.getLimit().getAddrType();
503
1.13k
    auto Type = toLLVMType(Context->LLContext, AddrType);
504
1.13k
    Context->MemoryAddrTypes.push_back(Type);
505
1.13k
  }
506
2.32k
}
507
508
void Compiler::compile(const AST::TableSection &TableSec,
509
2.32k
                       const AST::ElementSection &) noexcept {
510
2.32k
  for (const auto &TableSeg : TableSec.getContent()) {
511
202
    const auto AddrType = TableSeg.getTableType().getLimit().getAddrType();
512
202
    auto Type = toLLVMType(Context->LLContext, AddrType);
513
202
    Context->TableAddrTypes.push_back(Type);
514
202
  }
515
2.32k
}
516
517
2.32k
void Compiler::compile(const AST::TagSection &TagSec) noexcept {
518
2.32k
  for (const auto &TgType : TagSec.getContent()) {
519
43
    Context->Tags.push_back(TgType.getTypeIdx());
520
43
  }
521
2.32k
}
522
523
void Compiler::compileSections(const AST::Module &Module,
524
2.32k
                               bool DeclarationsOnly) noexcept {
525
  // Compile Function Types
526
2.32k
  compile(Module.getTypeSection(), DeclarationsOnly);
527
  // Compile ImportSection
528
2.32k
  compile(Module.getImportSection());
529
  // Compile GlobalSection
530
2.32k
  compile(Module.getGlobalSection());
531
  // Compile MemorySection (MemorySec, DataSec)
532
2.32k
  compile(Module.getMemorySection(), Module.getDataSection());
533
  // Compile TableSection (TableSec, ElemSec)
534
2.32k
  compile(Module.getTableSection(), Module.getElementSection());
535
  // Compile TagSection
536
2.32k
  compile(Module.getTagSection());
537
  // Create function declarations without compiling bodies. (FunctionSec,
538
  // CodeSec)
539
2.32k
  compileFunctionDeclarations(Module.getFunctionSection(),
540
2.32k
                              Module.getCodeSection());
541
2.32k
}
542
543
void Compiler::compileFunctionDeclarations(
544
    const AST::FunctionSection &FunctionSec,
545
2.32k
    const AST::CodeSection &CodeSec) noexcept {
546
2.32k
  const auto &TypeIdxs = FunctionSec.getContent();
547
2.32k
  const auto &CodeSegs = CodeSec.getContent();
548
2.32k
  assuming(TypeIdxs.size() == CodeSegs.size());
549
550
12.7k
  for (size_t I = 0; I < CodeSegs.size(); ++I) {
551
10.3k
    const auto &TypeIdx = TypeIdxs[I];
552
10.3k
    const auto &Code = CodeSegs[I];
553
10.3k
    assuming(TypeIdx < Context->CompositeTypes.size());
554
10.3k
    assuming(Context->CompositeTypes[TypeIdx]->isFunc());
555
10.3k
    const auto &FuncType = Context->CompositeTypes[TypeIdx]->getFuncType();
556
10.3k
    const auto FuncID = Context->Functions.size();
557
10.3k
    auto FTy = toLLVMType(Context->LLContext, Context->ExecCtxPtrTy, FuncType);
558
10.3k
    LLVM::FunctionCallee F = {FTy, Context->LLModule.get().addFunction(
559
10.3k
                                       FTy, LLVMExternalLinkage,
560
10.3k
                                       fmt::format("f{}"sv, FuncID).c_str())};
561
10.3k
    F.Fn.setVisibility(LLVMProtectedVisibility);
562
10.3k
    F.Fn.setDSOLocal(true);
563
10.3k
    F.Fn.setDLLStorageClass(LLVMDLLExportStorageClass);
564
10.3k
    F.Fn.addFnAttr(Context->NoStackArgProbe);
565
10.3k
    F.Fn.addFnAttr(Context->StrictFP);
566
10.3k
    F.Fn.addFnAttr(Context->UWTable);
567
10.3k
    F.Fn.addParamAttr(0, Context->ReadOnly);
568
10.3k
    F.Fn.addParamAttr(0, Context->NoAlias);
569
570
10.3k
    Context->Functions.emplace_back(TypeIdx, F, &Code);
571
10.3k
  }
572
2.32k
}
573
574
10.3k
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.3k
  uint32_t GlobalFuncIndex = Context->ImportCount + LocalFuncIndex;
578
10.3k
  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.3k
  auto &[T, F, Code] = Context->Functions[GlobalFuncIndex];
585
10.3k
  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.3k
  if (F.Fn.countBasicBlocks() > 0) {
593
0
    spdlog::debug("[lazy-jit]: function {} already compiled"sv, LocalFuncIndex);
594
0
    return {};
595
0
  }
596
597
10.3k
  spdlog::debug("[lazy-jit]: compiling function {}"sv, LocalFuncIndex);
598
599
10.3k
  std::vector<ValType> Locals;
600
10.3k
  for (const auto &Local : Code->getLocals()) {
601
470k
    for (unsigned I = 0; I < Local.first; ++I) {
602
469k
      Locals.push_back(Local.second);
603
469k
    }
604
1.59k
  }
605
606
10.3k
  FunctionCompiler FC(
607
10.3k
      *Context, F, Locals, Conf.getCompilerConfigure().isInterruptible(),
608
10.3k
      Conf.getStatisticsConfigure().isInstructionCounting(),
609
10.3k
      Conf.getStatisticsConfigure().isCostMeasuring(),
610
10.3k
      Conf.getRuntimeConfigure().getRunMode() == RunMode::LazyJIT);
611
10.3k
  auto Type = Context->resolveBlockType(T);
612
10.3k
  EXPECTED_TRY(FC.compile(*Code, std::move(Type)));
613
10.3k
  F.Fn.eliminateUnreachableBlocks();
614
615
10.3k
  return {};
616
10.3k
}
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