/src/WasmEdge/lib/llvm/compiler/numericInstr.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | #include "compiler/function_compiler.h" |
5 | | |
6 | | #include <limits> |
7 | | |
8 | | namespace WasmEdge { |
9 | | |
10 | | Expect<void> |
11 | 143k | FunctionCompiler::compileNumericOp(const AST::Instruction &Instr) noexcept { |
12 | 143k | switch (Instr.getOpCode()) { |
13 | 7.19k | case OpCode::I32__eqz: |
14 | 7.19k | stackPush(Builder.createZExt( |
15 | 7.19k | Builder.createICmpEQ(stackPop(), LLContext.getInt32(0)), |
16 | 7.19k | Context.Int32Ty)); |
17 | 7.19k | break; |
18 | 1.24k | case OpCode::I64__eqz: |
19 | 1.24k | stackPush(Builder.createZExt( |
20 | 1.24k | Builder.createICmpEQ(stackPop(), LLContext.getInt64(0)), |
21 | 1.24k | Context.Int32Ty)); |
22 | 1.24k | break; |
23 | 1.97k | case OpCode::I32__clz: |
24 | 1.97k | assuming(LLVM::Core::Ctlz != LLVM::Core::NotIntrinsic); |
25 | 1.97k | stackPush(Builder.createIntrinsic(LLVM::Core::Ctlz, {Context.Int32Ty}, |
26 | 1.97k | {stackPop(), LLContext.getFalse()})); |
27 | 1.97k | break; |
28 | 360 | case OpCode::I64__clz: |
29 | 360 | assuming(LLVM::Core::Ctlz != LLVM::Core::NotIntrinsic); |
30 | 360 | stackPush(Builder.createIntrinsic(LLVM::Core::Ctlz, {Context.Int64Ty}, |
31 | 360 | {stackPop(), LLContext.getFalse()})); |
32 | 360 | break; |
33 | 1.99k | case OpCode::I32__ctz: |
34 | 1.99k | assuming(LLVM::Core::Cttz != LLVM::Core::NotIntrinsic); |
35 | 1.99k | stackPush(Builder.createIntrinsic(LLVM::Core::Cttz, {Context.Int32Ty}, |
36 | 1.99k | {stackPop(), LLContext.getFalse()})); |
37 | 1.99k | break; |
38 | 546 | case OpCode::I64__ctz: |
39 | 546 | assuming(LLVM::Core::Cttz != LLVM::Core::NotIntrinsic); |
40 | 546 | stackPush(Builder.createIntrinsic(LLVM::Core::Cttz, {Context.Int64Ty}, |
41 | 546 | {stackPop(), LLContext.getFalse()})); |
42 | 546 | break; |
43 | 15.5k | case OpCode::I32__popcnt: |
44 | 17.4k | case OpCode::I64__popcnt: |
45 | 17.4k | assuming(LLVM::Core::Ctpop != LLVM::Core::NotIntrinsic); |
46 | 17.4k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Ctpop, stackPop())); |
47 | 17.4k | break; |
48 | 842 | case OpCode::F32__abs: |
49 | 1.41k | case OpCode::F64__abs: |
50 | 1.41k | assuming(LLVM::Core::Fabs != LLVM::Core::NotIntrinsic); |
51 | 1.41k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Fabs, stackPop())); |
52 | 1.41k | break; |
53 | 862 | case OpCode::F32__neg: |
54 | 1.46k | case OpCode::F64__neg: |
55 | 1.46k | stackPush(Builder.createFNeg(stackPop())); |
56 | 1.46k | break; |
57 | 1.48k | case OpCode::F32__ceil: |
58 | 3.71k | case OpCode::F64__ceil: |
59 | 3.71k | assuming(LLVM::Core::Ceil != LLVM::Core::NotIntrinsic); |
60 | 3.71k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Ceil, stackPop())); |
61 | 3.71k | break; |
62 | 690 | case OpCode::F32__floor: |
63 | 1.07k | case OpCode::F64__floor: |
64 | 1.07k | assuming(LLVM::Core::Floor != LLVM::Core::NotIntrinsic); |
65 | 1.07k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Floor, stackPop())); |
66 | 1.07k | break; |
67 | 554 | case OpCode::F32__trunc: |
68 | 842 | case OpCode::F64__trunc: |
69 | 842 | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
70 | 842 | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Trunc, stackPop())); |
71 | 842 | break; |
72 | 660 | case OpCode::F32__nearest: |
73 | 1.03k | case OpCode::F64__nearest: { |
74 | 1.03k | const bool IsFloat = Instr.getOpCode() == OpCode::F32__nearest; |
75 | 1.03k | LLVM::Value Value = stackPop(); |
76 | | |
77 | | #if LLVM_VERSION_MAJOR < 12 || defined(__s390x__) |
78 | | #else |
79 | 1.03k | assuming(LLVM::Core::Roundeven != LLVM::Core::NotIntrinsic); |
80 | 1.03k | if (LLVM::Core::Roundeven != LLVM::Core::NotIntrinsic) { |
81 | 1.03k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Roundeven, Value)); |
82 | 1.03k | break; |
83 | 1.03k | } |
84 | 0 | #endif |
85 | | |
86 | | // The VectorSize is only used when SSE4_1 or NEON is supported. |
87 | 0 | [[maybe_unused]] const uint32_t VectorSize = IsFloat ? 4 : 2; |
88 | 0 | #if defined(__x86_64__) |
89 | 0 | if (Context.SupportSSE4_1) { |
90 | 0 | auto Zero = LLContext.getInt64(0); |
91 | 0 | auto VectorTy = LLVM::Type::getVectorType(Value.getType(), VectorSize); |
92 | 0 | LLVM::Value Ret = LLVM::Value::getUndef(VectorTy); |
93 | 0 | Ret = Builder.createInsertElement(Ret, Value, Zero); |
94 | 0 | auto ID = |
95 | 0 | IsFloat ? LLVM::Core::X86SSE41RoundSs : LLVM::Core::X86SSE41RoundSd; |
96 | 0 | assuming(ID != LLVM::Core::NotIntrinsic); |
97 | 0 | Ret = Builder.createIntrinsic(ID, {}, {Ret, Ret, LLContext.getInt32(8)}); |
98 | 0 | Ret = Builder.createExtractElement(Ret, Zero); |
99 | 0 | stackPush(Ret); |
100 | 0 | break; |
101 | 0 | } |
102 | 0 | #endif |
103 | | |
104 | | #if defined(__aarch64__) |
105 | | if (Context.SupportNEON && |
106 | | LLVM::Core::AArch64NeonFRIntN != LLVM::Core::NotIntrinsic) { |
107 | | auto Zero = LLContext.getInt64(0); |
108 | | auto VectorTy = LLVM::Type::getVectorType(Value.getType(), VectorSize); |
109 | | LLVM::Value Ret = LLVM::Value::getUndef(VectorTy); |
110 | | Ret = Builder.createInsertElement(Ret, Value, Zero); |
111 | | Ret = Builder.createUnaryIntrinsic(LLVM::Core::AArch64NeonFRIntN, Ret); |
112 | | Ret = Builder.createExtractElement(Ret, Zero); |
113 | | stackPush(Ret); |
114 | | break; |
115 | | } |
116 | | #endif |
117 | | |
118 | | // Fallback case. |
119 | | // If the SSE4.1 is not supported on the x86_64 platform or |
120 | | // the NEON is not supported on the aarch64 platform, |
121 | | // then fallback to this. |
122 | 0 | assuming(LLVM::Core::Nearbyint != LLVM::Core::NotIntrinsic); |
123 | 0 | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Nearbyint, Value)); |
124 | 0 | break; |
125 | 0 | } |
126 | 395 | case OpCode::F32__sqrt: |
127 | 1.58k | case OpCode::F64__sqrt: |
128 | 1.58k | assuming(LLVM::Core::Sqrt != LLVM::Core::NotIntrinsic); |
129 | 1.58k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Sqrt, stackPop())); |
130 | 1.58k | break; |
131 | 337 | case OpCode::I32__wrap_i64: |
132 | 337 | stackPush(Builder.createTrunc(stackPop(), Context.Int32Ty)); |
133 | 337 | break; |
134 | 1.26k | case OpCode::I32__trunc_f32_s: |
135 | 1.26k | compileSignedTrunc(Context.Int32Ty); |
136 | 1.26k | break; |
137 | 277 | case OpCode::I32__trunc_f64_s: |
138 | 277 | compileSignedTrunc(Context.Int32Ty); |
139 | 277 | break; |
140 | 174 | case OpCode::I32__trunc_f32_u: |
141 | 174 | compileUnsignedTrunc(Context.Int32Ty); |
142 | 174 | break; |
143 | 1.06k | case OpCode::I32__trunc_f64_u: |
144 | 1.06k | compileUnsignedTrunc(Context.Int32Ty); |
145 | 1.06k | break; |
146 | 2.07k | case OpCode::I64__extend_i32_s: |
147 | 2.07k | stackPush(Builder.createSExt(stackPop(), Context.Int64Ty)); |
148 | 2.07k | break; |
149 | 432 | case OpCode::I64__extend_i32_u: |
150 | 432 | stackPush(Builder.createZExt(stackPop(), Context.Int64Ty)); |
151 | 432 | break; |
152 | 57 | case OpCode::I64__trunc_f32_s: |
153 | 57 | compileSignedTrunc(Context.Int64Ty); |
154 | 57 | break; |
155 | 348 | case OpCode::I64__trunc_f64_s: |
156 | 348 | compileSignedTrunc(Context.Int64Ty); |
157 | 348 | break; |
158 | 1.10k | case OpCode::I64__trunc_f32_u: |
159 | 1.10k | compileUnsignedTrunc(Context.Int64Ty); |
160 | 1.10k | break; |
161 | 1.55k | case OpCode::I64__trunc_f64_u: |
162 | 1.55k | compileUnsignedTrunc(Context.Int64Ty); |
163 | 1.55k | break; |
164 | 1.57k | case OpCode::F32__convert_i32_s: |
165 | 1.96k | case OpCode::F32__convert_i64_s: |
166 | 1.96k | stackPush(Builder.createSIToFP(stackPop(), Context.FloatTy)); |
167 | 1.96k | break; |
168 | 618 | case OpCode::F32__convert_i32_u: |
169 | 1.88k | case OpCode::F32__convert_i64_u: |
170 | 1.88k | stackPush(Builder.createUIToFP(stackPop(), Context.FloatTy)); |
171 | 1.88k | break; |
172 | 1.37k | case OpCode::F64__convert_i32_s: |
173 | 5.32k | case OpCode::F64__convert_i64_s: |
174 | 5.32k | stackPush(Builder.createSIToFP(stackPop(), Context.DoubleTy)); |
175 | 5.32k | break; |
176 | 2.13k | case OpCode::F64__convert_i32_u: |
177 | 2.30k | case OpCode::F64__convert_i64_u: |
178 | 2.30k | stackPush(Builder.createUIToFP(stackPop(), Context.DoubleTy)); |
179 | 2.30k | break; |
180 | 189 | case OpCode::F32__demote_f64: |
181 | 189 | stackPush(Builder.createFPTrunc(stackPop(), Context.FloatTy)); |
182 | 189 | break; |
183 | 86 | case OpCode::F64__promote_f32: |
184 | 86 | stackPush(Builder.createFPExt(stackPop(), Context.DoubleTy)); |
185 | 86 | break; |
186 | 568 | case OpCode::I32__reinterpret_f32: |
187 | 568 | stackPush(Builder.createBitCast(stackPop(), Context.Int32Ty)); |
188 | 568 | break; |
189 | 614 | case OpCode::I64__reinterpret_f64: |
190 | 614 | stackPush(Builder.createBitCast(stackPop(), Context.Int64Ty)); |
191 | 614 | break; |
192 | 4.03k | case OpCode::F32__reinterpret_i32: |
193 | 4.03k | stackPush(Builder.createBitCast(stackPop(), Context.FloatTy)); |
194 | 4.03k | break; |
195 | 1.12k | case OpCode::F64__reinterpret_i64: |
196 | 1.12k | stackPush(Builder.createBitCast(stackPop(), Context.DoubleTy)); |
197 | 1.12k | break; |
198 | 2.60k | case OpCode::I32__extend8_s: |
199 | 2.60k | stackPush(Builder.createSExt( |
200 | 2.60k | Builder.createTrunc(stackPop(), Context.Int8Ty), Context.Int32Ty)); |
201 | 2.60k | break; |
202 | 2.75k | case OpCode::I32__extend16_s: |
203 | 2.75k | stackPush(Builder.createSExt( |
204 | 2.75k | Builder.createTrunc(stackPop(), Context.Int16Ty), Context.Int32Ty)); |
205 | 2.75k | break; |
206 | 360 | case OpCode::I64__extend8_s: |
207 | 360 | stackPush(Builder.createSExt( |
208 | 360 | Builder.createTrunc(stackPop(), Context.Int8Ty), Context.Int64Ty)); |
209 | 360 | break; |
210 | 614 | case OpCode::I64__extend16_s: |
211 | 614 | stackPush(Builder.createSExt( |
212 | 614 | Builder.createTrunc(stackPop(), Context.Int16Ty), Context.Int64Ty)); |
213 | 614 | break; |
214 | 616 | case OpCode::I64__extend32_s: |
215 | 616 | stackPush(Builder.createSExt( |
216 | 616 | Builder.createTrunc(stackPop(), Context.Int32Ty), Context.Int64Ty)); |
217 | 616 | break; |
218 | | |
219 | | // Binary Numeric Instructions |
220 | 1.11k | case OpCode::I32__eq: |
221 | 1.34k | case OpCode::I64__eq: { |
222 | 1.34k | LLVM::Value RHS = stackPop(); |
223 | 1.34k | LLVM::Value LHS = stackPop(); |
224 | 1.34k | stackPush( |
225 | 1.34k | Builder.createZExt(Builder.createICmpEQ(LHS, RHS), Context.Int32Ty)); |
226 | 1.34k | break; |
227 | 1.11k | } |
228 | 715 | case OpCode::I32__ne: |
229 | 733 | case OpCode::I64__ne: { |
230 | 733 | LLVM::Value RHS = stackPop(); |
231 | 733 | LLVM::Value LHS = stackPop(); |
232 | 733 | stackPush( |
233 | 733 | Builder.createZExt(Builder.createICmpNE(LHS, RHS), Context.Int32Ty)); |
234 | 733 | break; |
235 | 715 | } |
236 | 4.35k | case OpCode::I32__lt_s: |
237 | 4.86k | case OpCode::I64__lt_s: { |
238 | 4.86k | LLVM::Value RHS = stackPop(); |
239 | 4.86k | LLVM::Value LHS = stackPop(); |
240 | 4.86k | stackPush( |
241 | 4.86k | Builder.createZExt(Builder.createICmpSLT(LHS, RHS), Context.Int32Ty)); |
242 | 4.86k | break; |
243 | 4.35k | } |
244 | 5.83k | case OpCode::I32__lt_u: |
245 | 6.18k | case OpCode::I64__lt_u: { |
246 | 6.18k | LLVM::Value RHS = stackPop(); |
247 | 6.18k | LLVM::Value LHS = stackPop(); |
248 | 6.18k | stackPush( |
249 | 6.18k | Builder.createZExt(Builder.createICmpULT(LHS, RHS), Context.Int32Ty)); |
250 | 6.18k | break; |
251 | 5.83k | } |
252 | 847 | case OpCode::I32__gt_s: |
253 | 1.36k | case OpCode::I64__gt_s: { |
254 | 1.36k | LLVM::Value RHS = stackPop(); |
255 | 1.36k | LLVM::Value LHS = stackPop(); |
256 | 1.36k | stackPush( |
257 | 1.36k | Builder.createZExt(Builder.createICmpSGT(LHS, RHS), Context.Int32Ty)); |
258 | 1.36k | break; |
259 | 847 | } |
260 | 5.52k | case OpCode::I32__gt_u: |
261 | 5.71k | case OpCode::I64__gt_u: { |
262 | 5.71k | LLVM::Value RHS = stackPop(); |
263 | 5.71k | LLVM::Value LHS = stackPop(); |
264 | 5.71k | stackPush( |
265 | 5.71k | Builder.createZExt(Builder.createICmpUGT(LHS, RHS), Context.Int32Ty)); |
266 | 5.71k | break; |
267 | 5.52k | } |
268 | 1.84k | case OpCode::I32__le_s: |
269 | 2.71k | case OpCode::I64__le_s: { |
270 | 2.71k | LLVM::Value RHS = stackPop(); |
271 | 2.71k | LLVM::Value LHS = stackPop(); |
272 | 2.71k | stackPush( |
273 | 2.71k | Builder.createZExt(Builder.createICmpSLE(LHS, RHS), Context.Int32Ty)); |
274 | 2.71k | break; |
275 | 1.84k | } |
276 | 484 | case OpCode::I32__le_u: |
277 | 1.78k | case OpCode::I64__le_u: { |
278 | 1.78k | LLVM::Value RHS = stackPop(); |
279 | 1.78k | LLVM::Value LHS = stackPop(); |
280 | 1.78k | stackPush( |
281 | 1.78k | Builder.createZExt(Builder.createICmpULE(LHS, RHS), Context.Int32Ty)); |
282 | 1.78k | break; |
283 | 484 | } |
284 | 1.00k | case OpCode::I32__ge_s: |
285 | 1.03k | case OpCode::I64__ge_s: { |
286 | 1.03k | LLVM::Value RHS = stackPop(); |
287 | 1.03k | LLVM::Value LHS = stackPop(); |
288 | 1.03k | stackPush( |
289 | 1.03k | Builder.createZExt(Builder.createICmpSGE(LHS, RHS), Context.Int32Ty)); |
290 | 1.03k | break; |
291 | 1.00k | } |
292 | 980 | case OpCode::I32__ge_u: |
293 | 1.73k | case OpCode::I64__ge_u: { |
294 | 1.73k | LLVM::Value RHS = stackPop(); |
295 | 1.73k | LLVM::Value LHS = stackPop(); |
296 | 1.73k | stackPush( |
297 | 1.73k | Builder.createZExt(Builder.createICmpUGE(LHS, RHS), Context.Int32Ty)); |
298 | 1.73k | break; |
299 | 980 | } |
300 | 165 | case OpCode::F32__eq: |
301 | 219 | case OpCode::F64__eq: { |
302 | 219 | LLVM::Value RHS = stackPop(); |
303 | 219 | LLVM::Value LHS = stackPop(); |
304 | 219 | stackPush( |
305 | 219 | Builder.createZExt(Builder.createFCmpOEQ(LHS, RHS), Context.Int32Ty)); |
306 | 219 | break; |
307 | 165 | } |
308 | 80 | case OpCode::F32__ne: |
309 | 105 | case OpCode::F64__ne: { |
310 | 105 | LLVM::Value RHS = stackPop(); |
311 | 105 | LLVM::Value LHS = stackPop(); |
312 | 105 | stackPush( |
313 | 105 | Builder.createZExt(Builder.createFCmpUNE(LHS, RHS), Context.Int32Ty)); |
314 | 105 | break; |
315 | 80 | } |
316 | 172 | case OpCode::F32__lt: |
317 | 291 | case OpCode::F64__lt: { |
318 | 291 | LLVM::Value RHS = stackPop(); |
319 | 291 | LLVM::Value LHS = stackPop(); |
320 | 291 | stackPush( |
321 | 291 | Builder.createZExt(Builder.createFCmpOLT(LHS, RHS), Context.Int32Ty)); |
322 | 291 | break; |
323 | 172 | } |
324 | 127 | case OpCode::F32__gt: |
325 | 176 | case OpCode::F64__gt: { |
326 | 176 | LLVM::Value RHS = stackPop(); |
327 | 176 | LLVM::Value LHS = stackPop(); |
328 | 176 | stackPush( |
329 | 176 | Builder.createZExt(Builder.createFCmpOGT(LHS, RHS), Context.Int32Ty)); |
330 | 176 | break; |
331 | 127 | } |
332 | 78 | case OpCode::F32__le: |
333 | 184 | case OpCode::F64__le: { |
334 | 184 | LLVM::Value RHS = stackPop(); |
335 | 184 | LLVM::Value LHS = stackPop(); |
336 | 184 | stackPush( |
337 | 184 | Builder.createZExt(Builder.createFCmpOLE(LHS, RHS), Context.Int32Ty)); |
338 | 184 | break; |
339 | 78 | } |
340 | 212 | case OpCode::F32__ge: |
341 | 237 | case OpCode::F64__ge: { |
342 | 237 | LLVM::Value RHS = stackPop(); |
343 | 237 | LLVM::Value LHS = stackPop(); |
344 | 237 | stackPush( |
345 | 237 | Builder.createZExt(Builder.createFCmpOGE(LHS, RHS), Context.Int32Ty)); |
346 | 237 | break; |
347 | 212 | } |
348 | 725 | case OpCode::I32__add: |
349 | 1.16k | case OpCode::I64__add: { |
350 | 1.16k | LLVM::Value RHS = stackPop(); |
351 | 1.16k | LLVM::Value LHS = stackPop(); |
352 | 1.16k | stackPush(Builder.createAdd(LHS, RHS)); |
353 | 1.16k | break; |
354 | 725 | } |
355 | 1.91k | case OpCode::I32__sub: |
356 | 2.35k | case OpCode::I64__sub: { |
357 | 2.35k | LLVM::Value RHS = stackPop(); |
358 | 2.35k | LLVM::Value LHS = stackPop(); |
359 | | |
360 | 2.35k | stackPush(Builder.createSub(LHS, RHS)); |
361 | 2.35k | break; |
362 | 1.91k | } |
363 | 572 | case OpCode::I32__mul: |
364 | 1.04k | case OpCode::I64__mul: { |
365 | 1.04k | LLVM::Value RHS = stackPop(); |
366 | 1.04k | LLVM::Value LHS = stackPop(); |
367 | 1.04k | stackPush(Builder.createMul(LHS, RHS)); |
368 | 1.04k | break; |
369 | 572 | } |
370 | 1.16k | case OpCode::I32__div_s: |
371 | 1.50k | case OpCode::I64__div_s: { |
372 | 1.50k | LLVM::Value RHS = stackPop(); |
373 | 1.50k | LLVM::Value LHS = stackPop(); |
374 | 1.50k | if constexpr (LLVM::kForceDivCheck) { |
375 | 1.50k | const bool Is32 = Instr.getOpCode() == OpCode::I32__div_s; |
376 | 1.50k | LLVM::Value IntZero = |
377 | 1.50k | Is32 ? LLContext.getInt32(0) : LLContext.getInt64(0); |
378 | 1.50k | LLVM::Value IntMinusOne = |
379 | 1.50k | Is32 ? LLContext.getInt32(static_cast<uint32_t>(INT32_C(-1))) |
380 | 1.50k | : LLContext.getInt64(static_cast<uint64_t>(INT64_C(-1))); |
381 | 1.50k | LLVM::Value IntMin = Is32 ? LLContext.getInt32(static_cast<uint32_t>( |
382 | 1.16k | std::numeric_limits<int32_t>::min())) |
383 | 1.50k | : LLContext.getInt64(static_cast<uint64_t>( |
384 | 336 | std::numeric_limits<int64_t>::min())); |
385 | | |
386 | 1.50k | auto NoZeroBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.nozero"); |
387 | 1.50k | auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.ok"); |
388 | | |
389 | 1.50k | auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero)); |
390 | 1.50k | Builder.createCondBr(IsNotZero, NoZeroBB, |
391 | 1.50k | getTrapBB(ErrCode::Value::DivideByZero)); |
392 | | |
393 | 1.50k | Builder.positionAtEnd(NoZeroBB); |
394 | 1.50k | auto NotOverflow = Builder.createLikely( |
395 | 1.50k | Builder.createOr(Builder.createICmpNE(LHS, IntMin), |
396 | 1.50k | Builder.createICmpNE(RHS, IntMinusOne))); |
397 | 1.50k | Builder.createCondBr(NotOverflow, OkBB, |
398 | 1.50k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
399 | | |
400 | 1.50k | Builder.positionAtEnd(OkBB); |
401 | 1.50k | } |
402 | 1.50k | stackPush(Builder.createSDiv(LHS, RHS)); |
403 | 1.50k | break; |
404 | 1.16k | } |
405 | 2.96k | case OpCode::I32__div_u: |
406 | 3.28k | case OpCode::I64__div_u: { |
407 | 3.28k | LLVM::Value RHS = stackPop(); |
408 | 3.28k | LLVM::Value LHS = stackPop(); |
409 | 3.28k | if constexpr (LLVM::kForceDivCheck) { |
410 | 3.28k | const bool Is32 = Instr.getOpCode() == OpCode::I32__div_u; |
411 | 3.28k | LLVM::Value IntZero = |
412 | 3.28k | Is32 ? LLContext.getInt32(0) : LLContext.getInt64(0); |
413 | 3.28k | auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.ok"); |
414 | | |
415 | 3.28k | auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero)); |
416 | 3.28k | Builder.createCondBr(IsNotZero, OkBB, |
417 | 3.28k | getTrapBB(ErrCode::Value::DivideByZero)); |
418 | 3.28k | Builder.positionAtEnd(OkBB); |
419 | 3.28k | } |
420 | 3.28k | stackPush(Builder.createUDiv(LHS, RHS)); |
421 | 3.28k | break; |
422 | 2.96k | } |
423 | 1.12k | case OpCode::I32__rem_s: |
424 | 1.54k | case OpCode::I64__rem_s: { |
425 | 1.54k | LLVM::Value RHS = stackPop(); |
426 | 1.54k | LLVM::Value LHS = stackPop(); |
427 | | // handle INT32_MIN % -1 |
428 | 1.54k | const bool Is32 = Instr.getOpCode() == OpCode::I32__rem_s; |
429 | 1.54k | LLVM::Value IntMinusOne = |
430 | 1.54k | Is32 ? LLContext.getInt32(static_cast<uint32_t>(INT32_C(-1))) |
431 | 1.54k | : LLContext.getInt64(static_cast<uint64_t>(INT64_C(-1))); |
432 | 1.54k | LLVM::Value IntMin = |
433 | 1.54k | Is32 ? LLContext.getInt32( |
434 | 1.12k | static_cast<uint32_t>(std::numeric_limits<int32_t>::min())) |
435 | 1.54k | : LLContext.getInt64( |
436 | 427 | static_cast<uint64_t>(std::numeric_limits<int64_t>::min())); |
437 | 1.54k | LLVM::Value IntZero = Is32 ? LLContext.getInt32(0) : LLContext.getInt64(0); |
438 | | |
439 | 1.54k | auto NoOverflowBB = |
440 | 1.54k | LLVM::BasicBlock::create(LLContext, F.Fn, "no.overflow"); |
441 | 1.54k | auto EndBB = LLVM::BasicBlock::create(LLContext, F.Fn, "end.overflow"); |
442 | | |
443 | 1.54k | if constexpr (LLVM::kForceDivCheck) { |
444 | 1.54k | auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "rem.ok"); |
445 | | |
446 | 1.54k | auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero)); |
447 | 1.54k | Builder.createCondBr(IsNotZero, OkBB, |
448 | 1.54k | getTrapBB(ErrCode::Value::DivideByZero)); |
449 | 1.54k | Builder.positionAtEnd(OkBB); |
450 | 1.54k | } |
451 | | |
452 | 1.54k | auto CurrBB = Builder.getInsertBlock(); |
453 | | |
454 | 1.54k | auto NotOverflow = Builder.createLikely( |
455 | 1.54k | Builder.createOr(Builder.createICmpNE(LHS, IntMin), |
456 | 1.54k | Builder.createICmpNE(RHS, IntMinusOne))); |
457 | 1.54k | Builder.createCondBr(NotOverflow, NoOverflowBB, EndBB); |
458 | | |
459 | 1.54k | Builder.positionAtEnd(NoOverflowBB); |
460 | 1.54k | auto Ret1 = Builder.createSRem(LHS, RHS); |
461 | 1.54k | Builder.createBr(EndBB); |
462 | | |
463 | 1.54k | Builder.positionAtEnd(EndBB); |
464 | 1.54k | auto Ret = Builder.createPHI(Ret1.getType()); |
465 | 1.54k | Ret.addIncoming(Ret1, NoOverflowBB); |
466 | 1.54k | Ret.addIncoming(IntZero, CurrBB); |
467 | | |
468 | 1.54k | stackPush(Ret); |
469 | 1.54k | break; |
470 | 1.12k | } |
471 | 1.75k | case OpCode::I32__rem_u: |
472 | 2.48k | case OpCode::I64__rem_u: { |
473 | 2.48k | LLVM::Value RHS = stackPop(); |
474 | 2.48k | LLVM::Value LHS = stackPop(); |
475 | 2.48k | if constexpr (LLVM::kForceDivCheck) { |
476 | 2.48k | LLVM::Value IntZero = Instr.getOpCode() == OpCode::I32__rem_u |
477 | 2.48k | ? LLContext.getInt32(0) |
478 | 2.48k | : LLContext.getInt64(0); |
479 | 2.48k | auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "rem.ok"); |
480 | | |
481 | 2.48k | auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero)); |
482 | 2.48k | Builder.createCondBr(IsNotZero, OkBB, |
483 | 2.48k | getTrapBB(ErrCode::Value::DivideByZero)); |
484 | 2.48k | Builder.positionAtEnd(OkBB); |
485 | 2.48k | } |
486 | 2.48k | stackPush(Builder.createURem(LHS, RHS)); |
487 | 2.48k | break; |
488 | 1.75k | } |
489 | 530 | case OpCode::I32__and: |
490 | 1.86k | case OpCode::I64__and: { |
491 | 1.86k | LLVM::Value RHS = stackPop(); |
492 | 1.86k | LLVM::Value LHS = stackPop(); |
493 | 1.86k | stackPush(Builder.createAnd(LHS, RHS)); |
494 | 1.86k | break; |
495 | 530 | } |
496 | 899 | case OpCode::I32__or: |
497 | 1.22k | case OpCode::I64__or: { |
498 | 1.22k | LLVM::Value RHS = stackPop(); |
499 | 1.22k | LLVM::Value LHS = stackPop(); |
500 | 1.22k | stackPush(Builder.createOr(LHS, RHS)); |
501 | 1.22k | break; |
502 | 899 | } |
503 | 1.53k | case OpCode::I32__xor: |
504 | 2.14k | case OpCode::I64__xor: { |
505 | 2.14k | LLVM::Value RHS = stackPop(); |
506 | 2.14k | LLVM::Value LHS = stackPop(); |
507 | 2.14k | stackPush(Builder.createXor(LHS, RHS)); |
508 | 2.14k | break; |
509 | 1.53k | } |
510 | 1.49k | case OpCode::I32__shl: |
511 | 1.83k | case OpCode::I64__shl: { |
512 | 1.83k | LLVM::Value Mask = Instr.getOpCode() == OpCode::I32__shl |
513 | 1.83k | ? LLContext.getInt32(31) |
514 | 1.83k | : LLContext.getInt64(63); |
515 | 1.83k | LLVM::Value RHS = Builder.createAnd(stackPop(), Mask); |
516 | 1.83k | LLVM::Value LHS = stackPop(); |
517 | 1.83k | stackPush(Builder.createShl(LHS, RHS)); |
518 | 1.83k | break; |
519 | 1.49k | } |
520 | 1.58k | case OpCode::I32__shr_s: |
521 | 1.97k | case OpCode::I64__shr_s: { |
522 | 1.97k | LLVM::Value Mask = Instr.getOpCode() == OpCode::I32__shr_s |
523 | 1.97k | ? LLContext.getInt32(31) |
524 | 1.97k | : LLContext.getInt64(63); |
525 | 1.97k | LLVM::Value RHS = Builder.createAnd(stackPop(), Mask); |
526 | 1.97k | LLVM::Value LHS = stackPop(); |
527 | 1.97k | stackPush(Builder.createAShr(LHS, RHS)); |
528 | 1.97k | break; |
529 | 1.58k | } |
530 | 3.88k | case OpCode::I32__shr_u: |
531 | 4.16k | case OpCode::I64__shr_u: { |
532 | 4.16k | LLVM::Value Mask = Instr.getOpCode() == OpCode::I32__shr_u |
533 | 4.16k | ? LLContext.getInt32(31) |
534 | 4.16k | : LLContext.getInt64(63); |
535 | 4.16k | LLVM::Value RHS = Builder.createAnd(stackPop(), Mask); |
536 | 4.16k | LLVM::Value LHS = stackPop(); |
537 | 4.16k | stackPush(Builder.createLShr(LHS, RHS)); |
538 | 4.16k | break; |
539 | 3.88k | } |
540 | 2.44k | case OpCode::I32__rotl: { |
541 | 2.44k | LLVM::Value RHS = stackPop(); |
542 | 2.44k | LLVM::Value LHS = stackPop(); |
543 | 2.44k | assuming(LLVM::Core::FShl != LLVM::Core::NotIntrinsic); |
544 | 2.44k | stackPush(Builder.createIntrinsic(LLVM::Core::FShl, {Context.Int32Ty}, |
545 | 2.44k | {LHS, LHS, RHS})); |
546 | 2.44k | break; |
547 | 2.44k | } |
548 | 868 | case OpCode::I32__rotr: { |
549 | 868 | LLVM::Value RHS = stackPop(); |
550 | 868 | LLVM::Value LHS = stackPop(); |
551 | 868 | assuming(LLVM::Core::FShr != LLVM::Core::NotIntrinsic); |
552 | 868 | stackPush(Builder.createIntrinsic(LLVM::Core::FShr, {Context.Int32Ty}, |
553 | 868 | {LHS, LHS, RHS})); |
554 | 868 | break; |
555 | 868 | } |
556 | 897 | case OpCode::I64__rotl: { |
557 | 897 | LLVM::Value RHS = stackPop(); |
558 | 897 | LLVM::Value LHS = stackPop(); |
559 | 897 | assuming(LLVM::Core::FShl != LLVM::Core::NotIntrinsic); |
560 | 897 | stackPush(Builder.createIntrinsic(LLVM::Core::FShl, {Context.Int64Ty}, |
561 | 897 | {LHS, LHS, RHS})); |
562 | 897 | break; |
563 | 897 | } |
564 | 1.30k | case OpCode::I64__rotr: { |
565 | 1.30k | LLVM::Value RHS = stackPop(); |
566 | 1.30k | LLVM::Value LHS = stackPop(); |
567 | 1.30k | assuming(LLVM::Core::FShr != LLVM::Core::NotIntrinsic); |
568 | 1.30k | stackPush(Builder.createIntrinsic(LLVM::Core::FShr, {Context.Int64Ty}, |
569 | 1.30k | {LHS, LHS, RHS})); |
570 | 1.30k | break; |
571 | 1.30k | } |
572 | 285 | case OpCode::F32__add: |
573 | 633 | case OpCode::F64__add: { |
574 | 633 | LLVM::Value RHS = stackPop(); |
575 | 633 | LLVM::Value LHS = stackPop(); |
576 | 633 | stackPush(Builder.createFAdd(LHS, RHS)); |
577 | 633 | break; |
578 | 285 | } |
579 | 134 | case OpCode::F32__sub: |
580 | 435 | case OpCode::F64__sub: { |
581 | 435 | LLVM::Value RHS = stackPop(); |
582 | 435 | LLVM::Value LHS = stackPop(); |
583 | 435 | stackPush(Builder.createFSub(LHS, RHS)); |
584 | 435 | break; |
585 | 134 | } |
586 | 437 | case OpCode::F32__mul: |
587 | 563 | case OpCode::F64__mul: { |
588 | 563 | LLVM::Value RHS = stackPop(); |
589 | 563 | LLVM::Value LHS = stackPop(); |
590 | 563 | stackPush(Builder.createFMul(LHS, RHS)); |
591 | 563 | break; |
592 | 437 | } |
593 | 186 | case OpCode::F32__div: |
594 | 470 | case OpCode::F64__div: { |
595 | 470 | LLVM::Value RHS = stackPop(); |
596 | 470 | LLVM::Value LHS = stackPop(); |
597 | 470 | stackPush(Builder.createFDiv(LHS, RHS)); |
598 | 470 | break; |
599 | 186 | } |
600 | 283 | case OpCode::F32__min: |
601 | 533 | case OpCode::F64__min: { |
602 | 533 | LLVM::Value RHS = stackPop(); |
603 | 533 | LLVM::Value LHS = stackPop(); |
604 | 533 | auto FpTy = Instr.getOpCode() == OpCode::F32__min ? Context.FloatTy |
605 | 533 | : Context.DoubleTy; |
606 | 533 | auto IntTy = Instr.getOpCode() == OpCode::F32__min ? Context.Int32Ty |
607 | 533 | : Context.Int64Ty; |
608 | | |
609 | 533 | auto UEQ = Builder.createFCmpUEQ(LHS, RHS); |
610 | 533 | auto UNO = Builder.createFCmpUNO(LHS, RHS); |
611 | | |
612 | 533 | auto LHSInt = Builder.createBitCast(LHS, IntTy); |
613 | 533 | auto RHSInt = Builder.createBitCast(RHS, IntTy); |
614 | 533 | auto OrInt = Builder.createOr(LHSInt, RHSInt); |
615 | 533 | auto OrFp = Builder.createBitCast(OrInt, FpTy); |
616 | | |
617 | 533 | auto AddFp = Builder.createFAdd(LHS, RHS); |
618 | | |
619 | 533 | assuming(LLVM::Core::MinNum != LLVM::Core::NotIntrinsic); |
620 | 533 | auto MinFp = Builder.createIntrinsic(LLVM::Core::MinNum, {LHS.getType()}, |
621 | 533 | {LHS, RHS}); |
622 | | |
623 | 533 | auto Ret = Builder.createSelect(UEQ, Builder.createSelect(UNO, AddFp, OrFp), |
624 | 533 | MinFp); |
625 | 533 | stackPush(Ret); |
626 | 533 | break; |
627 | 533 | } |
628 | 338 | case OpCode::F32__max: |
629 | 806 | case OpCode::F64__max: { |
630 | 806 | LLVM::Value RHS = stackPop(); |
631 | 806 | LLVM::Value LHS = stackPop(); |
632 | 806 | auto FpTy = Instr.getOpCode() == OpCode::F32__max ? Context.FloatTy |
633 | 806 | : Context.DoubleTy; |
634 | 806 | auto IntTy = Instr.getOpCode() == OpCode::F32__max ? Context.Int32Ty |
635 | 806 | : Context.Int64Ty; |
636 | | |
637 | 806 | auto UEQ = Builder.createFCmpUEQ(LHS, RHS); |
638 | 806 | auto UNO = Builder.createFCmpUNO(LHS, RHS); |
639 | | |
640 | 806 | auto LHSInt = Builder.createBitCast(LHS, IntTy); |
641 | 806 | auto RHSInt = Builder.createBitCast(RHS, IntTy); |
642 | 806 | auto AndInt = Builder.createAnd(LHSInt, RHSInt); |
643 | 806 | auto AndFp = Builder.createBitCast(AndInt, FpTy); |
644 | | |
645 | 806 | auto AddFp = Builder.createFAdd(LHS, RHS); |
646 | | |
647 | 806 | assuming(LLVM::Core::MaxNum != LLVM::Core::NotIntrinsic); |
648 | 806 | auto MaxFp = Builder.createIntrinsic(LLVM::Core::MaxNum, {LHS.getType()}, |
649 | 806 | {LHS, RHS}); |
650 | | |
651 | 806 | auto Ret = Builder.createSelect( |
652 | 806 | UEQ, Builder.createSelect(UNO, AddFp, AndFp), MaxFp); |
653 | 806 | stackPush(Ret); |
654 | 806 | break; |
655 | 806 | } |
656 | 462 | case OpCode::F32__copysign: |
657 | 861 | case OpCode::F64__copysign: { |
658 | 861 | LLVM::Value RHS = stackPop(); |
659 | 861 | LLVM::Value LHS = stackPop(); |
660 | 861 | assuming(LLVM::Core::CopySign != LLVM::Core::NotIntrinsic); |
661 | 861 | stackPush(Builder.createIntrinsic(LLVM::Core::CopySign, {LHS.getType()}, |
662 | 861 | {LHS, RHS})); |
663 | 861 | break; |
664 | 861 | } |
665 | | |
666 | | // Saturating Truncation Numeric Instructions |
667 | 185 | case OpCode::I32__trunc_sat_f32_s: |
668 | 185 | compileSignedTruncSat(Context.Int32Ty); |
669 | 185 | break; |
670 | 97 | case OpCode::I32__trunc_sat_f32_u: |
671 | 97 | compileUnsignedTruncSat(Context.Int32Ty); |
672 | 97 | break; |
673 | 574 | case OpCode::I32__trunc_sat_f64_s: |
674 | 574 | compileSignedTruncSat(Context.Int32Ty); |
675 | 574 | break; |
676 | 522 | case OpCode::I32__trunc_sat_f64_u: |
677 | 522 | compileUnsignedTruncSat(Context.Int32Ty); |
678 | 522 | break; |
679 | 421 | case OpCode::I64__trunc_sat_f32_s: |
680 | 421 | compileSignedTruncSat(Context.Int64Ty); |
681 | 421 | break; |
682 | 363 | case OpCode::I64__trunc_sat_f32_u: |
683 | 363 | compileUnsignedTruncSat(Context.Int64Ty); |
684 | 363 | break; |
685 | 90 | case OpCode::I64__trunc_sat_f64_s: |
686 | 90 | compileSignedTruncSat(Context.Int64Ty); |
687 | 90 | break; |
688 | 311 | case OpCode::I64__trunc_sat_f64_u: |
689 | 311 | compileUnsignedTruncSat(Context.Int64Ty); |
690 | 311 | break; |
691 | | |
692 | | // Wide Arithmetic Instructions |
693 | 0 | case OpCode::I64__add128: { |
694 | 0 | LLVM::Value Shift = LLVM::Value::getConstInt(Context.Int128Ty, 64); |
695 | 0 | LLVM::Value RHSHi = Builder.createZExt(stackPop(), Context.Int128Ty); |
696 | 0 | LLVM::Value RHSLo = Builder.createZExt(stackPop(), Context.Int128Ty); |
697 | 0 | LLVM::Value LHSHi = Builder.createZExt(stackPop(), Context.Int128Ty); |
698 | 0 | LLVM::Value LHSLo = Builder.createZExt(stackPop(), Context.Int128Ty); |
699 | 0 | LLVM::Value RHS = Builder.createOr(RHSLo, Builder.createShl(RHSHi, Shift)); |
700 | 0 | LLVM::Value LHS = Builder.createOr(LHSLo, Builder.createShl(LHSHi, Shift)); |
701 | 0 | LLVM::Value Result = Builder.createAdd(LHS, RHS); |
702 | 0 | stackPush(Builder.createTrunc(Result, Context.Int64Ty)); |
703 | 0 | stackPush(Builder.createTrunc(Builder.createLShr(Result, Shift), |
704 | 0 | Context.Int64Ty)); |
705 | 0 | break; |
706 | 861 | } |
707 | 0 | case OpCode::I64__sub128: { |
708 | 0 | LLVM::Value Shift = LLVM::Value::getConstInt(Context.Int128Ty, 64); |
709 | 0 | LLVM::Value RHSHi = Builder.createZExt(stackPop(), Context.Int128Ty); |
710 | 0 | LLVM::Value RHSLo = Builder.createZExt(stackPop(), Context.Int128Ty); |
711 | 0 | LLVM::Value LHSHi = Builder.createZExt(stackPop(), Context.Int128Ty); |
712 | 0 | LLVM::Value LHSLo = Builder.createZExt(stackPop(), Context.Int128Ty); |
713 | 0 | LLVM::Value RHS = Builder.createOr(RHSLo, Builder.createShl(RHSHi, Shift)); |
714 | 0 | LLVM::Value LHS = Builder.createOr(LHSLo, Builder.createShl(LHSHi, Shift)); |
715 | 0 | LLVM::Value Result = Builder.createSub(LHS, RHS); |
716 | 0 | stackPush(Builder.createTrunc(Result, Context.Int64Ty)); |
717 | 0 | stackPush(Builder.createTrunc(Builder.createLShr(Result, Shift), |
718 | 0 | Context.Int64Ty)); |
719 | 0 | break; |
720 | 861 | } |
721 | 0 | case OpCode::I64__mul_wide_s: { |
722 | 0 | LLVM::Value RHS = Builder.createSExt(stackPop(), Context.Int128Ty); |
723 | 0 | LLVM::Value LHS = Builder.createSExt(stackPop(), Context.Int128Ty); |
724 | 0 | LLVM::Value Result = Builder.createMul(LHS, RHS); |
725 | 0 | stackPush(Builder.createTrunc(Result, Context.Int64Ty)); |
726 | 0 | stackPush(Builder.createTrunc( |
727 | 0 | Builder.createLShr(Result, |
728 | 0 | LLVM::Value::getConstInt(Context.Int128Ty, 64)), |
729 | 0 | Context.Int64Ty)); |
730 | 0 | break; |
731 | 861 | } |
732 | 0 | case OpCode::I64__mul_wide_u: { |
733 | 0 | LLVM::Value RHS = Builder.createZExt(stackPop(), Context.Int128Ty); |
734 | 0 | LLVM::Value LHS = Builder.createZExt(stackPop(), Context.Int128Ty); |
735 | 0 | LLVM::Value Result = Builder.createMul(LHS, RHS); |
736 | 0 | stackPush(Builder.createTrunc(Result, Context.Int64Ty)); |
737 | 0 | stackPush(Builder.createTrunc( |
738 | 0 | Builder.createLShr(Result, |
739 | 0 | LLVM::Value::getConstInt(Context.Int128Ty, 64)), |
740 | 0 | Context.Int64Ty)); |
741 | 0 | break; |
742 | 861 | } |
743 | | |
744 | | // SIMD Memory Instructions |
745 | 0 | default: |
746 | 0 | assumingUnreachable(); |
747 | 143k | } |
748 | 143k | return {}; |
749 | 143k | } |
750 | | |
751 | 1.94k | void FunctionCompiler::compileSignedTrunc(LLVM::Type IntType) noexcept { |
752 | 1.94k | auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.norm"); |
753 | 1.94k | auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.notmin"); |
754 | 1.94k | auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.notmax"); |
755 | 1.94k | auto Value = stackPop(); |
756 | 1.94k | const auto [Precise, MinFp, MaxFp] = |
757 | 1.94k | [IntType, Value]() -> std::tuple<bool, LLVM::Value, LLVM::Value> { |
758 | 1.94k | const auto BitWidth = IntType.getIntegerBitWidth(); |
759 | 1.94k | const auto [Min, Max] = [BitWidth]() -> std::tuple<int64_t, int64_t> { |
760 | 1.94k | switch (BitWidth) { |
761 | 1.53k | case 32: |
762 | 1.53k | return {std::numeric_limits<int32_t>::min(), |
763 | 1.53k | std::numeric_limits<int32_t>::max()}; |
764 | 405 | case 64: |
765 | 405 | return {std::numeric_limits<int64_t>::min(), |
766 | 405 | std::numeric_limits<int64_t>::max()}; |
767 | 0 | default: |
768 | 0 | assumingUnreachable(); |
769 | 1.94k | } |
770 | 1.94k | }(); |
771 | 1.94k | auto FPType = Value.getType(); |
772 | 1.94k | assuming(FPType.isFloatTy() || FPType.isDoubleTy()); |
773 | 1.94k | const auto FPWidth = FPType.getFPMantissaWidth(); |
774 | 1.94k | return {BitWidth <= FPWidth, LLVM::Value::getConstReal(FPType, Min), |
775 | 1.94k | LLVM::Value::getConstReal(FPType, Max)}; |
776 | 1.94k | }(); |
777 | | |
778 | 1.94k | auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value)); |
779 | 1.94k | Builder.createCondBr(IsNotNan, NormBB, |
780 | 1.94k | getTrapBB(ErrCode::Value::InvalidConvToInt)); |
781 | | |
782 | 1.94k | Builder.positionAtEnd(NormBB); |
783 | 1.94k | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
784 | 1.94k | auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value); |
785 | 1.94k | auto IsNotUnderflow = |
786 | 1.94k | Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp)); |
787 | 1.94k | Builder.createCondBr(IsNotUnderflow, NotMinBB, |
788 | 1.94k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
789 | | |
790 | 1.94k | Builder.positionAtEnd(NotMinBB); |
791 | 1.94k | auto IsNotOverflow = Builder.createLikely( |
792 | 1.94k | Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp)); |
793 | 1.94k | Builder.createCondBr(IsNotOverflow, NotMaxBB, |
794 | 1.94k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
795 | | |
796 | 1.94k | Builder.positionAtEnd(NotMaxBB); |
797 | 1.94k | stackPush(Builder.createFPToSI(Trunc, IntType)); |
798 | 1.94k | } |
799 | | |
800 | 1.27k | void FunctionCompiler::compileSignedTruncSat(LLVM::Type IntType) noexcept { |
801 | 1.27k | auto CurrBB = Builder.getInsertBlock(); |
802 | 1.27k | auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.norm"); |
803 | 1.27k | auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.notmin"); |
804 | 1.27k | auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.notmax"); |
805 | 1.27k | auto EndBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.end"); |
806 | 1.27k | auto Value = stackPop(); |
807 | 1.27k | const auto [Precise, MinInt, MaxInt, MinFp, MaxFp] = [IntType, Value]() |
808 | 1.27k | -> std::tuple<bool, uint64_t, uint64_t, LLVM::Value, LLVM::Value> { |
809 | 1.27k | const auto BitWidth = IntType.getIntegerBitWidth(); |
810 | 1.27k | const auto [Min, Max] = [BitWidth]() -> std::tuple<int64_t, int64_t> { |
811 | 1.27k | switch (BitWidth) { |
812 | 759 | case 32: |
813 | 759 | return {std::numeric_limits<int32_t>::min(), |
814 | 759 | std::numeric_limits<int32_t>::max()}; |
815 | 511 | case 64: |
816 | 511 | return {std::numeric_limits<int64_t>::min(), |
817 | 511 | std::numeric_limits<int64_t>::max()}; |
818 | 0 | default: |
819 | 0 | assumingUnreachable(); |
820 | 1.27k | } |
821 | 1.27k | }(); |
822 | 1.27k | auto FPType = Value.getType(); |
823 | 1.27k | assuming(FPType.isFloatTy() || FPType.isDoubleTy()); |
824 | 1.27k | const auto FPWidth = FPType.getFPMantissaWidth(); |
825 | 1.27k | return {BitWidth <= FPWidth, static_cast<uint64_t>(Min), |
826 | 1.27k | static_cast<uint64_t>(Max), LLVM::Value::getConstReal(FPType, Min), |
827 | 1.27k | LLVM::Value::getConstReal(FPType, Max)}; |
828 | 1.27k | }(); |
829 | | |
830 | 1.27k | auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value)); |
831 | 1.27k | Builder.createCondBr(IsNotNan, NormBB, EndBB); |
832 | | |
833 | 1.27k | Builder.positionAtEnd(NormBB); |
834 | 1.27k | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
835 | 1.27k | auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value); |
836 | 1.27k | auto IsNotUnderflow = |
837 | 1.27k | Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp)); |
838 | 1.27k | Builder.createCondBr(IsNotUnderflow, NotMinBB, EndBB); |
839 | | |
840 | 1.27k | Builder.positionAtEnd(NotMinBB); |
841 | 1.27k | auto IsNotOverflow = Builder.createLikely( |
842 | 1.27k | Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp)); |
843 | 1.27k | Builder.createCondBr(IsNotOverflow, NotMaxBB, EndBB); |
844 | | |
845 | 1.27k | Builder.positionAtEnd(NotMaxBB); |
846 | 1.27k | auto IntValue = Builder.createFPToSI(Trunc, IntType); |
847 | 1.27k | Builder.createBr(EndBB); |
848 | | |
849 | 1.27k | Builder.positionAtEnd(EndBB); |
850 | 1.27k | auto PHIRet = Builder.createPHI(IntType); |
851 | 1.27k | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, 0, true), CurrBB); |
852 | 1.27k | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MinInt, true), NormBB); |
853 | 1.27k | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MaxInt, true), NotMinBB); |
854 | 1.27k | PHIRet.addIncoming(IntValue, NotMaxBB); |
855 | | |
856 | 1.27k | stackPush(PHIRet); |
857 | 1.27k | } |
858 | | |
859 | 3.89k | void FunctionCompiler::compileUnsignedTrunc(LLVM::Type IntType) noexcept { |
860 | 3.89k | auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.norm"); |
861 | 3.89k | auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.notmin"); |
862 | 3.89k | auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.notmax"); |
863 | 3.89k | auto Value = stackPop(); |
864 | 3.89k | const auto [Precise, MinFp, MaxFp] = |
865 | 3.89k | [IntType, Value]() -> std::tuple<bool, LLVM::Value, LLVM::Value> { |
866 | 3.89k | const auto BitWidth = IntType.getIntegerBitWidth(); |
867 | 3.89k | const auto [Min, Max] = [BitWidth]() -> std::tuple<uint64_t, uint64_t> { |
868 | 3.89k | switch (BitWidth) { |
869 | 1.23k | case 32: |
870 | 1.23k | return {std::numeric_limits<uint32_t>::min(), |
871 | 1.23k | std::numeric_limits<uint32_t>::max()}; |
872 | 2.66k | case 64: |
873 | 2.66k | return {std::numeric_limits<uint64_t>::min(), |
874 | 2.66k | std::numeric_limits<uint64_t>::max()}; |
875 | 0 | default: |
876 | 0 | assumingUnreachable(); |
877 | 3.89k | } |
878 | 3.89k | }(); |
879 | 3.89k | auto FPType = Value.getType(); |
880 | 3.89k | assuming(FPType.isFloatTy() || FPType.isDoubleTy()); |
881 | 3.89k | const auto FPWidth = FPType.getFPMantissaWidth(); |
882 | 3.89k | return {BitWidth <= FPWidth, LLVM::Value::getConstReal(FPType, Min), |
883 | 3.89k | LLVM::Value::getConstReal(FPType, Max)}; |
884 | 3.89k | }(); |
885 | | |
886 | 3.89k | auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value)); |
887 | 3.89k | Builder.createCondBr(IsNotNan, NormBB, |
888 | 3.89k | getTrapBB(ErrCode::Value::InvalidConvToInt)); |
889 | | |
890 | 3.89k | Builder.positionAtEnd(NormBB); |
891 | 3.89k | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
892 | 3.89k | auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value); |
893 | 3.89k | auto IsNotUnderflow = |
894 | 3.89k | Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp)); |
895 | 3.89k | Builder.createCondBr(IsNotUnderflow, NotMinBB, |
896 | 3.89k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
897 | | |
898 | 3.89k | Builder.positionAtEnd(NotMinBB); |
899 | 3.89k | auto IsNotOverflow = Builder.createLikely( |
900 | 3.89k | Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp)); |
901 | 3.89k | Builder.createCondBr(IsNotOverflow, NotMaxBB, |
902 | 3.89k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
903 | | |
904 | 3.89k | Builder.positionAtEnd(NotMaxBB); |
905 | 3.89k | stackPush(Builder.createFPToUI(Trunc, IntType)); |
906 | 3.89k | } |
907 | | |
908 | 1.29k | void FunctionCompiler::compileUnsignedTruncSat(LLVM::Type IntType) noexcept { |
909 | 1.29k | auto CurrBB = Builder.getInsertBlock(); |
910 | 1.29k | auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.norm"); |
911 | 1.29k | auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.notmax"); |
912 | 1.29k | auto EndBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.end"); |
913 | 1.29k | auto Value = stackPop(); |
914 | 1.29k | const auto [Precise, MinInt, MaxInt, MinFp, MaxFp] = [IntType, Value]() |
915 | 1.29k | -> std::tuple<bool, uint64_t, uint64_t, LLVM::Value, LLVM::Value> { |
916 | 1.29k | const auto BitWidth = IntType.getIntegerBitWidth(); |
917 | 1.29k | const auto [Min, Max] = [BitWidth]() -> std::tuple<uint64_t, uint64_t> { |
918 | 1.29k | switch (BitWidth) { |
919 | 619 | case 32: |
920 | 619 | return {std::numeric_limits<uint32_t>::min(), |
921 | 619 | std::numeric_limits<uint32_t>::max()}; |
922 | 674 | case 64: |
923 | 674 | return {std::numeric_limits<uint64_t>::min(), |
924 | 674 | std::numeric_limits<uint64_t>::max()}; |
925 | 0 | default: |
926 | 0 | assumingUnreachable(); |
927 | 1.29k | } |
928 | 1.29k | }(); |
929 | 1.29k | auto FPType = Value.getType(); |
930 | 1.29k | assuming(FPType.isFloatTy() || FPType.isDoubleTy()); |
931 | 1.29k | const auto FPWidth = FPType.getFPMantissaWidth(); |
932 | 1.29k | return {BitWidth <= FPWidth, Min, Max, |
933 | 1.29k | LLVM::Value::getConstReal(FPType, Min), |
934 | 1.29k | LLVM::Value::getConstReal(FPType, Max)}; |
935 | 1.29k | }(); |
936 | | |
937 | 1.29k | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
938 | 1.29k | auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value); |
939 | 1.29k | auto IsNotUnderflow = |
940 | 1.29k | Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp)); |
941 | 1.29k | Builder.createCondBr(IsNotUnderflow, NormBB, EndBB); |
942 | | |
943 | 1.29k | Builder.positionAtEnd(NormBB); |
944 | 1.29k | auto IsNotOverflow = Builder.createLikely( |
945 | 1.29k | Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp)); |
946 | 1.29k | Builder.createCondBr(IsNotOverflow, NotMaxBB, EndBB); |
947 | | |
948 | 1.29k | Builder.positionAtEnd(NotMaxBB); |
949 | 1.29k | auto IntValue = Builder.createFPToUI(Trunc, IntType); |
950 | 1.29k | Builder.createBr(EndBB); |
951 | | |
952 | 1.29k | Builder.positionAtEnd(EndBB); |
953 | 1.29k | auto PHIRet = Builder.createPHI(IntType); |
954 | 1.29k | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MinInt), CurrBB); |
955 | 1.29k | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MaxInt), NormBB); |
956 | 1.29k | PHIRet.addIncoming(IntValue, NotMaxBB); |
957 | | |
958 | 1.29k | stackPush(PHIRet); |
959 | 1.29k | } |
960 | | |
961 | | } // namespace WasmEdge |