# RUN: llc -mtriple=aarch64-linux-gnu -run-pass machine-combiner -verify-machineinstrs -o - %s | FileCheck %s
# The test cases in this file check following transformation if the right form
# can reduce latency.
# A - (B + C) ==> (A - B) - C
---
# 32 bit.
# CHECK-LABEL: name: test1
# CHECK: [[TMP:%[0-9]+]]:gpr32common = SUBWrr killed %3, %4
# CHECK-NEXT: %7:gpr32 = SUBWrr killed [[TMP]], %5
name: test1
registers:
- { id: 0, class: gpr32common }
- { id: 1, class: gpr32 }
- { id: 2, class: gpr32 }
- { id: 3, class: gpr32common }
- { id: 4, class: gpr32common }
- { id: 5, class: gpr32 }
- { id: 6, class: gpr32 }
- { id: 7, class: gpr32 }
- { id: 8, class: gpr32 }
body: |
bb.0:
%2:gpr32 = COPY $w2
%1:gpr32 = COPY $w1
%0:gpr32common = COPY $w0
%3:gpr32common = ORRWri %2:gpr32, 1600
%4:gpr32common = ADDWri %0:gpr32common, 100, 0
%5:gpr32 = EORWrs %1:gpr32, %4:gpr32common, 8
%6:gpr32 = ADDWrr %5:gpr32, %4:gpr32common
%7:gpr32 = SUBWrr killed %3:gpr32common, killed %6:gpr32
%8:gpr32 = EORWrs killed %7:gpr32, %5:gpr32, 141
$w0 = COPY %8:gpr32
RET_ReallyLR implicit $w0
...
---
# 64 bit.
# CHECK-LABEL: name: test2
# CHECK: [[TMP:%[0-9]+]]:gpr64common = SUBXrr killed %3, %4
# CHECK-NEXT: %7:gpr64 = SUBXrr killed [[TMP]], %5
name: test2
registers:
- { id: 0, class: gpr64common }
- { id: 1, class: gpr64 }
- { id: 2, class: gpr64 }
- { id: 3, class: gpr64common }
- { id: 4, class: gpr64common }
- { id: 5, class: gpr64 }
- { id: 6, class: gpr64 }
- { id: 7, class: gpr64 }
- { id: 8, class: gpr64 }
body: |
bb.0:
%2:gpr64 = COPY $x2
%1:gpr64 = COPY $x1
%0:gpr64common = COPY $x0
%3:gpr64common = ORRXri %2:gpr64, 1600
%4:gpr64common = ADDXri %0:gpr64common, 100, 0
%5:gpr64 = EORXrs %1:gpr64, %4:gpr64common, 8
%6:gpr64 = ADDXrr %5:gpr64, %4:gpr64common
%7:gpr64 = SUBXrr killed %3:gpr64common, killed %6:gpr64
%8:gpr64 = EORXrs killed %7:gpr64, %5:gpr64, 141
$x0 = COPY %8:gpr64
RET_ReallyLR implicit $x0
...
---
# Negative test. The right form can't reduce latency.
# CHECK-LABEL: name: test3
# CHECK: %6:gpr32 = ADDWrr killed %3, %4
# CHECK-NEXT: %7:gpr32 = SUBWrr %5, killed %6
name: test3
registers:
- { id: 0, class: gpr32common }
- { id: 1, class: gpr32 }
- { id: 2, class: gpr32 }
- { id: 3, class: gpr32common }
- { id: 4, class: gpr32common }
- { id: 5, class: gpr32 }
- { id: 6, class: gpr32 }
- { id: 7, class: gpr32 }
- { id: 8, class: gpr32 }
body: |
bb.0:
%2:gpr32 = COPY $w2
%1:gpr32 = COPY $w1
%0:gpr32common = COPY $w0
%3:gpr32common = ORRWri %2:gpr32, 1600
%4:gpr32common = ADDWri %0:gpr32common, 100, 0
%5:gpr32 = EORWrs %1:gpr32, %4:gpr32common, 8
%6:gpr32 = ADDWrr killed %3:gpr32common, %4:gpr32common
%7:gpr32 = SUBWrr %5:gpr32, killed %6:gpr32
%8:gpr32 = EORWrs killed %7:gpr32, %5:gpr32, 141
$w0 = COPY %8:gpr32
RET_ReallyLR implicit $w0
...
---
# Dead define of flag registers should not block transformation.
# CHECK-LABEL: name: test4
# CHECK: [[TMP:%[0-9]+]]:gpr64common = SUBXrr killed %3, %4
# CHECK-NEXT: %7:gpr64 = SUBXrr killed [[TMP]], %5
name: test4
registers:
- { id: 0, class: gpr64common }
- { id: 1, class: gpr64 }
- { id: 2, class: gpr64 }
- { id: 3, class: gpr64common }
- { id: 4, class: gpr64common }
- { id: 5, class: gpr64 }
- { id: 6, class: gpr64 }
- { id: 7, class: gpr64 }
- { id: 8, class: gpr64 }
body: |
bb.0:
%2:gpr64 = COPY $x2
%1:gpr64 = COPY $x1
%0:gpr64common = COPY $x0
%3:gpr64common = ORRXri %2:gpr64, 1600
%4:gpr64common = ADDXri %0:gpr64common, 100, 0
%5:gpr64 = EORXrs %1:gpr64, %4:gpr64common, 8
%6:gpr64 = ADDSXrr %5:gpr64, %4:gpr64common, implicit-def dead $nzcv
%7:gpr64 = SUBSXrr killed %3:gpr64common, killed %6:gpr64, implicit-def dead $nzcv
%8:gpr64 = EORXrs killed %7:gpr64, %5:gpr64, 141
$x0 = COPY %8:gpr64
RET_ReallyLR implicit $x0
...
---
# Non dead define of flag register in SUB can block the transformation.
# CHECK-LABEL: name: test5
# CHECK: %6:gpr32 = ADDWrr %5, %4
# CHECK-NEXT: %7:gpr32 = SUBSWrr killed %3, killed %6, implicit-def $nzcv
name: test5
registers:
- { id: 0, class: gpr32common }
- { id: 1, class: gpr32 }
- { id: 2, class: gpr32 }
- { id: 3, class: gpr32common }
- { id: 4, class: gpr32common }
- { id: 5, class: gpr32 }
- { id: 6, class: gpr32 }
- { id: 7, class: gpr32 }
- { id: 8, class: gpr32 }
body: |
bb.0:
%2:gpr32 = COPY $w2
%1:gpr32 = COPY $w1
%0:gpr32common = COPY $w0
%3:gpr32common = ORRWri %2:gpr32, 1600
%4:gpr32common = ADDWri %0:gpr32common, 100, 0
%5:gpr32 = EORWrs %1:gpr32, %4:gpr32common, 8
%6:gpr32 = ADDWrr %5:gpr32, %4:gpr32common
%7:gpr32 = SUBSWrr killed %3:gpr32common, killed %6:gpr32, implicit-def $nzcv
%8:gpr32 = EORWrs killed %7:gpr32, %5:gpr32, 141
$w0 = COPY %8:gpr32
RET_ReallyLR implicit $w0
...
---
# Non dead define of flag register in ADD can block the transformation.
# CHECK-LABEL: name: test6
# CHECK: %6:gpr64 = ADDSXrr %5, %4, implicit-def $nzcv
# CHECK-NEXT: %7:gpr64 = SUBXrr killed %3, killed %6
name: test6
registers:
- { id: 0, class: gpr64common }
- { id: 1, class: gpr64 }
- { id: 2, class: gpr64 }
- { id: 3, class: gpr64common }
- { id: 4, class: gpr64common }
- { id: 5, class: gpr64 }
- { id: 6, class: gpr64 }
- { id: 7, class: gpr64 }
- { id: 8, class: gpr64 }
body: |
bb.0:
%2:gpr64 = COPY $x2
%1:gpr64 = COPY $x1
%0:gpr64common = COPY $x0
%3:gpr64common = ORRXri %2:gpr64, 1600
%4:gpr64common = ADDXri %0:gpr64common, 100, 0
%5:gpr64 = EORXrs %1:gpr64, %4:gpr64common, 8
%6:gpr64 = ADDSXrr %5:gpr64, %4:gpr64common, implicit-def $nzcv
%7:gpr64 = SUBXrr killed %3:gpr64common, killed %6:gpr64
%8:gpr64 = EORXrs killed %7:gpr64, %5:gpr64, 141
$x0 = COPY %8:gpr64
RET_ReallyLR implicit $x0
...
---
# ADD has multiple uses, so it is always required, we should not transform it.
# CHECK-LABEL: name: test7
# CHECK: %6:gpr32 = ADDWrr %5, %4
# CHECK-NEXT: %7:gpr32 = SUBWrr killed %3, %6
name: test7
registers:
- { id: 0, class: gpr32common }
- { id: 1, class: gpr32 }
- { id: 2, class: gpr32 }
- { id: 3, class: gpr32common }
- { id: 4, class: gpr32common }
- { id: 5, class: gpr32 }
- { id: 6, class: gpr32 }
- { id: 7, class: gpr32 }
- { id: 8, class: gpr32 }
- { id: 9, class: gpr32 }
body: |
bb.0:
%2:gpr32 = COPY $w2
%1:gpr32 = COPY $w1
%0:gpr32common = COPY $w0
%3:gpr32common = ORRWri %2:gpr32, 1600
%4:gpr32common = ADDWri %0:gpr32common, 100, 0
%5:gpr32 = EORWrs %1:gpr32, %4:gpr32common, 8
%6:gpr32 = ADDWrr %5:gpr32, %4:gpr32common
%7:gpr32 = SUBWrr killed %3:gpr32common, %6:gpr32
%8:gpr32 = EORWrs killed %7:gpr32, %5:gpr32, 141
%9:gpr32 = ADDWrr %8:gpr32, %6:gpr32
$w0 = COPY %9:gpr32
RET_ReallyLR implicit $w0
...
---
# Drop nowrap flags in SUB
# CHECK-LABEL: name: test8
# CHECK: [[SUBXrr:%[0-9]+]]:gpr64 = SUBXrr %1, %0
# CHECK-NEXT: %4:gpr64common = SUBXrr killed [[SUBXrr]], killed %2
name: test8
registers:
- { id: 0, class: gpr64 }
- { id: 1, class: gpr64 }
- { id: 2, class: gpr64common }
- { id: 3, class: gpr64 }
- { id: 4, class: gpr64common }
- { id: 5, class: gpr64 }
body: |
bb.0:
%1:gpr64 = COPY $x1
%0:gpr64 = COPY $x0
%2:gpr64common = ORRXri %0:gpr64, 4096
%3:gpr64 = ADDXrr killed %2:gpr64common, %0:gpr64
%4:gpr64common = nsw SUBSXrr %1:gpr64, killed %3:gpr64, implicit-def dead $nzcv
%5:gpr64 = SUBSXri %4:gpr64common, 0, 0, implicit-def $nzcv
$x0 = COPY %5:gpr64
RET_ReallyLR implicit $x0
...