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