@@ -416,9 +416,11 @@ impl Assembler
416416 // being used. It is okay not to use their output here.
417417 #[ allow( unused_must_use) ]
418418 match & mut insn {
419- Insn :: Add { left, right, .. } => {
419+ Insn :: Sub { left, right, out } |
420+ Insn :: Add { left, right, out } => {
420421 match ( * left, * right) {
421422 ( Opnd :: Reg ( _) | Opnd :: VReg { .. } , Opnd :: Reg ( _) | Opnd :: VReg { .. } ) => {
423+ merge_three_reg_mov ( & live_ranges, & mut iterator, left, right, out) ;
422424 asm. push_insn ( insn) ;
423425 } ,
424426 ( reg_opnd @ ( Opnd :: Reg ( _) | Opnd :: VReg { .. } ) , other_opnd) |
@@ -441,18 +443,7 @@ impl Assembler
441443 * left = opnd0;
442444 * right = opnd1;
443445
444- // Since these instructions are lowered to an instruction that have 2 input
445- // registers and an output register, look to merge with an `Insn::Mov` that
446- // follows which puts the output in another register. For example:
447- // `Add a, b => out` followed by `Mov c, out` becomes `Add a, b => c`.
448- if let ( Opnd :: Reg ( _) , Opnd :: Reg ( _) , Some ( Insn :: Mov { dest, src } ) ) = ( left, right, iterator. peek ( ) . map ( |( _, insn) | insn) ) {
449- if live_ranges[ out. vreg_idx ( ) ] . end ( ) == index + 1 {
450- if out == src && matches ! ( * dest, Opnd :: Reg ( _) ) {
451- * out = * dest;
452- iterator. next ( ) ; // Pop merged Insn::Mov
453- }
454- }
455- }
446+ merge_three_reg_mov ( & live_ranges, & mut iterator, left, right, out) ;
456447
457448 asm. push_insn ( insn) ;
458449 }
@@ -700,11 +691,6 @@ impl Assembler
700691 }
701692 }
702693 } ,
703- Insn :: Sub { left, right, .. } => {
704- * left = split_load_operand ( asm, * left) ;
705- * right = split_shifted_immediate ( asm, * right) ;
706- asm. push_insn ( insn) ;
707- } ,
708694 Insn :: Mul { left, right, .. } => {
709695 * left = split_load_operand ( asm, * left) ;
710696 * right = split_load_operand ( asm, * right) ;
@@ -1337,6 +1323,36 @@ impl Assembler
13371323 }
13381324}
13391325
1326+ /// LIR Instructions that are lowered to an instruction that have 2 input registers and an output
1327+ /// register can look to merge with a succeeding `Insn::Mov`.
1328+ /// For example:
1329+ ///
1330+ /// Add out, a, b
1331+ /// Mov c, out
1332+ ///
1333+ /// Can become:
1334+ ///
1335+ /// Add c, a, b
1336+ ///
1337+ /// If a, b, and c are all registers.
1338+ fn merge_three_reg_mov (
1339+ live_ranges : & Vec < LiveRange > ,
1340+ iterator : & mut std:: iter:: Peekable < impl Iterator < Item = ( usize , Insn ) > > ,
1341+ left : & Opnd ,
1342+ right : & Opnd ,
1343+ out : & mut Opnd ,
1344+ ) {
1345+ if let ( Opnd :: Reg ( _) | Opnd :: VReg { ..} ,
1346+ Opnd :: Reg ( _) | Opnd :: VReg { ..} ,
1347+ Some ( ( mov_idx, Insn :: Mov { dest, src } ) ) )
1348+ = ( left, right, iterator. peek ( ) ) {
1349+ if out == src && live_ranges[ out. vreg_idx ( ) ] . end ( ) == * mov_idx && matches ! ( * dest, Opnd :: Reg ( _) | Opnd :: VReg { ..} ) {
1350+ * out = * dest;
1351+ iterator. next ( ) ; // Pop merged Insn::Mov
1352+ }
1353+ }
1354+ }
1355+
13401356#[ cfg( test) ]
13411357mod tests {
13421358 use super :: * ;
@@ -1363,6 +1379,23 @@ mod tests {
13631379 " } ) ;
13641380 }
13651381
1382+ #[ test]
1383+ fn sp_movements_are_single_instruction ( ) {
1384+ let ( mut asm, mut cb) = setup_asm ( ) ;
1385+
1386+ let sp = Opnd :: Reg ( XZR_REG ) ;
1387+ let new_sp = asm. add ( sp, 0x20 . into ( ) ) ;
1388+ asm. mov ( sp, new_sp) ;
1389+ let new_sp = asm. sub ( sp, 0x20 . into ( ) ) ;
1390+ asm. mov ( sp, new_sp) ;
1391+ asm. compile_with_num_regs ( & mut cb, 2 ) ;
1392+
1393+ assert_disasm ! ( cb, "e08300b11f000091e08300f11f000091" , { "
1394+ 0x0: add sp, sp, #0x20
1395+ 0x4: sub sp, sp, #0x20
1396+ " } ) ;
1397+ }
1398+
13661399 #[ test]
13671400 fn test_emit_add ( ) {
13681401 let ( mut asm, mut cb) = setup_asm ( ) ;
0 commit comments