@@ -11,7 +11,7 @@ mod visit;
11
11
12
12
use self :: {
13
13
instrs:: { InstrEncoder , InstrEncoderAllocations } ,
14
- layout:: StackLayout ,
14
+ layout:: { StackLayout , StackSpace } ,
15
15
stack:: {
16
16
BlockControlFrame ,
17
17
ControlFrame ,
@@ -23,19 +23,37 @@ use self::{
23
23
OperandIdx ,
24
24
Stack ,
25
25
StackAllocations ,
26
+ TempOperand ,
26
27
UnreachableControlFrame ,
27
28
} ,
28
29
utils:: { Reset , ReusableAllocations } ,
29
30
} ;
30
31
use crate :: {
31
32
core:: { FuelCostsProvider , Typed , TypedVal , ValType } ,
32
33
engine:: {
33
- translator:: { Instr , LabelRef , LabelRegistry , WasmTranslator } ,
34
+ translator:: {
35
+ comparator:: { CompareResult as _, NegateCmpInstr as _, TryIntoCmpBranchInstr as _} ,
36
+ Instr ,
37
+ LabelRef ,
38
+ LabelRegistry ,
39
+ WasmTranslator ,
40
+ } ,
34
41
BlockType ,
35
42
CompiledFuncEntity ,
36
43
TranslationError ,
37
44
} ,
38
- ir:: { BoundedRegSpan , Const16 , Const32 , Instruction , Reg , RegSpan } ,
45
+ ir:: {
46
+ BoundedRegSpan ,
47
+ BranchOffset ,
48
+ BranchOffset16 ,
49
+ Comparator ,
50
+ ComparatorAndOffset ,
51
+ Const16 ,
52
+ Const32 ,
53
+ Instruction ,
54
+ Reg ,
55
+ RegSpan ,
56
+ } ,
39
57
module:: { FuncIdx , ModuleHeader , WasmiValueType } ,
40
58
Engine ,
41
59
Error ,
@@ -341,6 +359,17 @@ impl FuncTranslator {
341
359
Ok ( ( ) )
342
360
}
343
361
362
+ /// Pushes the `instr` to the function with the associated `fuel_costs`.
363
+ fn push_instr (
364
+ & mut self ,
365
+ instr : Instruction ,
366
+ fuel_costs : impl FnOnce ( & FuelCostsProvider ) -> u64 ,
367
+ ) -> Result < ( ) , Error > {
368
+ let consume_fuel = self . stack . consume_fuel_instr ( ) ;
369
+ self . instrs . push_instr ( instr, consume_fuel, fuel_costs) ?;
370
+ Ok ( ( ) )
371
+ }
372
+
344
373
/// Translates a generic return instruction.
345
374
fn translate_return ( & mut self , consume_fuel : Option < Instr > ) -> Result < Instr , Error > {
346
375
let len_results = self . func_type_with ( FuncType :: len_results) ;
@@ -441,6 +470,126 @@ impl FuncTranslator {
441
470
todo ! ( )
442
471
}
443
472
473
+ /// Translates a `i32.eqz`+`br_if` or `if` conditional branch instruction.
474
+ fn translate_br_eqz ( & mut self , condition : Operand , label : LabelRef ) -> Result < ( ) , Error > {
475
+ self . translate_br_if ( condition, label, true )
476
+ }
477
+
478
+ /// Translates a `br_if` conditional branch instruction.
479
+ fn translate_br_nez ( & mut self , condition : Operand , label : LabelRef ) -> Result < ( ) , Error > {
480
+ self . translate_br_if ( condition, label, false )
481
+ }
482
+
483
+ /// Translates a generic `br_if` fused conditional branch instruction.
484
+ fn translate_br_if (
485
+ & mut self ,
486
+ condition : Operand ,
487
+ label : LabelRef ,
488
+ branch_eqz : bool ,
489
+ ) -> Result < ( ) , Error > {
490
+ if self . try_fuse_branch_cmp ( condition, label, branch_eqz) ? {
491
+ return Ok ( ( ) ) ;
492
+ }
493
+ let condition = match condition {
494
+ Operand :: Local ( condition) => self . layout . local_to_reg ( condition. local_index ( ) ) ?,
495
+ Operand :: Temp ( condition) => self . layout . temp_to_reg ( condition. operand_index ( ) ) ?,
496
+ Operand :: Immediate ( _condition) => {
497
+ todo ! ( ) // TODO: translate as `br`
498
+ }
499
+ } ;
500
+ let instr = self . instrs . next_instr ( ) ;
501
+ let offset = self . labels . try_resolve_label ( label, instr) ?;
502
+ let instr = match BranchOffset16 :: try_from ( offset) {
503
+ Ok ( offset) => match branch_eqz {
504
+ true => Instruction :: branch_i32_eq_imm16 ( condition, 0 , offset) ,
505
+ false => Instruction :: branch_i32_ne_imm16 ( condition, 0 , offset) ,
506
+ } ,
507
+ Err ( _) => {
508
+ let zero = self . layout . const_to_reg ( 0_i32 ) ?;
509
+ self . make_branch_cmp_fallback ( Comparator :: I32Eq , condition, zero, offset) ?
510
+ }
511
+ } ;
512
+ self . push_instr ( instr, FuelCostsProvider :: base)
513
+ }
514
+
515
+ /// Create an [`Instruction::BranchCmpFallback`].
516
+ fn make_branch_cmp_fallback (
517
+ & mut self ,
518
+ cmp : Comparator ,
519
+ lhs : Reg ,
520
+ rhs : Reg ,
521
+ offset : BranchOffset ,
522
+ ) -> Result < Instruction , Error > {
523
+ let params = self
524
+ . layout
525
+ . const_to_reg ( ComparatorAndOffset :: new ( cmp, offset) ) ?;
526
+ Ok ( Instruction :: branch_cmp_fallback ( lhs, rhs, params) )
527
+ }
528
+
529
+ /// Try to fuse a cmp+branch [`Instruction`] with optional negation.
530
+ fn try_fuse_branch_cmp (
531
+ & mut self ,
532
+ condition : Operand ,
533
+ label : LabelRef ,
534
+ negate : bool ,
535
+ ) -> Result < bool , Error > {
536
+ let Operand :: Temp ( condition) = condition else {
537
+ return Ok ( false ) ;
538
+ } ;
539
+ debug_assert_eq ! ( condition. ty( ) , ValType :: I32 ) ;
540
+ let Some ( origin) = condition. instr ( ) else {
541
+ return Ok ( false ) ;
542
+ } ;
543
+ let fused_instr = self . try_make_fused_branch_cmp_instr ( origin, condition, label, negate) ?;
544
+ let Some ( fused_instr) = fused_instr else {
545
+ return Ok ( false ) ;
546
+ } ;
547
+ self . instrs . try_replace_instr ( origin, fused_instr)
548
+ }
549
+
550
+ /// Try to return a fused cmp+branch [`Instruction`] from the given parameters.
551
+ ///
552
+ ///
553
+ /// # Note
554
+ ///
555
+ /// - The `instr` parameter refers to the to-be-fused cmp instruction.
556
+ /// - Returns `Ok(Some)` if cmp+branch fusion was successful.
557
+ /// - Returns `Ok(None)`, otherwise.
558
+ fn try_make_fused_branch_cmp_instr (
559
+ & mut self ,
560
+ instr : Instr ,
561
+ condition : TempOperand ,
562
+ label : LabelRef ,
563
+ negate : bool ,
564
+ ) -> Result < Option < Instruction > , Error > {
565
+ let cmp_instr = * self . instrs . get ( instr) ;
566
+ let Some ( result) = cmp_instr. compare_result ( ) else {
567
+ // Note: cannot fuse non-cmp instructions or cmp-instructions without result.
568
+ return Ok ( None ) ;
569
+ } ;
570
+ if matches ! ( self . layout. stack_space( result) , StackSpace :: Local ) {
571
+ // Note: cannot fuse cmp instructions with observable semantics.
572
+ return Ok ( None ) ;
573
+ }
574
+ if result != self . layout . temp_to_reg ( condition. operand_index ( ) ) ? {
575
+ // Note: cannot fuse cmp instruction with a result that differs
576
+ // from the condition operand.
577
+ return Ok ( None ) ;
578
+ }
579
+ let cmp_instr = match negate {
580
+ false => cmp_instr,
581
+ true => match cmp_instr. negate_cmp_instr ( ) {
582
+ Some ( negated) => negated,
583
+ None => {
584
+ // Note: cannot negate cmp instruction, thus not possible to fuse.
585
+ return Ok ( None ) ;
586
+ }
587
+ } ,
588
+ } ;
589
+ let offset = self . labels . try_resolve_label ( label, instr) ?;
590
+ cmp_instr. try_into_cmp_branch_instr ( offset, & mut self . layout )
591
+ }
592
+
444
593
/// Translates a commutative binary Wasm operator to Wasmi bytecode.
445
594
fn translate_binary_commutative < T , R > (
446
595
& mut self ,
0 commit comments