/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 | 135k | FunctionCompiler::compileNumericOp(const AST::Instruction &Instr) noexcept { |
12 | 135k | switch (Instr.getOpCode()) { |
13 | 6.95k | case OpCode::I32__eqz: |
14 | 6.95k | stackPush(Builder.createZExt( |
15 | 6.95k | Builder.createICmpEQ(stackPop(), LLContext.getInt32(0)), |
16 | 6.95k | Context.Int32Ty)); |
17 | 6.95k | break; |
18 | 1.03k | case OpCode::I64__eqz: |
19 | 1.03k | stackPush(Builder.createZExt( |
20 | 1.03k | Builder.createICmpEQ(stackPop(), LLContext.getInt64(0)), |
21 | 1.03k | Context.Int32Ty)); |
22 | 1.03k | break; |
23 | 1.93k | case OpCode::I32__clz: |
24 | 1.93k | assuming(LLVM::Core::Ctlz != LLVM::Core::NotIntrinsic); |
25 | 1.93k | stackPush(Builder.createIntrinsic(LLVM::Core::Ctlz, {Context.Int32Ty}, |
26 | 1.93k | {stackPop(), LLContext.getFalse()})); |
27 | 1.93k | break; |
28 | 361 | case OpCode::I64__clz: |
29 | 361 | assuming(LLVM::Core::Ctlz != LLVM::Core::NotIntrinsic); |
30 | 361 | stackPush(Builder.createIntrinsic(LLVM::Core::Ctlz, {Context.Int64Ty}, |
31 | 361 | {stackPop(), LLContext.getFalse()})); |
32 | 361 | 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 | 544 | case OpCode::I64__ctz: |
39 | 544 | assuming(LLVM::Core::Cttz != LLVM::Core::NotIntrinsic); |
40 | 544 | stackPush(Builder.createIntrinsic(LLVM::Core::Cttz, {Context.Int64Ty}, |
41 | 544 | {stackPop(), LLContext.getFalse()})); |
42 | 544 | break; |
43 | 14.7k | case OpCode::I32__popcnt: |
44 | 16.6k | case OpCode::I64__popcnt: |
45 | 16.6k | assuming(LLVM::Core::Ctpop != LLVM::Core::NotIntrinsic); |
46 | 16.6k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Ctpop, stackPop())); |
47 | 16.6k | break; |
48 | 843 | 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 | 860 | case OpCode::F32__neg: |
54 | 1.45k | case OpCode::F64__neg: |
55 | 1.45k | stackPush(Builder.createFNeg(stackPop())); |
56 | 1.45k | break; |
57 | 1.49k | case OpCode::F32__ceil: |
58 | 3.69k | case OpCode::F64__ceil: |
59 | 3.69k | assuming(LLVM::Core::Ceil != LLVM::Core::NotIntrinsic); |
60 | 3.69k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Ceil, stackPop())); |
61 | 3.69k | break; |
62 | 691 | 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 | 661 | 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 | 391 | case OpCode::F32__sqrt: |
127 | 1.57k | case OpCode::F64__sqrt: |
128 | 1.57k | assuming(LLVM::Core::Sqrt != LLVM::Core::NotIntrinsic); |
129 | 1.57k | stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Sqrt, stackPop())); |
130 | 1.57k | break; |
131 | 328 | case OpCode::I32__wrap_i64: |
132 | 328 | stackPush(Builder.createTrunc(stackPop(), Context.Int32Ty)); |
133 | 328 | break; |
134 | 1.23k | case OpCode::I32__trunc_f32_s: |
135 | 1.23k | compileSignedTrunc(Context.Int32Ty); |
136 | 1.23k | break; |
137 | 266 | case OpCode::I32__trunc_f64_s: |
138 | 266 | compileSignedTrunc(Context.Int32Ty); |
139 | 266 | break; |
140 | 171 | case OpCode::I32__trunc_f32_u: |
141 | 171 | compileUnsignedTrunc(Context.Int32Ty); |
142 | 171 | break; |
143 | 990 | case OpCode::I32__trunc_f64_u: |
144 | 990 | compileUnsignedTrunc(Context.Int32Ty); |
145 | 990 | break; |
146 | 2.03k | case OpCode::I64__extend_i32_s: |
147 | 2.03k | stackPush(Builder.createSExt(stackPop(), Context.Int64Ty)); |
148 | 2.03k | break; |
149 | 419 | case OpCode::I64__extend_i32_u: |
150 | 419 | stackPush(Builder.createZExt(stackPop(), Context.Int64Ty)); |
151 | 419 | break; |
152 | 54 | case OpCode::I64__trunc_f32_s: |
153 | 54 | compileSignedTrunc(Context.Int64Ty); |
154 | 54 | break; |
155 | 346 | case OpCode::I64__trunc_f64_s: |
156 | 346 | compileSignedTrunc(Context.Int64Ty); |
157 | 346 | break; |
158 | 830 | case OpCode::I64__trunc_f32_u: |
159 | 830 | compileUnsignedTrunc(Context.Int64Ty); |
160 | 830 | break; |
161 | 1.12k | case OpCode::I64__trunc_f64_u: |
162 | 1.12k | compileUnsignedTrunc(Context.Int64Ty); |
163 | 1.12k | break; |
164 | 1.55k | case OpCode::F32__convert_i32_s: |
165 | 1.93k | case OpCode::F32__convert_i64_s: |
166 | 1.93k | stackPush(Builder.createSIToFP(stackPop(), Context.FloatTy)); |
167 | 1.93k | break; |
168 | 606 | case OpCode::F32__convert_i32_u: |
169 | 1.57k | case OpCode::F32__convert_i64_u: |
170 | 1.57k | stackPush(Builder.createUIToFP(stackPop(), Context.FloatTy)); |
171 | 1.57k | break; |
172 | 1.23k | case OpCode::F64__convert_i32_s: |
173 | 4.66k | case OpCode::F64__convert_i64_s: |
174 | 4.66k | stackPush(Builder.createSIToFP(stackPop(), Context.DoubleTy)); |
175 | 4.66k | break; |
176 | 1.67k | case OpCode::F64__convert_i32_u: |
177 | 1.83k | case OpCode::F64__convert_i64_u: |
178 | 1.83k | stackPush(Builder.createUIToFP(stackPop(), Context.DoubleTy)); |
179 | 1.83k | 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 | 537 | case OpCode::I32__reinterpret_f32: |
187 | 537 | stackPush(Builder.createBitCast(stackPop(), Context.Int32Ty)); |
188 | 537 | break; |
189 | 609 | case OpCode::I64__reinterpret_f64: |
190 | 609 | stackPush(Builder.createBitCast(stackPop(), Context.Int64Ty)); |
191 | 609 | break; |
192 | 3.93k | case OpCode::F32__reinterpret_i32: |
193 | 3.93k | stackPush(Builder.createBitCast(stackPop(), Context.FloatTy)); |
194 | 3.93k | break; |
195 | 1.12k | case OpCode::F64__reinterpret_i64: |
196 | 1.12k | stackPush(Builder.createBitCast(stackPop(), Context.DoubleTy)); |
197 | 1.12k | break; |
198 | 2.48k | case OpCode::I32__extend8_s: |
199 | 2.48k | stackPush(Builder.createSExt( |
200 | 2.48k | Builder.createTrunc(stackPop(), Context.Int8Ty), Context.Int32Ty)); |
201 | 2.48k | break; |
202 | 2.58k | case OpCode::I32__extend16_s: |
203 | 2.58k | stackPush(Builder.createSExt( |
204 | 2.58k | Builder.createTrunc(stackPop(), Context.Int16Ty), Context.Int32Ty)); |
205 | 2.58k | break; |
206 | 361 | case OpCode::I64__extend8_s: |
207 | 361 | stackPush(Builder.createSExt( |
208 | 361 | Builder.createTrunc(stackPop(), Context.Int8Ty), Context.Int64Ty)); |
209 | 361 | break; |
210 | 615 | case OpCode::I64__extend16_s: |
211 | 615 | stackPush(Builder.createSExt( |
212 | 615 | Builder.createTrunc(stackPop(), Context.Int16Ty), Context.Int64Ty)); |
213 | 615 | break; |
214 | 617 | case OpCode::I64__extend32_s: |
215 | 617 | stackPush(Builder.createSExt( |
216 | 617 | Builder.createTrunc(stackPop(), Context.Int32Ty), Context.Int64Ty)); |
217 | 617 | break; |
218 | | |
219 | | // Binary Numeric Instructions |
220 | 1.05k | case OpCode::I32__eq: |
221 | 1.25k | case OpCode::I64__eq: { |
222 | 1.25k | LLVM::Value RHS = stackPop(); |
223 | 1.25k | LLVM::Value LHS = stackPop(); |
224 | 1.25k | stackPush( |
225 | 1.25k | Builder.createZExt(Builder.createICmpEQ(LHS, RHS), Context.Int32Ty)); |
226 | 1.25k | break; |
227 | 1.05k | } |
228 | 693 | case OpCode::I32__ne: |
229 | 713 | case OpCode::I64__ne: { |
230 | 713 | LLVM::Value RHS = stackPop(); |
231 | 713 | LLVM::Value LHS = stackPop(); |
232 | 713 | stackPush( |
233 | 713 | Builder.createZExt(Builder.createICmpNE(LHS, RHS), Context.Int32Ty)); |
234 | 713 | break; |
235 | 693 | } |
236 | 4.27k | case OpCode::I32__lt_s: |
237 | 4.76k | case OpCode::I64__lt_s: { |
238 | 4.76k | LLVM::Value RHS = stackPop(); |
239 | 4.76k | LLVM::Value LHS = stackPop(); |
240 | 4.76k | stackPush( |
241 | 4.76k | Builder.createZExt(Builder.createICmpSLT(LHS, RHS), Context.Int32Ty)); |
242 | 4.76k | break; |
243 | 4.27k | } |
244 | 5.50k | case OpCode::I32__lt_u: |
245 | 5.85k | case OpCode::I64__lt_u: { |
246 | 5.85k | LLVM::Value RHS = stackPop(); |
247 | 5.85k | LLVM::Value LHS = stackPop(); |
248 | 5.85k | stackPush( |
249 | 5.85k | Builder.createZExt(Builder.createICmpULT(LHS, RHS), Context.Int32Ty)); |
250 | 5.85k | break; |
251 | 5.50k | } |
252 | 843 | case OpCode::I32__gt_s: |
253 | 1.23k | case OpCode::I64__gt_s: { |
254 | 1.23k | LLVM::Value RHS = stackPop(); |
255 | 1.23k | LLVM::Value LHS = stackPop(); |
256 | 1.23k | stackPush( |
257 | 1.23k | Builder.createZExt(Builder.createICmpSGT(LHS, RHS), Context.Int32Ty)); |
258 | 1.23k | break; |
259 | 843 | } |
260 | 5.14k | case OpCode::I32__gt_u: |
261 | 5.30k | case OpCode::I64__gt_u: { |
262 | 5.30k | LLVM::Value RHS = stackPop(); |
263 | 5.30k | LLVM::Value LHS = stackPop(); |
264 | 5.30k | stackPush( |
265 | 5.30k | Builder.createZExt(Builder.createICmpUGT(LHS, RHS), Context.Int32Ty)); |
266 | 5.30k | break; |
267 | 5.14k | } |
268 | 1.80k | case OpCode::I32__le_s: |
269 | 2.54k | case OpCode::I64__le_s: { |
270 | 2.54k | LLVM::Value RHS = stackPop(); |
271 | 2.54k | LLVM::Value LHS = stackPop(); |
272 | 2.54k | stackPush( |
273 | 2.54k | Builder.createZExt(Builder.createICmpSLE(LHS, RHS), Context.Int32Ty)); |
274 | 2.54k | break; |
275 | 1.80k | } |
276 | 479 | case OpCode::I32__le_u: |
277 | 1.77k | case OpCode::I64__le_u: { |
278 | 1.77k | LLVM::Value RHS = stackPop(); |
279 | 1.77k | LLVM::Value LHS = stackPop(); |
280 | 1.77k | stackPush( |
281 | 1.77k | Builder.createZExt(Builder.createICmpULE(LHS, RHS), Context.Int32Ty)); |
282 | 1.77k | break; |
283 | 479 | } |
284 | 981 | case OpCode::I32__ge_s: |
285 | 1.01k | case OpCode::I64__ge_s: { |
286 | 1.01k | LLVM::Value RHS = stackPop(); |
287 | 1.01k | LLVM::Value LHS = stackPop(); |
288 | 1.01k | stackPush( |
289 | 1.01k | Builder.createZExt(Builder.createICmpSGE(LHS, RHS), Context.Int32Ty)); |
290 | 1.01k | break; |
291 | 981 | } |
292 | 982 | case OpCode::I32__ge_u: |
293 | 1.55k | case OpCode::I64__ge_u: { |
294 | 1.55k | LLVM::Value RHS = stackPop(); |
295 | 1.55k | LLVM::Value LHS = stackPop(); |
296 | 1.55k | stackPush( |
297 | 1.55k | Builder.createZExt(Builder.createICmpUGE(LHS, RHS), Context.Int32Ty)); |
298 | 1.55k | break; |
299 | 982 | } |
300 | 128 | case OpCode::F32__eq: |
301 | 179 | case OpCode::F64__eq: { |
302 | 179 | LLVM::Value RHS = stackPop(); |
303 | 179 | LLVM::Value LHS = stackPop(); |
304 | 179 | stackPush( |
305 | 179 | Builder.createZExt(Builder.createFCmpOEQ(LHS, RHS), Context.Int32Ty)); |
306 | 179 | break; |
307 | 128 | } |
308 | 81 | case OpCode::F32__ne: |
309 | 107 | case OpCode::F64__ne: { |
310 | 107 | LLVM::Value RHS = stackPop(); |
311 | 107 | LLVM::Value LHS = stackPop(); |
312 | 107 | stackPush( |
313 | 107 | Builder.createZExt(Builder.createFCmpUNE(LHS, RHS), Context.Int32Ty)); |
314 | 107 | break; |
315 | 81 | } |
316 | 138 | case OpCode::F32__lt: |
317 | 258 | case OpCode::F64__lt: { |
318 | 258 | LLVM::Value RHS = stackPop(); |
319 | 258 | LLVM::Value LHS = stackPop(); |
320 | 258 | stackPush( |
321 | 258 | Builder.createZExt(Builder.createFCmpOLT(LHS, RHS), Context.Int32Ty)); |
322 | 258 | break; |
323 | 138 | } |
324 | 128 | case OpCode::F32__gt: |
325 | 178 | case OpCode::F64__gt: { |
326 | 178 | LLVM::Value RHS = stackPop(); |
327 | 178 | LLVM::Value LHS = stackPop(); |
328 | 178 | stackPush( |
329 | 178 | Builder.createZExt(Builder.createFCmpOGT(LHS, RHS), Context.Int32Ty)); |
330 | 178 | break; |
331 | 128 | } |
332 | 66 | case OpCode::F32__le: |
333 | 169 | case OpCode::F64__le: { |
334 | 169 | LLVM::Value RHS = stackPop(); |
335 | 169 | LLVM::Value LHS = stackPop(); |
336 | 169 | stackPush( |
337 | 169 | Builder.createZExt(Builder.createFCmpOLE(LHS, RHS), Context.Int32Ty)); |
338 | 169 | break; |
339 | 66 | } |
340 | 213 | case OpCode::F32__ge: |
341 | 240 | case OpCode::F64__ge: { |
342 | 240 | LLVM::Value RHS = stackPop(); |
343 | 240 | LLVM::Value LHS = stackPop(); |
344 | 240 | stackPush( |
345 | 240 | Builder.createZExt(Builder.createFCmpOGE(LHS, RHS), Context.Int32Ty)); |
346 | 240 | break; |
347 | 213 | } |
348 | 701 | case OpCode::I32__add: |
349 | 1.14k | case OpCode::I64__add: { |
350 | 1.14k | LLVM::Value RHS = stackPop(); |
351 | 1.14k | LLVM::Value LHS = stackPop(); |
352 | 1.14k | stackPush(Builder.createAdd(LHS, RHS)); |
353 | 1.14k | break; |
354 | 701 | } |
355 | 1.85k | case OpCode::I32__sub: |
356 | 2.28k | case OpCode::I64__sub: { |
357 | 2.28k | LLVM::Value RHS = stackPop(); |
358 | 2.28k | LLVM::Value LHS = stackPop(); |
359 | | |
360 | 2.28k | stackPush(Builder.createSub(LHS, RHS)); |
361 | 2.28k | break; |
362 | 1.85k | } |
363 | 569 | case OpCode::I32__mul: |
364 | 1.03k | case OpCode::I64__mul: { |
365 | 1.03k | LLVM::Value RHS = stackPop(); |
366 | 1.03k | LLVM::Value LHS = stackPop(); |
367 | 1.03k | stackPush(Builder.createMul(LHS, RHS)); |
368 | 1.03k | break; |
369 | 569 | } |
370 | 996 | case OpCode::I32__div_s: |
371 | 1.33k | case OpCode::I64__div_s: { |
372 | 1.33k | LLVM::Value RHS = stackPop(); |
373 | 1.33k | LLVM::Value LHS = stackPop(); |
374 | 1.33k | if constexpr (LLVM::kForceDivCheck) { |
375 | 1.33k | const bool Is32 = Instr.getOpCode() == OpCode::I32__div_s; |
376 | 1.33k | LLVM::Value IntZero = |
377 | 1.33k | Is32 ? LLContext.getInt32(0) : LLContext.getInt64(0); |
378 | 1.33k | LLVM::Value IntMinusOne = |
379 | 1.33k | Is32 ? LLContext.getInt32(static_cast<uint32_t>(INT32_C(-1))) |
380 | 1.33k | : LLContext.getInt64(static_cast<uint64_t>(INT64_C(-1))); |
381 | 1.33k | LLVM::Value IntMin = Is32 ? LLContext.getInt32(static_cast<uint32_t>( |
382 | 996 | std::numeric_limits<int32_t>::min())) |
383 | 1.33k | : LLContext.getInt64(static_cast<uint64_t>( |
384 | 343 | std::numeric_limits<int64_t>::min())); |
385 | | |
386 | 1.33k | auto NoZeroBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.nozero"); |
387 | 1.33k | auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.ok"); |
388 | | |
389 | 1.33k | auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero)); |
390 | 1.33k | Builder.createCondBr(IsNotZero, NoZeroBB, |
391 | 1.33k | getTrapBB(ErrCode::Value::DivideByZero)); |
392 | | |
393 | 1.33k | Builder.positionAtEnd(NoZeroBB); |
394 | 1.33k | auto NotOverflow = Builder.createLikely( |
395 | 1.33k | Builder.createOr(Builder.createICmpNE(LHS, IntMin), |
396 | 1.33k | Builder.createICmpNE(RHS, IntMinusOne))); |
397 | 1.33k | Builder.createCondBr(NotOverflow, OkBB, |
398 | 1.33k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
399 | | |
400 | 1.33k | Builder.positionAtEnd(OkBB); |
401 | 1.33k | } |
402 | 1.33k | stackPush(Builder.createSDiv(LHS, RHS)); |
403 | 1.33k | break; |
404 | 996 | } |
405 | 2.89k | case OpCode::I32__div_u: |
406 | 3.22k | case OpCode::I64__div_u: { |
407 | 3.22k | LLVM::Value RHS = stackPop(); |
408 | 3.22k | LLVM::Value LHS = stackPop(); |
409 | 3.22k | if constexpr (LLVM::kForceDivCheck) { |
410 | 3.22k | const bool Is32 = Instr.getOpCode() == OpCode::I32__div_u; |
411 | 3.22k | LLVM::Value IntZero = |
412 | 3.22k | Is32 ? LLContext.getInt32(0) : LLContext.getInt64(0); |
413 | 3.22k | auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.ok"); |
414 | | |
415 | 3.22k | auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero)); |
416 | 3.22k | Builder.createCondBr(IsNotZero, OkBB, |
417 | 3.22k | getTrapBB(ErrCode::Value::DivideByZero)); |
418 | 3.22k | Builder.positionAtEnd(OkBB); |
419 | 3.22k | } |
420 | 3.22k | stackPush(Builder.createUDiv(LHS, RHS)); |
421 | 3.22k | break; |
422 | 2.89k | } |
423 | 1.10k | case OpCode::I32__rem_s: |
424 | 1.53k | case OpCode::I64__rem_s: { |
425 | 1.53k | LLVM::Value RHS = stackPop(); |
426 | 1.53k | LLVM::Value LHS = stackPop(); |
427 | | // handle INT32_MIN % -1 |
428 | 1.53k | const bool Is32 = Instr.getOpCode() == OpCode::I32__rem_s; |
429 | 1.53k | LLVM::Value IntMinusOne = |
430 | 1.53k | Is32 ? LLContext.getInt32(static_cast<uint32_t>(INT32_C(-1))) |
431 | 1.53k | : LLContext.getInt64(static_cast<uint64_t>(INT64_C(-1))); |
432 | 1.53k | LLVM::Value IntMin = |
433 | 1.53k | Is32 ? LLContext.getInt32( |
434 | 1.10k | static_cast<uint32_t>(std::numeric_limits<int32_t>::min())) |
435 | 1.53k | : LLContext.getInt64( |
436 | 430 | static_cast<uint64_t>(std::numeric_limits<int64_t>::min())); |
437 | 1.53k | LLVM::Value IntZero = Is32 ? LLContext.getInt32(0) : LLContext.getInt64(0); |
438 | | |
439 | 1.53k | auto NoOverflowBB = |
440 | 1.53k | LLVM::BasicBlock::create(LLContext, F.Fn, "no.overflow"); |
441 | 1.53k | auto EndBB = LLVM::BasicBlock::create(LLContext, F.Fn, "end.overflow"); |
442 | | |
443 | 1.53k | if constexpr (LLVM::kForceDivCheck) { |
444 | 1.53k | auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "rem.ok"); |
445 | | |
446 | 1.53k | auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero)); |
447 | 1.53k | Builder.createCondBr(IsNotZero, OkBB, |
448 | 1.53k | getTrapBB(ErrCode::Value::DivideByZero)); |
449 | 1.53k | Builder.positionAtEnd(OkBB); |
450 | 1.53k | } |
451 | | |
452 | 1.53k | auto CurrBB = Builder.getInsertBlock(); |
453 | | |
454 | 1.53k | auto NotOverflow = Builder.createLikely( |
455 | 1.53k | Builder.createOr(Builder.createICmpNE(LHS, IntMin), |
456 | 1.53k | Builder.createICmpNE(RHS, IntMinusOne))); |
457 | 1.53k | Builder.createCondBr(NotOverflow, NoOverflowBB, EndBB); |
458 | | |
459 | 1.53k | Builder.positionAtEnd(NoOverflowBB); |
460 | 1.53k | auto Ret1 = Builder.createSRem(LHS, RHS); |
461 | 1.53k | Builder.createBr(EndBB); |
462 | | |
463 | 1.53k | Builder.positionAtEnd(EndBB); |
464 | 1.53k | auto Ret = Builder.createPHI(Ret1.getType()); |
465 | 1.53k | Ret.addIncoming(Ret1, NoOverflowBB); |
466 | 1.53k | Ret.addIncoming(IntZero, CurrBB); |
467 | | |
468 | 1.53k | stackPush(Ret); |
469 | 1.53k | break; |
470 | 1.10k | } |
471 | 1.72k | case OpCode::I32__rem_u: |
472 | 2.44k | case OpCode::I64__rem_u: { |
473 | 2.44k | LLVM::Value RHS = stackPop(); |
474 | 2.44k | LLVM::Value LHS = stackPop(); |
475 | 2.44k | if constexpr (LLVM::kForceDivCheck) { |
476 | 2.44k | LLVM::Value IntZero = Instr.getOpCode() == OpCode::I32__rem_u |
477 | 2.44k | ? LLContext.getInt32(0) |
478 | 2.44k | : LLContext.getInt64(0); |
479 | 2.44k | auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "rem.ok"); |
480 | | |
481 | 2.44k | auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero)); |
482 | 2.44k | Builder.createCondBr(IsNotZero, OkBB, |
483 | 2.44k | getTrapBB(ErrCode::Value::DivideByZero)); |
484 | 2.44k | Builder.positionAtEnd(OkBB); |
485 | 2.44k | } |
486 | 2.44k | stackPush(Builder.createURem(LHS, RHS)); |
487 | 2.44k | break; |
488 | 1.72k | } |
489 | 510 | case OpCode::I32__and: |
490 | 1.84k | case OpCode::I64__and: { |
491 | 1.84k | LLVM::Value RHS = stackPop(); |
492 | 1.84k | LLVM::Value LHS = stackPop(); |
493 | 1.84k | stackPush(Builder.createAnd(LHS, RHS)); |
494 | 1.84k | break; |
495 | 510 | } |
496 | 947 | case OpCode::I32__or: |
497 | 1.27k | case OpCode::I64__or: { |
498 | 1.27k | LLVM::Value RHS = stackPop(); |
499 | 1.27k | LLVM::Value LHS = stackPop(); |
500 | 1.27k | stackPush(Builder.createOr(LHS, RHS)); |
501 | 1.27k | break; |
502 | 947 | } |
503 | 1.43k | case OpCode::I32__xor: |
504 | 2.05k | case OpCode::I64__xor: { |
505 | 2.05k | LLVM::Value RHS = stackPop(); |
506 | 2.05k | LLVM::Value LHS = stackPop(); |
507 | 2.05k | stackPush(Builder.createXor(LHS, RHS)); |
508 | 2.05k | break; |
509 | 1.43k | } |
510 | 1.51k | case OpCode::I32__shl: |
511 | 1.85k | case OpCode::I64__shl: { |
512 | 1.85k | LLVM::Value Mask = Instr.getOpCode() == OpCode::I32__shl |
513 | 1.85k | ? LLContext.getInt32(31) |
514 | 1.85k | : LLContext.getInt64(63); |
515 | 1.85k | LLVM::Value RHS = Builder.createAnd(stackPop(), Mask); |
516 | 1.85k | LLVM::Value LHS = stackPop(); |
517 | 1.85k | stackPush(Builder.createShl(LHS, RHS)); |
518 | 1.85k | break; |
519 | 1.51k | } |
520 | 1.44k | case OpCode::I32__shr_s: |
521 | 1.84k | case OpCode::I64__shr_s: { |
522 | 1.84k | LLVM::Value Mask = Instr.getOpCode() == OpCode::I32__shr_s |
523 | 1.84k | ? LLContext.getInt32(31) |
524 | 1.84k | : LLContext.getInt64(63); |
525 | 1.84k | LLVM::Value RHS = Builder.createAnd(stackPop(), Mask); |
526 | 1.84k | LLVM::Value LHS = stackPop(); |
527 | 1.84k | stackPush(Builder.createAShr(LHS, RHS)); |
528 | 1.84k | break; |
529 | 1.44k | } |
530 | 3.59k | case OpCode::I32__shr_u: |
531 | 3.88k | case OpCode::I64__shr_u: { |
532 | 3.88k | LLVM::Value Mask = Instr.getOpCode() == OpCode::I32__shr_u |
533 | 3.88k | ? LLContext.getInt32(31) |
534 | 3.88k | : LLContext.getInt64(63); |
535 | 3.88k | LLVM::Value RHS = Builder.createAnd(stackPop(), Mask); |
536 | 3.88k | LLVM::Value LHS = stackPop(); |
537 | 3.88k | stackPush(Builder.createLShr(LHS, RHS)); |
538 | 3.88k | break; |
539 | 3.59k | } |
540 | 2.20k | case OpCode::I32__rotl: { |
541 | 2.20k | LLVM::Value RHS = stackPop(); |
542 | 2.20k | LLVM::Value LHS = stackPop(); |
543 | 2.20k | assuming(LLVM::Core::FShl != LLVM::Core::NotIntrinsic); |
544 | 2.20k | stackPush(Builder.createIntrinsic(LLVM::Core::FShl, {Context.Int32Ty}, |
545 | 2.20k | {LHS, LHS, RHS})); |
546 | 2.20k | break; |
547 | 2.20k | } |
548 | 776 | case OpCode::I32__rotr: { |
549 | 776 | LLVM::Value RHS = stackPop(); |
550 | 776 | LLVM::Value LHS = stackPop(); |
551 | 776 | assuming(LLVM::Core::FShr != LLVM::Core::NotIntrinsic); |
552 | 776 | stackPush(Builder.createIntrinsic(LLVM::Core::FShr, {Context.Int32Ty}, |
553 | 776 | {LHS, LHS, RHS})); |
554 | 776 | break; |
555 | 776 | } |
556 | 875 | case OpCode::I64__rotl: { |
557 | 875 | LLVM::Value RHS = stackPop(); |
558 | 875 | LLVM::Value LHS = stackPop(); |
559 | 875 | assuming(LLVM::Core::FShl != LLVM::Core::NotIntrinsic); |
560 | 875 | stackPush(Builder.createIntrinsic(LLVM::Core::FShl, {Context.Int64Ty}, |
561 | 875 | {LHS, LHS, RHS})); |
562 | 875 | break; |
563 | 875 | } |
564 | 1.31k | case OpCode::I64__rotr: { |
565 | 1.31k | LLVM::Value RHS = stackPop(); |
566 | 1.31k | LLVM::Value LHS = stackPop(); |
567 | 1.31k | assuming(LLVM::Core::FShr != LLVM::Core::NotIntrinsic); |
568 | 1.31k | stackPush(Builder.createIntrinsic(LLVM::Core::FShr, {Context.Int64Ty}, |
569 | 1.31k | {LHS, LHS, RHS})); |
570 | 1.31k | break; |
571 | 1.31k | } |
572 | 213 | case OpCode::F32__add: |
573 | 461 | case OpCode::F64__add: { |
574 | 461 | LLVM::Value RHS = stackPop(); |
575 | 461 | LLVM::Value LHS = stackPop(); |
576 | 461 | stackPush(Builder.createFAdd(LHS, RHS)); |
577 | 461 | break; |
578 | 213 | } |
579 | 128 | case OpCode::F32__sub: |
580 | 379 | case OpCode::F64__sub: { |
581 | 379 | LLVM::Value RHS = stackPop(); |
582 | 379 | LLVM::Value LHS = stackPop(); |
583 | 379 | stackPush(Builder.createFSub(LHS, RHS)); |
584 | 379 | break; |
585 | 128 | } |
586 | 429 | case OpCode::F32__mul: |
587 | 547 | case OpCode::F64__mul: { |
588 | 547 | LLVM::Value RHS = stackPop(); |
589 | 547 | LLVM::Value LHS = stackPop(); |
590 | 547 | stackPush(Builder.createFMul(LHS, RHS)); |
591 | 547 | break; |
592 | 429 | } |
593 | 179 | case OpCode::F32__div: |
594 | 463 | case OpCode::F64__div: { |
595 | 463 | LLVM::Value RHS = stackPop(); |
596 | 463 | LLVM::Value LHS = stackPop(); |
597 | 463 | stackPush(Builder.createFDiv(LHS, RHS)); |
598 | 463 | break; |
599 | 179 | } |
600 | 253 | case OpCode::F32__min: |
601 | 529 | case OpCode::F64__min: { |
602 | 529 | LLVM::Value RHS = stackPop(); |
603 | 529 | LLVM::Value LHS = stackPop(); |
604 | 529 | auto FpTy = Instr.getOpCode() == OpCode::F32__min ? Context.FloatTy |
605 | 529 | : Context.DoubleTy; |
606 | 529 | auto IntTy = Instr.getOpCode() == OpCode::F32__min ? Context.Int32Ty |
607 | 529 | : Context.Int64Ty; |
608 | | |
609 | 529 | auto UEQ = Builder.createFCmpUEQ(LHS, RHS); |
610 | 529 | auto UNO = Builder.createFCmpUNO(LHS, RHS); |
611 | | |
612 | 529 | auto LHSInt = Builder.createBitCast(LHS, IntTy); |
613 | 529 | auto RHSInt = Builder.createBitCast(RHS, IntTy); |
614 | 529 | auto OrInt = Builder.createOr(LHSInt, RHSInt); |
615 | 529 | auto OrFp = Builder.createBitCast(OrInt, FpTy); |
616 | | |
617 | 529 | auto AddFp = Builder.createFAdd(LHS, RHS); |
618 | | |
619 | 529 | assuming(LLVM::Core::MinNum != LLVM::Core::NotIntrinsic); |
620 | 529 | auto MinFp = Builder.createIntrinsic(LLVM::Core::MinNum, {LHS.getType()}, |
621 | 529 | {LHS, RHS}); |
622 | | |
623 | 529 | auto Ret = Builder.createSelect(UEQ, Builder.createSelect(UNO, AddFp, OrFp), |
624 | 529 | MinFp); |
625 | 529 | stackPush(Ret); |
626 | 529 | break; |
627 | 529 | } |
628 | 261 | case OpCode::F32__max: |
629 | 730 | case OpCode::F64__max: { |
630 | 730 | LLVM::Value RHS = stackPop(); |
631 | 730 | LLVM::Value LHS = stackPop(); |
632 | 730 | auto FpTy = Instr.getOpCode() == OpCode::F32__max ? Context.FloatTy |
633 | 730 | : Context.DoubleTy; |
634 | 730 | auto IntTy = Instr.getOpCode() == OpCode::F32__max ? Context.Int32Ty |
635 | 730 | : Context.Int64Ty; |
636 | | |
637 | 730 | auto UEQ = Builder.createFCmpUEQ(LHS, RHS); |
638 | 730 | auto UNO = Builder.createFCmpUNO(LHS, RHS); |
639 | | |
640 | 730 | auto LHSInt = Builder.createBitCast(LHS, IntTy); |
641 | 730 | auto RHSInt = Builder.createBitCast(RHS, IntTy); |
642 | 730 | auto AndInt = Builder.createAnd(LHSInt, RHSInt); |
643 | 730 | auto AndFp = Builder.createBitCast(AndInt, FpTy); |
644 | | |
645 | 730 | auto AddFp = Builder.createFAdd(LHS, RHS); |
646 | | |
647 | 730 | assuming(LLVM::Core::MaxNum != LLVM::Core::NotIntrinsic); |
648 | 730 | auto MaxFp = Builder.createIntrinsic(LLVM::Core::MaxNum, {LHS.getType()}, |
649 | 730 | {LHS, RHS}); |
650 | | |
651 | 730 | auto Ret = Builder.createSelect( |
652 | 730 | UEQ, Builder.createSelect(UNO, AddFp, AndFp), MaxFp); |
653 | 730 | stackPush(Ret); |
654 | 730 | break; |
655 | 730 | } |
656 | 463 | case OpCode::F32__copysign: |
657 | 831 | case OpCode::F64__copysign: { |
658 | 831 | LLVM::Value RHS = stackPop(); |
659 | 831 | LLVM::Value LHS = stackPop(); |
660 | 831 | assuming(LLVM::Core::CopySign != LLVM::Core::NotIntrinsic); |
661 | 831 | stackPush(Builder.createIntrinsic(LLVM::Core::CopySign, {LHS.getType()}, |
662 | 831 | {LHS, RHS})); |
663 | 831 | break; |
664 | 831 | } |
665 | | |
666 | | // Saturating Truncation Numeric Instructions |
667 | 175 | case OpCode::I32__trunc_sat_f32_s: |
668 | 175 | compileSignedTruncSat(Context.Int32Ty); |
669 | 175 | break; |
670 | 97 | case OpCode::I32__trunc_sat_f32_u: |
671 | 97 | compileUnsignedTruncSat(Context.Int32Ty); |
672 | 97 | break; |
673 | 370 | case OpCode::I32__trunc_sat_f64_s: |
674 | 370 | compileSignedTruncSat(Context.Int32Ty); |
675 | 370 | break; |
676 | 328 | case OpCode::I32__trunc_sat_f64_u: |
677 | 328 | compileUnsignedTruncSat(Context.Int32Ty); |
678 | 328 | break; |
679 | 416 | case OpCode::I64__trunc_sat_f32_s: |
680 | 416 | compileSignedTruncSat(Context.Int64Ty); |
681 | 416 | break; |
682 | 362 | case OpCode::I64__trunc_sat_f32_u: |
683 | 362 | compileUnsignedTruncSat(Context.Int64Ty); |
684 | 362 | break; |
685 | 51 | case OpCode::I64__trunc_sat_f64_s: |
686 | 51 | compileSignedTruncSat(Context.Int64Ty); |
687 | 51 | break; |
688 | 198 | case OpCode::I64__trunc_sat_f64_u: |
689 | 198 | compileUnsignedTruncSat(Context.Int64Ty); |
690 | 198 | break; |
691 | | |
692 | | // SIMD Memory Instructions |
693 | 0 | default: |
694 | 0 | assumingUnreachable(); |
695 | 135k | } |
696 | 135k | return {}; |
697 | 135k | } |
698 | | |
699 | 1.89k | void FunctionCompiler::compileSignedTrunc(LLVM::Type IntType) noexcept { |
700 | 1.89k | auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.norm"); |
701 | 1.89k | auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.notmin"); |
702 | 1.89k | auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.notmax"); |
703 | 1.89k | auto Value = stackPop(); |
704 | 1.89k | const auto [Precise, MinFp, MaxFp] = |
705 | 1.89k | [IntType, Value]() -> std::tuple<bool, LLVM::Value, LLVM::Value> { |
706 | 1.89k | const auto BitWidth = IntType.getIntegerBitWidth(); |
707 | 1.89k | const auto [Min, Max] = [BitWidth]() -> std::tuple<int64_t, int64_t> { |
708 | 1.89k | switch (BitWidth) { |
709 | 1.49k | case 32: |
710 | 1.49k | return {std::numeric_limits<int32_t>::min(), |
711 | 1.49k | std::numeric_limits<int32_t>::max()}; |
712 | 400 | case 64: |
713 | 400 | return {std::numeric_limits<int64_t>::min(), |
714 | 400 | std::numeric_limits<int64_t>::max()}; |
715 | 0 | default: |
716 | 0 | assumingUnreachable(); |
717 | 1.89k | } |
718 | 1.89k | }(); |
719 | 1.89k | auto FPType = Value.getType(); |
720 | 1.89k | assuming(FPType.isFloatTy() || FPType.isDoubleTy()); |
721 | 1.89k | const auto FPWidth = FPType.getFPMantissaWidth(); |
722 | 1.89k | return {BitWidth <= FPWidth, LLVM::Value::getConstReal(FPType, Min), |
723 | 1.89k | LLVM::Value::getConstReal(FPType, Max)}; |
724 | 1.89k | }(); |
725 | | |
726 | 1.89k | auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value)); |
727 | 1.89k | Builder.createCondBr(IsNotNan, NormBB, |
728 | 1.89k | getTrapBB(ErrCode::Value::InvalidConvToInt)); |
729 | | |
730 | 1.89k | Builder.positionAtEnd(NormBB); |
731 | 1.89k | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
732 | 1.89k | auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value); |
733 | 1.89k | auto IsNotUnderflow = |
734 | 1.89k | Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp)); |
735 | 1.89k | Builder.createCondBr(IsNotUnderflow, NotMinBB, |
736 | 1.89k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
737 | | |
738 | 1.89k | Builder.positionAtEnd(NotMinBB); |
739 | 1.89k | auto IsNotOverflow = Builder.createLikely( |
740 | 1.89k | Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp)); |
741 | 1.89k | Builder.createCondBr(IsNotOverflow, NotMaxBB, |
742 | 1.89k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
743 | | |
744 | 1.89k | Builder.positionAtEnd(NotMaxBB); |
745 | 1.89k | stackPush(Builder.createFPToSI(Trunc, IntType)); |
746 | 1.89k | } |
747 | | |
748 | 1.01k | void FunctionCompiler::compileSignedTruncSat(LLVM::Type IntType) noexcept { |
749 | 1.01k | auto CurrBB = Builder.getInsertBlock(); |
750 | 1.01k | auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.norm"); |
751 | 1.01k | auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.notmin"); |
752 | 1.01k | auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.notmax"); |
753 | 1.01k | auto EndBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.end"); |
754 | 1.01k | auto Value = stackPop(); |
755 | 1.01k | const auto [Precise, MinInt, MaxInt, MinFp, MaxFp] = [IntType, Value]() |
756 | 1.01k | -> std::tuple<bool, uint64_t, uint64_t, LLVM::Value, LLVM::Value> { |
757 | 1.01k | const auto BitWidth = IntType.getIntegerBitWidth(); |
758 | 1.01k | const auto [Min, Max] = [BitWidth]() -> std::tuple<int64_t, int64_t> { |
759 | 1.01k | switch (BitWidth) { |
760 | 545 | case 32: |
761 | 545 | return {std::numeric_limits<int32_t>::min(), |
762 | 545 | std::numeric_limits<int32_t>::max()}; |
763 | 467 | case 64: |
764 | 467 | return {std::numeric_limits<int64_t>::min(), |
765 | 467 | std::numeric_limits<int64_t>::max()}; |
766 | 0 | default: |
767 | 0 | assumingUnreachable(); |
768 | 1.01k | } |
769 | 1.01k | }(); |
770 | 1.01k | auto FPType = Value.getType(); |
771 | 1.01k | assuming(FPType.isFloatTy() || FPType.isDoubleTy()); |
772 | 1.01k | const auto FPWidth = FPType.getFPMantissaWidth(); |
773 | 1.01k | return {BitWidth <= FPWidth, static_cast<uint64_t>(Min), |
774 | 1.01k | static_cast<uint64_t>(Max), LLVM::Value::getConstReal(FPType, Min), |
775 | 1.01k | LLVM::Value::getConstReal(FPType, Max)}; |
776 | 1.01k | }(); |
777 | | |
778 | 1.01k | auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value)); |
779 | 1.01k | Builder.createCondBr(IsNotNan, NormBB, EndBB); |
780 | | |
781 | 1.01k | Builder.positionAtEnd(NormBB); |
782 | 1.01k | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
783 | 1.01k | auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value); |
784 | 1.01k | auto IsNotUnderflow = |
785 | 1.01k | Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp)); |
786 | 1.01k | Builder.createCondBr(IsNotUnderflow, NotMinBB, EndBB); |
787 | | |
788 | 1.01k | Builder.positionAtEnd(NotMinBB); |
789 | 1.01k | auto IsNotOverflow = Builder.createLikely( |
790 | 1.01k | Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp)); |
791 | 1.01k | Builder.createCondBr(IsNotOverflow, NotMaxBB, EndBB); |
792 | | |
793 | 1.01k | Builder.positionAtEnd(NotMaxBB); |
794 | 1.01k | auto IntValue = Builder.createFPToSI(Trunc, IntType); |
795 | 1.01k | Builder.createBr(EndBB); |
796 | | |
797 | 1.01k | Builder.positionAtEnd(EndBB); |
798 | 1.01k | auto PHIRet = Builder.createPHI(IntType); |
799 | 1.01k | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, 0, true), CurrBB); |
800 | 1.01k | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MinInt, true), NormBB); |
801 | 1.01k | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MaxInt, true), NotMinBB); |
802 | 1.01k | PHIRet.addIncoming(IntValue, NotMaxBB); |
803 | | |
804 | 1.01k | stackPush(PHIRet); |
805 | 1.01k | } |
806 | | |
807 | 3.12k | void FunctionCompiler::compileUnsignedTrunc(LLVM::Type IntType) noexcept { |
808 | 3.12k | auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.norm"); |
809 | 3.12k | auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.notmin"); |
810 | 3.12k | auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.notmax"); |
811 | 3.12k | auto Value = stackPop(); |
812 | 3.12k | const auto [Precise, MinFp, MaxFp] = |
813 | 3.12k | [IntType, Value]() -> std::tuple<bool, LLVM::Value, LLVM::Value> { |
814 | 3.12k | const auto BitWidth = IntType.getIntegerBitWidth(); |
815 | 3.12k | const auto [Min, Max] = [BitWidth]() -> std::tuple<uint64_t, uint64_t> { |
816 | 3.12k | switch (BitWidth) { |
817 | 1.16k | case 32: |
818 | 1.16k | return {std::numeric_limits<uint32_t>::min(), |
819 | 1.16k | std::numeric_limits<uint32_t>::max()}; |
820 | 1.95k | case 64: |
821 | 1.95k | return {std::numeric_limits<uint64_t>::min(), |
822 | 1.95k | std::numeric_limits<uint64_t>::max()}; |
823 | 0 | default: |
824 | 0 | assumingUnreachable(); |
825 | 3.12k | } |
826 | 3.12k | }(); |
827 | 3.12k | auto FPType = Value.getType(); |
828 | 3.12k | assuming(FPType.isFloatTy() || FPType.isDoubleTy()); |
829 | 3.12k | const auto FPWidth = FPType.getFPMantissaWidth(); |
830 | 3.12k | return {BitWidth <= FPWidth, LLVM::Value::getConstReal(FPType, Min), |
831 | 3.12k | LLVM::Value::getConstReal(FPType, Max)}; |
832 | 3.12k | }(); |
833 | | |
834 | 3.12k | auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value)); |
835 | 3.12k | Builder.createCondBr(IsNotNan, NormBB, |
836 | 3.12k | getTrapBB(ErrCode::Value::InvalidConvToInt)); |
837 | | |
838 | 3.12k | Builder.positionAtEnd(NormBB); |
839 | 3.12k | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
840 | 3.12k | auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value); |
841 | 3.12k | auto IsNotUnderflow = |
842 | 3.12k | Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp)); |
843 | 3.12k | Builder.createCondBr(IsNotUnderflow, NotMinBB, |
844 | 3.12k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
845 | | |
846 | 3.12k | Builder.positionAtEnd(NotMinBB); |
847 | 3.12k | auto IsNotOverflow = Builder.createLikely( |
848 | 3.12k | Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp)); |
849 | 3.12k | Builder.createCondBr(IsNotOverflow, NotMaxBB, |
850 | 3.12k | getTrapBB(ErrCode::Value::IntegerOverflow)); |
851 | | |
852 | 3.12k | Builder.positionAtEnd(NotMaxBB); |
853 | 3.12k | stackPush(Builder.createFPToUI(Trunc, IntType)); |
854 | 3.12k | } |
855 | | |
856 | 985 | void FunctionCompiler::compileUnsignedTruncSat(LLVM::Type IntType) noexcept { |
857 | 985 | auto CurrBB = Builder.getInsertBlock(); |
858 | 985 | auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.norm"); |
859 | 985 | auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.notmax"); |
860 | 985 | auto EndBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.end"); |
861 | 985 | auto Value = stackPop(); |
862 | 985 | const auto [Precise, MinInt, MaxInt, MinFp, MaxFp] = [IntType, Value]() |
863 | 985 | -> std::tuple<bool, uint64_t, uint64_t, LLVM::Value, LLVM::Value> { |
864 | 985 | const auto BitWidth = IntType.getIntegerBitWidth(); |
865 | 985 | const auto [Min, Max] = [BitWidth]() -> std::tuple<uint64_t, uint64_t> { |
866 | 985 | switch (BitWidth) { |
867 | 425 | case 32: |
868 | 425 | return {std::numeric_limits<uint32_t>::min(), |
869 | 425 | std::numeric_limits<uint32_t>::max()}; |
870 | 560 | case 64: |
871 | 560 | return {std::numeric_limits<uint64_t>::min(), |
872 | 560 | std::numeric_limits<uint64_t>::max()}; |
873 | 0 | default: |
874 | 0 | assumingUnreachable(); |
875 | 985 | } |
876 | 985 | }(); |
877 | 985 | auto FPType = Value.getType(); |
878 | 985 | assuming(FPType.isFloatTy() || FPType.isDoubleTy()); |
879 | 985 | const auto FPWidth = FPType.getFPMantissaWidth(); |
880 | 985 | return {BitWidth <= FPWidth, Min, Max, |
881 | 985 | LLVM::Value::getConstReal(FPType, Min), |
882 | 985 | LLVM::Value::getConstReal(FPType, Max)}; |
883 | 985 | }(); |
884 | | |
885 | 985 | assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic); |
886 | 985 | auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value); |
887 | 985 | auto IsNotUnderflow = |
888 | 985 | Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp)); |
889 | 985 | Builder.createCondBr(IsNotUnderflow, NormBB, EndBB); |
890 | | |
891 | 985 | Builder.positionAtEnd(NormBB); |
892 | 985 | auto IsNotOverflow = Builder.createLikely( |
893 | 985 | Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp)); |
894 | 985 | Builder.createCondBr(IsNotOverflow, NotMaxBB, EndBB); |
895 | | |
896 | 985 | Builder.positionAtEnd(NotMaxBB); |
897 | 985 | auto IntValue = Builder.createFPToUI(Trunc, IntType); |
898 | 985 | Builder.createBr(EndBB); |
899 | | |
900 | 985 | Builder.positionAtEnd(EndBB); |
901 | 985 | auto PHIRet = Builder.createPHI(IntType); |
902 | 985 | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MinInt), CurrBB); |
903 | 985 | PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MaxInt), NormBB); |
904 | 985 | PHIRet.addIncoming(IntValue, NotMaxBB); |
905 | | |
906 | 985 | stackPush(PHIRet); |
907 | 985 | } |
908 | | |
909 | | } // namespace WasmEdge |