# RUN: llc -O0 -run-pass=aarch64-prelegalizer-combiner -global-isel %s -o - | FileCheck %s # RUN: llc -O0 -run-pass=aarch64-prelegalizer-combiner -global-isel %s -o - \ # RUN: -debug-only=aarch64-prelegalizer-combiner,gi-combiner 2>&1 >/dev/null \ # RUN: | FileCheck %s --check-prefix=CHECK-WORKLIST # REQUIRES: asserts --- | target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" target triple = "aarch64--" define void @multiple_copies(i8* %addr) { entry: br i1 0, label %if, label %else if: br label %exit else: br label %exit exit: ret void } define void @sink_to_phi_trivially_dominating(i8* %addr) { entry: br i1 0, label %if, label %exit if: br label %exit exit: ret void } define void @sink_to_phi_nondominating(i8* %addr) { entry: br i1 0, label %if, label %else if: br label %exit else: br label %exit exit: ret void } define void @sink_to_phi_emptyblock(i8* %addr) { entry: br i1 0, label %if, label %else if: br label %exit else: br label %else2 else2: br label %exit exit: ret void } define void @use_doesnt_def_anything(i8* %addr) { entry: ret void } define void @op0_isnt_a_reg(i8* %addr) { entry: ret void } ... --- name: multiple_copies # CHECK-LABEL: name: multiple_copies tracksRegLiveness: true body: | bb.0.entry: liveins: $x0, $w1 successors: %bb.1(0x40000000), %bb.2(0x40000000); %bb.1(50.00%), %bb.2(50.00%) ; CHECK: [[T0:%[0-9]+]]:_(s32) = G_SEXTLOAD %0:_(p0) = COPY $x0 %1:_(s32) = COPY $w1 %2:_(s8) = G_LOAD %0 :: (load (s8) from %ir.addr) %3:_(s32) = G_SEXT %2 %4:_(s32) = G_CONSTANT i32 1 %5:_(s1) = G_ICMP intpred(ne), %1:_(s32), %4:_ G_BRCOND %5:_(s1), %bb.1 G_BR %bb.2.else bb.1.if: ; CHECK: bb.1.if: successors: %bb.3(0x80000000) %10:_(s8) = G_CONSTANT i8 1 ; CHECK: [[T1:%[0-9]+]]:_(s8) = G_TRUNC [[T0]](s32) %6:_(s8) = G_ADD %2, %10 ; CHECK: [[T2:%[0-9]+]]:_(s8) = G_ADD [[T1]], {{.*}} G_BR %bb.3.exit bb.2.else: ; CHECK: bb.2.else: successors: %bb.3(0x80000000) %11:_(s8) = G_CONSTANT i8 1 ; CHECK: [[T3:%[0-9]+]]:_(s8) = G_TRUNC [[T0]](s32) %7:_(s8) = G_SUB %2, %11 ; CHECK: [[T4:%[0-9]+]]:_(s8) = G_SUB [[T3]], {{.*}} G_BR %bb.3.exit bb.3.exit: ; CHECK: bb.3.exit: %8:_(s8) = G_PHI %6:_(s8), %bb.1, %7:_(s8), %bb.2 ; CHECK: [[T5:%[0-9]+]]:_(s8) = G_PHI [[T2]](s8), %bb.1, [[T4]](s8) %9:_(s32) = G_ZEXT %8 ; CHECK: [[T6:%[0-9]+]]:_(s32) = G_ZEXT [[T5]](s8) ; CHECK: $w0 = COPY [[T0]](s32) ; CHECK: $w1 = COPY [[T6]](s32) $w0 = COPY %3 $w1 = COPY %9 # Check that we report the correct modifications to the observer. This acts as # a test of the debug output and a test. # # CHECK-WORKLIST-LABEL: Generic MI Combiner for: multiple_copies # CHECK-WORKLIST: Try combining [[IN0:%[0-9]+]]:_(s8) = G_LOAD [[IN1:%[0-9]+]]:_(p0){{.*}} :: (load (s8) from %ir.addr) # CHECK-WORKLIST: Preferred use is: [[IN2:%[0-9]+]]:_(s32) = G_SEXT [[IN0]]:_(s8) # CHECK-WORKLIST-DAG: Changing: [[IN0]]:_(s8) = G_LOAD [[IN1]]:_(p0){{.*}} :: (load (s8) from %ir.addr) # CHECK-WORKLIST-DAG: Changing: [[IN3:%[0-9]+]]:_(s8) = G_ADD [[IN0]]:_, [[IN4:%[0-9]+]]:_ # CHECK-WORKLIST-DAG: Changed: [[IN3]]:_(s8) = G_ADD [[NEW1:%[0-9]+]]:_, [[IN4]]:_ # CHECK-WORKLIST-DAG: Changing: [[IN5:%[0-9]+]]:_(s8) = G_SUB [[IN0]]:_, [[IN6:%[0-9]+]]:_ # CHECK-WORKLIST-DAG: Changed: [[IN5]]:_(s8) = G_SUB [[NEW2:%[0-9]+]]:_, [[IN6]]:_ # CHECK-WORKLIST-DAG: Erasing: [[IN2]]:_(s32) = G_SEXT [[IN0]]:_(s8) # CHECK-WORKLIST-DAG: Changed: [[IN2]]:_(s32) = G_SEXTLOAD [[IN1]]:_(p0){{.*}} :: (load (s8) from %ir.addr) # CHECK-WORKLIST-DAG: Created: [[NEW1]]:_(s8) = G_TRUNC [[IN2]]:_(s32) # CHECK-WORKLIST-DAG: Created: [[NEW2]]:_(s8) = G_TRUNC [[IN2]]:_(s32) # CHECK-WORKLIST: Try combining ... --- name: sink_to_phi_trivially_dominating # CHECK-LABEL: name: sink_to_phi_trivially_dominating # This test currently tests that we don't sink if we would sink to a phi. This # is needed to avoid inserting into the middle of the leading G_PHI instructions # of a BB tracksRegLiveness: true body: | bb.0.entry: liveins: $x0, $w1 successors: %bb.1(0x40000000), %bb.2(0x40000000); %bb.1(50.00%), %bb.2(50.00%) ; CHECK: [[T0:%[0-9]+]]:_(s32) = G_SEXTLOAD %0:_(p0) = COPY $x0 %1:_(s32) = COPY $w1 %2:_(s8) = G_LOAD %0 :: (load (s8) from %ir.addr) ; CHECK: [[T4:%[0-9]+]]:_(s8) = G_TRUNC [[T0]](s32) %3:_(s32) = G_SEXT %2 %4:_(s32) = G_CONSTANT i32 1 %5:_(s1) = G_ICMP intpred(ne), %1:_(s32), %4:_ G_BRCOND %5:_(s1), %bb.1 G_BR %bb.2.exit bb.1.if: ; CHECK: bb.1.if: successors: %bb.2(0x80000000) %10:_(s8) = G_CONSTANT i8 1 ; CHECK: [[T1:%[0-9]+]]:_(s8) = G_TRUNC [[T0]](s32) %6:_(s8) = G_ADD %2, %10 ; CHECK: [[T2:%[0-9]+]]:_(s8) = G_ADD [[T1]], {{.*}} G_BR %bb.2.exit bb.2.exit: ; CHECK: bb.2.exit: %8:_(s8) = G_PHI %6:_(s8), %bb.1, %2:_(s8), %bb.0 ; CHECK: [[T5:%[0-9]+]]:_(s8) = G_PHI [[T2]](s8), %bb.1, [[T4]](s8) %9:_(s32) = G_ZEXT %8 ; CHECK: [[T6:%[0-9]+]]:_(s32) = G_ZEXT [[T5]](s8) ; CHECK: $w0 = COPY [[T0]](s32) ; CHECK: $w1 = COPY [[T6]](s32) $w0 = COPY %3 $w1 = COPY %9 ... --- name: sink_to_phi_nondominating # CHECK-LABEL: name: sink_to_phi_nondominating tracksRegLiveness: true body: | bb.0.entry: liveins: $x0, $w1 successors: %bb.1(0x40000000), %bb.2(0x40000000); %bb.1(50.00%), %bb.2(50.00%) ; CHECK: [[T0:%[0-9]+]]:_(s32) = G_SEXTLOAD %0:_(p0) = COPY $x0 %1:_(s32) = COPY $w1 %2:_(s8) = G_LOAD %0 :: (load (s8) from %ir.addr) %3:_(s32) = G_CONSTANT i32 1 %4:_(s1) = G_ICMP intpred(ne), %1:_(s32), %3:_ G_BRCOND %4:_(s1), %bb.1 G_BR %bb.2.else bb.1.if: ; CHECK: bb.1.if: successors: %bb.3(0x80000000) %5:_(s8) = G_CONSTANT i8 1 ; CHECK: [[T1:%[0-9]+]]:_(s8) = G_TRUNC [[T0]](s32) %6:_(s8) = G_ADD %2, %5 ; CHECK: [[T2:%[0-9]+]]:_(s8) = G_ADD [[T1]], {{.*}} G_BR %bb.3.exit bb.2.else: ; CHECK: bb.2.else: successors: %bb.3(0x80000000) %7:_(s8) = G_CONSTANT i8 1 ; CHECK: [[T3:%[0-9]+]]:_(s8) = G_TRUNC [[T0]](s32) %8:_(s8) = G_SUB %2, %7 ; CHECK: [[T4:%[0-9]+]]:_(s8) = G_SUB [[T3]], {{.*}} G_BR %bb.3.exit bb.3.exit: ; CHECK: bb.3.exit: %9:_(s8) = G_PHI %6:_(s8), %bb.1, %8:_(s8), %bb.2 ; CHECK: [[T5:%[0-9]+]]:_(s8) = G_PHI [[T2]](s8), %bb.1, [[T4]](s8) %10:_(s32) = G_SEXT %2 %11:_(s32) = G_ZEXT %9 ; CHECK: [[T6:%[0-9]+]]:_(s32) = G_ZEXT [[T5]](s8) ; CHECK: $w0 = COPY [[T0]](s32) ; CHECK: $w1 = COPY [[T6]](s32) $w0 = COPY %10 $w1 = COPY %11 # CHECK-WORKLIST-LABEL: Generic MI Combiner for: sink_to_phi_nondominating # CHECK-WORKLIST: Try combining [[IN0:%[0-9]+]]:_(s8) = G_LOAD [[IN1:%[0-9]+]]:_(p0){{.*}} :: (load (s8) from %ir.addr) # CHECK-WORKLIST: Preferred use is: [[IN2:%[0-9]+]]:_(s32) = G_SEXT [[IN0]]:_(s8) # CHECK-WORKLIST-DAG: Changing: [[IN0]]:_(s8) = G_LOAD [[IN1]]:_(p0){{.*}} :: (load (s8) from %ir.addr) # CHECK-WORKLIST-DAG: Creating: G_TRUNC # CHECK-WORKLIST-DAG: Changing: [[IN3:%[0-9]+]]:_(s8) = G_ADD [[IN0]]:_, [[IN4:%[0-9]+]]:_ # CHECK-WORKLIST-DAG: Changed: [[IN3]]:_(s8) = G_ADD [[OUT1:%[0-9]+]]:_, [[IN4]]:_ # CHECK-WORKLIST-DAG: Creating: G_TRUNC # CHECK-WORKLIST-DAG: Changing: [[IN5:%[0-9]+]]:_(s8) = G_SUB [[IN0]]:_, [[IN6:%[0-9]+]]:_ # CHECK-WORKLIST-DAG: Changed: [[IN5]]:_(s8) = G_SUB [[OUT2:%[0-9]+]]:_, [[IN6]]:_ # CHECK-WORKLIST-DAG: Erasing: [[IN2]]:_(s32) = G_SEXT [[IN0]]:_(s8) # CHECK-WORKLIST-DAG: Changed: [[IN2]]:_(s32) = G_SEXTLOAD [[IN1]]:_(p0){{.*}} :: (load (s8) from %ir.addr) # CHECK-WORKLIST-DAG: Created: [[OUT1]]:_(s8) = G_TRUNC [[IN2]]:_(s32) # CHECK-WORKLIST-DAG: Created: [[OUT2]]:_(s8) = G_TRUNC [[IN2]]:_(s32) # CHECK-WORKLIST: Try combining ... --- name: sink_to_phi_emptyblock # CHECK-LABEL: name: sink_to_phi_emptyblock tracksRegLiveness: true body: | bb.0.entry: liveins: $x0, $w1 successors: %bb.1(0x40000000), %bb.2(0x40000000); %bb.1(50.00%), %bb.2(50.00%) ; CHECK: [[T0:%[0-9]+]]:_(s32) = G_SEXTLOAD %0:_(p0) = COPY $x0 %1:_(s32) = COPY $w1 %2:_(s8) = G_LOAD %0 :: (load (s8) from %ir.addr) %3:_(s32) = G_SEXT %2 %4:_(s32) = G_CONSTANT i32 1 %5:_(s1) = G_ICMP intpred(ne), %1:_(s32), %4:_ G_BRCOND %5:_(s1), %bb.1 G_BR %bb.2.else bb.1.if: ; CHECK: bb.1.if: successors: %bb.4(0x80000000) %10:_(s8) = G_CONSTANT i8 1 ; CHECK: [[T1:%[0-9]+]]:_(s8) = G_TRUNC [[T0]](s32) %6:_(s8) = G_ADD %2, %10 ; CHECK: [[T2:%[0-9]+]]:_(s8) = G_ADD [[T1]], {{.*}} G_BR %bb.4.exit bb.2.else: ; CHECK: bb.2.else: successors: %bb.3(0x80000000) G_BR %bb.3.else2 bb.3.else2: ; CHECK: bb.3.else2: successors: %bb.4(0x80000000) ; CHECK: [[T4:%[0-9]+]]:_(s8) = G_TRUNC [[T0]](s32) ; Fallthrough bb.4.exit: ; CHECK: bb.4.exit: %8:_(s8) = G_PHI %6:_(s8), %bb.1, %2:_(s8), %bb.3 ; CHECK: [[T5:%[0-9]+]]:_(s8) = G_PHI [[T2]](s8), %bb.1, [[T4]](s8) %9:_(s32) = G_ZEXT %8 ; CHECK: [[T6:%[0-9]+]]:_(s32) = G_ZEXT [[T5]](s8) ; CHECK: $w0 = COPY [[T0]](s32) ; CHECK: $w1 = COPY [[T6]](s32) $w0 = COPY %3 $w1 = COPY %9 ... --- name: use_doesnt_def_anything # CHECK-LABEL: name: use_doesnt_def_anything # Check that we don't crash when inspecting a use that doesn't define anything. # The real issue which was that the combine rule was looking through # non-truncates as if they were truncates and attempting to obtain the result # register. It would usually go on to make the right overall decision anyway but # would sometimes crash on things like (SOME_INTRINSIC imm). This test covers # the case that it would recover from. tracksRegLiveness: true body: | bb.0.entry: liveins: $x0, $w1 %0:_(p0) = COPY $x0 %1:_(s8) = G_LOAD %0 :: (load (s8) from %ir.addr) ; CHECK: %1:_(s8) = G_LOAD %0(p0) :: (load (s8) from %ir.addr) G_STORE %1(s8), %0(p0) :: (store (s8) into %ir.addr) ; CHECK: G_STORE %1(s8), %0(p0) :: (store (s8) into %ir.addr) ... --- name: op0_isnt_a_reg # CHECK-LABEL: name: op0_isnt_a_reg # This test covers the variant of use_doesnt_def_anything that would crash. tracksRegLiveness: true body: | bb.0.entry: liveins: $x0, $w1 %0:_(p0) = COPY $x0 %1:_(s8) = G_LOAD %0 :: (load (s8) from %ir.addr) ; CHECK: %1:_(s8) = G_LOAD %0(p0) :: (load (s8) from %ir.addr) G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.aarch64.hint), %1(s8) ; CHECK: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.aarch64.hint), %1(s8) ...