Coverage Report

Created: 2026-08-08 06:32

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.81k
  case OpCode::I32__eqz:
14
7.81k
    stackPush(Builder.createZExt(
15
7.81k
        Builder.createICmpEQ(stackPop(), LLContext.getInt32(0)),
16
7.81k
        Context.Int32Ty));
17
7.81k
    break;
18
1.27k
  case OpCode::I64__eqz:
19
1.27k
    stackPush(Builder.createZExt(
20
1.27k
        Builder.createICmpEQ(stackPop(), LLContext.getInt64(0)),
21
1.27k
        Context.Int32Ty));
22
1.27k
    break;
23
2.11k
  case OpCode::I32__clz:
24
2.11k
    assuming(LLVM::Core::Ctlz != LLVM::Core::NotIntrinsic);
25
2.11k
    stackPush(Builder.createIntrinsic(LLVM::Core::Ctlz, {Context.Int32Ty},
26
2.11k
                                      {stackPop(), LLContext.getFalse()}));
27
2.11k
    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
17.3k
  case OpCode::I32__popcnt:
44
19.2k
  case OpCode::I64__popcnt:
45
19.2k
    assuming(LLVM::Core::Ctpop != LLVM::Core::NotIntrinsic);
46
19.2k
    stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Ctpop, stackPop()));
47
19.2k
    break;
48
818
  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
859
  case OpCode::F32__neg:
54
1.45k
  case OpCode::F64__neg:
55
1.45k
    stackPush(Builder.createFNeg(stackPop()));
56
1.45k
    break;
57
1.48k
  case OpCode::F32__ceil:
58
3.76k
  case OpCode::F64__ceil:
59
3.76k
    assuming(LLVM::Core::Ceil != LLVM::Core::NotIntrinsic);
60
3.76k
    stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Ceil, stackPop()));
61
3.76k
    break;
62
689
  case OpCode::F32__floor:
63
1.06k
  case OpCode::F64__floor:
64
1.06k
    assuming(LLVM::Core::Floor != LLVM::Core::NotIntrinsic);
65
1.06k
    stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Floor, stackPop()));
66
1.06k
    break;
67
552
  case OpCode::F32__trunc:
68
838
  case OpCode::F64__trunc:
69
838
    assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic);
70
838
    stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Trunc, stackPop()));
71
838
    break;
72
660
  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.57k
  case OpCode::F64__sqrt:
127
1.57k
    assuming(LLVM::Core::Sqrt != LLVM::Core::NotIntrinsic);
128
1.57k
    stackPush(Builder.createUnaryIntrinsic(LLVM::Core::Sqrt, stackPop()));
129
1.57k
    break;
130
340
  case OpCode::I32__wrap_i64:
131
340
    stackPush(Builder.createTrunc(stackPop(), Context.Int32Ty));
132
340
    break;
133
1.48k
  case OpCode::I32__trunc_f32_s:
134
1.48k
    compileSignedTrunc(Context.Int32Ty);
135
1.48k
    break;
136
260
  case OpCode::I32__trunc_f64_s:
137
260
    compileSignedTrunc(Context.Int32Ty);
138
260
    break;
139
227
  case OpCode::I32__trunc_f32_u:
140
227
    compileUnsignedTrunc(Context.Int32Ty);
141
227
    break;
142
1.36k
  case OpCode::I32__trunc_f64_u:
143
1.36k
    compileUnsignedTrunc(Context.Int32Ty);
144
1.36k
    break;
145
2.17k
  case OpCode::I64__extend_i32_s:
146
2.17k
    stackPush(Builder.createSExt(stackPop(), Context.Int64Ty));
147
2.17k
    break;
148
426
  case OpCode::I64__extend_i32_u:
149
426
    stackPush(Builder.createZExt(stackPop(), Context.Int64Ty));
150
426
    break;
151
52
  case OpCode::I64__trunc_f32_s:
152
52
    compileSignedTrunc(Context.Int64Ty);
153
52
    break;
154
351
  case OpCode::I64__trunc_f64_s:
155
351
    compileSignedTrunc(Context.Int64Ty);
156
351
    break;
157
1.12k
  case OpCode::I64__trunc_f32_u:
158
1.12k
    compileUnsignedTrunc(Context.Int64Ty);
159
1.12k
    break;
160
1.52k
  case OpCode::I64__trunc_f64_u:
161
1.52k
    compileUnsignedTrunc(Context.Int64Ty);
162
1.52k
    break;
163
1.80k
  case OpCode::F32__convert_i32_s:
164
2.18k
  case OpCode::F32__convert_i64_s:
165
2.18k
    stackPush(Builder.createSIToFP(stackPop(), Context.FloatTy));
166
2.18k
    break;
167
791
  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.41k
  case OpCode::F64__convert_i32_s:
172
5.55k
  case OpCode::F64__convert_i64_s:
173
5.55k
    stackPush(Builder.createSIToFP(stackPop(), Context.DoubleTy));
174
5.55k
    break;
175
2.00k
  case OpCode::F64__convert_i32_u:
176
2.17k
  case OpCode::F64__convert_i64_u:
177
2.17k
    stackPush(Builder.createUIToFP(stackPop(), Context.DoubleTy));
178
2.17k
    break;
179
231
  case OpCode::F32__demote_f64:
180
231
    stackPush(Builder.createFPTrunc(stackPop(), Context.FloatTy));
181
231
    break;
182
85
  case OpCode::F64__promote_f32:
183
85
    stackPush(Builder.createFPExt(stackPop(), Context.DoubleTy));
184
85
    break;
185
550
  case OpCode::I32__reinterpret_f32:
186
550
    stackPush(Builder.createBitCast(stackPop(), Context.Int32Ty));
187
550
    break;
188
615
  case OpCode::I64__reinterpret_f64:
189
615
    stackPush(Builder.createBitCast(stackPop(), Context.Int64Ty));
190
615
    break;
191
4.11k
  case OpCode::F32__reinterpret_i32:
192
4.11k
    stackPush(Builder.createBitCast(stackPop(), Context.FloatTy));
193
4.11k
    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.76k
  case OpCode::I32__extend16_s:
202
2.76k
    stackPush(Builder.createSExt(
203
2.76k
        Builder.createTrunc(stackPop(), Context.Int16Ty), Context.Int32Ty));
204
2.76k
    break;
205
361
  case OpCode::I64__extend8_s:
206
361
    stackPush(Builder.createSExt(
207
361
        Builder.createTrunc(stackPop(), Context.Int8Ty), Context.Int64Ty));
208
361
    break;
209
617
  case OpCode::I64__extend16_s:
210
617
    stackPush(Builder.createSExt(
211
617
        Builder.createTrunc(stackPop(), Context.Int16Ty), Context.Int64Ty));
212
617
    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.13k
  case OpCode::I32__eq:
220
1.36k
  case OpCode::I64__eq: {
221
1.36k
    LLVM::Value RHS = stackPop();
222
1.36k
    LLVM::Value LHS = stackPop();
223
1.36k
    stackPush(
224
1.36k
        Builder.createZExt(Builder.createICmpEQ(LHS, RHS), Context.Int32Ty));
225
1.36k
    break;
226
1.13k
  }
227
752
  case OpCode::I32__ne:
228
770
  case OpCode::I64__ne: {
229
770
    LLVM::Value RHS = stackPop();
230
770
    LLVM::Value LHS = stackPop();
231
770
    stackPush(
232
770
        Builder.createZExt(Builder.createICmpNE(LHS, RHS), Context.Int32Ty));
233
770
    break;
234
752
  }
235
2.90k
  case OpCode::I32__lt_s:
236
3.43k
  case OpCode::I64__lt_s: {
237
3.43k
    LLVM::Value RHS = stackPop();
238
3.43k
    LLVM::Value LHS = stackPop();
239
3.43k
    stackPush(
240
3.43k
        Builder.createZExt(Builder.createICmpSLT(LHS, RHS), Context.Int32Ty));
241
3.43k
    break;
242
2.90k
  }
243
6.10k
  case OpCode::I32__lt_u:
244
6.45k
  case OpCode::I64__lt_u: {
245
6.45k
    LLVM::Value RHS = stackPop();
246
6.45k
    LLVM::Value LHS = stackPop();
247
6.45k
    stackPush(
248
6.45k
        Builder.createZExt(Builder.createICmpULT(LHS, RHS), Context.Int32Ty));
249
6.45k
    break;
250
6.10k
  }
251
1.18k
  case OpCode::I32__gt_s:
252
1.71k
  case OpCode::I64__gt_s: {
253
1.71k
    LLVM::Value RHS = stackPop();
254
1.71k
    LLVM::Value LHS = stackPop();
255
1.71k
    stackPush(
256
1.71k
        Builder.createZExt(Builder.createICmpSGT(LHS, RHS), Context.Int32Ty));
257
1.71k
    break;
258
1.18k
  }
259
5.82k
  case OpCode::I32__gt_u:
260
5.99k
  case OpCode::I64__gt_u: {
261
5.99k
    LLVM::Value RHS = stackPop();
262
5.99k
    LLVM::Value LHS = stackPop();
263
5.99k
    stackPush(
264
5.99k
        Builder.createZExt(Builder.createICmpUGT(LHS, RHS), Context.Int32Ty));
265
5.99k
    break;
266
5.82k
  }
267
1.97k
  case OpCode::I32__le_s:
268
2.87k
  case OpCode::I64__le_s: {
269
2.87k
    LLVM::Value RHS = stackPop();
270
2.87k
    LLVM::Value LHS = stackPop();
271
2.87k
    stackPush(
272
2.87k
        Builder.createZExt(Builder.createICmpSLE(LHS, RHS), Context.Int32Ty));
273
2.87k
    break;
274
1.97k
  }
275
448
  case OpCode::I32__le_u:
276
1.75k
  case OpCode::I64__le_u: {
277
1.75k
    LLVM::Value RHS = stackPop();
278
1.75k
    LLVM::Value LHS = stackPop();
279
1.75k
    stackPush(
280
1.75k
        Builder.createZExt(Builder.createICmpULE(LHS, RHS), Context.Int32Ty));
281
1.75k
    break;
282
448
  }
283
1.11k
  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.11k
  }
291
1.02k
  case OpCode::I32__ge_u:
292
1.78k
  case OpCode::I64__ge_u: {
293
1.78k
    LLVM::Value RHS = stackPop();
294
1.78k
    LLVM::Value LHS = stackPop();
295
1.78k
    stackPush(
296
1.78k
        Builder.createZExt(Builder.createICmpUGE(LHS, RHS), Context.Int32Ty));
297
1.78k
    break;
298
1.02k
  }
299
163
  case OpCode::F32__eq:
300
214
  case OpCode::F64__eq: {
301
214
    LLVM::Value RHS = stackPop();
302
214
    LLVM::Value LHS = stackPop();
303
214
    stackPush(
304
214
        Builder.createZExt(Builder.createFCmpOEQ(LHS, RHS), Context.Int32Ty));
305
214
    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
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
180
  case OpCode::F64__le: {
333
180
    LLVM::Value RHS = stackPop();
334
180
    LLVM::Value LHS = stackPop();
335
180
    stackPush(
336
180
        Builder.createZExt(Builder.createFCmpOLE(LHS, RHS), Context.Int32Ty));
337
180
    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
730
  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
730
  }
354
2.07k
  case OpCode::I32__sub:
355
2.51k
  case OpCode::I64__sub: {
356
2.51k
    LLVM::Value RHS = stackPop();
357
2.51k
    LLVM::Value LHS = stackPop();
358
359
2.51k
    stackPush(Builder.createSub(LHS, RHS));
360
2.51k
    break;
361
2.07k
  }
362
587
  case OpCode::I32__mul:
363
1.04k
  case OpCode::I64__mul: {
364
1.04k
    LLVM::Value RHS = stackPop();
365
1.04k
    LLVM::Value LHS = stackPop();
366
1.04k
    stackPush(Builder.createMul(LHS, RHS));
367
1.04k
    break;
368
587
  }
369
1.16k
  case OpCode::I32__div_s:
370
1.50k
  case OpCode::I64__div_s: {
371
1.50k
    LLVM::Value RHS = stackPop();
372
1.50k
    LLVM::Value LHS = stackPop();
373
1.50k
    if constexpr (LLVM::kForceDivCheck) {
374
1.50k
      const bool Is32 = Instr.getOpCode() == OpCode::I32__div_s;
375
1.50k
      LLVM::Value IntZero =
376
1.50k
          Is32 ? LLContext.getInt32(0) : LLContext.getInt64(0);
377
1.50k
      LLVM::Value IntMinusOne =
378
1.50k
          Is32 ? LLContext.getInt32(static_cast<uint32_t>(INT32_C(-1)))
379
1.50k
               : LLContext.getInt64(static_cast<uint64_t>(INT64_C(-1)));
380
1.50k
      LLVM::Value IntMin = Is32 ? LLContext.getInt32(static_cast<uint32_t>(
381
1.16k
                                      std::numeric_limits<int32_t>::min()))
382
1.50k
                                : LLContext.getInt64(static_cast<uint64_t>(
383
340
                                      std::numeric_limits<int64_t>::min()));
384
385
1.50k
      auto NoZeroBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.nozero");
386
1.50k
      auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.ok");
387
388
1.50k
      auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero));
389
1.50k
      Builder.createCondBr(IsNotZero, NoZeroBB,
390
1.50k
                           getTrapBB(ErrCode::Value::DivideByZero));
391
392
1.50k
      Builder.positionAtEnd(NoZeroBB);
393
1.50k
      auto NotOverflow = Builder.createLikely(
394
1.50k
          Builder.createOr(Builder.createICmpNE(LHS, IntMin),
395
1.50k
                           Builder.createICmpNE(RHS, IntMinusOne)));
396
1.50k
      Builder.createCondBr(NotOverflow, OkBB,
397
1.50k
                           getTrapBB(ErrCode::Value::IntegerOverflow));
398
399
1.50k
      Builder.positionAtEnd(OkBB);
400
1.50k
    }
401
1.50k
    stackPush(Builder.createSDiv(LHS, RHS));
402
1.50k
    break;
403
1.16k
  }
404
3.10k
  case OpCode::I32__div_u:
405
3.42k
  case OpCode::I64__div_u: {
406
3.42k
    LLVM::Value RHS = stackPop();
407
3.42k
    LLVM::Value LHS = stackPop();
408
3.42k
    if constexpr (LLVM::kForceDivCheck) {
409
3.42k
      const bool Is32 = Instr.getOpCode() == OpCode::I32__div_u;
410
3.42k
      LLVM::Value IntZero =
411
3.42k
          Is32 ? LLContext.getInt32(0) : LLContext.getInt64(0);
412
3.42k
      auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "div.ok");
413
414
3.42k
      auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero));
415
3.42k
      Builder.createCondBr(IsNotZero, OkBB,
416
3.42k
                           getTrapBB(ErrCode::Value::DivideByZero));
417
3.42k
      Builder.positionAtEnd(OkBB);
418
3.42k
    }
419
3.42k
    stackPush(Builder.createUDiv(LHS, RHS));
420
3.42k
    break;
421
3.10k
  }
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
429
                   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.98k
  case OpCode::I32__rem_u:
471
2.71k
  case OpCode::I64__rem_u: {
472
2.71k
    LLVM::Value RHS = stackPop();
473
2.71k
    LLVM::Value LHS = stackPop();
474
2.71k
    if constexpr (LLVM::kForceDivCheck) {
475
2.71k
      LLVM::Value IntZero = Instr.getOpCode() == OpCode::I32__rem_u
476
2.71k
                                ? LLContext.getInt32(0)
477
2.71k
                                : LLContext.getInt64(0);
478
2.71k
      auto OkBB = LLVM::BasicBlock::create(LLContext, F.Fn, "rem.ok");
479
480
2.71k
      auto IsNotZero = Builder.createLikely(Builder.createICmpNE(RHS, IntZero));
481
2.71k
      Builder.createCondBr(IsNotZero, OkBB,
482
2.71k
                           getTrapBB(ErrCode::Value::DivideByZero));
483
2.71k
      Builder.positionAtEnd(OkBB);
484
2.71k
    }
485
2.71k
    stackPush(Builder.createURem(LHS, RHS));
486
2.71k
    break;
487
1.98k
  }
488
652
  case OpCode::I32__and:
489
1.99k
  case OpCode::I64__and: {
490
1.99k
    LLVM::Value RHS = stackPop();
491
1.99k
    LLVM::Value LHS = stackPop();
492
1.99k
    stackPush(Builder.createAnd(LHS, RHS));
493
1.99k
    break;
494
652
  }
495
1.15k
  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.15k
  }
502
1.50k
  case OpCode::I32__xor:
503
2.16k
  case OpCode::I64__xor: {
504
2.16k
    LLVM::Value RHS = stackPop();
505
2.16k
    LLVM::Value LHS = stackPop();
506
2.16k
    stackPush(Builder.createXor(LHS, RHS));
507
2.16k
    break;
508
1.50k
  }
509
1.96k
  case OpCode::I32__shl:
510
2.31k
  case OpCode::I64__shl: {
511
2.31k
    LLVM::Value Mask = Instr.getOpCode() == OpCode::I32__shl
512
2.31k
                           ? LLContext.getInt32(31)
513
2.31k
                           : LLContext.getInt64(63);
514
2.31k
    LLVM::Value RHS = Builder.createAnd(stackPop(), Mask);
515
2.31k
    LLVM::Value LHS = stackPop();
516
2.31k
    stackPush(Builder.createShl(LHS, RHS));
517
2.31k
    break;
518
1.96k
  }
519
2.45k
  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.45k
  }
529
3.86k
  case OpCode::I32__shr_u:
530
4.15k
  case OpCode::I64__shr_u: {
531
4.15k
    LLVM::Value Mask = Instr.getOpCode() == OpCode::I32__shr_u
532
4.15k
                           ? LLContext.getInt32(31)
533
4.15k
                           : LLContext.getInt64(63);
534
4.15k
    LLVM::Value RHS = Builder.createAnd(stackPop(), Mask);
535
4.15k
    LLVM::Value LHS = stackPop();
536
4.15k
    stackPush(Builder.createLShr(LHS, RHS));
537
4.15k
    break;
538
3.86k
  }
539
2.58k
  case OpCode::I32__rotl: {
540
2.58k
    LLVM::Value RHS = stackPop();
541
2.58k
    LLVM::Value LHS = stackPop();
542
2.58k
    assuming(LLVM::Core::FShl != LLVM::Core::NotIntrinsic);
543
2.58k
    stackPush(Builder.createIntrinsic(LLVM::Core::FShl, {Context.Int32Ty},
544
2.58k
                                      {LHS, LHS, RHS}));
545
2.58k
    break;
546
2.58k
  }
547
894
  case OpCode::I32__rotr: {
548
894
    LLVM::Value RHS = stackPop();
549
894
    LLVM::Value LHS = stackPop();
550
894
    assuming(LLVM::Core::FShr != LLVM::Core::NotIntrinsic);
551
894
    stackPush(Builder.createIntrinsic(LLVM::Core::FShr, {Context.Int32Ty},
552
894
                                      {LHS, LHS, RHS}));
553
894
    break;
554
894
  }
555
907
  case OpCode::I64__rotl: {
556
907
    LLVM::Value RHS = stackPop();
557
907
    LLVM::Value LHS = stackPop();
558
907
    assuming(LLVM::Core::FShl != LLVM::Core::NotIntrinsic);
559
907
    stackPush(Builder.createIntrinsic(LLVM::Core::FShl, {Context.Int64Ty},
560
907
                                      {LHS, LHS, RHS}));
561
907
    break;
562
907
  }
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
276
  case OpCode::F32__add:
572
578
  case OpCode::F64__add: {
573
578
    LLVM::Value RHS = stackPop();
574
578
    LLVM::Value LHS = stackPop();
575
578
    stackPush(Builder.createFAdd(LHS, RHS));
576
578
    break;
577
276
  }
578
123
  case OpCode::F32__sub:
579
488
  case OpCode::F64__sub: {
580
488
    LLVM::Value RHS = stackPop();
581
488
    LLVM::Value LHS = stackPop();
582
488
    stackPush(Builder.createFSub(LHS, RHS));
583
488
    break;
584
123
  }
585
431
  case OpCode::F32__mul:
586
558
  case OpCode::F64__mul: {
587
558
    LLVM::Value RHS = stackPop();
588
558
    LLVM::Value LHS = stackPop();
589
558
    stackPush(Builder.createFMul(LHS, RHS));
590
558
    break;
591
431
  }
592
186
  case OpCode::F32__div:
593
468
  case OpCode::F64__div: {
594
468
    LLVM::Value RHS = stackPop();
595
468
    LLVM::Value LHS = stackPop();
596
468
    stackPush(Builder.createFDiv(LHS, RHS));
597
468
    break;
598
186
  }
599
282
  case OpCode::F32__min:
600
608
  case OpCode::F64__min: {
601
608
    LLVM::Value RHS = stackPop();
602
608
    LLVM::Value LHS = stackPop();
603
608
    auto FpTy = Instr.getOpCode() == OpCode::F32__min ? Context.FloatTy
604
608
                                                      : Context.DoubleTy;
605
608
    auto IntTy = Instr.getOpCode() == OpCode::F32__min ? Context.Int32Ty
606
608
                                                       : Context.Int64Ty;
607
608
608
    auto UEQ = Builder.createFCmpUEQ(LHS, RHS);
609
608
    auto UNO = Builder.createFCmpUNO(LHS, RHS);
610
611
608
    auto LHSInt = Builder.createBitCast(LHS, IntTy);
612
608
    auto RHSInt = Builder.createBitCast(RHS, IntTy);
613
608
    auto OrInt = Builder.createOr(LHSInt, RHSInt);
614
608
    auto OrFp = Builder.createBitCast(OrInt, FpTy);
615
616
608
    auto AddFp = Builder.createFAdd(LHS, RHS);
617
618
608
    assuming(LLVM::Core::MinNum != LLVM::Core::NotIntrinsic);
619
608
    auto MinFp = Builder.createIntrinsic(LLVM::Core::MinNum, {LHS.getType()},
620
608
                                         {LHS, RHS});
621
622
608
    auto Ret = Builder.createSelect(UEQ, Builder.createSelect(UNO, AddFp, OrFp),
623
608
                                    MinFp);
624
608
    stackPush(Ret);
625
608
    break;
626
608
  }
627
337
  case OpCode::F32__max:
628
803
  case OpCode::F64__max: {
629
803
    LLVM::Value RHS = stackPop();
630
803
    LLVM::Value LHS = stackPop();
631
803
    auto FpTy = Instr.getOpCode() == OpCode::F32__max ? Context.FloatTy
632
803
                                                      : Context.DoubleTy;
633
803
    auto IntTy = Instr.getOpCode() == OpCode::F32__max ? Context.Int32Ty
634
803
                                                       : Context.Int64Ty;
635
636
803
    auto UEQ = Builder.createFCmpUEQ(LHS, RHS);
637
803
    auto UNO = Builder.createFCmpUNO(LHS, RHS);
638
639
803
    auto LHSInt = Builder.createBitCast(LHS, IntTy);
640
803
    auto RHSInt = Builder.createBitCast(RHS, IntTy);
641
803
    auto AndInt = Builder.createAnd(LHSInt, RHSInt);
642
803
    auto AndFp = Builder.createBitCast(AndInt, FpTy);
643
644
803
    auto AddFp = Builder.createFAdd(LHS, RHS);
645
646
803
    assuming(LLVM::Core::MaxNum != LLVM::Core::NotIntrinsic);
647
803
    auto MaxFp = Builder.createIntrinsic(LLVM::Core::MaxNum, {LHS.getType()},
648
803
                                         {LHS, RHS});
649
650
803
    auto Ret = Builder.createSelect(
651
803
        UEQ, Builder.createSelect(UNO, AddFp, AndFp), MaxFp);
652
803
    stackPush(Ret);
653
803
    break;
654
803
  }
655
461
  case OpCode::F32__copysign:
656
873
  case OpCode::F64__copysign: {
657
873
    LLVM::Value RHS = stackPop();
658
873
    LLVM::Value LHS = stackPop();
659
873
    assuming(LLVM::Core::CopySign != LLVM::Core::NotIntrinsic);
660
873
    stackPush(Builder.createIntrinsic(LLVM::Core::CopySign, {LHS.getType()},
661
873
                                      {LHS, RHS}));
662
873
    break;
663
873
  }
664
665
  // Saturating Truncation Numeric Instructions
666
215
  case OpCode::I32__trunc_sat_f32_s:
667
215
    compileSignedTruncSat(Context.Int32Ty);
668
215
    break;
669
102
  case OpCode::I32__trunc_sat_f32_u:
670
102
    compileUnsignedTruncSat(Context.Int32Ty);
671
102
    break;
672
508
  case OpCode::I32__trunc_sat_f64_s:
673
508
    compileSignedTruncSat(Context.Int32Ty);
674
508
    break;
675
433
  case OpCode::I32__trunc_sat_f64_u:
676
433
    compileUnsignedTruncSat(Context.Int32Ty);
677
433
    break;
678
578
  case OpCode::I64__trunc_sat_f32_s:
679
578
    compileSignedTruncSat(Context.Int64Ty);
680
578
    break;
681
372
  case OpCode::I64__trunc_sat_f32_u:
682
372
    compileUnsignedTruncSat(Context.Int64Ty);
683
372
    break;
684
188
  case OpCode::I64__trunc_sat_f64_s:
685
188
    compileSignedTruncSat(Context.Int64Ty);
686
188
    break;
687
365
  case OpCode::I64__trunc_sat_f64_u:
688
365
    compileUnsignedTruncSat(Context.Int64Ty);
689
365
    break;
690
691
    // SIMD Memory Instructions
692
0
  default:
693
0
    assumingUnreachable();
694
151k
  }
695
151k
  return {};
696
151k
}
697
698
2.14k
void FunctionCompiler::compileSignedTrunc(LLVM::Type IntType) noexcept {
699
2.14k
  auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.norm");
700
2.14k
  auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.notmin");
701
2.14k
  auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "strunc.notmax");
702
2.14k
  auto Value = stackPop();
703
2.14k
  const auto [Precise, MinFp, MaxFp] =
704
2.14k
      [IntType, Value]() -> std::tuple<bool, LLVM::Value, LLVM::Value> {
705
2.14k
    const auto BitWidth = IntType.getIntegerBitWidth();
706
2.14k
    const auto [Min, Max] = [BitWidth]() -> std::tuple<int64_t, int64_t> {
707
2.14k
      switch (BitWidth) {
708
1.74k
      case 32:
709
1.74k
        return {std::numeric_limits<int32_t>::min(),
710
1.74k
                std::numeric_limits<int32_t>::max()};
711
403
      case 64:
712
403
        return {std::numeric_limits<int64_t>::min(),
713
403
                std::numeric_limits<int64_t>::max()};
714
0
      default:
715
0
        assumingUnreachable();
716
2.14k
      }
717
2.14k
    }();
718
2.14k
    auto FPType = Value.getType();
719
2.14k
    assuming(FPType.isFloatTy() || FPType.isDoubleTy());
720
2.14k
    const auto FPWidth = FPType.getFPMantissaWidth();
721
2.14k
    return {BitWidth <= FPWidth, LLVM::Value::getConstReal(FPType, Min),
722
2.14k
            LLVM::Value::getConstReal(FPType, Max)};
723
2.14k
  }();
724
725
2.14k
  auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value));
726
2.14k
  Builder.createCondBr(IsNotNan, NormBB,
727
2.14k
                       getTrapBB(ErrCode::Value::InvalidConvToInt));
728
729
2.14k
  Builder.positionAtEnd(NormBB);
730
2.14k
  assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic);
731
2.14k
  auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value);
732
2.14k
  auto IsNotUnderflow =
733
2.14k
      Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp));
734
2.14k
  Builder.createCondBr(IsNotUnderflow, NotMinBB,
735
2.14k
                       getTrapBB(ErrCode::Value::IntegerOverflow));
736
737
2.14k
  Builder.positionAtEnd(NotMinBB);
738
2.14k
  auto IsNotOverflow = Builder.createLikely(
739
2.14k
      Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp));
740
2.14k
  Builder.createCondBr(IsNotOverflow, NotMaxBB,
741
2.14k
                       getTrapBB(ErrCode::Value::IntegerOverflow));
742
743
2.14k
  Builder.positionAtEnd(NotMaxBB);
744
2.14k
  stackPush(Builder.createFPToSI(Trunc, IntType));
745
2.14k
}
746
747
1.48k
void FunctionCompiler::compileSignedTruncSat(LLVM::Type IntType) noexcept {
748
1.48k
  auto CurrBB = Builder.getInsertBlock();
749
1.48k
  auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.norm");
750
1.48k
  auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.notmin");
751
1.48k
  auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.notmax");
752
1.48k
  auto EndBB = LLVM::BasicBlock::create(LLContext, F.Fn, "ssat.end");
753
1.48k
  auto Value = stackPop();
754
1.48k
  const auto [Precise, MinInt, MaxInt, MinFp, MaxFp] = [IntType, Value]()
755
1.48k
      -> std::tuple<bool, uint64_t, uint64_t, LLVM::Value, LLVM::Value> {
756
1.48k
    const auto BitWidth = IntType.getIntegerBitWidth();
757
1.48k
    const auto [Min, Max] = [BitWidth]() -> std::tuple<int64_t, int64_t> {
758
1.48k
      switch (BitWidth) {
759
723
      case 32:
760
723
        return {std::numeric_limits<int32_t>::min(),
761
723
                std::numeric_limits<int32_t>::max()};
762
766
      case 64:
763
766
        return {std::numeric_limits<int64_t>::min(),
764
766
                std::numeric_limits<int64_t>::max()};
765
0
      default:
766
0
        assumingUnreachable();
767
1.48k
      }
768
1.48k
    }();
769
1.48k
    auto FPType = Value.getType();
770
1.48k
    assuming(FPType.isFloatTy() || FPType.isDoubleTy());
771
1.48k
    const auto FPWidth = FPType.getFPMantissaWidth();
772
1.48k
    return {BitWidth <= FPWidth, static_cast<uint64_t>(Min),
773
1.48k
            static_cast<uint64_t>(Max), LLVM::Value::getConstReal(FPType, Min),
774
1.48k
            LLVM::Value::getConstReal(FPType, Max)};
775
1.48k
  }();
776
777
1.48k
  auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value));
778
1.48k
  Builder.createCondBr(IsNotNan, NormBB, EndBB);
779
780
1.48k
  Builder.positionAtEnd(NormBB);
781
1.48k
  assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic);
782
1.48k
  auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value);
783
1.48k
  auto IsNotUnderflow =
784
1.48k
      Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp));
785
1.48k
  Builder.createCondBr(IsNotUnderflow, NotMinBB, EndBB);
786
787
1.48k
  Builder.positionAtEnd(NotMinBB);
788
1.48k
  auto IsNotOverflow = Builder.createLikely(
789
1.48k
      Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp));
790
1.48k
  Builder.createCondBr(IsNotOverflow, NotMaxBB, EndBB);
791
792
1.48k
  Builder.positionAtEnd(NotMaxBB);
793
1.48k
  auto IntValue = Builder.createFPToSI(Trunc, IntType);
794
1.48k
  Builder.createBr(EndBB);
795
796
1.48k
  Builder.positionAtEnd(EndBB);
797
1.48k
  auto PHIRet = Builder.createPHI(IntType);
798
1.48k
  PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, 0, true), CurrBB);
799
1.48k
  PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MinInt, true), NormBB);
800
1.48k
  PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MaxInt, true), NotMinBB);
801
1.48k
  PHIRet.addIncoming(IntValue, NotMaxBB);
802
803
1.48k
  stackPush(PHIRet);
804
1.48k
}
805
806
4.23k
void FunctionCompiler::compileUnsignedTrunc(LLVM::Type IntType) noexcept {
807
4.23k
  auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.norm");
808
4.23k
  auto NotMinBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.notmin");
809
4.23k
  auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "utrunc.notmax");
810
4.23k
  auto Value = stackPop();
811
4.23k
  const auto [Precise, MinFp, MaxFp] =
812
4.23k
      [IntType, Value]() -> std::tuple<bool, LLVM::Value, LLVM::Value> {
813
4.23k
    const auto BitWidth = IntType.getIntegerBitWidth();
814
4.23k
    const auto [Min, Max] = [BitWidth]() -> std::tuple<uint64_t, uint64_t> {
815
4.23k
      switch (BitWidth) {
816
1.59k
      case 32:
817
1.59k
        return {std::numeric_limits<uint32_t>::min(),
818
1.59k
                std::numeric_limits<uint32_t>::max()};
819
2.64k
      case 64:
820
2.64k
        return {std::numeric_limits<uint64_t>::min(),
821
2.64k
                std::numeric_limits<uint64_t>::max()};
822
0
      default:
823
0
        assumingUnreachable();
824
4.23k
      }
825
4.23k
    }();
826
4.23k
    auto FPType = Value.getType();
827
4.23k
    assuming(FPType.isFloatTy() || FPType.isDoubleTy());
828
4.23k
    const auto FPWidth = FPType.getFPMantissaWidth();
829
4.23k
    return {BitWidth <= FPWidth, LLVM::Value::getConstReal(FPType, Min),
830
4.23k
            LLVM::Value::getConstReal(FPType, Max)};
831
4.23k
  }();
832
833
4.23k
  auto IsNotNan = Builder.createLikely(Builder.createFCmpORD(Value, Value));
834
4.23k
  Builder.createCondBr(IsNotNan, NormBB,
835
4.23k
                       getTrapBB(ErrCode::Value::InvalidConvToInt));
836
837
4.23k
  Builder.positionAtEnd(NormBB);
838
4.23k
  assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic);
839
4.23k
  auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value);
840
4.23k
  auto IsNotUnderflow =
841
4.23k
      Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp));
842
4.23k
  Builder.createCondBr(IsNotUnderflow, NotMinBB,
843
4.23k
                       getTrapBB(ErrCode::Value::IntegerOverflow));
844
845
4.23k
  Builder.positionAtEnd(NotMinBB);
846
4.23k
  auto IsNotOverflow = Builder.createLikely(
847
4.23k
      Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp));
848
4.23k
  Builder.createCondBr(IsNotOverflow, NotMaxBB,
849
4.23k
                       getTrapBB(ErrCode::Value::IntegerOverflow));
850
851
4.23k
  Builder.positionAtEnd(NotMaxBB);
852
4.23k
  stackPush(Builder.createFPToUI(Trunc, IntType));
853
4.23k
}
854
855
1.27k
void FunctionCompiler::compileUnsignedTruncSat(LLVM::Type IntType) noexcept {
856
1.27k
  auto CurrBB = Builder.getInsertBlock();
857
1.27k
  auto NormBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.norm");
858
1.27k
  auto NotMaxBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.notmax");
859
1.27k
  auto EndBB = LLVM::BasicBlock::create(LLContext, F.Fn, "usat.end");
860
1.27k
  auto Value = stackPop();
861
1.27k
  const auto [Precise, MinInt, MaxInt, MinFp, MaxFp] = [IntType, Value]()
862
1.27k
      -> std::tuple<bool, uint64_t, uint64_t, LLVM::Value, LLVM::Value> {
863
1.27k
    const auto BitWidth = IntType.getIntegerBitWidth();
864
1.27k
    const auto [Min, Max] = [BitWidth]() -> std::tuple<uint64_t, uint64_t> {
865
1.27k
      switch (BitWidth) {
866
535
      case 32:
867
535
        return {std::numeric_limits<uint32_t>::min(),
868
535
                std::numeric_limits<uint32_t>::max()};
869
737
      case 64:
870
737
        return {std::numeric_limits<uint64_t>::min(),
871
737
                std::numeric_limits<uint64_t>::max()};
872
0
      default:
873
0
        assumingUnreachable();
874
1.27k
      }
875
1.27k
    }();
876
1.27k
    auto FPType = Value.getType();
877
1.27k
    assuming(FPType.isFloatTy() || FPType.isDoubleTy());
878
1.27k
    const auto FPWidth = FPType.getFPMantissaWidth();
879
1.27k
    return {BitWidth <= FPWidth, Min, Max,
880
1.27k
            LLVM::Value::getConstReal(FPType, Min),
881
1.27k
            LLVM::Value::getConstReal(FPType, Max)};
882
1.27k
  }();
883
884
1.27k
  assuming(LLVM::Core::Trunc != LLVM::Core::NotIntrinsic);
885
1.27k
  auto Trunc = Builder.createUnaryIntrinsic(LLVM::Core::Trunc, Value);
886
1.27k
  auto IsNotUnderflow =
887
1.27k
      Builder.createLikely(Builder.createFCmpOGE(Trunc, MinFp));
888
1.27k
  Builder.createCondBr(IsNotUnderflow, NormBB, EndBB);
889
890
1.27k
  Builder.positionAtEnd(NormBB);
891
1.27k
  auto IsNotOverflow = Builder.createLikely(
892
1.27k
      Builder.createFCmp(Precise ? LLVMRealOLE : LLVMRealOLT, Trunc, MaxFp));
893
1.27k
  Builder.createCondBr(IsNotOverflow, NotMaxBB, EndBB);
894
895
1.27k
  Builder.positionAtEnd(NotMaxBB);
896
1.27k
  auto IntValue = Builder.createFPToUI(Trunc, IntType);
897
1.27k
  Builder.createBr(EndBB);
898
899
1.27k
  Builder.positionAtEnd(EndBB);
900
1.27k
  auto PHIRet = Builder.createPHI(IntType);
901
1.27k
  PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MinInt), CurrBB);
902
1.27k
  PHIRet.addIncoming(LLVM::Value::getConstInt(IntType, MaxInt), NormBB);
903
1.27k
  PHIRet.addIncoming(IntValue, NotMaxBB);
904
905
1.27k
  stackPush(PHIRet);
906
1.27k
}
907
908
} // namespace WasmEdge