blob: 87e2c00610ed281633feeb64a4bc7d5387430aa8 [file] [edit]
# 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
...