Coverage Report

Created: 2026-08-14 06:41

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