# RUN: llc -O0 -run-pass=regbankselect -global-isel %s -o - 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=FAST # RUN: llc -O0 -run-pass=regbankselect -global-isel %s -regbankselect-greedy -o - 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=GREEDY # REQUIRES: global-isel --- | ; ModuleID = 'generic-virtual-registers-type-error.mir' target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" target triple = "aarch64-apple-ios" define void @defaultMapping() { entry: ret void } define void @defaultMappingVector() { entry: ret void } define void @defaultMapping1Repair() { entry: ret void } define void @defaultMapping2Repairs() { entry: ret void } define void @defaultMappingDefRepair() { entry: ret void } define void @phiPropagation(i32* %src, i32* %dst, i1 %cond) { entry: %srcVal = load i32, i32* %src br i1 %cond, label %end, label %then then: %res = add i32 %srcVal, 36 br label %end end: %toStore = phi i32 [ %srcVal, %entry ], [ %res, %then ] store i32 %toStore, i32* %dst ret void } define void @defaultMappingUseRepairPhysReg() { entry: ret void } define void @defaultMappingDefRepairPhysReg() { entry: ret void } define void @greedyMappingOr() { entry: ret void } define void @greedyMappingOrWithConstraints() { entry: ret void } ... --- # Check that we assign a relevant register bank for %0. # Based on the type i32, this should be gpr. name: defaultMapping isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: gpr } registers: - { id: 0, class: _ } body: | bb.0.entry: liveins: %x0 ; CHECK: %0(32) = G_ADD i32 %x0 %0(32) = G_ADD i32 %x0, %x0 ... --- # Check that we assign a relevant register bank for %0. # Based on the type <2 x i32>, this should be fpr. # FPR is used for both floating point and vector registers. name: defaultMappingVector isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: fpr } registers: - { id: 0, class: _ } body: | bb.0.entry: liveins: %d0 ; CHECK: %0(32) = G_ADD <2 x i32> %d0 %0(32) = G_ADD <2 x i32> %d0, %d0 ... --- # Check that we repair the assignment for %0. # Indeed based on the source of the copy it should live # in FPR, but at the use, it should be GPR. name: defaultMapping1Repair isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: fpr } # CHECK-NEXT: - { id: 1, class: gpr } # CHECK-NEXT: - { id: 2, class: gpr } registers: - { id: 0, class: _ } - { id: 1, class: _ } body: | bb.0.entry: liveins: %s0, %x0 ; CHECK: %0(32) = COPY %s0 ; CHECK-NEXT: %2(32) = COPY %0 ; CHECK-NEXT: %1(32) = G_ADD i32 %2, %x0 %0(32) = COPY %s0 %1(32) = G_ADD i32 %0, %x0 ... # Check that we repair the assignment for %0 differently for both uses. name: defaultMapping2Repairs isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: fpr } # CHECK-NEXT: - { id: 1, class: gpr } # CHECK-NEXT: - { id: 2, class: gpr } # CHECK-NEXT: - { id: 3, class: gpr } registers: - { id: 0, class: _ } - { id: 1, class: _ } body: | bb.0.entry: liveins: %s0, %x0 ; CHECK: %0(32) = COPY %s0 ; CHECK-NEXT: %2(32) = COPY %0 ; CHECK-NEXT: %3(32) = COPY %0 ; CHECK-NEXT: %1(32) = G_ADD i32 %2, %3 %0(32) = COPY %s0 %1(32) = G_ADD i32 %0, %0 ... --- # Check that we repair the definition of %1. # %1 is forced to be into FPR, but its definition actually # requires that it lives in GPR. Make sure regbankselect # fixes that. name: defaultMappingDefRepair isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: gpr } # CHECK-NEXT: - { id: 1, class: fpr } # CHECK-NEXT: - { id: 2, class: gpr } registers: - { id: 0, class: _ } - { id: 1, class: fpr } body: | bb.0.entry: liveins: %w0 ; CHECK: %0(32) = COPY %w0 ; CHECK-NEXT: %2(32) = G_ADD i32 %0, %w0 ; CHECK-NEXT: %1(32) = COPY %2 %0(32) = COPY %w0 %1(32) = G_ADD i32 %0, %w0 ... --- # Check that we are able to propagate register banks from phis. name: phiPropagation isSSA: true tracksRegLiveness: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: gpr32 } # CHECK-NEXT: - { id: 1, class: gpr64sp } # CHECK-NEXT: - { id: 2, class: gpr32 } # CHECK-NEXT: - { id: 3, class: gpr } # CHECK-NEXT: - { id: 4, class: gpr } registers: - { id: 0, class: gpr32 } - { id: 1, class: gpr64sp } - { id: 2, class: gpr32 } - { id: 3, class: _ } - { id: 4, class: _ } body: | bb.0.entry: successors: %bb.2.end, %bb.1.then liveins: %x0, %x1, %w2 %0 = LDRWui killed %x0, 0 :: (load 4 from %ir.src) %1 = COPY %x1 %2 = COPY %w2 TBNZW killed %2, 0, %bb.2.end bb.1.then: successors: %bb.2.end %3(32) = G_ADD i32 %0, %0 bb.2.end: %4(32) = PHI %0, %bb.0.entry, %3, %bb.1.then STRWui killed %4, killed %1, 0 :: (store 4 into %ir.dst) RET_ReallyLR ... --- # Make sure we can repair physical register uses as well. name: defaultMappingUseRepairPhysReg isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: gpr } # CHECK-NEXT: - { id: 1, class: gpr } # CHECK-NEXT: - { id: 2, class: gpr } registers: - { id: 0, class: _ } - { id: 1, class: _ } body: | bb.0.entry: liveins: %w0, %s0 ; CHECK: %0(32) = COPY %w0 ; CHECK-NEXT: %2(32) = COPY %s0 ; CHECK-NEXT: %1(32) = G_ADD i32 %0, %2 %0(32) = COPY %w0 %1(32) = G_ADD i32 %0, %s0 ... --- # Make sure we can repair physical register defs. name: defaultMappingDefRepairPhysReg isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: gpr } # CHECK-NEXT: - { id: 1, class: gpr } registers: - { id: 0, class: _ } body: | bb.0.entry: liveins: %w0 ; CHECK: %0(32) = COPY %w0 ; CHECK-NEXT: %1(32) = G_ADD i32 %0, %0 ; CHECK-NEXT: %s0 = COPY %1 %0(32) = COPY %w0 %s0 = G_ADD i32 %0, %0 ... --- # Check that the greedy mode is able to switch the # G_OR instruction from fpr to gpr. name: greedyMappingOr isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: gpr } # CHECK-NEXT: - { id: 1, class: gpr } # Fast mode maps vector instruction on FPR. # FAST-NEXT: - { id: 2, class: fpr } # Fast mode needs two extra copies. # FAST-NEXT: - { id: 3, class: fpr } # FAST-NEXT: - { id: 4, class: fpr } # Greedy mode coalesce the computation on the GPR register # because it is the cheapest. # GREEDY-NEXT: - { id: 2, class: gpr } registers: - { id: 0, class: _ } - { id: 1, class: _ } - { id: 2, class: _ } body: | bb.0.entry: liveins: %x0, %x1 ; CHECK: %0(64) = COPY %x0 ; CHECK-NEXT: %1(64) = COPY %x1 ; Fast mode tries to reuse the source of the copy for the destination. ; Now, the default mapping says that %0 and %1 need to be in FPR. ; The repairing code insert two copies to materialize that. ; FAST-NEXT: %3(64) = COPY %0 ; FAST-NEXT: %4(64) = COPY %1 ; The mapping of G_OR is on FPR. ; FAST-NEXT: %2(64) = G_OR <2 x i32> %3, %4 ; Greedy mode remapped the instruction on the GPR bank. ; GREEDY-NEXT: %2(64) = G_OR <2 x i32> %0, %1 %0(64) = COPY %x0 %1(64) = COPY %x1 %2(64) = G_OR <2 x i32> %0, %1 ... --- # Check that the greedy mode is able to switch the # G_OR instruction from fpr to gpr, while still honoring # %2 constraint. name: greedyMappingOrWithConstraints isSSA: true # CHECK: registers: # CHECK-NEXT: - { id: 0, class: gpr } # CHECK-NEXT: - { id: 1, class: gpr } # CHECK-NEXT: - { id: 2, class: fpr } # Fast mode maps vector instruction on FPR. # Fast mode needs two extra copies. # FAST-NEXT: - { id: 3, class: fpr } # FAST-NEXT: - { id: 4, class: fpr } # Greedy mode coalesce the computation on the GPR register because it # is the cheapest, but will need one extra copy to materialize %2 into a FPR. # GREEDY-NEXT: - { id: 3, class: gpr } registers: - { id: 0, class: _ } - { id: 1, class: _ } - { id: 2, class: fpr } body: | bb.0.entry: liveins: %x0, %x1 ; CHECK: %0(64) = COPY %x0 ; CHECK-NEXT: %1(64) = COPY %x1 ; Fast mode tries to reuse the source of the copy for the destination. ; Now, the default mapping says that %0 and %1 need to be in FPR. ; The repairing code insert two copies to materialize that. ; FAST-NEXT: %3(64) = COPY %0 ; FAST-NEXT: %4(64) = COPY %1 ; The mapping of G_OR is on FPR. ; FAST-NEXT: %2(64) = G_OR <2 x i32> %3, %4 ; Greedy mode remapped the instruction on the GPR bank. ; GREEDY-NEXT: %3(64) = G_OR <2 x i32> %0, %1 ; We need to keep %2 into FPR because we do not know anything about it. ; GREEDY-NEXT: %2(64) = COPY %3 %0(64) = COPY %x0 %1(64) = COPY %x1 %2(64) = G_OR <2 x i32> %0, %1 ...