# REQUIRES: aarch64-registered-target # REQUIRES: asserts # RUN: llc -o /dev/null %s -mtriple=aarch64 -mattr=+fuse-address \ # RUN: -run-pass machine-scheduler -debug-only=machine-scheduler 2>&1 \ # RUN: | FileCheck %s # RUN: llc -o /dev/null %s -mtriple=aarch64-apple-macos -mcpu=apple-m5 \ # RUN: -run-pass machine-scheduler -debug-only=machine-scheduler 2>&1 \ # RUN: | FileCheck %s #------------------------------------------------------------------------------ # Test macro fusion conflicts with other macro fusions and with ld/st clustering. # # Conflict matrix (conflict-source × which-SU-is-clustered): # # | first-clustered | second-clustered | both-clustered # -----------------+-----------------+------------------+---------------- # fusion-fusion | TEST | (a) | (a) # load-fusion | (b) | TEST | TEST # store-fusion | (b) | TEST | TEST # # Unconstructible cases: # (a) fusion-fusion second/both-clustered: the anchor (SecondSU) is visited # exactly once in the fusion loop. It can only be pre-clustered by ld/st # clustering (a different mutation), never by a prior fusion iteration. # both-clustered requires SecondSU to be fusion-clustered, same issue. # (b) load/store-fusion first-clustered: ADRP is not a load/store, so it # cannot be ld/st clustered. The only way ADRP gets clustered is via a # prior fusion, making it identical to fusion-fusion first-clustered. # There are currently no other fusions with loads/stores on AArch64. #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Fusion-fusion conflicts #------------------------------------------------------------------------------ # Fusion-fusion, first-clustered: ADRP fans out to two loads at non-adjacent # offsets (so they are NOT clustered by LoadClusterDAGMutation). Both ADRP→LDR # pairs are valid address fusion pairs. SU(0)→SU(1) fuses first; when # SU(0)→SU(2) is attempted, SU(0) is already clustered. --- name: fusion-fusion-first-clustered tracksRegLiveness: true body: | bb.0: ; CHECK-LABEL: fusion-fusion-first-clustered ; CHECK: Macro fuse: SU(0) - SU(1) / ADRP - LDRXui ; CHECK: Fusion conflict: cannot fuse SU(0) and SU(2) ; CHECK-NEXT: SU(0) already clustered ; CHECK: SU(0): $x0 = ADRP 0 ; CHECK: SU(1): $x1 = LDRXui $x0, 5 :: (load (s64)) ; CHECK: SU(2): $x2 = LDRXui $x0, 10 :: (load (s64)) $x0 = ADRP 0 $x1 = LDRXui $x0, 5 :: (load (s64)) $x2 = LDRXui $x0, 10 :: (load (s64)) RET_ReallyLR ... #------------------------------------------------------------------------------ # Load-fusion conflicts #------------------------------------------------------------------------------ # Load-fusion, second-clustered: two adjacent loads get clustered by # LoadClusterDAGMutation. Then address fusion (ADRP + LDRXui) is attempted # but the load SUnit is already clustered. --- name: load-fusion-second-clustered tracksRegLiveness: true body: | bb.0: ; CHECK-LABEL: load-fusion-second-clustered ; CHECK: Cluster ld/st SU(1) - SU(2) ; CHECK: Fusion conflict: cannot fuse SU(0) and SU(1) ; CHECK-NEXT: SU(1) already clustered ; CHECK: SU(0): $x0 = ADRP 0 ; CHECK: SU(1): $x1 = LDRXui $x0, 0 :: (load (s64)) ; CHECK: SU(2): $x2 = LDRXui $x0, 1 :: (load (s64)) $x0 = ADRP 0 $x1 = LDRXui $x0, 0 :: (load (s64)) $x2 = LDRXui $x0, 1 :: (load (s64)) RET_ReallyLR ... # Load-fusion, both-clustered: LDR2 and LDR3 get ld/st clustered (adjacent # offsets), then ADRP fuses with LDR1 (non-adjacent offset). When fusion tries # ADRP + LDR2, both are already clustered. --- name: load-fusion-both-clustered tracksRegLiveness: true body: | bb.0: ; CHECK-LABEL: load-fusion-both-clustered ; CHECK: Cluster ld/st SU(2) - SU(3) ; CHECK: Macro fuse: SU(0) - SU(1) / ADRP - LDRXui ; CHECK: Fusion conflict: cannot fuse SU(0) and SU(2) ; CHECK-NEXT: SU(0) already clustered ; CHECK-NEXT: SU(2) already clustered ; CHECK: SU(0): $x0 = ADRP 0 ; CHECK: SU(1): $x1 = LDRXui $x0, 5 :: (load (s64)) ; CHECK: SU(2): $x2 = LDRXui $x0, 0 :: (load (s64)) ; CHECK: SU(3): $x3 = LDRXui $x0, 1 :: (load (s64)) $x0 = ADRP 0 $x1 = LDRXui $x0, 5 :: (load (s64)) $x2 = LDRXui $x0, 0 :: (load (s64)) $x3 = LDRXui $x0, 1 :: (load (s64)) RET_ReallyLR ... #------------------------------------------------------------------------------ # Store-fusion conflicts #------------------------------------------------------------------------------ # Store-fusion, second-clustered: two adjacent stores get clustered by # StoreClusterDAGMutation (runs before AArch64 macro fusion). Then address # fusion (ADRP + STRXui) is attempted but the store SUnit is already # clustered. --- name: store-fusion-second-clustered tracksRegLiveness: true body: | bb.0: liveins: $x0 ; CHECK-LABEL: store-fusion-second-clustered ; CHECK: Cluster ld/st SU(1) - SU(2) ; CHECK: Fusion conflict: cannot fuse SU(0) and SU(1) ; CHECK-NEXT: SU(1) already clustered ; CHECK: SU(0): $x1 = ADRP 0 ; CHECK: SU(1): STRXui $x0, $x1, 0 :: (store (s64)) ; CHECK: SU(2): STRXui $x0, $x1, 1 :: (store (s64)) $x1 = ADRP 0 STRXui $x0, $x1, 0 :: (store (s64)) STRXui $x0, $x1, 1 :: (store (s64)) RET_ReallyLR ... # Store-fusion, both-clustered: STR2 and STR3 get ld/st clustered (adjacent # offsets), then ADRP fuses with STR1 (non-adjacent offset). When fusion tries # ADRP + STR2, both are already clustered. --- name: store-fusion-both-clustered tracksRegLiveness: true body: | bb.0: liveins: $x0 ; CHECK-LABEL: store-fusion-both-clustered ; CHECK: Cluster ld/st SU(2) - SU(3) ; CHECK: Macro fuse: SU(0) - SU(1) / ADRP - STRXui ; CHECK: Fusion conflict: cannot fuse SU(0) and SU(2) ; CHECK-NEXT: SU(0) already clustered ; CHECK-NEXT: SU(2) already clustered ; CHECK: SU(0): $x1 = ADRP 0 ; CHECK: SU(1): STRXui $x0, $x1, 5 :: (store (s64)) ; CHECK: SU(2): STRXui $x0, $x1, 0 :: (store (s64)) ; CHECK: SU(3): STRXui $x0, $x1, 1 :: (store (s64)) $x1 = ADRP 0 STRXui $x0, $x1, 5 :: (store (s64)) STRXui $x0, $x1, 0 :: (store (s64)) STRXui $x0, $x1, 1 :: (store (s64)) RET_ReallyLR ...