Coverage Report

Created: 2026-11-09 06:49

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