Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/llvm/jit.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "llvm/jit.h"
5
6
#include "data.h"
7
#include "llvm.h"
8
#include "spdlog/spdlog.h"
9
10
#include <mutex>
11
12
#include <fmt/format.h>
13
14
namespace LLVM = WasmEdge::LLVM;
15
using namespace std::literals;
16
17
namespace {
18
19
0
std::string errorToString(LLVM::Error &&E) noexcept {
20
0
  auto Msg = E.message();
21
0
  return std::string(Msg.string_view());
22
0
}
23
24
0
static WasmEdge::Expect<LLVM::OrcLLJIT> createTunedLazyLLJIT() noexcept {
25
0
  LLVMOrcLLJITBuilderRef Builder = LLVM::OrcLLJIT::getBuilder();
26
0
  if (!Builder) {
27
0
    Builder = LLVMOrcCreateLLJITBuilder();
28
0
  }
29
30
0
  LLVMTargetRef TheTarget = nullptr;
31
0
  LLVM::Message Triple(LLVMGetDefaultTargetTriple());
32
0
  char *TripleErr = nullptr;
33
0
  if (LLVMGetTargetFromTriple(Triple.string_view().data(), &TheTarget,
34
0
                              &TripleErr)) {
35
0
    spdlog::error("[lazy-jit]: getTargetFromTriple failed: {}"sv,
36
0
                  TripleErr ? TripleErr : "");
37
0
    LLVMDisposeMessage(TripleErr);
38
0
    LLVMOrcDisposeLLJITBuilder(Builder);
39
0
    return WasmEdge::Unexpect(WasmEdge::ErrCode::Value::LazyCompilationError);
40
0
  }
41
42
0
  LLVM::Message CPU(LLVMGetHostCPUName());
43
0
  LLVM::Message Features(LLVMGetHostCPUFeatures());
44
45
  // LLVMCodeGenLevelNone is used deliberately here as a performance trade-off.
46
  // ORC's IRCompileLayer inherits the codegen optimisation level from this
47
  // target machine and applies it during every lazy materialisation.
48
  // Note that this codegen level is independent of the optimisation
49
  // level passed through WasmEdge::Configure, which governs the
50
  // IR compilation step, this setting only controls the ORC materialisation
51
  // pipeline for lazy JIT.
52
0
  LLVMTargetMachineRef TM = LLVMCreateTargetMachine(
53
0
      TheTarget, Triple.string_view().data(), CPU.string_view().data(),
54
0
      Features.string_view().data(), LLVMCodeGenLevelNone, LLVMRelocDefault,
55
0
      LLVMCodeModelJITDefault);
56
57
0
  if (!TM) {
58
0
    spdlog::error("[lazy-jit]: createTargetMachine failed"sv);
59
0
    LLVMOrcDisposeLLJITBuilder(Builder);
60
0
    return WasmEdge::Unexpect(WasmEdge::ErrCode::Value::LazyCompilationError);
61
0
  }
62
63
0
  LLVMOrcJITTargetMachineBuilderRef JTMB =
64
0
      LLVMOrcJITTargetMachineBuilderCreateFromTargetMachine(TM);
65
0
  LLVMOrcLLJITBuilderSetJITTargetMachineBuilder(Builder, JTMB);
66
67
0
  LLVM::OrcLLJIT Result;
68
0
  if (LLVMErrorRef CreateErr = LLVMOrcCreateLLJIT(&Result.unwrap(), Builder)) {
69
0
    LLVM::ErrorMessage Msg(LLVMGetErrorMessage(CreateErr));
70
0
    spdlog::error("[lazy-jit]: LLVMOrcCreateLLJIT failed: {}"sv,
71
0
                  Msg.string_view());
72
0
    return WasmEdge::Unexpect(WasmEdge::ErrCode::Value::LazyCompilationError);
73
0
  }
74
0
  return Result;
75
0
}
76
77
} // namespace
78
79
namespace WasmEdge::LLVM {
80
81
JITLibrary::JITLibrary(std::shared_ptr<LLVM::OrcLLJIT> JIT,
82
                       bool IsLazy) noexcept
83
0
    : J(std::move(JIT)), IsLazy(IsLazy) {}
84
85
0
JITLibrary::~JITLibrary() noexcept {}
86
87
Symbol<const Executable::IntrinsicsTable *>
88
0
JITLibrary::getIntrinsics() noexcept {
89
0
  if (auto Symbol = J->lookup<const IntrinsicsTable *>("intrinsics")) {
90
0
    return createSymbol<const IntrinsicsTable *>(*Symbol);
91
0
  } else {
92
0
    spdlog::error("failed to lookup intrinsics symbol: {}"sv,
93
0
                  errorToString(std::move(Symbol.error())));
94
0
    return {};
95
0
  }
96
0
}
97
98
std::vector<Symbol<Executable::Wrapper>>
99
0
JITLibrary::getTypes(size_t Size) noexcept {
100
0
  std::vector<Symbol<Wrapper>> Result;
101
0
  Result.reserve(Size);
102
0
  for (size_t I = 0; I < Size; ++I) {
103
0
    const std::string Name = fmt::format("t{}"sv, I);
104
0
    if (auto Symbol = J->lookup<Wrapper>(Name.c_str())) {
105
0
      Result.push_back(createSymbol<Wrapper>(*Symbol));
106
0
    } else {
107
0
      spdlog::error("failed to lookup table symbol {}: {}"sv, Name,
108
0
                    errorToString(std::move(Symbol.error())));
109
0
      Result.emplace_back();
110
0
    }
111
0
  }
112
113
0
  return Result;
114
0
}
115
116
std::vector<Symbol<void>> JITLibrary::getCodes(size_t Offset,
117
0
                                               size_t Size) noexcept {
118
0
  std::vector<Symbol<void>> Result;
119
0
  Result.reserve(Size);
120
0
  for (size_t I = 0; I < Size; ++I) {
121
0
    const std::string Name = fmt::format("f{}"sv, I + Offset);
122
0
    void *Addr = nullptr;
123
0
    auto AddrOrErr = J->lookup<void *>(Name.c_str());
124
0
    if (AddrOrErr) {
125
0
      Addr = *AddrOrErr;
126
0
    } else {
127
0
      auto ErrMsg = errorToString(std::move(AddrOrErr.error()));
128
      // consuming only symbol not found errors for lazy-jit
129
0
      if (!IsLazy || ErrMsg.find("Symbols not found"sv) == std::string::npos) {
130
0
        spdlog::error("failed to lookup function symbol {}: {}"sv, Name,
131
0
                      ErrMsg);
132
0
      }
133
0
    }
134
0
    if (Addr) {
135
0
      Result.push_back(createSymbol<void>(Addr));
136
0
    } else {
137
0
      if (IsLazy) {
138
0
        spdlog::debug("[lazy-jit]: function {} not yet compiled"sv, I + Offset);
139
0
      }
140
0
      Result.emplace_back();
141
0
    }
142
0
  }
143
144
0
  return Result;
145
0
}
146
147
0
Expect<std::shared_ptr<Executable>> JIT::load(Data D) noexcept {
148
0
  return loadImpl(D, false);
149
0
}
150
151
0
Expect<std::shared_ptr<Executable>> JIT::loadLazy(Data &D) noexcept {
152
0
  return loadImpl(D, true);
153
0
}
154
155
Expect<std::shared_ptr<Executable>> JIT::loadImpl(Data &D,
156
0
                                                  bool IsLazy) noexcept {
157
0
  OrcLLJIT LLJITInstance;
158
0
  if (IsLazy) {
159
0
    auto R = createTunedLazyLLJIT();
160
0
    if (!R) {
161
0
      spdlog::error("[lazy-jit]: failed to create LLJIT"sv);
162
0
      return Unexpect(R.error());
163
0
    }
164
0
    LLJITInstance = std::move(*R);
165
0
  } else {
166
0
    auto R = OrcLLJIT::create();
167
0
    if (!R) {
168
0
      spdlog::error("failed to create LLJIT: {}"sv,
169
0
                    R.error().message().string_view());
170
0
      return Unexpect(ErrCode::Value::HostFuncError);
171
0
    }
172
0
    LLJITInstance = std::move(*R);
173
0
  }
174
175
0
  auto &LLModule = D.extract().LLModule;
176
0
  auto &TSContext = D.extract().getTSContext();
177
178
0
  if (Conf.getCompilerConfigure().isDumpIR()) {
179
0
    const auto *Filename = IsLazy ? "wasm-lazy-jit.ll" : "wasm-jit.ll";
180
0
    if (auto ErrorMessage = LLModule.printModuleToFile(Filename)) {
181
0
      spdlog::error("printModuleToFile failed"sv);
182
0
    }
183
0
  }
184
185
0
  auto MainJD = LLJITInstance.getMainJITDylib();
186
0
  if (auto Err = LLJITInstance.addLLVMIRModule(
187
0
          MainJD, OrcThreadSafeModule(LLModule.release(), TSContext))) {
188
0
    spdlog::error("failed to add LLVM IR module: {}"sv,
189
0
                  Err.message().string_view());
190
0
    return Unexpect(ErrCode::Value::HostFuncError);
191
0
  }
192
193
0
  return std::make_shared<JITLibrary>(
194
0
      std::make_shared<OrcLLJIT>(std::move(LLJITInstance)), IsLazy);
195
0
}
196
197
Expect<std::vector<WasmFunctionCodeAddress>>
198
JIT::add(JITLibrary &Lib, Data &D,
199
0
         Span<const uint32_t> GlobalFuncIndices) noexcept {
200
0
  if (GlobalFuncIndices.empty()) {
201
0
    spdlog::error("JIT::add: empty function index list"sv);
202
0
    return Unexpect(ErrCode::Value::LazyCompilationError);
203
0
  }
204
205
0
  auto &LLModule = D.extract().LLModule;
206
0
  auto &TSContext = D.extract().getTSContext();
207
208
0
  auto JD = Lib.J->getMainJITDylib();
209
0
  auto RT = JD.createResourceTracker();
210
0
  if (!RT) {
211
0
    spdlog::error("[lazy-jit]: failed to create resource tracker"sv);
212
0
    return Unexpect(ErrCode::Value::LazyCompilationError);
213
0
  }
214
215
0
  if (auto Err = Lib.J->addLLVMIRModuleWithRT(
216
0
          RT, OrcThreadSafeModule(LLModule.release(), TSContext))) {
217
0
    spdlog::error("[lazy-jit]: failed to add LLVM IR module: {}"sv,
218
0
                  Err.message().string_view());
219
0
    return Unexpect(ErrCode::Value::LazyCompilationError);
220
0
  }
221
222
0
  std::vector<WasmFunctionCodeAddress> Addresses;
223
0
  Addresses.reserve(GlobalFuncIndices.size());
224
0
  for (uint32_t GlobalFuncIndex : GlobalFuncIndices) {
225
0
    const std::string SymName = fmt::format("f{}"sv, GlobalFuncIndex);
226
0
    auto AddrOrErr = Lib.J->lookup<void *>(SymName.c_str());
227
0
    if (!AddrOrErr) {
228
0
      spdlog::error("[lazy-jit]: failed to lookup function symbol {}: {}"sv,
229
0
                    SymName, errorToString(std::move(AddrOrErr.error())));
230
0
      if (auto RemoveErr = RT.remove()) {
231
0
        spdlog::error(
232
0
            "[lazy-jit]: failed to remove failed module from tracker: {}"sv,
233
0
            RemoveErr.message().string_view());
234
0
      }
235
0
      return Unexpect(ErrCode::Value::LazyCompilationError);
236
0
    }
237
0
    Addresses.push_back(*AddrOrErr);
238
0
  }
239
0
  return Addresses;
240
0
}
241
} // namespace WasmEdge::LLVM