cranelift-codegen 0.130.0

Low-level code generator library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
;; Prelude definitions specific to the mid-end.

;; Any `extern` definitions here are generally implemented in `src/opts.rs`.

;;;;; eclass and enode access ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Extract any node(s) for the given eclass ID.
(decl multi inst_data_value (Type InstructionData) Value)
(extern extractor inst_data_value inst_data_value_etor)

;; An extractor from an `Inst` to its `InstructionData`.
(decl inst_data (InstructionData) Inst)
(extern extractor inst_data inst_data_etor)

;; Identical to `inst_data_value`, just with a different ISLE type.  This is
;; basically a manual version of `curry`/`uncurry` in Haskell: to compose
;; extractors the outer one needs to be single-parameter, so this combines the
;; two parameters of `inst_data_value` into one.
(type TypeAndInstructionData (primitive TypeAndInstructionData))
(decl multi inst_data_value_tupled (TypeAndInstructionData) Value)
(extern extractor inst_data_value_tupled inst_data_value_tupled_etor)

;; Construct a pure node, returning a new (or deduplicated
;; already-existing) eclass ID.
(decl make_inst (Type InstructionData) Value)
(extern constructor make_inst make_inst_ctor)

;; Make a new side-effectful instruction, do not insert it into the layout, and
;; return its `Inst`.
(decl make_skeleton_inst (InstructionData) Inst)
(extern constructor make_skeleton_inst make_skeleton_inst_ctor)

;; Constructors for value arrays.
(decl value_array_2_ctor (Value Value) ValueArray2)
(extern constructor value_array_2_ctor value_array_2_ctor)
(decl value_array_3_ctor (Value Value Value) ValueArray3)
(extern constructor value_array_3_ctor value_array_3_ctor)

(rule (eq ty x y) (icmp ty (IntCC.Equal) x y))
(rule (ne ty x y) (icmp ty (IntCC.NotEqual) x y))
(rule (ult ty x y) (icmp ty (IntCC.UnsignedLessThan) x y))
(rule (ule ty x y) (icmp ty (IntCC.UnsignedLessThanOrEqual) x y))
(rule (ugt ty x y) (icmp ty (IntCC.UnsignedGreaterThan) x y))
(rule (uge ty x y) (icmp ty (IntCC.UnsignedGreaterThanOrEqual) x y))
(rule (slt ty x y) (icmp ty (IntCC.SignedLessThan) x y))
(rule (sle ty x y) (icmp ty (IntCC.SignedLessThanOrEqual) x y))
(rule (sgt ty x y) (icmp ty (IntCC.SignedGreaterThan) x y))
(rule (sge ty x y) (icmp ty (IntCC.SignedGreaterThanOrEqual) x y))

;; 3-way comparison, returning -1/0/+1 in I8
(decl spaceship_s (Type Value Value) Value)
(rule (spaceship_s ty x y) (isub $I8 (sgt ty x y) (slt ty x y)))
(extractor (spaceship_s ty x y) (isub $I8 (sgt ty x y) (slt ty x y)))
(decl spaceship_u (Type Value Value) Value)
(rule (spaceship_u ty x y) (isub $I8 (ugt ty x y) (ult ty x y)))
(extractor (spaceship_u ty x y) (isub $I8 (ugt ty x y) (ult ty x y)))

;;;;; optimization toplevel ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; The main matcher rule invoked by the toplevel driver.
(decl multi simplify (Value) Value)

;; The kind of simplification to perform on a skeleton instruction.
(type SkeletonInstSimplification
      (enum
        ;; Remove the instruction being simplified from the skeleton, it is
        ;; unnecessary.
        ;;
        ;; The instruction must not define any results.
        (Remove)

        ;; Remove the instruction being simplified from the skeleton, and
        ;; replace its result value with the given `val`.
        (RemoveWithVal (val Value))

        ;; Replace the instruction being simplified with the given instruction.
        ;;
        ;; The given instruction must not already be in a block and must define
        ;; the same number and types of results as the old instruction that it
        ;; is replacing.
        (Replace (inst Inst))

        ;; Like `Replace` but replace the old instruction's result value with
        ;; the given `val`.
        ;;
        ;; The old instruction must define a single result value and `val` must
        ;; match its type. The new instruction need not define the same number
        ;; or types of results as the old instruction.
        (ReplaceWithVal (inst Inst) (val Value))))

(decl pure inst_to_skeleton_inst_simplification (Inst) SkeletonInstSimplification)
(rule (inst_to_skeleton_inst_simplification inst)
      (SkeletonInstSimplification.Replace inst))

(decl pure value_to_skeleton_inst_simplification (Value) SkeletonInstSimplification)
(rule (value_to_skeleton_inst_simplification val)
      (SkeletonInstSimplification.RemoveWithVal val))

(decl pure remove_inst () SkeletonInstSimplification)
(rule (remove_inst) (SkeletonInstSimplification.Remove))

(decl pure replace_with_val (Inst Value) SkeletonInstSimplification)
(rule (replace_with_val inst val) (SkeletonInstSimplification.ReplaceWithVal inst val))

(convert Inst SkeletonInstSimplification inst_to_skeleton_inst_simplification)
(convert Value SkeletonInstSimplification value_to_skeleton_inst_simplification)

;; The main term for simplifying side-effectful instructions, invoked by the
;; egraph driver.
(decl multi simplify_skeleton (Inst) SkeletonInstSimplification)

;; Mark a node as requiring remat when used in a different block.
(decl remat (Value) Value)
(extern constructor remat remat)

;; Mark a node as subsuming whatever else it's rewritten from -- this
;; is definitely preferable, not just a possible option. Useful for,
;; e.g., constant propagation where we arrive at a definite "final
;; answer".
(decl subsume (Value) Value)
(extern constructor subsume subsume)

;;;;; constructors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(decl iconst_sextend_etor (Type i64) TypeAndInstructionData)
(extern extractor iconst_sextend_etor iconst_sextend_etor)

;; Construct an `iconst` from an `i64` or Extract an `i64` from an `iconst`
;; by treating the constant as signed.
;; When extracting, smaller types get their value sign-extended to 64-bits,
;; so that `iconst.i8 255` will give you a `-1_i64`.
;; When constructing, the rule will fail if the value cannot be represented in
;; the target type.  If it fits, it'll be masked accordingly in the constant.
;;
;; Recursion: may recurse at most once to reduce reduce 128-bit to 64-bit.
(decl rec iconst_s (Type i64) Value)
(extractor (iconst_s ty c) (inst_data_value_tupled (iconst_sextend_etor ty c)))
(rule 0 (iconst_s ty c)
    (if-let c_masked (u64_and (i64_cast_unsigned c)
                              (ty_umax ty)))
    (if-let c_reextended (i64_sextend_u64 ty c_masked))
    (if-let true (i64_eq c c_reextended))
    (iconst ty (imm64 c_masked)))
(rule 1 (iconst_s $I128 c) (sextend $I128 (iconst_s $I64 c)))

;; Construct an `iconst` from a `u64` or Extract a `u64` from an `iconst`
;; by treating the constant as unsigned.
;; When extracting, smaller types get their value zero-extended to 64-bits,
;; so that `iconst.i8 255` will give you a `255_u64`.
;; When constructing, the rule will fail if the value cannot be represented in
;; the target type.
;;
;; Recursion: may recurse at most once to reduce reduce 128-bit to 64-bit.
(decl rec iconst_u (Type u64) Value)
(extractor (iconst_u ty c) (iconst ty (u64_from_imm64 c)))
(rule 0 (iconst_u ty c)
    (if-let true (u64_lt_eq c (ty_umax ty)))
    (iconst ty (imm64 c)))
(rule 1 (iconst_u $I128 c) (uextend $I128 (iconst_u $I64 c)))

;; These take `Value`, rather than going through `inst_data_value_tupled`,
;; because most of the time they want to return the original `Value`, and it
;; would be a waste to need to re-GVN the instruction data in those cases.
(decl multi sextend_maybe_etor (Type Value) Value)
(extern extractor infallible sextend_maybe_etor sextend_maybe_etor)
(decl multi uextend_maybe_etor (Type Value) Value)
(extern extractor infallible uextend_maybe_etor uextend_maybe_etor)

;; Match or Construct a possibly-`uextend`ed value.
;; Gives the extended-to type and inner value when matching something that was
;; extended, or the input value and its type when the value isn't an extension.
;; Useful to write a single pattern that can match things that may or may not
;; have undergone C's "usual arithmetic conversions".
;; When generating values, extending to the same type is invalid CLIF,
;; so this avoids doing that where there's no extension actually needed.
(decl uextend_maybe (Type Value) Value)
(extractor (uextend_maybe ty val) (uextend_maybe_etor ty val))
(rule 0 (uextend_maybe ty val) (uextend ty val))
(rule 1 (uextend_maybe ty val@(value_type ty)) val)

;; Same as `uextend_maybe` above, just for `sextend`.
(decl sextend_maybe (Type Value) Value)
(extractor (sextend_maybe ty val) (sextend_maybe_etor ty val))
(rule 0 (sextend_maybe ty val) (sextend ty val))
(rule 1 (sextend_maybe ty val@(value_type ty)) val)

;;;;;; Helper CLIF Extractors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(decl eq (Type Value Value) Value)
(extractor (eq ty x y) (icmp ty (IntCC.Equal) x y))

(decl ne (Type Value Value) Value)
(extractor (ne ty x y) (icmp ty (IntCC.NotEqual) x y))

(decl ult (Type Value Value) Value)
(extractor (ult ty x y) (icmp ty (IntCC.UnsignedLessThan) x y))

(decl ule (Type Value Value) Value)
(extractor (ule ty x y) (icmp ty (IntCC.UnsignedLessThanOrEqual) x y))

(decl ugt (Type Value Value) Value)
(extractor (ugt ty x y) (icmp ty (IntCC.UnsignedGreaterThan) x y))

(decl uge (Type Value Value) Value)
(extractor (uge ty x y) (icmp ty (IntCC.UnsignedGreaterThanOrEqual) x y))

(decl slt (Type Value Value) Value)
(extractor (slt ty x y) (icmp ty (IntCC.SignedLessThan) x y))

(decl sle (Type Value Value) Value)
(extractor (sle ty x y) (icmp ty (IntCC.SignedLessThanOrEqual) x y))

(decl sgt (Type Value Value) Value)
(extractor (sgt ty x y) (icmp ty (IntCC.SignedGreaterThan) x y))

(decl sge (Type Value Value) Value)
(extractor (sge ty x y) (icmp ty (IntCC.SignedGreaterThanOrEqual) x y))

;;;;;; Divison-By-Constant Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(decl pure i64_is_negative_power_of_two (i64) bool)
(rule (i64_is_negative_power_of_two x)
      (u64_is_power_of_two (i64_cast_unsigned (i64_wrapping_neg x))))

(decl pure i64_is_any_sign_power_of_two (i64) bool)
(rule 2 (i64_is_any_sign_power_of_two x)
      (if-let true (u64_is_power_of_two (i64_cast_unsigned x)))
      true)
(rule 1 (i64_is_any_sign_power_of_two x)
      (if-let true (i64_is_negative_power_of_two x))
      true)
(rule 0 (i64_is_any_sign_power_of_two _) false)

(type DivConstMagicU32 (enum (U32 (mul_by u32)
                                  (do_add bool)
                                  (shift_by u32))))
(type DivConstMagicU64 (enum (U64 (mul_by u64)
                                  (do_add bool)
                                  (shift_by u32))))
(type DivConstMagicS32 (enum (S32 (mul_by i32)
                                  (shift_by u32))))
(type DivConstMagicS64 (enum (S64 (mul_by i64)
                                  (shift_by u32))))

;; Extern magic-const constructors and accessors.

(decl div_const_magic_u32 (u32) DivConstMagicU32)
(extern constructor div_const_magic_u32 div_const_magic_u32)

(decl div_const_magic_u64 (u64) DivConstMagicU64)
(extern constructor div_const_magic_u64 div_const_magic_u64)

(decl div_const_magic_s32 (i32) DivConstMagicS32)
(extern constructor div_const_magic_s32 div_const_magic_s32)

(decl div_const_magic_s64 (i64) DivConstMagicS64)
(extern constructor div_const_magic_s64 div_const_magic_s64)

;; Applying div-const magic for u32.
(decl apply_div_const_magic_u32 (Opcode Value u32) Value)
(rule (apply_div_const_magic_u32 opcode numerator divisor)
      (apply_div_const_magic_u32_inner opcode numerator divisor (div_const_magic_u32 divisor)))

;; q0 = umuli numerator, mul_by
(decl apply_div_const_magic_u32_inner (Opcode Value u32 DivConstMagicU32) Value)
(rule (apply_div_const_magic_u32_inner opcode
                                       numerator
                                       divisor
                                       magic @ (DivConstMagicU32.U32 mul_by _do_add _shift_by))
      (let ((q0 Value (umulhi $I32 numerator (iconst_u $I32 mul_by))))
        (apply_div_const_magic_u32_maybe_add opcode numerator divisor magic q0)))

;; qf = if do_add {
;;     q1 = isub numerator, q0
;;     q2 = ushr q1, 1
;;     q3 = iadd q0, q2
;;     maybe_shift(q3)
;; } else {
;;     ushr q0, shift_by
;; };
(decl apply_div_const_magic_u32_maybe_add (Opcode Value u32 DivConstMagicU32 Value) Value)
(rule (apply_div_const_magic_u32_maybe_add opcode
                                           numerator
                                           divisor
                                           magic @ (DivConstMagicU32.U32 _mul_by true _shift_by)
                                           q0)
      (let ((q1 Value (isub $I32 numerator q0))
            (q2 Value (ushr $I32 q1 (iconst_u $I32 1)))
            (q3 Value (iadd $I32 q0 q2)))
        (apply_div_const_magic_u32_maybe_shift opcode numerator divisor magic q3)))
(rule (apply_div_const_magic_u32_maybe_add opcode
                                           numerator
                                           divisor
                                           magic @ (DivConstMagicU32.U32 _mul_by false shift_by)
                                           q0)
      (let ((q3 Value (ushr $I32 q0 (iconst_u $I32 shift_by))))
        (apply_div_const_magic_u32_finish opcode numerator divisor q3)))

;; qf = if shift_by == 0 {
;;     q3
;; } else {
;;     ushr q3, shift_by - 1
;; };
(decl apply_div_const_magic_u32_maybe_shift (Opcode Value u32 DivConstMagicU32 Value) Value)
(rule 2 (apply_div_const_magic_u32_maybe_shift opcode
                                                          numerator
                                                          divisor
                                                          (DivConstMagicU32.U32 _mul_by _do_add 0)
                                                          q3)
      (apply_div_const_magic_u32_finish opcode numerator divisor q3))
(rule 1 (apply_div_const_magic_u32_maybe_shift opcode
                                               numerator
                                               divisor
                                               (DivConstMagicU32.U32 _mul_by
                                                                     _do_add
                                                                     (u32_extract_non_zero shift_by))
                                               q3)
      (let ((qf Value (ushr $I32 q3 (iconst_u $I32 (u32_sub shift_by 1)))))
        (apply_div_const_magic_u32_finish opcode numerator divisor qf)))

;; Now `qf` holds the final quotient. If necessary calculate the remainder
;; instead.
;;
;; if Opcode == Urem {
;;     tt = imul qf, divisor
;;     isub numerator, tt
;; } else {
;;     qf
;; }
(decl apply_div_const_magic_u32_finish (Opcode Value u32 Value) Value)
(rule (apply_div_const_magic_u32_finish (Opcode.Udiv) _ _ qf) qf)
(rule (apply_div_const_magic_u32_finish (Opcode.Urem) numerator divisor qf)
      (let ((tt Value (imul $I32 qf (iconst_u $I32 divisor))))
        (isub $I32 numerator tt)))

;; Applying div-const magic for u64.
(decl apply_div_const_magic_u64 (Opcode Value u64) Value)
(rule (apply_div_const_magic_u64 opcode numerator divisor)
      (apply_div_const_magic_u64_inner opcode numerator divisor (div_const_magic_u64 divisor)))

;; q0 = umuli numerator, mul_by
(decl apply_div_const_magic_u64_inner (Opcode Value u64 DivConstMagicU64) Value)
(rule (apply_div_const_magic_u64_inner opcode
                                       numerator
                                       divisor
                                       magic @ (DivConstMagicU64.U64 mul_by _do_add _shift_by))
      (let ((q0 Value (umulhi $I64 numerator (iconst_u $I64 mul_by))))
        (apply_div_const_magic_u64_maybe_add opcode numerator divisor magic q0)))

;; qf = if do_add {
;;     q1 = isub numerator, q0
;;     q2 = ushr q1, 1
;;     q3 = iadd q0, q2
;;     maybe_shift(q3)
;; } else {
;;     ushr q0, shift_by
;; };
(decl apply_div_const_magic_u64_maybe_add (Opcode Value u64 DivConstMagicU64 Value) Value)
(rule (apply_div_const_magic_u64_maybe_add opcode
                                           numerator
                                           divisor
                                           magic @ (DivConstMagicU64.U64 _mul_by true _shift_by)
                                           q0)
      (let ((q1 Value (isub $I64 numerator q0))
            (q2 Value (ushr $I64 q1 (iconst_u $I64 1)))
            (q3 Value (iadd $I64 q0 q2)))
        (apply_div_const_magic_u64_maybe_shift opcode numerator divisor magic q3)))
(rule (apply_div_const_magic_u64_maybe_add opcode
                                           numerator
                                           divisor
                                           magic @ (DivConstMagicU64.U64 _mul_by false shift_by)
                                           q0)
      (let ((q3 Value (ushr $I64 q0 (iconst_u $I64 shift_by))))
        (apply_div_const_magic_u64_finish opcode numerator divisor q3)))

;; qf = if shift_by == 0 {
;;     q3
;; } else {
;;     ushr q3, shift_by - 1
;; };
(decl apply_div_const_magic_u64_maybe_shift (Opcode Value u64 DivConstMagicU64 Value) Value)
(rule 2 (apply_div_const_magic_u64_maybe_shift opcode
                                                          numerator
                                                          divisor
                                                          (DivConstMagicU64.U64 _mul_by _do_add 0)
                                                          q3)
      (apply_div_const_magic_u64_finish opcode numerator divisor q3))
(rule 1 (apply_div_const_magic_u64_maybe_shift opcode
                                               numerator
                                               divisor
                                               (DivConstMagicU64.U64 _mul_by
                                                                     _do_add
                                                                     (u32_extract_non_zero shift_by))
                                               q3)
      (let ((qf Value (ushr $I64 q3 (iconst_u $I64 (u64_sub shift_by 1)))))
        (apply_div_const_magic_u64_finish opcode numerator divisor qf)))

;; Now `qf` holds the final quotient. If necessary calculate the remainder
;; instead.
;;
;; if Opcode == Urem {
;;     tt = imul qf, divisor
;;     isub numerator, tt
;; } else {
;;     qf
;; }
(decl apply_div_const_magic_u64_finish (Opcode Value u64 Value) Value)
(rule (apply_div_const_magic_u64_finish (Opcode.Udiv) _ _ qf) qf)
(rule (apply_div_const_magic_u64_finish (Opcode.Urem) numerator divisor qf)
      (let ((tt Value (imul $I64 qf (iconst_u $I64 divisor))))
        (isub $I64 numerator tt)))

;; Applying div-const magic for s32.

(decl apply_div_const_magic_s32 (Opcode Value i32) Value)
(rule (apply_div_const_magic_s32 opcode numerator divisor)
      (apply_div_const_magic_s32_inner opcode numerator divisor (div_const_magic_s32 divisor)))

;; q0 = iconst.i32 mul_by
;; q1 = smulhi numerator, q0
(decl apply_div_const_magic_s32_inner (Opcode Value i32 DivConstMagicS32) Value)
(rule (apply_div_const_magic_s32_inner opcode
                                       numerator
                                       divisor
                                       magic @ (DivConstMagicS32.S32 mul_by _shift_by))
      (let ((q0 Value (iconst_s $I32 mul_by))
            (q1 Value (smulhi $I32 numerator q0)))
        (apply_div_const_magic_s32_add_sub opcode numerator divisor magic q1)))

;; q2 = if divisor > 0 && mul_by < 0 {
;;     iadd q1, numerator
;; } else if divisor < 0 && mul_by > 0 {
;;     isub q1, numerator
;; } else {
;;     q1
;; };
(decl apply_div_const_magic_s32_add_sub (Opcode Value i32 DivConstMagicS32 Value) Value)
(rule 2 (apply_div_const_magic_s32_add_sub opcode
                                           numerator
                                           divisor
                                           magic @ (DivConstMagicS32.S32 mul_by _shift_by)
                                           q1)
      (if-let true (i32_gt divisor 0))
      (if-let true (i32_lt mul_by 0))
      (let ((q2 Value (iadd $I32 q1 numerator)))
        (apply_div_const_magic_s32_shift opcode numerator divisor magic q2)))
(rule 1 (apply_div_const_magic_s32_add_sub opcode
                                           numerator
                                           divisor
                                           magic @ (DivConstMagicS32.S32 mul_by _shift_by)
                                           q1)
      (if-let true (i32_lt divisor 0))
      (if-let true (i32_gt mul_by 0))
      (let ((q2 Value (isub $I32 q1 numerator)))
        (apply_div_const_magic_s32_shift opcode numerator divisor magic q2)))
(rule 0 (apply_div_const_magic_s32_add_sub opcode
                                           numerator
                                           divisor
                                           magic
                                           q1)
      (apply_div_const_magic_s32_shift opcode numerator divisor magic q1))

;; q3 = sshr q2, shift_by
;; t1 = ushr q3, 31
;; qf = iadd q3, t1
(decl apply_div_const_magic_s32_shift (Opcode Value i32 DivConstMagicS32 Value) Value)
(rule (apply_div_const_magic_s32_shift opcode
                                       numerator
                                       divisor
                                       magic @ (DivConstMagicS32.S32 _mul_by shift_by)
                                       q2)
      ;; Note: let other rules clean up `q2` when `shift_by == 0`.
      (let ((q3 Value (sshr $I32 q2 (iconst_s $I32 shift_by)))
            (t1 Value (ushr $I32 q3 (iconst_s $I32 31)))
            (qf Value (iadd $I32 q3 t1)))
        (apply_div_const_magic_s32_finish opcode numerator divisor qf)))

;; Now `qf` holds the final quotient. If necessary, produce the remainder
;; instead.
;;
;; if Opcode == Srem {
;;     tt = imul qf, divisor
;;     isub numerator, tt
;; } else {
;;     qf
;; }
(decl apply_div_const_magic_s32_finish (Opcode Value i32 Value) Value)
(rule (apply_div_const_magic_s32_finish (Opcode.Srem) numerator divisor qf)
      (let ((tt Value (imul $I32 qf (iconst_s $I32 divisor))))
        (isub $I32 numerator tt)))
(rule (apply_div_const_magic_s32_finish (Opcode.Sdiv) _numerator _divisor qf)
      qf)

;; Applying div-const magic for s64.

(decl apply_div_const_magic_s64 (Opcode Value i64) Value)
(rule (apply_div_const_magic_s64 opcode numerator divisor)
      (apply_div_const_magic_s64_inner opcode numerator divisor (div_const_magic_s64 divisor)))

;; q0 = iconst.i64 mul_by
;; q1 = smulhi numerator, q0
(decl apply_div_const_magic_s64_inner (Opcode Value i64 DivConstMagicS64) Value)
(rule (apply_div_const_magic_s64_inner opcode
                                       numerator
                                       divisor
                                       magic @ (DivConstMagicS64.S64 mul_by _shift_by))
      (let ((q0 Value (iconst_s $I64 mul_by))
            (q1 Value (smulhi $I64 numerator q0)))
        (apply_div_const_magic_s64_add_sub opcode numerator divisor magic q1)))

;; q2 = if divisor > 0 && mul_by < 0 {
;;     iadd q1, numerator
;; } else if divisor < 0 && mul_by > 0 {
;;     isub q1, numerator
;; } else {
;;     q1
;; };
(decl apply_div_const_magic_s64_add_sub (Opcode Value i64 DivConstMagicS64 Value) Value)
(rule 2 (apply_div_const_magic_s64_add_sub opcode
                                           numerator
                                           divisor
                                           magic @ (DivConstMagicS64.S64 mul_by _shift_by)
                                           q1)
      (if-let true (i64_gt divisor 0))
      (if-let true (i64_lt mul_by 0))
      (let ((q2 Value (iadd $I64 q1 numerator)))
        (apply_div_const_magic_s64_shift opcode numerator divisor magic q2)))
(rule 1 (apply_div_const_magic_s64_add_sub opcode
                                           numerator
                                           divisor
                                           magic @ (DivConstMagicS64.S64 mul_by _shift_by)
                                           q1)
      (if-let true (i64_lt divisor 0))
      (if-let true (i64_gt mul_by 0))
      (let ((q2 Value (isub $I64 q1 numerator)))
        (apply_div_const_magic_s64_shift opcode numerator divisor magic q2)))
(rule 0 (apply_div_const_magic_s64_add_sub opcode
                                           numerator
                                           divisor
                                           magic
                                           q1)
      (apply_div_const_magic_s64_shift opcode numerator divisor magic q1))

;; q3 = sshr q2, shift_by
;; t1 = ushr q3, 63
;; qf = iadd q3, t1
(decl apply_div_const_magic_s64_shift (Opcode Value i64 DivConstMagicS64 Value) Value)
(rule (apply_div_const_magic_s64_shift opcode
                                       numerator
                                       divisor
                                       magic @ (DivConstMagicS64.S64 _mul_by shift_by)
                                       q2)
      ;; Note: let other rules clean up `q2` when `shift_by == 0`.
      (let ((q3 Value (sshr $I64 q2 (iconst_s $I64 shift_by)))
            (t1 Value (ushr $I64 q3 (iconst_s $I64 63)))
            (qf Value (iadd $I64 q3 t1)))
        (apply_div_const_magic_s64_finish opcode numerator divisor qf)))

;; Now `qf` holds the final quotient. If necessary, produce the remainder
;; instead.
;;
;; if Opcode == Srem {
;;     tt = imul qf, divisor
;;     isub numerator, tt
;; } else {
;;     qf
;; }
(decl apply_div_const_magic_s64_finish (Opcode Value i64 Value) Value)
(rule (apply_div_const_magic_s64_finish (Opcode.Srem) numerator divisor qf)
      (let ((tt Value (imul $I64 qf (iconst_s $I64 divisor))))
        (isub $I64 numerator tt)))
(rule (apply_div_const_magic_s64_finish (Opcode.Sdiv) _numerator _divisor qf)
      qf)