wasmi 2.0.0-beta.2

WebAssembly interpreter
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
use crate::ir::{self, BranchOffset, Op};

pub trait NegateCmpInstr: Sized {
    /// Negates the compare (`cmp`) [`Op`].
    fn negate_cmp_instr(&self) -> Option<Self>;
}

impl NegateCmpInstr for Op {
    fn negate_cmp_instr(&self) -> Option<Self> {
        #[rustfmt::skip]
        let negated = match *self {
            // i32
            | Op::I32Eq_Sss { result, lhs, rhs } => Op::i32_not_eq_sss(result, lhs, rhs),
            | Op::I32Eq_Ssi { result, lhs, rhs } => Op::i32_not_eq_ssi(result, lhs, rhs),
            | Op::I32And_Sss { result, lhs, rhs }
            | Op::I32BitAnd_Sss { result, lhs, rhs } => Op::i32_not_and_sss(result, lhs, rhs),
            | Op::I32And_Ssi { result, lhs, rhs }
            | Op::I32BitAnd_Ssi { result, lhs, rhs } => Op::i32_not_and_ssi(result, lhs, rhs),
            | Op::I32Or_Sss { result, lhs, rhs }
            | Op::I32BitOr_Sss { result, lhs, rhs } => Op::i32_not_or_sss(result, lhs, rhs),
            | Op::I32Or_Ssi { result, lhs, rhs }
            | Op::I32BitOr_Ssi { result, lhs, rhs } => Op::i32_not_or_ssi(result, lhs, rhs),
            | Op::I32NotEq_Sss { result, lhs, rhs }
            | Op::I32BitXor_Sss { result, lhs, rhs } => Op::i32_eq_sss(result, lhs, rhs),
            | Op::I32NotEq_Ssi { result, lhs, rhs }
            | Op::I32BitXor_Ssi { result, lhs, rhs } => Op::i32_eq_ssi(result, lhs, rhs),
            | Op::I32NotAnd_Sss { result, lhs, rhs } => Op::i32_and_sss(result, lhs, rhs),
            | Op::I32NotAnd_Ssi { result, lhs, rhs } => Op::i32_and_ssi(result, lhs, rhs),
            | Op::I32NotOr_Sss { result, lhs, rhs } => Op::i32_or_sss(result, lhs, rhs),
            | Op::I32NotOr_Ssi { result, lhs, rhs } => Op::i32_or_ssi(result, lhs, rhs),
            | Op::I32Lt_Sss { result, lhs, rhs } => Op::i32_le_sss(result, rhs, lhs),
            | Op::I32Lt_Ssi { result, lhs, rhs } => Op::i32_le_sis(result, rhs, lhs),
            | Op::I32Lt_Sis { result, lhs, rhs } => Op::i32_le_ssi(result, rhs, lhs),
            | Op::U32Lt_Sss { result, lhs, rhs } => Op::u32_le_sss(result, rhs, lhs),
            | Op::U32Lt_Ssi { result, lhs, rhs } => Op::u32_le_sis(result, rhs, lhs),
            | Op::U32Lt_Sis { result, lhs, rhs } => Op::u32_le_ssi(result, rhs, lhs),
            | Op::I32Le_Sss { result, lhs, rhs } => Op::i32_lt_sss(result, rhs, lhs),
            | Op::I32Le_Ssi { result, lhs, rhs } => Op::i32_lt_sis(result, rhs, lhs),
            | Op::I32Le_Sis { result, lhs, rhs } => Op::i32_lt_ssi(result, rhs, lhs),
            | Op::U32Le_Sss { result, lhs, rhs } => Op::u32_lt_sss(result, rhs, lhs),
            | Op::U32Le_Ssi { result, lhs, rhs } => Op::u32_lt_sis(result, rhs, lhs),
            | Op::U32Le_Sis { result, lhs, rhs } => Op::u32_lt_ssi(result, rhs, lhs),
            // i64
            | Op::I64Eq_Sss { result, lhs, rhs } => Op::i64_not_eq_sss(result, lhs, rhs),
            | Op::I64Eq_Ssi { result, lhs, rhs } => Op::i64_not_eq_ssi(result, lhs, rhs),
            | Op::I64And_Sss { result, lhs, rhs }
            | Op::I64BitAnd_Sss { result, lhs, rhs } => Op::i64_not_and_sss(result, lhs, rhs),
            | Op::I64And_Ssi { result, lhs, rhs }
            | Op::I64BitAnd_Ssi { result, lhs, rhs } => Op::i64_not_and_ssi(result, lhs, rhs),
            | Op::I64Or_Sss { result, lhs, rhs }
            | Op::I64BitOr_Sss { result, lhs, rhs } => Op::i64_not_or_sss(result, lhs, rhs),
            | Op::I64Or_Ssi { result, lhs, rhs }
            | Op::I64BitOr_Ssi { result, lhs, rhs } => Op::i64_not_or_ssi(result, lhs, rhs),
            | Op::I64NotEq_Sss { result, lhs, rhs }
            | Op::I64BitXor_Sss { result, lhs, rhs } => Op::i64_eq_sss(result, lhs, rhs),
            | Op::I64NotEq_Ssi { result, lhs, rhs }
            | Op::I64BitXor_Ssi { result, lhs, rhs } => Op::i64_eq_ssi(result, lhs, rhs),
            | Op::I64NotAnd_Sss { result, lhs, rhs } => Op::i64_and_sss(result, lhs, rhs),
            | Op::I64NotAnd_Ssi { result, lhs, rhs } => Op::i64_and_ssi(result, lhs, rhs),
            | Op::I64NotOr_Sss { result, lhs, rhs } => Op::i64_or_sss(result, lhs, rhs),
            | Op::I64NotOr_Ssi { result, lhs, rhs } => Op::i64_or_ssi(result, lhs, rhs),
            | Op::I64Lt_Sss { result, lhs, rhs } => Op::i64_le_sss(result, rhs, lhs),
            | Op::I64Lt_Ssi { result, lhs, rhs } => Op::i64_le_sis(result, rhs, lhs),
            | Op::I64Lt_Sis { result, lhs, rhs } => Op::i64_le_ssi(result, rhs, lhs),
            | Op::U64Lt_Sss { result, lhs, rhs } => Op::u64_le_sss(result, rhs, lhs),
            | Op::U64Lt_Ssi { result, lhs, rhs } => Op::u64_le_sis(result, rhs, lhs),
            | Op::U64Lt_Sis { result, lhs, rhs } => Op::u64_le_ssi(result, rhs, lhs),
            | Op::I64Le_Sss { result, lhs, rhs } => Op::i64_lt_sss(result, rhs, lhs),
            | Op::I64Le_Ssi { result, lhs, rhs } => Op::i64_lt_sis(result, rhs, lhs),
            | Op::I64Le_Sis { result, lhs, rhs } => Op::i64_lt_ssi(result, rhs, lhs),
            | Op::U64Le_Sss { result, lhs, rhs } => Op::u64_lt_sss(result, rhs, lhs),
            | Op::U64Le_Ssi { result, lhs, rhs } => Op::u64_lt_sis(result, rhs, lhs),
            | Op::U64Le_Sis { result, lhs, rhs } => Op::u64_lt_ssi(result, rhs, lhs),
            // f32
            Op::F32Eq_Sss { result, lhs, rhs } => Op::f32_not_eq_sss(result, lhs, rhs),
            Op::F32Eq_Ssi { result, lhs, rhs } => Op::f32_not_eq_ssi(result, lhs, rhs),
            Op::F32Le_Sss { result, lhs, rhs } => Op::f32_not_le_sss(result, lhs, rhs),
            Op::F32Le_Ssi { result, lhs, rhs } => Op::f32_not_le_ssi(result, lhs, rhs),
            Op::F32Le_Sis { result, lhs, rhs } => Op::f32_not_le_sis(result, lhs, rhs),
            Op::F32Lt_Sss { result, lhs, rhs } => Op::f32_not_lt_sss(result, lhs, rhs),
            Op::F32Lt_Ssi { result, lhs, rhs } => Op::f32_not_lt_ssi(result, lhs, rhs),
            Op::F32Lt_Sis { result, lhs, rhs } => Op::f32_not_lt_sis(result, lhs, rhs),
            Op::F32NotEq_Sss { result, lhs, rhs } => Op::f32_eq_sss(result, lhs, rhs),
            Op::F32NotEq_Ssi { result, lhs, rhs } => Op::f32_eq_ssi(result, lhs, rhs),
            Op::F32NotLe_Sss { result, lhs, rhs } => Op::f32_le_sss(result, lhs, rhs),
            Op::F32NotLe_Ssi { result, lhs, rhs } => Op::f32_le_ssi(result, lhs, rhs),
            Op::F32NotLe_Sis { result, lhs, rhs } => Op::f32_le_sis(result, lhs, rhs),
            Op::F32NotLt_Sss { result, lhs, rhs } => Op::f32_lt_sss(result, lhs, rhs),
            Op::F32NotLt_Ssi { result, lhs, rhs } => Op::f32_lt_ssi(result, lhs, rhs),
            Op::F32NotLt_Sis { result, lhs, rhs } => Op::f32_lt_sis(result, lhs, rhs),
            // f64
            Op::F64Eq_Sss { result, lhs, rhs } => Op::f64_not_eq_sss(result, lhs, rhs),
            Op::F64Eq_Ssi { result, lhs, rhs } => Op::f64_not_eq_ssi(result, lhs, rhs),
            Op::F64Le_Sss { result, lhs, rhs } => Op::f64_not_le_sss(result, lhs, rhs),
            Op::F64Le_Ssi { result, lhs, rhs } => Op::f64_not_le_ssi(result, lhs, rhs),
            Op::F64Le_Sis { result, lhs, rhs } => Op::f64_not_le_sis(result, lhs, rhs),
            Op::F64Lt_Sss { result, lhs, rhs } => Op::f64_not_lt_sss(result, lhs, rhs),
            Op::F64Lt_Ssi { result, lhs, rhs } => Op::f64_not_lt_ssi(result, lhs, rhs),
            Op::F64Lt_Sis { result, lhs, rhs } => Op::f64_not_lt_sis(result, lhs, rhs),
            Op::F64NotEq_Sss { result, lhs, rhs } => Op::f64_eq_sss(result, lhs, rhs),
            Op::F64NotEq_Ssi { result, lhs, rhs } => Op::f64_eq_ssi(result, lhs, rhs),
            Op::F64NotLe_Sss { result, lhs, rhs } => Op::f64_le_sss(result, lhs, rhs),
            Op::F64NotLe_Ssi { result, lhs, rhs } => Op::f64_le_ssi(result, lhs, rhs),
            Op::F64NotLe_Sis { result, lhs, rhs } => Op::f64_le_sis(result, lhs, rhs),
            Op::F64NotLt_Sss { result, lhs, rhs } => Op::f64_lt_sss(result, lhs, rhs),
            Op::F64NotLt_Ssi { result, lhs, rhs } => Op::f64_lt_ssi(result, lhs, rhs),
            Op::F64NotLt_Sis { result, lhs, rhs } => Op::f64_lt_sis(result, lhs, rhs),
            _ => return None,
        };
        Some(negated)
    }
}

pub trait LogicalizeCmpInstr: Sized {
    /// Logicalizes the compare (`cmp`) [`Op`].
    ///
    /// This mainly turns bitwise [`Op`]s into logical ones.
    /// Logical instructions are simply unchanged.
    fn logicalize_cmp_instr(&self) -> Option<Self>;
}

impl LogicalizeCmpInstr for Op {
    fn logicalize_cmp_instr(&self) -> Option<Self> {
        #[rustfmt::skip]
        let logicalized = match *self {
            // Bitwise -> Logical: i32
            | Op::I32BitAnd_Sss { result, lhs, rhs } => Op::i32_and_sss(result, lhs, rhs),
            | Op::I32BitOr_Sss { result, lhs, rhs } => Op::i32_or_sss(result, lhs, rhs),
            | Op::I32BitXor_Sss { result, lhs, rhs } => Op::i32_not_eq_sss(result, lhs, rhs),
            | Op::I32BitAnd_Ssi { result, lhs, rhs } => Op::i32_and_ssi(result, lhs, rhs),
            | Op::I32BitOr_Ssi { result, lhs, rhs } => Op::i32_or_ssi(result, lhs, rhs),
            | Op::I32BitXor_Ssi { result, lhs, rhs } => Op::i32_not_eq_ssi(result, lhs, rhs),
            // Bitwise -> Logical: i64
            | Op::I64BitAnd_Sss { result, lhs, rhs } => Op::i64_and_sss(result, lhs, rhs),
            | Op::I64BitOr_Sss { result, lhs, rhs } => Op::i64_or_sss(result, lhs, rhs),
            | Op::I64BitXor_Sss { result, lhs, rhs } => Op::i64_not_eq_sss(result, lhs, rhs),
            | Op::I64BitAnd_Ssi { result, lhs, rhs } => Op::i64_and_ssi(result, lhs, rhs),
            | Op::I64BitOr_Ssi { result, lhs, rhs } => Op::i64_or_ssi(result, lhs, rhs),
            | Op::I64BitXor_Ssi { result, lhs, rhs } => Op::i64_not_eq_ssi(result, lhs, rhs),
            // Logical -> Logical
            // i32
            | Op::I32Eq_Sss { .. }
            | Op::I32Eq_Ssi { .. }
            | Op::I32And_Sss { .. }
            | Op::I32And_Ssi { .. }
            | Op::I32Or_Sss { .. }
            | Op::I32Or_Ssi { .. }
            | Op::I32NotEq_Sss { .. }
            | Op::I32NotEq_Ssi { .. }
            | Op::I32NotAnd_Sss { .. }
            | Op::I32NotAnd_Ssi { .. }
            | Op::I32NotOr_Sss { .. }
            | Op::I32NotOr_Ssi { .. }
            | Op::I32Lt_Sss { .. }
            | Op::I32Lt_Ssi { .. }
            | Op::I32Lt_Sis { .. }
            | Op::U32Lt_Sss { .. }
            | Op::U32Lt_Ssi { .. }
            | Op::U32Lt_Sis { .. }
            | Op::I32Le_Sss { .. }
            | Op::I32Le_Ssi { .. }
            | Op::I32Le_Sis { .. }
            | Op::U32Le_Sss { .. }
            | Op::U32Le_Ssi { .. }
            | Op::U32Le_Sis { .. }
            // i64
            | Op::I64Eq_Sss { .. }
            | Op::I64Eq_Ssi { .. }
            | Op::I64And_Sss { .. }
            | Op::I64And_Ssi { .. }
            | Op::I64Or_Sss { .. }
            | Op::I64Or_Ssi { .. }
            | Op::I64NotEq_Sss { .. }
            | Op::I64NotEq_Ssi { .. }
            | Op::I64NotAnd_Sss { .. }
            | Op::I64NotAnd_Ssi { .. }
            | Op::I64NotOr_Sss { .. }
            | Op::I64NotOr_Ssi { .. }
            | Op::I64Lt_Sss { .. }
            | Op::I64Lt_Ssi { .. }
            | Op::I64Lt_Sis { .. }
            | Op::U64Lt_Sss { .. }
            | Op::U64Lt_Ssi { .. }
            | Op::U64Lt_Sis { .. }
            | Op::I64Le_Sss { .. }
            | Op::I64Le_Ssi { .. }
            | Op::I64Le_Sis { .. }
            | Op::U64Le_Sss { .. }
            | Op::U64Le_Ssi { .. }
            | Op::U64Le_Sis { .. }
            // f32
            | Op::F32Eq_Sss { .. }
            | Op::F32Eq_Ssi { .. }
            | Op::F32Le_Sss { .. }
            | Op::F32Le_Ssi { .. }
            | Op::F32Le_Sis { .. }
            | Op::F32Lt_Sss { .. }
            | Op::F32Lt_Ssi { .. }
            | Op::F32Lt_Sis { .. }
            | Op::F32NotEq_Sss { .. }
            | Op::F32NotEq_Ssi { .. }
            | Op::F32NotLe_Sss { .. }
            | Op::F32NotLe_Ssi { .. }
            | Op::F32NotLe_Sis { .. }
            | Op::F32NotLt_Sss { .. }
            | Op::F32NotLt_Ssi { .. }
            | Op::F32NotLt_Sis { .. }
            // f64
            | Op::F64Eq_Sss { .. }
            | Op::F64Eq_Ssi { .. }
            | Op::F64Le_Sss { .. }
            | Op::F64Le_Ssi { .. }
            | Op::F64Le_Sis { .. }
            | Op::F64Lt_Sss { .. }
            | Op::F64Lt_Ssi { .. }
            | Op::F64Lt_Sis { .. }
            | Op::F64NotEq_Sss { .. }
            | Op::F64NotEq_Ssi { .. }
            | Op::F64NotLe_Sss { .. }
            | Op::F64NotLe_Ssi { .. }
            | Op::F64NotLe_Sis { .. }
            | Op::F64NotLt_Sss { .. }
            | Op::F64NotLt_Ssi { .. }
            | Op::F64NotLt_Sis { .. } => *self,
            _ => return None,
        };
        Some(logicalized)
    }
}

pub trait TryIntoCmpBranchInstr: Sized {
    fn try_into_cmp_branch_instr(&self, offset: BranchOffset) -> Option<Self>;
}

impl TryIntoCmpBranchInstr for Op {
    fn try_into_cmp_branch_instr(&self, offset: BranchOffset) -> Option<Self> {
        #[rustfmt::skip]
        let cmp_branch_instr = match *self {
            // i32
            | Op::I32Eq_Sss { lhs, rhs, .. } => Op::branch_i32_eq_ss(offset, lhs, rhs),
            | Op::I32Eq_Ssi { lhs, rhs, .. } => Op::branch_i32_eq_si(offset, lhs, rhs),
            | Op::I32And_Sss { lhs, rhs, .. }
            | Op::I32BitAnd_Sss { lhs, rhs, .. } => Op::branch_i32_and_ss(offset, lhs, rhs),
            | Op::I32And_Ssi { lhs, rhs, .. }
            | Op::I32BitAnd_Ssi { lhs, rhs, .. } => Op::branch_i32_and_si(offset, lhs, rhs),
            | Op::I32Or_Sss { lhs, rhs, .. }
            | Op::I32BitOr_Sss { lhs, rhs, .. } => Op::branch_i32_or_ss(offset, lhs, rhs),
            | Op::I32Or_Ssi { lhs, rhs, .. }
            | Op::I32BitOr_Ssi { lhs, rhs, .. } => Op::branch_i32_or_si(offset, lhs, rhs),
            | Op::I32NotEq_Sss { lhs, rhs, .. }
            | Op::I32BitXor_Sss { lhs, rhs, .. } => Op::branch_i32_not_eq_ss(offset, lhs, rhs),
            | Op::I32NotEq_Ssi { lhs, rhs, .. }
            | Op::I32BitXor_Ssi { lhs, rhs, .. } => Op::branch_i32_not_eq_si(offset, lhs, rhs),
            | Op::I32NotAnd_Sss { lhs, rhs, .. } => Op::branch_i32_not_and_ss(offset, lhs, rhs),
            | Op::I32NotAnd_Ssi { lhs, rhs, .. } => Op::branch_i32_not_and_si(offset, lhs, rhs),
            | Op::I32NotOr_Sss { lhs, rhs, .. } => Op::branch_i32_not_or_ss(offset, lhs, rhs),
            | Op::I32NotOr_Ssi { lhs, rhs, .. } => Op::branch_i32_not_or_si(offset, lhs, rhs),
            | Op::I32Lt_Sss { lhs, rhs, .. } => Op::branch_i32_lt_ss(offset, lhs, rhs),
            | Op::I32Lt_Ssi { lhs, rhs, .. } => Op::branch_i32_lt_si(offset, lhs, rhs),
            | Op::I32Lt_Sis { lhs, rhs, .. } => Op::branch_i32_lt_is(offset, lhs, rhs),
            | Op::U32Lt_Sss { lhs, rhs, .. } => Op::branch_u32_lt_ss(offset, lhs, rhs),
            | Op::U32Lt_Ssi { lhs, rhs, .. } => Op::branch_u32_lt_si(offset, lhs, rhs),
            | Op::U32Lt_Sis { lhs, rhs, .. } => Op::branch_u32_lt_is(offset, lhs, rhs),
            | Op::I32Le_Sss { lhs, rhs, .. } => Op::branch_i32_le_ss(offset, lhs, rhs),
            | Op::I32Le_Ssi { lhs, rhs, .. } => Op::branch_i32_le_si(offset, lhs, rhs),
            | Op::I32Le_Sis { lhs, rhs, .. } => Op::branch_i32_le_is(offset, lhs, rhs),
            | Op::U32Le_Sss { lhs, rhs, .. } => Op::branch_u32_le_ss(offset, lhs, rhs),
            | Op::U32Le_Ssi { lhs, rhs, .. } => Op::branch_u32_le_si(offset, lhs, rhs),
            | Op::U32Le_Sis { lhs, rhs, .. } => Op::branch_u32_le_is(offset, lhs, rhs),
            // i64
            | Op::I64Eq_Sss { lhs, rhs, .. } => Op::branch_i64_eq_ss(offset, lhs, rhs),
            | Op::I64Eq_Ssi { lhs, rhs, .. } => Op::branch_i64_eq_si(offset, lhs, rhs),
            | Op::I64And_Sss { lhs, rhs, .. }
            | Op::I64BitAnd_Sss { lhs, rhs, .. } => Op::branch_i64_and_ss(offset, lhs, rhs),
            | Op::I64And_Ssi { lhs, rhs, .. }
            | Op::I64BitAnd_Ssi { lhs, rhs, .. } => Op::branch_i64_and_si(offset, lhs, rhs),
            | Op::I64Or_Sss { lhs, rhs, .. }
            | Op::I64BitOr_Sss { lhs, rhs, .. } => Op::branch_i64_or_ss(offset, lhs, rhs),
            | Op::I64Or_Ssi { lhs, rhs, .. }
            | Op::I64BitOr_Ssi { lhs, rhs, .. } => Op::branch_i64_or_si(offset, lhs, rhs),
            | Op::I64NotEq_Sss { lhs, rhs, .. }
            | Op::I64BitXor_Sss { lhs, rhs, .. } => Op::branch_i64_not_eq_ss(offset, lhs, rhs),
            | Op::I64NotEq_Ssi { lhs, rhs, .. }
            | Op::I64BitXor_Ssi { lhs, rhs, .. } => Op::branch_i64_not_eq_si(offset, lhs, rhs),
            | Op::I64NotAnd_Sss { lhs, rhs, .. } => Op::branch_i64_not_and_ss(offset, lhs, rhs),
            | Op::I64NotAnd_Ssi { lhs, rhs, .. } => Op::branch_i64_not_and_si(offset, lhs, rhs),
            | Op::I64NotOr_Sss { lhs, rhs, .. } => Op::branch_i64_not_or_ss(offset, lhs, rhs),
            | Op::I64NotOr_Ssi { lhs, rhs, .. } => Op::branch_i64_not_or_si(offset, lhs, rhs),
            | Op::I64Lt_Sss { lhs, rhs, .. } => Op::branch_i64_lt_ss(offset, lhs, rhs),
            | Op::I64Lt_Ssi { lhs, rhs, .. } => Op::branch_i64_lt_si(offset, lhs, rhs),
            | Op::I64Lt_Sis { lhs, rhs, .. } => Op::branch_i64_lt_is(offset, lhs, rhs),
            | Op::U64Lt_Sss { lhs, rhs, .. } => Op::branch_u64_lt_ss(offset, lhs, rhs),
            | Op::U64Lt_Ssi { lhs, rhs, .. } => Op::branch_u64_lt_si(offset, lhs, rhs),
            | Op::U64Lt_Sis { lhs, rhs, .. } => Op::branch_u64_lt_is(offset, lhs, rhs),
            | Op::I64Le_Sss { lhs, rhs, .. } => Op::branch_i64_le_ss(offset, lhs, rhs),
            | Op::I64Le_Ssi { lhs, rhs, .. } => Op::branch_i64_le_si(offset, lhs, rhs),
            | Op::I64Le_Sis { lhs, rhs, .. } => Op::branch_i64_le_is(offset, lhs, rhs),
            | Op::U64Le_Sss { lhs, rhs, .. } => Op::branch_u64_le_ss(offset, lhs, rhs),
            | Op::U64Le_Ssi { lhs, rhs, .. } => Op::branch_u64_le_si(offset, lhs, rhs),
            | Op::U64Le_Sis { lhs, rhs, .. } => Op::branch_u64_le_is(offset, lhs, rhs),
            // f32
            | Op::F32Eq_Sss { lhs, rhs, .. } => Op::branch_f32_eq_ss(offset, lhs, rhs),
            | Op::F32Eq_Ssi { lhs, rhs, .. } => Op::branch_f32_eq_si(offset, lhs, rhs),
            | Op::F32Lt_Sss { lhs, rhs, .. } => Op::branch_f32_lt_ss(offset, lhs, rhs),
            | Op::F32Lt_Ssi { lhs, rhs, .. } => Op::branch_f32_lt_si(offset, lhs, rhs),
            | Op::F32Lt_Sis { lhs, rhs, .. } => Op::branch_f32_lt_is(offset, lhs, rhs),
            | Op::F32Le_Sss { lhs, rhs, .. } => Op::branch_f32_le_ss(offset, lhs, rhs),
            | Op::F32Le_Ssi { lhs, rhs, .. } => Op::branch_f32_le_si(offset, lhs, rhs),
            | Op::F32Le_Sis { lhs, rhs, .. } => Op::branch_f32_le_is(offset, lhs, rhs),
            | Op::F32NotEq_Sss { lhs, rhs, .. } => Op::branch_f32_not_eq_ss(offset, lhs, rhs),
            | Op::F32NotEq_Ssi { lhs, rhs, .. } => Op::branch_f32_not_eq_si(offset, lhs, rhs),
            | Op::F32NotLt_Sss { lhs, rhs, .. } => Op::branch_f32_not_lt_ss(offset, lhs, rhs),
            | Op::F32NotLt_Ssi { lhs, rhs, .. } => Op::branch_f32_not_lt_si(offset, lhs, rhs),
            | Op::F32NotLt_Sis { lhs, rhs, .. } => Op::branch_f32_not_lt_is(offset, lhs, rhs),
            | Op::F32NotLe_Sss { lhs, rhs, .. } => Op::branch_f32_not_le_ss(offset, lhs, rhs),
            | Op::F32NotLe_Ssi { lhs, rhs, .. } => Op::branch_f32_not_le_si(offset, lhs, rhs),
            | Op::F32NotLe_Sis { lhs, rhs, .. } => Op::branch_f32_not_le_is(offset, lhs, rhs),
            // f64
            | Op::F64Eq_Sss { lhs, rhs, .. } => Op::branch_f64_eq_ss(offset, lhs, rhs),
            | Op::F64Eq_Ssi { lhs, rhs, .. } => Op::branch_f64_eq_si(offset, lhs, rhs),
            | Op::F64Lt_Sss { lhs, rhs, .. } => Op::branch_f64_lt_ss(offset, lhs, rhs),
            | Op::F64Lt_Ssi { lhs, rhs, .. } => Op::branch_f64_lt_si(offset, lhs, rhs),
            | Op::F64Lt_Sis { lhs, rhs, .. } => Op::branch_f64_lt_is(offset, lhs, rhs),
            | Op::F64Le_Sss { lhs, rhs, .. } => Op::branch_f64_le_ss(offset, lhs, rhs),
            | Op::F64Le_Ssi { lhs, rhs, .. } => Op::branch_f64_le_si(offset, lhs, rhs),
            | Op::F64Le_Sis { lhs, rhs, .. } => Op::branch_f64_le_is(offset, lhs, rhs),
            | Op::F64NotEq_Sss { lhs, rhs, .. } => Op::branch_f64_not_eq_ss(offset, lhs, rhs),
            | Op::F64NotEq_Ssi { lhs, rhs, .. } => Op::branch_f64_not_eq_si(offset, lhs, rhs),
            | Op::F64NotLt_Sss { lhs, rhs, .. } => Op::branch_f64_not_lt_ss(offset, lhs, rhs),
            | Op::F64NotLt_Ssi { lhs, rhs, .. } => Op::branch_f64_not_lt_si(offset, lhs, rhs),
            | Op::F64NotLt_Sis { lhs, rhs, .. } => Op::branch_f64_not_lt_is(offset, lhs, rhs),
            | Op::F64NotLe_Sss { lhs, rhs, .. } => Op::branch_f64_not_le_ss(offset, lhs, rhs),
            | Op::F64NotLe_Ssi { lhs, rhs, .. } => Op::branch_f64_not_le_si(offset, lhs, rhs),
            | Op::F64NotLe_Sis { lhs, rhs, .. } => Op::branch_f64_not_le_is(offset, lhs, rhs),
            _ => return None,
        };
        Some(cmp_branch_instr)
    }
}

/// Extension trait for [`Op`] to update [`BranchOffset`] of branch operators.
pub trait UpdateBranchOffset: Sized {
    /// Updates the [`BranchOffset`] of `self` to `new_offset`.
    ///
    /// # Panics
    ///
    /// - If `self` does not have a [`BranchOffset`] to update.
    /// - If the [`BranchOffset`] of `self` is already initialized. (Debug)
    fn update_branch_offset(&mut self, new_offset: BranchOffset);

    /// Consumes `self` and returns it back with its [`BranchOffset`] set to `new_offset`.
    fn with_branch_offset(self, new_offset: BranchOffset) -> Self {
        let mut this = self;
        this.update_branch_offset(new_offset);
        this
    }
}

impl UpdateBranchOffset for ir::BranchOffset {
    fn update_branch_offset(&mut self, new_offset: BranchOffset) {
        *self = new_offset;
    }
}

impl UpdateBranchOffset for ir::BranchTableTarget {
    fn update_branch_offset(&mut self, new_offset: BranchOffset) {
        self.offset = new_offset;
    }
}

impl UpdateBranchOffset for Op {
    #[track_caller]
    fn update_branch_offset(&mut self, new_offset: BranchOffset) {
        match self {
            // unconditional
            | Op::Branch { offset }
            // i32
            | Op::BranchI32Eq_Ss { offset, .. }
            | Op::BranchI32Eq_Si { offset, .. }
            | Op::BranchI32And_Ss { offset, .. }
            | Op::BranchI32And_Si { offset, .. }
            | Op::BranchI32Or_Ss { offset, .. }
            | Op::BranchI32Or_Si { offset, .. }
            | Op::BranchI32NotEq_Ss { offset, .. }
            | Op::BranchI32NotEq_Si { offset, .. }
            | Op::BranchI32NotAnd_Ss { offset, .. }
            | Op::BranchI32NotAnd_Si { offset, .. }
            | Op::BranchI32NotOr_Ss { offset, .. }
            | Op::BranchI32NotOr_Si { offset, .. }
            | Op::BranchI32Lt_Ss { offset, .. }
            | Op::BranchI32Lt_Si { offset, .. }
            | Op::BranchI32Lt_Is { offset, .. }
            | Op::BranchU32Lt_Ss { offset, .. }
            | Op::BranchU32Lt_Si { offset, .. }
            | Op::BranchU32Lt_Is { offset, .. }
            | Op::BranchI32Le_Ss { offset, .. }
            | Op::BranchI32Le_Si { offset, .. }
            | Op::BranchI32Le_Is { offset, .. }
            | Op::BranchU32Le_Ss { offset, .. }
            | Op::BranchU32Le_Si { offset, .. }
            | Op::BranchU32Le_Is { offset, .. }
            // i64
            | Op::BranchI64Eq_Ss { offset, .. }
            | Op::BranchI64Eq_Si { offset, .. }
            | Op::BranchI64And_Ss { offset, .. }
            | Op::BranchI64And_Si { offset, .. }
            | Op::BranchI64Or_Ss { offset, .. }
            | Op::BranchI64Or_Si { offset, .. }
            | Op::BranchI64NotEq_Ss { offset, .. }
            | Op::BranchI64NotEq_Si { offset, .. }
            | Op::BranchI64NotAnd_Ss { offset, .. }
            | Op::BranchI64NotAnd_Si { offset, .. }
            | Op::BranchI64NotOr_Ss { offset, .. }
            | Op::BranchI64NotOr_Si { offset, .. }
            | Op::BranchI64Lt_Ss { offset, .. }
            | Op::BranchI64Lt_Si { offset, .. }
            | Op::BranchI64Lt_Is { offset, .. }
            | Op::BranchU64Lt_Ss { offset, .. }
            | Op::BranchU64Lt_Si { offset, .. }
            | Op::BranchU64Lt_Is { offset, .. }
            | Op::BranchI64Le_Ss { offset, .. }
            | Op::BranchI64Le_Si { offset, .. }
            | Op::BranchI64Le_Is { offset, .. }
            | Op::BranchU64Le_Ss { offset, .. }
            | Op::BranchU64Le_Si { offset, .. }
            | Op::BranchU64Le_Is { offset, .. }
            // f32
            | Op::BranchF32Eq_Ss { offset, .. }
            | Op::BranchF32Eq_Si { offset, .. }
            | Op::BranchF32Lt_Ss { offset, .. }
            | Op::BranchF32Lt_Si { offset, .. }
            | Op::BranchF32Lt_Is { offset, .. }
            | Op::BranchF32Le_Ss { offset, .. }
            | Op::BranchF32Le_Si { offset, .. }
            | Op::BranchF32Le_Is { offset, .. }
            | Op::BranchF32NotEq_Ss { offset, .. }
            | Op::BranchF32NotEq_Si { offset, .. }
            | Op::BranchF32NotLt_Ss { offset, .. }
            | Op::BranchF32NotLt_Si { offset, .. }
            | Op::BranchF32NotLt_Is { offset, .. }
            | Op::BranchF32NotLe_Ss { offset, .. }
            | Op::BranchF32NotLe_Si { offset, .. }
            | Op::BranchF32NotLe_Is { offset, .. }
            // f64
            | Op::BranchF64Eq_Ss { offset, .. }
            | Op::BranchF64Eq_Si { offset, .. }
            | Op::BranchF64Lt_Ss { offset, .. }
            | Op::BranchF64Lt_Si { offset, .. }
            | Op::BranchF64Lt_Is { offset, .. }
            | Op::BranchF64Le_Ss { offset, .. }
            | Op::BranchF64Le_Si { offset, .. }
            | Op::BranchF64Le_Is { offset, .. }
            | Op::BranchF64NotEq_Ss { offset, .. }
            | Op::BranchF64NotEq_Si { offset, .. }
            | Op::BranchF64NotLt_Ss { offset, .. }
            | Op::BranchF64NotLt_Si { offset, .. }
            | Op::BranchF64NotLt_Is { offset, .. }
            | Op::BranchF64NotLe_Ss { offset, .. }
            | Op::BranchF64NotLe_Si { offset, .. }
            | Op::BranchF64NotLe_Is { offset, .. } => {
                debug_assert!(!offset.is_init());
                *offset = new_offset;
            }
            _ => panic!("expected branch `Op` but found: {:?}", self),
        }
    }
}