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/context.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "compiler/context.h"
5
6
#include <algorithm>
7
#include <array>
8
#include <cstdint>
9
#include <initializer_list>
10
#include <string_view>
11
#include <utility>
12
#include <vector>
13
14
using namespace std::literals;
15
16
namespace WasmEdge::LLVM {
17
18
Compiler::CompileContext::CompileContext(LLVM::Context C, LLVM::Module &M,
19
                                         bool IsGenericBinary) noexcept
20
2.31k
    : LLContext(C), LLModule(M),
21
2.31k
      Cold(LLVM::Attribute::createEnum(C, LLVM::Core::Cold, 0)),
22
2.31k
      NoAlias(LLVM::Attribute::createEnum(C, LLVM::Core::NoAlias, 0)),
23
2.31k
      NoInline(LLVM::Attribute::createEnum(C, LLVM::Core::NoInline, 0)),
24
2.31k
      NoReturn(LLVM::Attribute::createEnum(C, LLVM::Core::NoReturn, 0)),
25
2.31k
      ReadOnly(LLVM::Attribute::createEnum(C, LLVM::Core::ReadOnly, 0)),
26
2.31k
      StrictFP(LLVM::Attribute::createEnum(C, LLVM::Core::StrictFP, 0)),
27
2.31k
      UWTable(LLVM::Attribute::createEnum(C, LLVM::Core::UWTable,
28
2.31k
                                          LLVM::Core::UWTableDefault)),
29
      NoStackArgProbe(
30
2.31k
          LLVM::Attribute::createString(C, "no-stack-arg-probe"sv, {})),
31
2.31k
      VoidTy(LLContext.getVoidTy()), Int8Ty(LLContext.getInt8Ty()),
32
2.31k
      Int16Ty(LLContext.getInt16Ty()), Int32Ty(LLContext.getInt32Ty()),
33
2.31k
      Int64Ty(LLContext.getInt64Ty()), Int128Ty(LLContext.getInt128Ty()),
34
2.31k
      FloatTy(LLContext.getFloatTy()), DoubleTy(LLContext.getDoubleTy()),
35
2.31k
      Int8x16Ty(LLVM::Type::getVectorType(Int8Ty, 16)),
36
2.31k
      Int16x8Ty(LLVM::Type::getVectorType(Int16Ty, 8)),
37
2.31k
      Int32x4Ty(LLVM::Type::getVectorType(Int32Ty, 4)),
38
2.31k
      Floatx4Ty(LLVM::Type::getVectorType(FloatTy, 4)),
39
2.31k
      Int64x2Ty(LLVM::Type::getVectorType(Int64Ty, 2)),
40
2.31k
      Doublex2Ty(LLVM::Type::getVectorType(DoubleTy, 2)),
41
2.31k
      Int128x1Ty(LLVM::Type::getVectorType(Int128Ty, 1)),
42
2.31k
      Int8PtrTy(Int8Ty.getPointerTo()), Int32PtrTy(Int32Ty.getPointerTo()),
43
2.31k
      Int64PtrTy(Int64Ty.getPointerTo()), Int128PtrTy(Int128Ty.getPointerTo()),
44
2.31k
      Int8PtrPtrTy(Int8PtrTy.getPointerTo()),
45
2.31k
      ExecCtxTy(LLVM::Type::getStructType(
46
2.31k
          "ExecCtx",
47
2.31k
          std::initializer_list<LLVM::Type>{
48
              // MemoryPtrs
49
2.31k
              Int8PtrTy.getPointerTo(),
50
              // MemorySizes
51
2.31k
              Int64PtrTy.getPointerTo(),
52
              // TableRefs
53
2.31k
              Int64x2Ty.getPointerTo().getPointerTo(),
54
              // TableSizes
55
2.31k
              Int64PtrTy.getPointerTo(),
56
              // Globals
57
2.31k
              Int128PtrTy.getPointerTo(),
58
              // Tags
59
2.31k
              Int8PtrPtrTy,
60
              // PendingExnTagAddr
61
2.31k
              Int8PtrPtrTy,
62
              // InstrCount
63
2.31k
              Int64PtrTy,
64
              // CostTable
65
2.31k
              LLVM::Type::getArrayType(Int64Ty, UINT16_MAX + 1).getPointerTo(),
66
              // Gas
67
2.31k
              Int64PtrTy,
68
              // GasLimit
69
2.31k
              Int64Ty,
70
              // StopToken
71
2.31k
              Int32PtrTy,
72
              // ModuleInst
73
2.31k
              Int8PtrTy,
74
2.31k
          })),
75
2.31k
      ExecCtxPtrTy(ExecCtxTy.getPointerTo()),
76
2.31k
      IntrinsicsTableTy(LLVM::Type::getArrayType(
77
2.31k
          Int8Ty.getPointerTo(),
78
2.31k
          static_cast<uint32_t>(Executable::Intrinsics::kIntrinsicMax))),
79
2.31k
      IntrinsicsTablePtrTy(IntrinsicsTableTy.getPointerTo()),
80
2.31k
      IntrinsicsTable(LLModule.get().addGlobal(IntrinsicsTablePtrTy, true,
81
2.31k
                                               LLVMExternalLinkage,
82
2.31k
                                               LLVM::Value(), "intrinsics")) {
83
2.31k
  Trap.Ty = LLVM::Type::getFunctionType(VoidTy, {Int32Ty});
84
2.31k
  Trap.Fn = LLModule.get().addFunction(Trap.Ty, LLVMPrivateLinkage, "trap");
85
2.31k
  Trap.Fn.setDSOLocal(true);
86
2.31k
  Trap.Fn.addFnAttr(NoStackArgProbe);
87
2.31k
  Trap.Fn.addFnAttr(StrictFP);
88
2.31k
  Trap.Fn.addFnAttr(UWTable);
89
2.31k
  Trap.Fn.addFnAttr(NoReturn);
90
2.31k
  Trap.Fn.addFnAttr(Cold);
91
2.31k
  Trap.Fn.addFnAttr(NoInline);
92
93
2.31k
  if (!IsGenericBinary) {
94
2.31k
    SubtargetFeatures = LLVM::getHostCPUFeatures();
95
2.31k
    auto Features = SubtargetFeatures.string_view();
96
201k
    while (!Features.empty()) {
97
199k
      std::string_view Feature;
98
199k
      if (auto Pos = Features.find(','); Pos != std::string_view::npos) {
99
196k
        Feature = Features.substr(0, Pos);
100
196k
        Features = Features.substr(Pos + 1);
101
196k
      } else {
102
2.31k
        Feature = std::exchange(Features, std::string_view());
103
2.31k
      }
104
199k
      if (Feature[0] != '+') {
105
122k
        continue;
106
122k
      }
107
76.4k
      Feature = Feature.substr(1);
108
109
76.4k
#if defined(__x86_64__)
110
76.4k
      if (!SupportXOP && Feature == "xop"sv) {
111
0
        SupportXOP = true;
112
0
      }
113
76.4k
      if (!SupportSSE4_1 && Feature == "sse4.1"sv) {
114
2.31k
        SupportSSE4_1 = true;
115
2.31k
      }
116
76.4k
      if (!SupportSSSE3 && Feature == "ssse3"sv) {
117
2.31k
        SupportSSSE3 = true;
118
2.31k
      }
119
76.4k
      if (!SupportSSE2 && Feature == "sse2"sv) {
120
0
        SupportSSE2 = true;
121
0
      }
122
#elif defined(__aarch64__)
123
      if (!SupportNEON && Feature == "neon"sv) {
124
        SupportNEON = true;
125
      }
126
#endif
127
76.4k
    }
128
2.31k
  }
129
130
2.31k
  compileTrap();
131
2.31k
}
132
133
36.1k
bool isVoidReturn(Span<const ValType> ValTypes) noexcept {
134
36.1k
  return ValTypes.empty();
135
36.1k
}
136
137
LLVM::Type toLLVMType(LLVM::Context LLContext,
138
532k
                      const ValType &ValType) noexcept {
139
532k
  switch (ValType.getCode()) {
140
56.8k
  case TypeCode::I32:
141
56.8k
    return LLContext.getInt32Ty();
142
209k
  case TypeCode::I64:
143
209k
    return LLContext.getInt64Ty();
144
685
  case TypeCode::Ref:
145
72.3k
  case TypeCode::RefNull:
146
113k
  case TypeCode::V128:
147
113k
    return LLVM::Type::getVectorType(LLContext.getInt64Ty(), 2);
148
34.2k
  case TypeCode::F32:
149
34.2k
    return LLContext.getFloatTy();
150
118k
  case TypeCode::F64:
151
118k
    return LLContext.getDoubleTy();
152
0
  default:
153
0
    assumingUnreachable();
154
532k
  }
155
532k
}
156
157
LLVM::Type toLLVMType(LLVM::Context LLContext,
158
1.41k
                      const AddressType AddrType) noexcept {
159
1.41k
  switch (AddrType) {
160
1.29k
  case AddressType::I32:
161
1.29k
    return LLContext.getInt32Ty();
162
120
  case AddressType::I64:
163
120
    return LLContext.getInt64Ty();
164
0
  default:
165
0
    assumingUnreachable();
166
1.41k
  }
167
1.41k
}
168
169
std::vector<LLVM::Type>
170
toLLVMTypeVector(LLVM::Context LLContext,
171
20.8k
                 Span<const ValType> ValTypes) noexcept {
172
20.8k
  std::vector<LLVM::Type> Result;
173
20.8k
  Result.reserve(ValTypes.size());
174
20.8k
  for (const auto &Type : ValTypes) {
175
20.1k
    Result.push_back(toLLVMType(LLContext, Type));
176
20.1k
  }
177
20.8k
  return Result;
178
20.8k
}
179
180
std::vector<LLVM::Type> toLLVMArgsType(LLVM::Context LLContext,
181
                                       LLVM::Type ExecCtxPtrTy,
182
16.6k
                                       Span<const ValType> ValTypes) noexcept {
183
16.6k
  auto Result = toLLVMTypeVector(LLContext, ValTypes);
184
16.6k
  Result.insert(Result.begin(), ExecCtxPtrTy);
185
16.6k
  return Result;
186
16.6k
}
187
188
LLVM::Type toLLVMRetsType(LLVM::Context LLContext,
189
16.6k
                          Span<const ValType> ValTypes) noexcept {
190
16.6k
  if (isVoidReturn(ValTypes)) {
191
4.32k
    return LLContext.getVoidTy();
192
4.32k
  }
193
12.3k
  if (ValTypes.size() == 1) {
194
11.6k
    return toLLVMType(LLContext, ValTypes.front());
195
11.6k
  }
196
696
  std::vector<LLVM::Type> Result;
197
696
  Result.reserve(ValTypes.size());
198
1.88k
  for (const auto &Type : ValTypes) {
199
1.88k
    Result.push_back(toLLVMType(LLContext, Type));
200
1.88k
  }
201
696
  return LLVM::Type::getStructType(Result);
202
12.3k
}
203
204
LLVM::Type toLLVMType(LLVM::Context LLContext, LLVM::Type ExecCtxPtrTy,
205
16.6k
                      const AST::FunctionType &FuncType) noexcept {
206
16.6k
  auto ArgsTy =
207
16.6k
      toLLVMArgsType(LLContext, ExecCtxPtrTy, FuncType.getParamTypes());
208
16.6k
  auto RetTy = toLLVMRetsType(LLContext, FuncType.getReturnTypes());
209
16.6k
  return LLVM::Type::getFunctionType(RetTy, ArgsTy);
210
16.6k
}
211
212
LLVM::Value toLLVMConstantZero(
213
    LLVM::Context LLContext, const ValType &ValType,
214
497k
    Span<const AST::CompositeType *const> CompositeTypes) noexcept {
215
497k
  switch (ValType.getCode()) {
216
38.1k
  case TypeCode::I32:
217
38.1k
    return LLVM::Value::getConstNull(LLContext.getInt32Ty());
218
205k
  case TypeCode::I64:
219
205k
    return LLVM::Value::getConstNull(LLContext.getInt64Ty());
220
683
  case TypeCode::Ref:
221
71.4k
  case TypeCode::RefNull: {
222
71.4k
    std::array<uint8_t, 16> Data{};
223
71.4k
    if (ValType.isAbsHeapType()) {
224
      // Abstract heap types are already fine for null refs.
225
23.2k
      const auto Raw = ValType.getRawData();
226
23.2k
      std::copy(Raw.begin(), Raw.end(), Data.begin());
227
48.2k
    } else {
228
      // For non-abstract heap types (concrete type indices), convert to the
229
      // abstract heap type so that ref.cast/ref.test won't dereference a null
230
      // pointer when checking the type.
231
48.2k
      assuming(ValType.getTypeIndex() < CompositeTypes.size());
232
48.2k
      const auto *CompType = CompositeTypes[ValType.getTypeIndex()];
233
48.2k
      assuming(CompType != nullptr);
234
48.2k
      WasmEdge::ValType VType =
235
48.2k
          CompType->isFunc() ? TypeCode::NullFuncRef : TypeCode::NullRef;
236
48.2k
      std::copy_n(VType.getRawData().cbegin(), 8, Data.begin());
237
48.2k
    }
238
71.4k
    return LLVM::Value::getConstVector8(LLContext, Data);
239
71.4k
  }
240
34.8k
  case TypeCode::V128:
241
34.8k
    return LLVM::Value::getConstNull(
242
34.8k
        LLVM::Type::getVectorType(LLContext.getInt64Ty(), 2));
243
31.6k
  case TypeCode::F32:
244
31.6k
    return LLVM::Value::getConstNull(LLContext.getFloatTy());
245
115k
  case TypeCode::F64:
246
115k
    return LLVM::Value::getConstNull(LLContext.getDoubleTy());
247
0
  default:
248
0
    assumingUnreachable();
249
497k
  }
250
497k
}
251
252
std::vector<LLVM::Value> unpackStruct(LLVM::Builder &Builder,
253
440
                                      LLVM::Value Struct) noexcept {
254
440
  const auto N = Struct.getType().getStructNumElements();
255
440
  std::vector<LLVM::Value> Ret;
256
440
  Ret.reserve(N);
257
1.59k
  for (unsigned I = 0; I < N; ++I) {
258
1.15k
    Ret.push_back(Builder.createExtractValue(Struct, I));
259
1.15k
  }
260
440
  return Ret;
261
440
}
262
263
} // namespace WasmEdge::LLVM