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