Files
clang-p2996/llvm/test/CodeGen/AArch64/logical_shifted_reg.ll
David Green adec922361 [AArch64] Make -mcpu=generic schedule for an in-order core
We would like to start pushing -mcpu=generic towards enabling the set of
features that improves performance for some CPUs, without hurting any
others. A blend of the performance options hopefully beneficial to all
CPUs. The largest part of that is enabling in-order scheduling using the
Cortex-A55 schedule model. This is similar to the Arm backend change
from eecb353d0e which made -mcpu=generic perform in-order scheduling
using the cortex-a8 schedule model.

The idea is that in-order cpu's require the most help in instruction
scheduling, whereas out-of-order cpus can for the most part out-of-order
schedule around different codegen. Our benchmarking suggests that
hypothesis holds. When running on an in-order core this improved
performance by 3.8% geomean on a set of DSP workloads, 2% geomean on
some other embedded benchmark and between 1% and 1.8% on a set of
singlecore and multicore workloads, all running on a Cortex-A55 cluster.

On an out-of-order cpu the results are a lot more noisy but show flat
performance or an improvement. On the set of DSP and embedded
benchmarks, run on a Cortex-A78 there was a very noisy 1% speed
improvement. Using the most detailed results I could find, SPEC2006 runs
on a Neoverse N1 show a small increase in instruction count (+0.127%),
but a decrease in cycle counts (-0.155%, on average). The instruction
count is very low noise, the cycle count is more noisy with a 0.15%
decrease not being significant. SPEC2k17 shows a small decrease (-0.2%)
in instruction count leading to a -0.296% decrease in cycle count. These
results are within noise margins but tend to show a small improvement in
general.

When specifying an Apple target, clang will set "-target-cpu apple-a7"
on the command line, so should not be affected by this change when
running from clang. This also doesn't enable more runtime unrolling like
-mcpu=cortex-a55 does, only changing the schedule used.

A lot of existing tests have updated. This is a summary of the important
differences:
 - Most changes are the same instructions in a different order.
 - Sometimes this leads to very minor inefficiencies, such as requiring
   an extra mov to move variables into r0/v0 for the return value of a test
   function.
 - misched-fusion.ll was no longer fusing the pairs of instructions it
   should, as per D110561. I've changed the schedule used in the test
   for now.
 - neon-mla-mls.ll now uses "mul; sub" as opposed to "neg; mla" due to
   the different latencies. This seems fine to me.
 - Some SVE tests do not always remove movprfx where they did before due
   to different register allocation giving different destructive forms.
 - The tests argument-blocks-array-of-struct.ll and arm64-windows-calls.ll
   produce two LDR where they previously produced an LDP due to
   store-pair-suppress kicking in.
 - arm64-ldp.ll and arm64-neon-copy.ll are missing pre/postinc on LPD.
 - Some tests such as arm64-neon-mul-div.ll and
   ragreedy-local-interval-cost.ll have more, less or just different
   spilling.
 - In aarch64_generated_funcs.ll.generated.expected one part of the
   function is no longer outlined. Interestingly if I switch this to use
   any other scheduled even less is outlined.

Some of these are expected to happen, such as differences in outlining
or register spilling. There will be places where these result in worse
codegen, places where they are better, with the SPEC instruction counts
suggesting it is not a decrease overall, on average.

Differential Revision: https://reviews.llvm.org/D110830
2021-10-09 15:58:31 +01:00

293 lines
9.6 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -verify-machineinstrs < %s -mtriple=aarch64-none-linux-gnu | FileCheck %s
@var1_32 = global i32 0
@var2_32 = global i32 0
@var1_64 = global i64 0
@var2_64 = global i64 0
define void @logical_32bit() minsize {
; CHECK-LABEL: logical_32bit:
; CHECK: // %bb.0:
; CHECK-NEXT: adrp x8, :got:var1_32
; CHECK-NEXT: adrp x9, :got:var2_32
; CHECK-NEXT: ldr x8, [x8, :got_lo12:var1_32]
; CHECK-NEXT: ldr x9, [x9, :got_lo12:var2_32]
; CHECK-NEXT: ldr w10, [x8]
; CHECK-NEXT: ldr w9, [x9]
; CHECK-NEXT: and w11, w10, w9
; CHECK-NEXT: bic w12, w10, w9
; CHECK-NEXT: orr w13, w10, w9
; CHECK-NEXT: str w11, [x8]
; CHECK-NEXT: orn w11, w10, w9
; CHECK-NEXT: str w12, [x8]
; CHECK-NEXT: eor w12, w10, w9
; CHECK-NEXT: str w13, [x8]
; CHECK-NEXT: eon w13, w9, w10
; CHECK-NEXT: str w11, [x8]
; CHECK-NEXT: and w11, w10, w9, lsl #31
; CHECK-NEXT: str w12, [x8]
; CHECK-NEXT: bic w12, w10, w9, lsl #31
; CHECK-NEXT: str w13, [x8]
; CHECK-NEXT: orr w13, w10, w9, lsl #31
; CHECK-NEXT: str w11, [x8]
; CHECK-NEXT: orn w11, w10, w9, lsl #31
; CHECK-NEXT: str w12, [x8]
; CHECK-NEXT: eor w12, w10, w9, lsl #31
; CHECK-NEXT: str w13, [x8]
; CHECK-NEXT: eon w13, w10, w9, lsl #31
; CHECK-NEXT: str w11, [x8]
; CHECK-NEXT: bic w11, w10, w9, asr #10
; CHECK-NEXT: str w12, [x8]
; CHECK-NEXT: eor w12, w10, w9, asr #10
; CHECK-NEXT: str w13, [x8]
; CHECK-NEXT: orn w13, w10, w9, lsr #1
; CHECK-NEXT: str w11, [x8]
; CHECK-NEXT: eor w11, w10, w9, lsr #1
; CHECK-NEXT: str w12, [x8]
; CHECK-NEXT: eon w12, w10, w9, ror #20
; CHECK-NEXT: and w9, w10, w9, ror #20
; CHECK-NEXT: str w13, [x8]
; CHECK-NEXT: str w11, [x8]
; CHECK-NEXT: str w12, [x8]
; CHECK-NEXT: str w9, [x8]
; CHECK-NEXT: ret
%val1 = load i32, i32* @var1_32
%val2 = load i32, i32* @var2_32
; First check basic and/bic/or/orn/eor/eon patterns with no shift
%neg_val2 = xor i32 -1, %val2
%and_noshift = and i32 %val1, %val2
store volatile i32 %and_noshift, i32* @var1_32
%bic_noshift = and i32 %neg_val2, %val1
store volatile i32 %bic_noshift, i32* @var1_32
%or_noshift = or i32 %val1, %val2
store volatile i32 %or_noshift, i32* @var1_32
%orn_noshift = or i32 %neg_val2, %val1
store volatile i32 %orn_noshift, i32* @var1_32
%xor_noshift = xor i32 %val1, %val2
store volatile i32 %xor_noshift, i32* @var1_32
%xorn_noshift = xor i32 %neg_val2, %val1
store volatile i32 %xorn_noshift, i32* @var1_32
; Check the maximum shift on each
%operand_lsl31 = shl i32 %val2, 31
%neg_operand_lsl31 = xor i32 -1, %operand_lsl31
%and_lsl31 = and i32 %val1, %operand_lsl31
store volatile i32 %and_lsl31, i32* @var1_32
%bic_lsl31 = and i32 %val1, %neg_operand_lsl31
store volatile i32 %bic_lsl31, i32* @var1_32
%or_lsl31 = or i32 %val1, %operand_lsl31
store volatile i32 %or_lsl31, i32* @var1_32
%orn_lsl31 = or i32 %val1, %neg_operand_lsl31
store volatile i32 %orn_lsl31, i32* @var1_32
%xor_lsl31 = xor i32 %val1, %operand_lsl31
store volatile i32 %xor_lsl31, i32* @var1_32
%xorn_lsl31 = xor i32 %val1, %neg_operand_lsl31
store volatile i32 %xorn_lsl31, i32* @var1_32
; Check other shifts on a subset
%operand_asr10 = ashr i32 %val2, 10
%neg_operand_asr10 = xor i32 -1, %operand_asr10
%bic_asr10 = and i32 %val1, %neg_operand_asr10
store volatile i32 %bic_asr10, i32* @var1_32
%xor_asr10 = xor i32 %val1, %operand_asr10
store volatile i32 %xor_asr10, i32* @var1_32
%operand_lsr1 = lshr i32 %val2, 1
%neg_operand_lsr1 = xor i32 -1, %operand_lsr1
%orn_lsr1 = or i32 %val1, %neg_operand_lsr1
store volatile i32 %orn_lsr1, i32* @var1_32
%xor_lsr1 = xor i32 %val1, %operand_lsr1
store volatile i32 %xor_lsr1, i32* @var1_32
%operand_ror20_big = shl i32 %val2, 12
%operand_ror20_small = lshr i32 %val2, 20
%operand_ror20 = or i32 %operand_ror20_big, %operand_ror20_small
%neg_operand_ror20 = xor i32 -1, %operand_ror20
%xorn_ror20 = xor i32 %val1, %neg_operand_ror20
store volatile i32 %xorn_ror20, i32* @var1_32
%and_ror20 = and i32 %val1, %operand_ror20
store volatile i32 %and_ror20, i32* @var1_32
ret void
}
define void @logical_64bit() minsize {
; CHECK-LABEL: logical_64bit:
; CHECK: // %bb.0:
; CHECK-NEXT: adrp x8, :got:var1_64
; CHECK-NEXT: adrp x9, :got:var2_64
; CHECK-NEXT: ldr x8, [x8, :got_lo12:var1_64]
; CHECK-NEXT: ldr x9, [x9, :got_lo12:var2_64]
; CHECK-NEXT: ldr x10, [x8]
; CHECK-NEXT: ldr x9, [x9]
; CHECK-NEXT: and x11, x10, x9
; CHECK-NEXT: bic x12, x10, x9
; CHECK-NEXT: orr x13, x10, x9
; CHECK-NEXT: str x11, [x8]
; CHECK-NEXT: orn x11, x10, x9
; CHECK-NEXT: str x12, [x8]
; CHECK-NEXT: eor x12, x10, x9
; CHECK-NEXT: str x13, [x8]
; CHECK-NEXT: eon x13, x9, x10
; CHECK-NEXT: str x11, [x8]
; CHECK-NEXT: and x11, x10, x9, lsl #63
; CHECK-NEXT: str x12, [x8]
; CHECK-NEXT: bic x12, x10, x9, lsl #63
; CHECK-NEXT: str x13, [x8]
; CHECK-NEXT: orr x13, x10, x9, lsl #63
; CHECK-NEXT: str x11, [x8]
; CHECK-NEXT: orn x11, x10, x9, lsl #63
; CHECK-NEXT: str x12, [x8]
; CHECK-NEXT: eor x12, x10, x9, lsl #63
; CHECK-NEXT: str x13, [x8]
; CHECK-NEXT: eon x13, x10, x9, lsl #63
; CHECK-NEXT: str x11, [x8]
; CHECK-NEXT: bic x11, x10, x9, asr #10
; CHECK-NEXT: str x12, [x8]
; CHECK-NEXT: eor x12, x10, x9, asr #10
; CHECK-NEXT: str x13, [x8]
; CHECK-NEXT: orn x13, x10, x9, lsr #1
; CHECK-NEXT: str x11, [x8]
; CHECK-NEXT: eor x11, x10, x9, lsr #1
; CHECK-NEXT: str x12, [x8]
; CHECK-NEXT: eon x12, x10, x9, ror #20
; CHECK-NEXT: and x9, x10, x9, ror #20
; CHECK-NEXT: str x13, [x8]
; CHECK-NEXT: str x11, [x8]
; CHECK-NEXT: str x12, [x8]
; CHECK-NEXT: str x9, [x8]
; CHECK-NEXT: ret
%val1 = load i64, i64* @var1_64
%val2 = load i64, i64* @var2_64
; First check basic and/bic/or/orn/eor/eon patterns with no shift
%neg_val2 = xor i64 -1, %val2
%and_noshift = and i64 %val1, %val2
store volatile i64 %and_noshift, i64* @var1_64
%bic_noshift = and i64 %neg_val2, %val1
store volatile i64 %bic_noshift, i64* @var1_64
%or_noshift = or i64 %val1, %val2
store volatile i64 %or_noshift, i64* @var1_64
%orn_noshift = or i64 %neg_val2, %val1
store volatile i64 %orn_noshift, i64* @var1_64
%xor_noshift = xor i64 %val1, %val2
store volatile i64 %xor_noshift, i64* @var1_64
%xorn_noshift = xor i64 %neg_val2, %val1
store volatile i64 %xorn_noshift, i64* @var1_64
; Check the maximum shift on each
%operand_lsl63 = shl i64 %val2, 63
%neg_operand_lsl63 = xor i64 -1, %operand_lsl63
%and_lsl63 = and i64 %val1, %operand_lsl63
store volatile i64 %and_lsl63, i64* @var1_64
%bic_lsl63 = and i64 %val1, %neg_operand_lsl63
store volatile i64 %bic_lsl63, i64* @var1_64
%or_lsl63 = or i64 %val1, %operand_lsl63
store volatile i64 %or_lsl63, i64* @var1_64
%orn_lsl63 = or i64 %val1, %neg_operand_lsl63
store volatile i64 %orn_lsl63, i64* @var1_64
%xor_lsl63 = xor i64 %val1, %operand_lsl63
store volatile i64 %xor_lsl63, i64* @var1_64
%xorn_lsl63 = xor i64 %val1, %neg_operand_lsl63
store volatile i64 %xorn_lsl63, i64* @var1_64
; Check other shifts on a subset
%operand_asr10 = ashr i64 %val2, 10
%neg_operand_asr10 = xor i64 -1, %operand_asr10
%bic_asr10 = and i64 %val1, %neg_operand_asr10
store volatile i64 %bic_asr10, i64* @var1_64
%xor_asr10 = xor i64 %val1, %operand_asr10
store volatile i64 %xor_asr10, i64* @var1_64
%operand_lsr1 = lshr i64 %val2, 1
%neg_operand_lsr1 = xor i64 -1, %operand_lsr1
%orn_lsr1 = or i64 %val1, %neg_operand_lsr1
store volatile i64 %orn_lsr1, i64* @var1_64
%xor_lsr1 = xor i64 %val1, %operand_lsr1
store volatile i64 %xor_lsr1, i64* @var1_64
; Construct a rotate-right from a bunch of other logical
; operations. DAGCombiner should ensure we the ROTR during
; selection
%operand_ror20_big = shl i64 %val2, 44
%operand_ror20_small = lshr i64 %val2, 20
%operand_ror20 = or i64 %operand_ror20_big, %operand_ror20_small
%neg_operand_ror20 = xor i64 -1, %operand_ror20
%xorn_ror20 = xor i64 %val1, %neg_operand_ror20
store volatile i64 %xorn_ror20, i64* @var1_64
%and_ror20 = and i64 %val1, %operand_ror20
store volatile i64 %and_ror20, i64* @var1_64
ret void
}
define void @flag_setting() {
; CHECK-LABEL: flag_setting:
; CHECK: // %bb.0:
; CHECK-NEXT: adrp x8, :got:var1_64
; CHECK-NEXT: adrp x10, :got:var2_64
; CHECK-NEXT: ldr x8, [x8, :got_lo12:var1_64]
; CHECK-NEXT: ldr x10, [x10, :got_lo12:var2_64]
; CHECK-NEXT: ldr x9, [x8]
; CHECK-NEXT: ldr x10, [x10]
; CHECK-NEXT: tst x9, x10
; CHECK-NEXT: b.gt .LBB2_2
; CHECK-NEXT: // %bb.1: // %test2
; CHECK-NEXT: tst x9, x10, lsl #63
; CHECK-NEXT: and x10, x9, x10, asr #12
; CHECK-NEXT: ccmp x10, #1, #0, ge
; CHECK-NEXT: b.lt .LBB2_3
; CHECK-NEXT: .LBB2_2: // %common.ret
; CHECK-NEXT: ret
; CHECK-NEXT: .LBB2_3: // %other_exit
; CHECK-NEXT: str x9, [x8]
; CHECK-NEXT: ret
%val1 = load i64, i64* @var1_64
%val2 = load i64, i64* @var2_64
%simple_and = and i64 %val1, %val2
%tst1 = icmp sgt i64 %simple_and, 0
br i1 %tst1, label %ret, label %test2, !prof !1
test2:
%shifted_op = shl i64 %val2, 63
%shifted_and = and i64 %val1, %shifted_op
%tst2 = icmp slt i64 %shifted_and, 0
br i1 %tst2, label %ret, label %test3, !prof !1
test3:
%asr_op = ashr i64 %val2, 12
%asr_and = and i64 %asr_op, %val1
%tst3 = icmp sgt i64 %asr_and, 0
br i1 %tst3, label %ret, label %other_exit, !prof !1
other_exit:
store volatile i64 %val1, i64* @var1_64
ret void
ret:
ret void
}
!1 = !{!"branch_weights", i32 1, i32 1}