Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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