diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-concat-vectors.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-concat-vectors.mir index 425b44e0ccee..2aee3f18bb1c 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/select-concat-vectors.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-concat-vectors.mir @@ -6,6 +6,7 @@ name: legal_v4s32_v2s32 alignment: 2 legalized: true regBankSelected: true +tracksRegLiveness: true registers: - { id: 0, class: fpr } - { id: 1, class: fpr } @@ -14,21 +15,23 @@ frameInfo: maxCallFrameSize: 0 body: | bb.0: + liveins: $d0, $d1 ; CHECK-LABEL: name: legal_v4s32_v2s32 + ; CHECK: liveins: $d0, $d1 ; CHECK: [[COPY:%[0-9]+]]:fpr64 = COPY $d0 ; CHECK: [[COPY1:%[0-9]+]]:fpr64 = COPY $d1 ; CHECK: [[DEF:%[0-9]+]]:fpr128 = IMPLICIT_DEF - ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:fpr128 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.dsub + ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:fpr128 = INSERT_SUBREG [[DEF]], [[COPY1]], %subreg.dsub ; CHECK: [[DEF1:%[0-9]+]]:fpr128 = IMPLICIT_DEF - ; CHECK: [[INSERT_SUBREG1:%[0-9]+]]:fpr128 = INSERT_SUBREG [[DEF1]], [[COPY1]], %subreg.dsub - ; CHECK: [[INSvi64lane:%[0-9]+]]:fpr128 = INSvi64lane [[INSERT_SUBREG]], 1, [[INSERT_SUBREG1]], 0 + ; CHECK: [[INSERT_SUBREG1:%[0-9]+]]:fpr128 = INSERT_SUBREG [[DEF1]], [[COPY]], %subreg.dsub + ; CHECK: [[INSvi64lane:%[0-9]+]]:fpr128 = INSvi64lane [[INSERT_SUBREG1]], 1, [[INSERT_SUBREG]], 0 ; CHECK: $q0 = COPY [[INSvi64lane]] - ; CHECK: RET_ReallyLR + ; CHECK: RET_ReallyLR implicit $q0 %0:fpr(<2 x s32>) = COPY $d0 %1:fpr(<2 x s32>) = COPY $d1 %2:fpr(<4 x s32>) = G_CONCAT_VECTORS %0(<2 x s32>), %1(<2 x s32>) $q0 = COPY %2(<4 x s32>) - RET_ReallyLR + RET_ReallyLR implicit $q0 ... --- @@ -36,6 +39,7 @@ name: legal_v8s16_v4s16 alignment: 2 legalized: true regBankSelected: true +tracksRegLiveness: true registers: - { id: 0, class: fpr } - { id: 1, class: fpr } @@ -44,20 +48,22 @@ frameInfo: maxCallFrameSize: 0 body: | bb.0: + liveins: $d0, $d1 ; CHECK-LABEL: name: legal_v8s16_v4s16 + ; CHECK: liveins: $d0, $d1 ; CHECK: [[COPY:%[0-9]+]]:fpr64 = COPY $d0 ; CHECK: [[COPY1:%[0-9]+]]:fpr64 = COPY $d1 ; CHECK: [[DEF:%[0-9]+]]:fpr128 = IMPLICIT_DEF - ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:fpr128 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.dsub + ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:fpr128 = INSERT_SUBREG [[DEF]], [[COPY1]], %subreg.dsub ; CHECK: [[DEF1:%[0-9]+]]:fpr128 = IMPLICIT_DEF - ; CHECK: [[INSERT_SUBREG1:%[0-9]+]]:fpr128 = INSERT_SUBREG [[DEF1]], [[COPY1]], %subreg.dsub - ; CHECK: [[INSvi64lane:%[0-9]+]]:fpr128 = INSvi64lane [[INSERT_SUBREG]], 1, [[INSERT_SUBREG1]], 0 + ; CHECK: [[INSERT_SUBREG1:%[0-9]+]]:fpr128 = INSERT_SUBREG [[DEF1]], [[COPY]], %subreg.dsub + ; CHECK: [[INSvi64lane:%[0-9]+]]:fpr128 = INSvi64lane [[INSERT_SUBREG1]], 1, [[INSERT_SUBREG]], 0 ; CHECK: $q0 = COPY [[INSvi64lane]] - ; CHECK: RET_ReallyLR + ; CHECK: RET_ReallyLR implicit $q0 %0:fpr(<4 x s16>) = COPY $d0 %1:fpr(<4 x s16>) = COPY $d1 %2:fpr(<8 x s16>) = G_CONCAT_VECTORS %0(<4 x s16>), %1(<4 x s16>) $q0 = COPY %2(<8 x s16>) - RET_ReallyLR + RET_ReallyLR implicit $q0 ... diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-int-ext.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-int-ext.mir index bc59d6df2b35..90de1f6df2cc 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/select-int-ext.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-int-ext.mir @@ -41,9 +41,10 @@ body: | liveins: $w0 ; CHECK-LABEL: name: anyext_s64_from_s32 - ; CHECK: [[COPY:%[0-9]+]]:gpr32all = COPY $w0 - ; CHECK: [[SUBREG_TO_REG:%[0-9]+]]:gpr64all = SUBREG_TO_REG 0, [[COPY]], %subreg.sub_32 - ; CHECK: $x0 = COPY [[SUBREG_TO_REG]] + ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w0 + ; CHECK: [[DEF:%[0-9]+]]:gpr64all = IMPLICIT_DEF + ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64all = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32 + ; CHECK: $x0 = COPY [[INSERT_SUBREG]] %0(s32) = COPY $w0 %1(s64) = G_ANYEXT %0 $x0 = COPY %1(s64) @@ -64,8 +65,8 @@ body: | ; CHECK-LABEL: name: anyext_s32_from_s8 ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w0 - ; CHECK: [[COPY2:%[0-9]+]]:gpr32all = COPY [[COPY]] - ; CHECK: $w0 = COPY [[COPY2]] + ; CHECK: [[COPY1:%[0-9]+]]:gpr32all = COPY [[COPY]] + ; CHECK: $w0 = COPY [[COPY1]] %2:gpr(s32) = COPY $w0 %0(s8) = G_TRUNC %2 %1(s32) = G_ANYEXT %0 @@ -236,8 +237,8 @@ body: | ; CHECK-LABEL: name: zext_s16_from_s8 ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w0 ; CHECK: [[UBFMWri:%[0-9]+]]:gpr32 = UBFMWri [[COPY]], 0, 7 - ; CHECK: [[COPY2:%[0-9]+]]:gpr32all = COPY [[UBFMWri]] - ; CHECK: $w0 = COPY [[COPY2]] + ; CHECK: [[COPY1:%[0-9]+]]:gpr32all = COPY [[UBFMWri]] + ; CHECK: $w0 = COPY [[COPY1]] %2:gpr(s32) = COPY $w0 %0(s8) = G_TRUNC %2 %1(s16) = G_ZEXT %0 @@ -339,8 +340,9 @@ body: | ; CHECK-LABEL: name: sext_s64_from_s32 ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w0 - ; CHECK: [[SUBREG_TO_REG:%[0-9]+]]:gpr64 = SUBREG_TO_REG 0, [[COPY]], %subreg.sub_32 - ; CHECK: [[SBFMXri:%[0-9]+]]:gpr64 = SBFMXri [[SUBREG_TO_REG]], 0, 31 + ; CHECK: [[DEF:%[0-9]+]]:gpr64all = IMPLICIT_DEF + ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32 + ; CHECK: [[SBFMXri:%[0-9]+]]:gpr64 = SBFMXri [[INSERT_SUBREG]], 0, 31 ; CHECK: $x0 = COPY [[SBFMXri]] %0(s32) = COPY $w0 %1(s64) = G_SEXT %0 @@ -409,8 +411,8 @@ body: | ; CHECK-LABEL: name: sext_s16_from_s8 ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w0 ; CHECK: [[SBFMWri:%[0-9]+]]:gpr32 = SBFMWri [[COPY]], 0, 7 - ; CHECK: [[COPY2:%[0-9]+]]:gpr32all = COPY [[SBFMWri]] - ; CHECK: $w0 = COPY [[COPY2]] + ; CHECK: [[COPY1:%[0-9]+]]:gpr32all = COPY [[SBFMWri]] + ; CHECK: $w0 = COPY [[COPY1]] %2:gpr(s32) = COPY $w0 %0(s8) = G_TRUNC %2 %1(s16) = G_SEXT %0 diff --git a/llvm/test/CodeGen/X86/GlobalISel/select-copy.mir b/llvm/test/CodeGen/X86/GlobalISel/select-copy.mir index 3ff398798209..5e36ffa6fc52 100644 --- a/llvm/test/CodeGen/X86/GlobalISel/select-copy.mir +++ b/llvm/test/CodeGen/X86/GlobalISel/select-copy.mir @@ -179,16 +179,18 @@ regBankSelected: true # ALL: registers: # ALL-NEXT: - { id: 0, class: gr32, preferred-register: '' } # ALL-NEXT: - { id: 1, class: gr16, preferred-register: '' } -# ALL-NEXT: - { id: 2, class: gr32, preferred-register: '' } +# ALL-NEXT: - { id: 2, class: low32_addr_access_rbp, preferred-register: '' } +# ALL-NEXT: - { id: 3, class: low32_addr_access_rbp, preferred-register: '' } registers: - { id: 0, class: gpr, preferred-register: '' } - { id: 1, class: gpr, preferred-register: '' } - { id: 2, class: gpr, preferred-register: '' } -# ALL %0:gr32 = COPY $edx -# ALL-NEXT %1:gr16 = COPY %0.sub_16bit -# ALL-NEXT %2:gr32 = SUBREG_TO_REG 0, %1, %subreg.sub_16bit -# ALL-NEXT $eax = COPY %2 -# ALL-NEXT RET 0, implicit $eax +# ALL: %0:gr32 = COPY $edx +# ALL-NEXT: %1:gr16 = COPY %0.sub_16bit +# ALL-NEXT: %3:low32_addr_access_rbp = IMPLICIT_DEF +# ALL-NEXT: %2:low32_addr_access_rbp = INSERT_SUBREG %3, %1, %subreg.sub_16bit +# ALL-NEXT: $eax = COPY %2 +# ALL-NEXT: RET 0, implicit $eax body: | bb.1 (%ir-block.0): liveins: $eax,$edx @@ -200,4 +202,3 @@ body: | RET 0, implicit $eax ... - diff --git a/llvm/test/CodeGen/X86/GlobalISel/select-ext-x86-64.mir b/llvm/test/CodeGen/X86/GlobalISel/select-ext-x86-64.mir index b5caaf0e3080..f384433c331b 100644 --- a/llvm/test/CodeGen/X86/GlobalISel/select-ext-x86-64.mir +++ b/llvm/test/CodeGen/X86/GlobalISel/select-ext-x86-64.mir @@ -186,8 +186,9 @@ body: | ; ALL-LABEL: name: anyext_s64_from_s32 ; ALL: [[COPY:%[0-9]+]]:gr64 = COPY $rdi ; ALL: [[COPY1:%[0-9]+]]:gr32 = COPY [[COPY]].sub_32bit - ; ALL: [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, [[COPY1]], %subreg.sub_32bit - ; ALL: $rax = COPY [[SUBREG_TO_REG]] + ; ALL: [[DEF:%[0-9]+]]:gr64 = IMPLICIT_DEF + ; ALL: [[INSERT_SUBREG:%[0-9]+]]:gr64 = INSERT_SUBREG [[DEF]], [[COPY1]], %subreg.sub_32bit + ; ALL: $rax = COPY [[INSERT_SUBREG]] ; ALL: RET 0, implicit $rax %0(s64) = COPY $rdi %1(s32) = G_TRUNC %0(s64) diff --git a/llvm/test/CodeGen/X86/GlobalISel/select-ext.mir b/llvm/test/CodeGen/X86/GlobalISel/select-ext.mir index dc077dda8802..ce3e7900731f 100644 --- a/llvm/test/CodeGen/X86/GlobalISel/select-ext.mir +++ b/llvm/test/CodeGen/X86/GlobalISel/select-ext.mir @@ -446,14 +446,16 @@ regBankSelected: true # ALL: registers: # ALL-NEXT: - { id: 0, class: gr32, preferred-register: '' } # ALL-NEXT: - { id: 1, class: gr16, preferred-register: '' } -# ALL-NEXT: - { id: 2, class: gr32, preferred-register: '' } +# ALL-NEXT: - { id: 2, class: low32_addr_access_rbp, preferred-register: '' } +# ALL-NEXT: - { id: 3, class: low32_addr_access_rbp, preferred-register: '' } registers: - { id: 0, class: gpr } - { id: 1, class: gpr } - { id: 2, class: gpr } # ALL: %0:gr32 = COPY $edi # ALL-NEXT: %1:gr16 = COPY %0.sub_16bit -# ALL-NEXT: %2:gr32 = SUBREG_TO_REG 0, %1, %subreg.sub_16bit +# ALL-NEXT: %3:low32_addr_access_rbp = IMPLICIT_DEF +# ALL-NEXT: %2:low32_addr_access_rbp = INSERT_SUBREG %3, %1, %subreg.sub_16bit # ALL-NEXT: $eax = COPY %2 # ALL-NEXT: RET 0, implicit $eax body: | diff --git a/llvm/test/CodeGen/X86/GlobalISel/x86_64-select-zext.mir b/llvm/test/CodeGen/X86/GlobalISel/x86_64-select-zext.mir index 1883368a4a1f..d5bccf9a0591 100644 --- a/llvm/test/CodeGen/X86/GlobalISel/x86_64-select-zext.mir +++ b/llvm/test/CodeGen/X86/GlobalISel/x86_64-select-zext.mir @@ -165,8 +165,9 @@ body: | ; CHECK-LABEL: name: zext_i1_to_i64 ; CHECK: liveins: $edi ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $edi - ; CHECK: [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, [[COPY]], %subreg.sub_32bit - ; CHECK: [[AND64ri8_:%[0-9]+]]:gr64 = AND64ri8 [[SUBREG_TO_REG]], 1, implicit-def $eflags + ; CHECK: [[DEF:%[0-9]+]]:gr64 = IMPLICIT_DEF + ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gr64 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32bit + ; CHECK: [[AND64ri8_:%[0-9]+]]:gr64 = AND64ri8 [[INSERT_SUBREG]], 1, implicit-def $eflags ; CHECK: $rax = COPY [[AND64ri8_]] ; CHECK: RET 0, implicit $rax %1:gpr(s32) = COPY $edi @@ -258,8 +259,9 @@ body: | ; CHECK-LABEL: name: zext_i8_to_i64 ; CHECK: liveins: $edi ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $edi - ; CHECK: [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, [[COPY]], %subreg.sub_32bit - ; CHECK: [[AND64ri32_:%[0-9]+]]:gr64 = AND64ri32 [[SUBREG_TO_REG]], 255, implicit-def $eflags + ; CHECK: [[DEF:%[0-9]+]]:gr64 = IMPLICIT_DEF + ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gr64 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32bit + ; CHECK: [[AND64ri32_:%[0-9]+]]:gr64 = AND64ri32 [[INSERT_SUBREG]], 255, implicit-def $eflags ; CHECK: $rax = COPY [[AND64ri32_]] ; CHECK: RET 0, implicit $rax %1:gpr(s32) = COPY $edi @@ -320,8 +322,9 @@ body: | ; CHECK-LABEL: name: zext_i16_to_i64 ; CHECK: liveins: $edi ; CHECK: [[COPY:%[0-9]+]]:gr32 = COPY $edi - ; CHECK: [[SUBREG_TO_REG:%[0-9]+]]:gr64 = SUBREG_TO_REG 0, [[COPY]], %subreg.sub_32bit - ; CHECK: [[AND64ri32_:%[0-9]+]]:gr64 = AND64ri32 [[SUBREG_TO_REG]], 65535, implicit-def $eflags + ; CHECK: [[DEF:%[0-9]+]]:gr64 = IMPLICIT_DEF + ; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gr64 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32bit + ; CHECK: [[AND64ri32_:%[0-9]+]]:gr64 = AND64ri32 [[INSERT_SUBREG]], 65535, implicit-def $eflags ; CHECK: $rax = COPY [[AND64ri32_]] ; CHECK: RET 0, implicit $rax %1:gpr(s32) = COPY $edi diff --git a/llvm/test/TableGen/GlobalISelEmitterSubreg.td b/llvm/test/TableGen/GlobalISelEmitterSubreg.td new file mode 100644 index 000000000000..63527366da0e --- /dev/null +++ b/llvm/test/TableGen/GlobalISelEmitterSubreg.td @@ -0,0 +1,119 @@ +// RUN: llvm-tblgen %s -gen-global-isel -warn-on-skipped-patterns -I %p/../../include -I %p/Common -o - 2> %t.skipped | FileCheck %s +// RUN: cat %t.skipped | FileCheck %s --check-prefix=SKIPPED + +include "llvm/Target/Target.td" +include "GlobalISelEmitterCommon.td" + +// Boilerplate code for setting up some registers with subregs. +class MyReg subregs = []> + : Register { + let SubRegs = subregs; +} + +class MyClass types, dag registers> + : RegisterClass<"Test", types, size, registers> { + let Size = size; +} + +def sub0 : SubRegIndex<16>; +def sub1 : SubRegIndex<16, 16>; +def S0 : MyReg<"s0">; +def S1 : MyReg<"s1">; +def S2 : MyReg<"s14">; +def S3 : MyReg<"s15">; +def SRegs : MyClass<16, [i16], (sequence "S%u", 0, 3)>; + +let SubRegIndices = [sub0, sub1] in { +def D0 : MyReg<"d0", [S0, S1]>; +def E0 : MyReg<"e0", [S2, S3]>; +} + +def DRegs : MyClass<32, [i32], (sequence "D%u", 0, 0)>; +def ERegs : MyClass<32, [i32], (sequence "E%u", 0, 0)>; +def SOP : RegisterOperand; +def DOP : RegisterOperand; +def SOME_INSN : I<(outs DRegs:$dst), (ins DOP:$src), []>; + +// We should skip cases where we don't have a given register class for the +// subregister source. +// SKIPPED: def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), i16:$src, sub0)>; +// SKIPPED: def : Pat<(i32 (anyext i16:$src)), (SOME_INSN (INSERT_SUBREG (i32 (IMPLICIT_DEF)), i16:$src, sub0))>; +def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), i16:$src, sub0)>; +def : Pat<(i32 (anyext i16:$src)), (SOME_INSN (INSERT_SUBREG (i32 (IMPLICIT_DEF)), i16:$src, sub0))>; + +// Test that we import INSERT_SUBREG when its subregister source has a given +// class. +def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), SOP:$src, sub0)>; +// CHECK-LABEL: (anyext:{ *:[i32] } i16:{ *:[i16] }:$src) => (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), SOP:{ *:[i16] }:$src, sub0:{ *:[i32] }) +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::IMPLICIT_DEF, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/1, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::INSERT_SUBREG, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src +// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/1, +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC DRegs*/1, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/1, /*RC DRegs*/1, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/2, /*RC SRegs*/0, + + +// Test that we can import INSERT_SUBREG when it is a subinstruction of another +// instruction. +def : Pat<(i32 (anyext i16:$src)), (SOME_INSN (INSERT_SUBREG (i32 (IMPLICIT_DEF)), SOP:$src, sub0))>; +// CHECK-LABEL: (anyext:{ *:[i32] } i16:{ *:[i16] }:$src) => (SOME_INSN:{ *:[i32] } (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), SOP:{ *:[i16] }:$src, sub0:{ *:[i32] })) +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/TargetOpcode::IMPLICIT_DEF, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/RegState::Define, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::INSERT_SUBREG, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/1, /*TempRegFlags*/0, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // src +// CHECK-NEXT: GIR_AddImm, /*InsnID*/1, /*Imm*/1, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, /*RC DRegs*/1, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/1, /*RC DRegs*/1, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/2, /*RC SRegs*/0, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::SOME_INSN, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0, +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, + + +// Test that we correctly infer the super register class for INSERT_SUBREG when +// we have COPY_TO_REGCLASS. We want to make sure we get an E register here, +// not a D register. +def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (COPY_TO_REGCLASS SOP:$src, ERegs)), SOP:$src, sub0)>; +// CHECK-LABEL: (anyext:{ *:[i32] } i16:{ *:[i16] }:$src) => (INSERT_SUBREG:{ *:[i32] } (COPY_TO_REGCLASS:{ *:[i32] } SOP:{ *:[i16] }:$src, ERegs:{ *:[i32] }), SOP:{ *:[i16] }:$src, sub0:{ *:[i32] }) +// CHECK: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::INSERT_SUBREG, +// CHECK-DAG: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC ERegs*/2, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/1, /*RC ERegs*/2, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/2, /*RC SRegs*/0, + +// Test that we can import INSERT_SUBREG when its subregister source is defined +// by a subinstruction. +def SUBSOME_INSN : I<(outs SRegs:$dst), (ins SOP:$src), []>; +def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), (SUBSOME_INSN SOP:$src), sub0)>; +// CHECK-LABEL: (anyext:{ *:[i32] } i16:{ *:[i16] }:$src) => (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), (SUBSOME_INSN:{ *:[i16] } SOP:{ *:[i16] }:$src), sub0:{ *:[i32] }) +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32, +// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s16, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/MyTarget::SUBSOME_INSN, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/RegState::Define, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/0, /*OpIdx*/1, // src +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::IMPLICIT_DEF, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define, +// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/1, +// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::INSERT_SUBREG, +// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0, +// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/1, /*TempRegFlags*/0, +// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/1, +// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC DRegs*/1, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/1, /*RC DRegs*/1, +// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/2, /*RC SRegs*/0, diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp index 47e6934ab367..14822024505c 100644 --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -295,6 +295,46 @@ CodeGenRegBank &CodeGenTarget::getRegBank() const { return *RegBank; } +Optional +CodeGenTarget::getSuperRegForSubReg(const ValueTypeByHwMode &ValueTy, + CodeGenRegBank &RegBank, + const CodeGenSubRegIndex *SubIdx) const { + std::vector Candidates; + auto &RegClasses = RegBank.getRegClasses(); + + // Try to find a register class which supports ValueTy, and also contains + // SubIdx. + for (CodeGenRegisterClass &RC : RegClasses) { + // Is there a subclass of this class which contains this subregister index? + CodeGenRegisterClass *SubClassWithSubReg = RC.getSubClassWithSubReg(SubIdx); + if (!SubClassWithSubReg) + continue; + + // We have a class. Check if it supports this value type. + if (llvm::none_of(SubClassWithSubReg->VTs, + [&ValueTy](const ValueTypeByHwMode &ClassVT) { + return ClassVT == ValueTy; + })) + continue; + + // We have a register class which supports both the value type and + // subregister index. Remember it. + Candidates.push_back(SubClassWithSubReg); + } + + // If we didn't find anything, we're done. + if (Candidates.empty()) + return None; + + // Find and return the largest of our candidate classes. + llvm::stable_sort(Candidates, [&](const CodeGenRegisterClass *A, + const CodeGenRegisterClass *B) { + return A->getMembers().size() > B->getMembers().size(); + }); + + return Candidates[0]; +} + void CodeGenTarget::ReadRegAltNameIndices() const { RegAltNameIndices = Records.getAllDerivedDefinitions("RegAltNameIndex"); llvm::sort(RegAltNameIndices, LessRecord()); diff --git a/llvm/utils/TableGen/CodeGenTarget.h b/llvm/utils/TableGen/CodeGenTarget.h index 1ab2de269c76..d52ffac4ce6c 100644 --- a/llvm/utils/TableGen/CodeGenTarget.h +++ b/llvm/utils/TableGen/CodeGenTarget.h @@ -103,6 +103,12 @@ public: /// getRegBank - Return the register bank description. CodeGenRegBank &getRegBank() const; + /// Return the largest register class on \p RegBank which supports \p Ty and + /// covers \p SubIdx if it exists. + Optional + getSuperRegForSubReg(const ValueTypeByHwMode &Ty, CodeGenRegBank &RegBank, + const CodeGenSubRegIndex *SubIdx) const; + /// getRegisterByName - If there is a register with the specific AsmName, /// return it. const CodeGenRegister *getRegisterByName(StringRef Name) const; diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index dc4f799a2850..0fb6a9c95e61 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -3164,6 +3164,24 @@ private: MatchTable buildMatchTable(MutableArrayRef Rules, bool Optimize, bool WithCoverage); + /// Infer a CodeGenRegisterClass for the type of \p SuperRegNode. The returned + /// CodeGenRegisterClass will support the CodeGenRegisterClass of + /// \p SubRegNode, and the subregister index defined by \p SubRegIdxNode. + /// If no register class is found, return None. + Optional + inferSuperRegisterClass(const TypeSetByHwMode &Ty, + TreePatternNode *SuperRegNode, + TreePatternNode *SubRegIdxNode); + + /// Return the CodeGenRegisterClass associated with \p Leaf if it has one. + Optional + getRegClassFromLeaf(TreePatternNode *Leaf); + + /// Return a CodeGenRegisterClass for \p N if one can be found. Return None + /// otherwise. + Optional + inferRegClassFromPattern(TreePatternNode *N); + public: /// Takes a sequence of \p Rules and group them based on the predicates /// they share. \p MatcherStorage is used as a memory container @@ -3771,6 +3789,12 @@ Expected GlobalISelEmitter::importExplicitUseRenderer( return InsertPt; } + if (ChildRec->isSubClassOf("SubRegIndex")) { + CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(ChildRec); + DstMIBuilder.addRenderer(SubIdx->EnumValue); + return InsertPt; + } + if (ChildRec->isSubClassOf("ComplexPattern")) { const auto &ComplexPattern = ComplexPatternEquivs.find(ChildRec); if (ComplexPattern == ComplexPatternEquivs.end()) @@ -3831,6 +3855,31 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer( if (auto Error = InsertPtOrError.takeError()) return std::move(Error); + // We need to make sure that when we import an INSERT_SUBREG as a + // subinstruction that it ends up being constrained to the correct super + // register and subregister classes. + if (Target.getInstruction(Dst->getOperator()).TheDef->getName() == + "INSERT_SUBREG") { + auto SubClass = inferRegClassFromPattern(Dst->getChild(1)); + if (!SubClass) + return failedImport( + "Cannot infer register class from INSERT_SUBREG operand #1"); + Optional SuperClass = inferSuperRegisterClass( + Dst->getExtType(0), Dst->getChild(0), Dst->getChild(2)); + if (!SuperClass) + return failedImport( + "Cannot infer register class for INSERT_SUBREG operand #0"); + // The destination and the super register source of an INSERT_SUBREG must + // be the same register class. + M.insertAction( + InsertPt, DstMIBuilder.getInsnID(), 0, **SuperClass); + M.insertAction( + InsertPt, DstMIBuilder.getInsnID(), 1, **SuperClass); + M.insertAction( + InsertPt, DstMIBuilder.getInsnID(), 2, **SubClass); + return InsertPtOrError.get(); + } + M.insertAction(InsertPt, DstMIBuilder.getInsnID()); return InsertPtOrError.get(); @@ -4008,6 +4057,110 @@ Error GlobalISelEmitter::importImplicitDefRenderers( return Error::success(); } +Optional +GlobalISelEmitter::getRegClassFromLeaf(TreePatternNode *Leaf) { + assert(Leaf && "Expected node?"); + assert(Leaf->isLeaf() && "Expected leaf?"); + Record *RCRec = getInitValueAsRegClass(Leaf->getLeafValue()); + if (!RCRec) + return None; + CodeGenRegisterClass *RC = CGRegs.getRegClass(RCRec); + if (!RC) + return None; + return RC; +} + +Optional +GlobalISelEmitter::inferRegClassFromPattern(TreePatternNode *N) { + if (!N) + return None; + + if (N->isLeaf()) + return getRegClassFromLeaf(N); + + // We don't have a leaf node, so we have to try and infer something. Check + // that we have an instruction that we an infer something from. + + // Only handle things that produce a single type. + if (N->getNumTypes() != 1) + return None; + Record *OpRec = N->getOperator(); + + // We only want instructions. + if (!OpRec->isSubClassOf("Instruction")) + return None; + + // Don't want to try and infer things when there could potentially be more + // than one candidate register class. + auto &Inst = Target.getInstruction(OpRec); + if (Inst.Operands.NumDefs > 1) + return None; + + // Handle any special-case instructions which we can safely infer register + // classes from. + StringRef InstName = Inst.TheDef->getName(); + if (InstName == "COPY_TO_REGCLASS") { + // If we have a COPY_TO_REGCLASS, then we need to handle it specially. It + // has the desired register class as the first child. + TreePatternNode *RCChild = N->getChild(1); + if (!RCChild->isLeaf()) + return None; + return getRegClassFromLeaf(RCChild); + } + + // Handle destination record types that we can safely infer a register class + // from. + const auto &DstIOperand = Inst.Operands[0]; + Record *DstIOpRec = DstIOperand.Rec; + if (DstIOpRec->isSubClassOf("RegisterOperand")) { + DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); + const CodeGenRegisterClass &RC = Target.getRegisterClass(DstIOpRec); + return &RC; + } + + if (DstIOpRec->isSubClassOf("RegisterClass")) { + const CodeGenRegisterClass &RC = Target.getRegisterClass(DstIOpRec); + return &RC; + } + + return None; +} + +Optional +GlobalISelEmitter::inferSuperRegisterClass(const TypeSetByHwMode &Ty, + TreePatternNode *SuperRegNode, + TreePatternNode *SubRegIdxNode) { + // Check if we already have a defined register class for the super register + // node. If we do, then we should preserve that rather than inferring anything + // from the subregister index node. We can assume that whoever wrote the + // pattern in the first place made sure that the super register and + // subregister are compatible. + if (Optional SuperRegisterClass = + inferRegClassFromPattern(SuperRegNode)) + return SuperRegisterClass; + + // We need a ValueTypeByHwMode for getSuperRegForSubReg. + if (!Ty.isValueTypeByHwMode(false)) + return None; + + // We don't know anything about the super register. Try to use the subregister + // index to infer an appropriate register class. + if (!SubRegIdxNode->isLeaf()) + return None; + DefInit *SubRegInit = dyn_cast(SubRegIdxNode->getLeafValue()); + if (!SubRegInit) + return None; + CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef()); + + // Use the information we found above to find a minimal register class which + // supports the subregister and type we want. + auto RC = + Target.getSuperRegForSubReg(Ty.getValueTypeByHwMode(), CGRegs, SubIdx); + if (!RC) + return None; + return *RC; +} + Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { // Keep track of the matchers and actions to emit. int Score = P.getPatternComplexity(CGP); @@ -4126,8 +4279,22 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { DstIOpRec = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue()); if (DstIOpRec == nullptr) + return failedImport("EXTRACT_SUBREG operand #0 isn't a register class"); + } else if (DstI.TheDef->getName() == "INSERT_SUBREG") { + auto MaybeSuperClass = + inferSuperRegisterClass(VTy, Dst->getChild(0), Dst->getChild(2)); + if (!MaybeSuperClass) return failedImport( - "EXTRACT_SUBREG operand #0 isn't a register class"); + "Cannot infer register class for INSERT_SUBREG operand #0"); + // Move to the next pattern here, because the register class we found + // doesn't necessarily have a record associated with it. So, we can't + // set DstIOpRec using this. + OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); + OM.setSymbolicName(DstIOperand.Name); + M.defineOperand(OM.getSymbolicName(), OM); + OM.addPredicate(**MaybeSuperClass); + ++OpIdx; + continue; } else if (DstIOpRec->isSubClassOf("RegisterOperand")) DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); else if (!DstIOpRec->isSubClassOf("RegisterClass")) @@ -4217,6 +4384,27 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { return std::move(M); } + if (DstI.TheDef->getName() == "INSERT_SUBREG") { + assert(Src->getExtTypes().size() == 1 && + "Expected Src of INSERT_SUBREG to have one result type"); + // We need to constrain the destination, a super regsister source, and a + // subregister source. + auto SubClass = inferRegClassFromPattern(Dst->getChild(1)); + if (!SubClass) + return failedImport( + "Cannot infer register class from INSERT_SUBREG operand #1"); + auto SuperClass = inferSuperRegisterClass( + Src->getExtType(0), Dst->getChild(0), Dst->getChild(2)); + if (!SuperClass) + return failedImport( + "Cannot infer register class for INSERT_SUBREG operand #0"); + M.addAction(0, 0, **SuperClass); + M.addAction(0, 1, **SuperClass); + M.addAction(0, 2, **SubClass); + ++NumPatternImported; + return std::move(M); + } + M.addAction(0); // We're done with this pattern! It's eligible for GISel emission; return it.