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/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.32k
    : LLContext(C), LLModule(M),
21
2.32k
      Cold(LLVM::Attribute::createEnum(C, LLVM::Core::Cold, 0)),
22
2.32k
      NoAlias(LLVM::Attribute::createEnum(C, LLVM::Core::NoAlias, 0)),
23
2.32k
      NoInline(LLVM::Attribute::createEnum(C, LLVM::Core::NoInline, 0)),
24
2.32k
      NoReturn(LLVM::Attribute::createEnum(C, LLVM::Core::NoReturn, 0)),
25
2.32k
      ReadOnly(LLVM::Attribute::createEnum(C, LLVM::Core::ReadOnly, 0)),
26
2.32k
      StrictFP(LLVM::Attribute::createEnum(C, LLVM::Core::StrictFP, 0)),
27
2.32k
      UWTable(LLVM::Attribute::createEnum(C, LLVM::Core::UWTable,
28
2.32k
                                          LLVM::Core::UWTableDefault)),
29
      NoStackArgProbe(
30
2.32k
          LLVM::Attribute::createString(C, "no-stack-arg-probe"sv, {})),
31
2.32k
      VoidTy(LLContext.getVoidTy()), Int8Ty(LLContext.getInt8Ty()),
32
2.32k
      Int16Ty(LLContext.getInt16Ty()), Int32Ty(LLContext.getInt32Ty()),
33
2.32k
      Int64Ty(LLContext.getInt64Ty()), Int128Ty(LLContext.getInt128Ty()),
34
2.32k
      FloatTy(LLContext.getFloatTy()), DoubleTy(LLContext.getDoubleTy()),
35
2.32k
      Int8x16Ty(LLVM::Type::getVectorType(Int8Ty, 16)),
36
2.32k
      Int16x8Ty(LLVM::Type::getVectorType(Int16Ty, 8)),
37
2.32k
      Int32x4Ty(LLVM::Type::getVectorType(Int32Ty, 4)),
38
2.32k
      Floatx4Ty(LLVM::Type::getVectorType(FloatTy, 4)),
39
2.32k
      Int64x2Ty(LLVM::Type::getVectorType(Int64Ty, 2)),
40
2.32k
      Doublex2Ty(LLVM::Type::getVectorType(DoubleTy, 2)),
41
2.32k
      Int128x1Ty(LLVM::Type::getVectorType(Int128Ty, 1)),
42
2.32k
      Int8PtrTy(Int8Ty.getPointerTo()), Int32PtrTy(Int32Ty.getPointerTo()),
43
2.32k
      Int64PtrTy(Int64Ty.getPointerTo()), Int128PtrTy(Int128Ty.getPointerTo()),
44
2.32k
      Int8PtrPtrTy(Int8PtrTy.getPointerTo()),
45
2.32k
      ExecCtxTy(LLVM::Type::getStructType(
46
2.32k
          "ExecCtx",
47
2.32k
          std::initializer_list<LLVM::Type>{
48
              // MemoryPtrs
49
2.32k
              Int8PtrTy.getPointerTo(),
50
              // MemorySizes
51
2.32k
              Int64PtrTy.getPointerTo(),
52
              // TableRefs
53
2.32k
              Int64x2Ty.getPointerTo().getPointerTo(),
54
              // TableSizes
55
2.32k
              Int64PtrTy.getPointerTo(),
56
              // Globals
57
2.32k
              Int128PtrTy.getPointerTo(),
58
              // Tags
59
2.32k
              Int8PtrPtrTy,
60
              // PendingExnTagAddr
61
2.32k
              Int8PtrPtrTy,
62
              // InstrCount
63
2.32k
              Int64PtrTy,
64
              // CostTable
65
2.32k
              LLVM::Type::getArrayType(Int64Ty, UINT16_MAX + 1).getPointerTo(),
66
              // Gas
67
2.32k
              Int64PtrTy,
68
              // GasLimit
69
2.32k
              Int64Ty,
70
              // StopToken
71
2.32k
              Int32PtrTy,
72
              // ModuleInst
73
2.32k
              Int8PtrTy,
74
2.32k
          })),
75
2.32k
      ExecCtxPtrTy(ExecCtxTy.getPointerTo()),
76
2.32k
      IntrinsicsTableTy(LLVM::Type::getArrayType(
77
2.32k
          Int8Ty.getPointerTo(),
78
2.32k
          static_cast<uint32_t>(Executable::Intrinsics::kIntrinsicMax))),
79
2.32k
      IntrinsicsTablePtrTy(IntrinsicsTableTy.getPointerTo()),
80
2.32k
      IntrinsicsTable(LLModule.get().addGlobal(IntrinsicsTablePtrTy, true,
81
2.32k
                                               LLVMExternalLinkage,
82
2.32k
                                               LLVM::Value(), "intrinsics")) {
83
2.32k
  Trap.Ty = LLVM::Type::getFunctionType(VoidTy, {Int32Ty});
84
2.32k
  Trap.Fn = LLModule.get().addFunction(Trap.Ty, LLVMPrivateLinkage, "trap");
85
2.32k
  Trap.Fn.setDSOLocal(true);
86
2.32k
  Trap.Fn.addFnAttr(NoStackArgProbe);
87
2.32k
  Trap.Fn.addFnAttr(StrictFP);
88
2.32k
  Trap.Fn.addFnAttr(UWTable);
89
2.32k
  Trap.Fn.addFnAttr(NoReturn);
90
2.32k
  Trap.Fn.addFnAttr(Cold);
91
2.32k
  Trap.Fn.addFnAttr(NoInline);
92
93
2.32k
  if (!IsGenericBinary) {
94
2.32k
    SubtargetFeatures = LLVM::getHostCPUFeatures();
95
2.32k
    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
197k
        Feature = Features.substr(0, Pos);
100
197k
        Features = Features.substr(Pos + 1);
101
197k
      } else {
102
2.32k
        Feature = std::exchange(Features, std::string_view());
103
2.32k
      }
104
199k
      if (Feature[0] != '+') {
105
122k
        continue;
106
122k
      }
107
76.5k
      Feature = Feature.substr(1);
108
109
76.5k
#if defined(__x86_64__)
110
76.5k
      if (!SupportXOP && Feature == "xop"sv) {
111
0
        SupportXOP = true;
112
0
      }
113
76.5k
      if (!SupportSSE4_1 && Feature == "sse4.1"sv) {
114
2.32k
        SupportSSE4_1 = true;
115
2.32k
      }
116
76.5k
      if (!SupportSSSE3 && Feature == "ssse3"sv) {
117
2.32k
        SupportSSSE3 = true;
118
2.32k
      }
119
76.5k
      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.5k
    }
128
2.32k
  }
129
130
2.32k
  compileTrap();
131
2.32k
}
132
133
35.2k
bool isVoidReturn(Span<const ValType> ValTypes) noexcept {
134
35.2k
  return ValTypes.empty();
135
35.2k
}
136
137
LLVM::Type toLLVMType(LLVM::Context LLContext,
138
504k
                      const ValType &ValType) noexcept {
139
504k
  switch (ValType.getCode()) {
140
56.5k
  case TypeCode::I32:
141
56.5k
    return LLContext.getInt32Ty();
142
195k
  case TypeCode::I64:
143
195k
    return LLContext.getInt64Ty();
144
552
  case TypeCode::Ref:
145
58.3k
  case TypeCode::RefNull:
146
99.6k
  case TypeCode::V128:
147
99.6k
    return LLVM::Type::getVectorType(LLContext.getInt64Ty(), 2);
148
34.1k
  case TypeCode::F32:
149
34.1k
    return LLContext.getFloatTy();
150
118k
  case TypeCode::F64:
151
118k
    return LLContext.getDoubleTy();
152
0
  default:
153
0
    assumingUnreachable();
154
504k
  }
155
504k
}
156
157
LLVM::Type toLLVMType(LLVM::Context LLContext,
158
1.43k
                      const AddressType AddrType) noexcept {
159
1.43k
  switch (AddrType) {
160
1.31k
  case AddressType::I32:
161
1.31k
    return LLContext.getInt32Ty();
162
122
  case AddressType::I64:
163
122
    return LLContext.getInt64Ty();
164
0
  default:
165
0
    assumingUnreachable();
166
1.43k
  }
167
1.43k
}
168
169
std::vector<LLVM::Type>
170
toLLVMTypeVector(LLVM::Context LLContext,
171
20.5k
                 Span<const ValType> ValTypes) noexcept {
172
20.5k
  std::vector<LLVM::Type> Result;
173
20.5k
  Result.reserve(ValTypes.size());
174
20.5k
  for (const auto &Type : ValTypes) {
175
20.0k
    Result.push_back(toLLVMType(LLContext, Type));
176
20.0k
  }
177
20.5k
  return Result;
178
20.5k
}
179
180
std::vector<LLVM::Type> toLLVMArgsType(LLVM::Context LLContext,
181
                                       LLVM::Type ExecCtxPtrTy,
182
16.4k
                                       Span<const ValType> ValTypes) noexcept {
183
16.4k
  auto Result = toLLVMTypeVector(LLContext, ValTypes);
184
16.4k
  Result.insert(Result.begin(), ExecCtxPtrTy);
185
16.4k
  return Result;
186
16.4k
}
187
188
LLVM::Type toLLVMRetsType(LLVM::Context LLContext,
189
16.4k
                          Span<const ValType> ValTypes) noexcept {
190
16.4k
  if (isVoidReturn(ValTypes)) {
191
4.31k
    return LLContext.getVoidTy();
192
4.31k
  }
193
12.1k
  if (ValTypes.size() == 1) {
194
11.4k
    return toLLVMType(LLContext, ValTypes.front());
195
11.4k
  }
196
676
  std::vector<LLVM::Type> Result;
197
676
  Result.reserve(ValTypes.size());
198
1.84k
  for (const auto &Type : ValTypes) {
199
1.84k
    Result.push_back(toLLVMType(LLContext, Type));
200
1.84k
  }
201
676
  return LLVM::Type::getStructType(Result);
202
12.1k
}
203
204
LLVM::Type toLLVMType(LLVM::Context LLContext, LLVM::Type ExecCtxPtrTy,
205
16.4k
                      const AST::FunctionType &FuncType) noexcept {
206
16.4k
  auto ArgsTy =
207
16.4k
      toLLVMArgsType(LLContext, ExecCtxPtrTy, FuncType.getParamTypes());
208
16.4k
  auto RetTy = toLLVMRetsType(LLContext, FuncType.getReturnTypes());
209
16.4k
  return LLVM::Type::getFunctionType(RetTy, ArgsTy);
210
16.4k
}
211
212
LLVM::Value toLLVMConstantZero(
213
    LLVM::Context LLContext, const ValType &ValType,
214
469k
    Span<const AST::CompositeType *const> CompositeTypes) noexcept {
215
469k
  switch (ValType.getCode()) {
216
38.1k
  case TypeCode::I32:
217
38.1k
    return LLVM::Value::getConstNull(LLContext.getInt32Ty());
218
191k
  case TypeCode::I64:
219
191k
    return LLVM::Value::getConstNull(LLContext.getInt64Ty());
220
551
  case TypeCode::Ref:
221
57.5k
  case TypeCode::RefNull: {
222
57.5k
    std::array<uint8_t, 16> Data{};
223
57.5k
    if (ValType.isAbsHeapType()) {
224
      // Abstract heap types are already fine for null refs.
225
23.3k
      const auto Raw = ValType.getRawData();
226
23.3k
      std::copy(Raw.begin(), Raw.end(), Data.begin());
227
34.1k
    } 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
34.1k
      assuming(ValType.getTypeIndex() < CompositeTypes.size());
232
34.1k
      const auto *CompType = CompositeTypes[ValType.getTypeIndex()];
233
34.1k
      assuming(CompType != nullptr);
234
34.1k
      WasmEdge::ValType VType =
235
34.1k
          CompType->isFunc() ? TypeCode::NullFuncRef : TypeCode::NullRef;
236
34.1k
      std::copy_n(VType.getRawData().cbegin(), 8, Data.begin());
237
34.1k
    }
238
57.5k
    return LLVM::Value::getConstVector8(LLContext, Data);
239
57.5k
  }
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
469k
  }
250
469k
}
251
252
std::vector<LLVM::Value> unpackStruct(LLVM::Builder &Builder,
253
430
                                      LLVM::Value Struct) noexcept {
254
430
  const auto N = Struct.getType().getStructNumElements();
255
430
  std::vector<LLVM::Value> Ret;
256
430
  Ret.reserve(N);
257
1.56k
  for (unsigned I = 0; I < N; ++I) {
258
1.13k
    Ret.push_back(Builder.createExtractValue(Struct, I));
259
1.13k
  }
260
430
  return Ret;
261
430
}
262
263
} // namespace WasmEdge::LLVM