Coverage Report

Created: 2026-07-16 07:09

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