/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 |