blob: 46a684fb233e8f6b53c6d9602bf5b5db77a74383 [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
; NOTE: Test cases for FCMP-FCSEL and CMP/CMN-CSEL code layout optimization
; RUN: llc < %s -verify-machineinstrs -mtriple=aarch64-apple-darwin -aarch64-code-layout-opt-enable=fcmp-fcsel,cmp-csel | FileCheck %s --check-prefixes=CHECK,ENABLED
; RUN: llc < %s -verify-machineinstrs -mtriple=aarch64-apple-darwin -mcpu=apple-m4 -aarch64-code-layout-opt-enable=fcmp-fcsel,cmp-csel | FileCheck %s --check-prefixes=CHECK,ENABLED
; Default for -mcpu=apple-m4 enables both fcmp-fcsel and cmp-csel; expect identical output.
; RUN: llc < %s -verify-machineinstrs -mtriple=aarch64-apple-darwin -mcpu=apple-m4 | FileCheck %s --check-prefixes=CHECK,ENABLED
; RUN: llc < %s -verify-machineinstrs -mtriple=aarch64-apple-darwin -mcpu=apple-m4 -aarch64-code-layout-opt-enable=none | FileCheck %s --check-prefixes=CHECK,DISABLED
; RUN: llc < %s -verify-machineinstrs -mtriple=aarch64-apple-darwin | FileCheck %s --check-prefixes=CHECK,DEFAULT
; Test coverage for optimizeForCodeLayout function:
; * Basic FCMP-FCSEL instruction pair detection and function alignment (single/double precision)
; * Multiple FCMP-FCSEL pairs in same function (also tests different predicates)
; * Mixed single and double precision in same function
; * FCMP with immediate operand (#0.0) is excluded from optimization
; * Instruction pairs with function calls
; * Negative tests (no false positives)
; * Basic CMP-CSEL and CMN-CSEL instruction pair detection and function alignment
; * CMP/CMN with immediate <=15 qualifies; immediate >15 is excluded
; * Basic single-precision FCMP-FCSEL instruction pair
define float @test_basic_fcmp_fcsel_single(float %a, float %b, float %c, float %d) {
; ENABLED-LABEL: test_basic_fcmp_fcsel_single:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: fcmp s0, s1
; ENABLED-NEXT: fcsel s0, s2, s3, eq
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_basic_fcmp_fcsel_single:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: fcmp s0, s1
; DISABLED-NEXT: fcsel s0, s2, s3, eq
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_basic_fcmp_fcsel_single:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: fcmp s0, s1
; DEFAULT-NEXT: fcsel s0, s2, s3, eq
; DEFAULT-NEXT: ret
entry:
%cmp = fcmp oeq float %a, %b
%sel = select i1 %cmp, float %c, float %d
ret float %sel
}
; * Basic double-precision FCMP-FCSEL instruction pair
define double @test_basic_fcmp_fcsel_double(double %a, double %b, double %c, double %d) {
; ENABLED-LABEL: test_basic_fcmp_fcsel_double:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: fcmp d0, d1
; ENABLED-NEXT: fcsel d0, d2, d3, eq
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_basic_fcmp_fcsel_double:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: fcmp d0, d1
; DISABLED-NEXT: fcsel d0, d2, d3, eq
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_basic_fcmp_fcsel_double:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: fcmp d0, d1
; DEFAULT-NEXT: fcsel d0, d2, d3, eq
; DEFAULT-NEXT: ret
entry:
%cmp = fcmp oeq double %a, %b
%sel = select i1 %cmp, double %c, double %d
ret double %sel
}
; * Multiple FCMP-FCSEL instruction pairs in same function
define float @test_multiple_patterns(float %a, float %b, float %c, float %d, float %e, float %f) {
; ENABLED-LABEL: test_multiple_patterns:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: fcmp s0, s1
; ENABLED-NEXT: fcsel s0, s2, s3, eq
; ENABLED-NEXT: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.1: ; %entry
; ENABLED-NEXT: fcmp s0, s4
; ENABLED-NEXT: fcsel s0, s0, s5, gt
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_multiple_patterns:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: fcmp s0, s1
; DISABLED-NEXT: fcsel s0, s2, s3, eq
; DISABLED-NEXT: fcmp s0, s4
; DISABLED-NEXT: fcsel s0, s0, s5, gt
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_multiple_patterns:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: fcmp s0, s1
; DEFAULT-NEXT: fcsel s0, s2, s3, eq
; DEFAULT-NEXT: fcmp s0, s4
; DEFAULT-NEXT: fcsel s0, s0, s5, gt
; DEFAULT-NEXT: ret
entry:
%cmp1 = fcmp oeq float %a, %b
%sel1 = select i1 %cmp1, float %c, float %d
%cmp2 = fcmp ogt float %sel1, %e
%sel2 = select i1 %cmp2, float %sel1, float %f
ret float %sel2
}
; * FCMP with comparison to zero (immediate) - excluded from optimization
; FCMP #0.0 uses the ri-form opcode which is not in the detection list
define float @test_fcmp_immediate(float %a, float %b) {
; CHECK-LABEL: test_fcmp_immediate:
; CHECK: ; %bb.0: ; %entry
; CHECK-NEXT: fcmp s0, #0.0
; CHECK-NEXT: fcsel s0, s0, s1, eq
; CHECK-NEXT: ret
entry:
%cmp = fcmp oeq float %a, 0.0
%sel = select i1 %cmp, float %a, float %b
ret float %sel
}
; * Mixed single and double precision in same function
define float @test_mixed_precision(float %a, float %b, double %c, double %d) {
; ENABLED-LABEL: test_mixed_precision:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: fcmp s0, s1
; ENABLED-NEXT: fcsel s0, s0, s1, gt
; ENABLED-NEXT: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.1: ; %entry
; ENABLED-NEXT: fcmp d2, d3
; ENABLED-NEXT: fcsel d1, d2, d3, mi
; ENABLED-NEXT: fcvt s1, d1
; ENABLED-NEXT: fadd s0, s0, s1
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_mixed_precision:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: fcmp s0, s1
; DISABLED-NEXT: fcsel s0, s0, s1, gt
; DISABLED-NEXT: fcmp d2, d3
; DISABLED-NEXT: fcsel d1, d2, d3, mi
; DISABLED-NEXT: fcvt s1, d1
; DISABLED-NEXT: fadd s0, s0, s1
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_mixed_precision:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: fcmp s0, s1
; DEFAULT-NEXT: fcsel s0, s0, s1, gt
; DEFAULT-NEXT: fcmp d2, d3
; DEFAULT-NEXT: fcsel d1, d2, d3, mi
; DEFAULT-NEXT: fcvt s1, d1
; DEFAULT-NEXT: fadd s0, s0, s1
; DEFAULT-NEXT: ret
entry:
%cmp_single = fcmp ogt float %a, %b
%sel_single = select i1 %cmp_single, float %a, float %b
%cmp_double = fcmp olt double %c, %d
%sel_double = select i1 %cmp_double, double %c, double %d
%trunc = fptrunc double %sel_double to float
%final = fadd float %sel_single, %trunc
ret float %final
}
; * FCMP-FCSEL instruction pair with a function call present
declare float @external_func(float)
define float @test_with_function_calls(float %a, float %b, float %c, float %d) {
; ENABLED-LABEL: test_with_function_calls:
; ENABLED: ; %bb.0: ; %entry
; ENABLED-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill
; ENABLED-NEXT: .cfi_def_cfa_offset 16
; ENABLED-NEXT: .cfi_offset w30, -8
; ENABLED-NEXT: .cfi_offset w29, -16
; ENABLED-NEXT: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.1: ; %entry
; ENABLED-NEXT: fcmp s0, s1
; ENABLED-NEXT: fcsel s0, s2, s3, gt
; ENABLED-NEXT: bl _external_func
; ENABLED-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_with_function_calls:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill
; DISABLED-NEXT: .cfi_def_cfa_offset 16
; DISABLED-NEXT: .cfi_offset w30, -8
; DISABLED-NEXT: .cfi_offset w29, -16
; DISABLED-NEXT: fcmp s0, s1
; DISABLED-NEXT: fcsel s0, s2, s3, gt
; DISABLED-NEXT: bl _external_func
; DISABLED-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_with_function_calls:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: stp x29, x30, [sp, #-16]! ; 16-byte Folded Spill
; DEFAULT-NEXT: .cfi_def_cfa_offset 16
; DEFAULT-NEXT: .cfi_offset w30, -8
; DEFAULT-NEXT: .cfi_offset w29, -16
; DEFAULT-NEXT: fcmp s0, s1
; DEFAULT-NEXT: fcsel s0, s2, s3, gt
; DEFAULT-NEXT: bl _external_func
; DEFAULT-NEXT: ldp x29, x30, [sp], #16 ; 16-byte Folded Reload
; DEFAULT-NEXT: ret
entry:
%cmp = fcmp ogt float %a, %b
%sel = select i1 %cmp, float %c, float %d
%result = call float @external_func(float %sel)
ret float %result
}
; * Verify no false positives - FCMP without FCSEL
define i32 @test_fcmp_without_fcsel(float %a, float %b) {
; CHECK-LABEL: test_fcmp_without_fcsel:
; CHECK: ; %bb.0: ; %entry
; CHECK-NEXT: fcmp s0, s1
; CHECK-NEXT: cset w0, gt
; CHECK-NEXT: ret
entry:
%cmp = fcmp ogt float %a, %b
%result = zext i1 %cmp to i32
ret i32 %result
}
; * Verify no false positives - FCSEL without preceding FCMP
; CHECK: .globl _test_fcsel_without_fcmp
; CHECK-NEXT: .p2align 2
define float @test_fcsel_without_fcmp(i1 %cond, float %a, float %b) {
; CHECK-LABEL: test_fcsel_without_fcmp:
; CHECK: ; %bb.0: ; %entry
; CHECK-NEXT: tst w0, #0x1
; CHECK-NEXT: fcsel s0, s0, s1, ne
; CHECK-NEXT: ret
entry:
%result = select i1 %cond, float %a, float %b
ret float %result
}
;------------------------------------------------------------------------------
; CMP/CMN-CSEL tests (cmp-csel flag of -aarch64-code-layout-opt-enable)
;------------------------------------------------------------------------------
; * Basic CMP-CSEL instruction pair (integer register comparison)
define i32 @test_basic_cmp_csel(i32 %a, i32 %b, i32 %c, i32 %d) {
; ENABLED-LABEL: test_basic_cmp_csel:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: cmp w0, w1
; ENABLED-NEXT: csel w0, w2, w3, eq
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_basic_cmp_csel:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: cmp w0, w1
; DISABLED-NEXT: csel w0, w2, w3, eq
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_basic_cmp_csel:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: cmp w0, w1
; DEFAULT-NEXT: csel w0, w2, w3, eq
; DEFAULT-NEXT: ret
entry:
%cmp = icmp eq i32 %a, %b
%sel = select i1 %cmp, i32 %c, i32 %d
ret i32 %sel
}
; * CMP-CSEL instruction pair with small immediate (<=15, qualifies for optimization)
define i32 @test_cmp_small_imm_csel(i32 %a, i32 %b, i32 %c) {
; ENABLED-LABEL: test_cmp_small_imm_csel:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: cmp w0, #7
; ENABLED-NEXT: csel w0, w1, w2, eq
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_cmp_small_imm_csel:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: cmp w0, #7
; DISABLED-NEXT: csel w0, w1, w2, eq
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_cmp_small_imm_csel:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: cmp w0, #7
; DEFAULT-NEXT: csel w0, w1, w2, eq
; DEFAULT-NEXT: ret
entry:
%cmp = icmp eq i32 %a, 7
%sel = select i1 %cmp, i32 %b, i32 %c
ret i32 %sel
}
; * CMP-CSEL with immediate > 15 - excluded from optimization
define i32 @test_cmp_large_imm_csel(i32 %a, i32 %b, i32 %c) {
; CHECK-LABEL: test_cmp_large_imm_csel:
; CHECK: ; %bb.0: ; %entry
; CHECK-NEXT: cmp w0, #100
; CHECK-NEXT: csel w0, w1, w2, eq
; CHECK-NEXT: ret
entry:
%cmp = icmp eq i32 %a, 100
%sel = select i1 %cmp, i32 %b, i32 %c
ret i32 %sel
}
; * Basic CMN-CSEL instruction pair (ADDSWrr with WZR destination)
define i32 @test_basic_cmn_csel(i32 %a, i32 %b, i32 %c, i32 %d) {
; ENABLED-LABEL: test_basic_cmn_csel:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: cmn w0, w1
; ENABLED-NEXT: csel w0, w2, w3, eq
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_basic_cmn_csel:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: cmn w0, w1
; DISABLED-NEXT: csel w0, w2, w3, eq
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_basic_cmn_csel:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: cmn w0, w1
; DEFAULT-NEXT: csel w0, w2, w3, eq
; DEFAULT-NEXT: ret
entry:
%sum = add i32 %a, %b
%cmp = icmp eq i32 %sum, 0
%sel = select i1 %cmp, i32 %c, i32 %d
ret i32 %sel
}
define i32 @test_subtarget_m4(i32 %a, i32 %b, i32 %c) "target-cpu"="apple-m4" {
; ENABLED-LABEL: test_subtarget_m4:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: cmn w0, #7
; ENABLED-NEXT: csel w0, w1, w2, eq
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_subtarget_m4:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: cmn w0, #7
; DISABLED-NEXT: csel w0, w1, w2, eq
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_subtarget_m4:
; DEFAULT: .p2align 6, , 4
; DEFAULT-NEXT: ; %bb.0: ; %entry
; DEFAULT-NEXT: cmn w0, #7
; DEFAULT-NEXT: csel w0, w1, w2, eq
; DEFAULT-NEXT: ret
entry:
%cmp = icmp eq i32 %a, -7
%sel = select i1 %cmp, i32 %b, i32 %c
ret i32 %sel
}
; * CMN-CSEL instruction pair with small immediate (ADDSWri imm=7, qualifies)
define i32 @test_cmn_small_imm_csel(i32 %a, i32 %b, i32 %c) {
; ENABLED-LABEL: test_cmn_small_imm_csel:
; ENABLED: .p2align 6, , 4
; ENABLED-NEXT: ; %bb.0: ; %entry
; ENABLED-NEXT: cmn w0, #7
; ENABLED-NEXT: csel w0, w1, w2, eq
; ENABLED-NEXT: ret
;
; DISABLED-LABEL: test_cmn_small_imm_csel:
; DISABLED: ; %bb.0: ; %entry
; DISABLED-NEXT: cmn w0, #7
; DISABLED-NEXT: csel w0, w1, w2, eq
; DISABLED-NEXT: ret
;
; DEFAULT-LABEL: test_cmn_small_imm_csel:
; DEFAULT: ; %bb.0: ; %entry
; DEFAULT-NEXT: cmn w0, #7
; DEFAULT-NEXT: csel w0, w1, w2, eq
; DEFAULT-NEXT: ret
entry:
%cmp = icmp eq i32 %a, -7
%sel = select i1 %cmp, i32 %b, i32 %c
ret i32 %sel
}
; * CMP without CSEL - no false positive
define i32 @test_cmp_without_csel(i32 %a, i32 %b) {
; CHECK-LABEL: test_cmp_without_csel:
; CHECK: ; %bb.0: ; %entry
; CHECK-NEXT: cmp w0, w1
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
entry:
%cmp = icmp eq i32 %a, %b
%result = zext i1 %cmp to i32
ret i32 %result
}