forked from OSchip/llvm-project
GlobalISel/TableGen: Fix handling of EXTRACT_SUBREG constraints
This was only using the correct register constraints if this was the final result instruction. If the extract was a sub instruction of the result, it would attempt to use GIR_ConstrainSelectedInstOperands on a COPY, which won't work. Move the handling to createAndImportSubInstructionRenderer so it works correctly. I don't fully understand why runOnPattern and createAndImportSubInstructionRenderer both need to handle these special cases, and constrain them with slightly different methods. If I remove the runOnPattern handling, it does break the constraint when the final result instruction is EXTRACT_SUBREG. llvm-svn: 371150
This commit is contained in:
parent
60c8b8bcf2
commit
9ceb6edf11
|
@ -76,7 +76,7 @@ body: |
|
|||
; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w1
|
||||
; CHECK: [[COPY1:%[0-9]+]]:gpr64sp = COPY $x2
|
||||
; CHECK: [[DEF:%[0-9]+]]:gpr64all = IMPLICIT_DEF
|
||||
; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64all = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32
|
||||
; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32
|
||||
; CHECK: [[COPY2:%[0-9]+]]:gpr32 = COPY [[INSERT_SUBREG]].sub_32
|
||||
; CHECK: early-clobber %5:gpr32 = STLXRB [[COPY2]], [[COPY1]] :: (volatile store 1 into %ir.addr)
|
||||
; CHECK: $w0 = COPY %5
|
||||
|
@ -106,7 +106,7 @@ body: |
|
|||
; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w1
|
||||
; CHECK: [[COPY1:%[0-9]+]]:gpr64sp = COPY $x2
|
||||
; CHECK: [[DEF:%[0-9]+]]:gpr64all = IMPLICIT_DEF
|
||||
; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64all = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32
|
||||
; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32
|
||||
; CHECK: [[COPY2:%[0-9]+]]:gpr32 = COPY [[INSERT_SUBREG]].sub_32
|
||||
; CHECK: early-clobber %5:gpr32 = STLXRH [[COPY2]], [[COPY1]] :: (volatile store 2 into %ir.addr)
|
||||
; CHECK: $w0 = COPY %5
|
||||
|
|
|
@ -23,7 +23,7 @@ body: |
|
|||
; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w1
|
||||
; CHECK: [[COPY1:%[0-9]+]]:gpr64sp = COPY $x2
|
||||
; CHECK: [[DEF:%[0-9]+]]:gpr64all = IMPLICIT_DEF
|
||||
; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64all = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32
|
||||
; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32
|
||||
; CHECK: [[COPY2:%[0-9]+]]:gpr32 = COPY [[INSERT_SUBREG]].sub_32
|
||||
; CHECK: early-clobber %5:gpr32 = STXRB [[COPY2]], [[COPY1]] :: (volatile store 1 into %ir.addr)
|
||||
; CHECK: $w0 = COPY %5
|
||||
|
@ -54,7 +54,7 @@ body: |
|
|||
; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $w1
|
||||
; CHECK: [[COPY1:%[0-9]+]]:gpr64sp = COPY $x2
|
||||
; CHECK: [[DEF:%[0-9]+]]:gpr64all = IMPLICIT_DEF
|
||||
; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64all = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32
|
||||
; CHECK: [[INSERT_SUBREG:%[0-9]+]]:gpr64 = INSERT_SUBREG [[DEF]], [[COPY]], %subreg.sub_32
|
||||
; CHECK: [[COPY2:%[0-9]+]]:gpr32 = COPY [[INSERT_SUBREG]].sub_32
|
||||
; CHECK: early-clobber %5:gpr32 = STXRH [[COPY2]], [[COPY1]] :: (volatile store 2 into %ir.addr)
|
||||
; CHECK: $w0 = COPY %5
|
||||
|
|
|
@ -33,6 +33,7 @@ def ERegs : MyClass<32, [i32], (sequence "E%u", 0, 0)>;
|
|||
def SOP : RegisterOperand<SRegs>;
|
||||
def DOP : RegisterOperand<DRegs>;
|
||||
def SOME_INSN : I<(outs DRegs:$dst), (ins DOP:$src), []>;
|
||||
def SUBSOME_INSN : I<(outs SRegs:$dst), (ins SOP:$src), []>;
|
||||
|
||||
// We should skip cases where we don't have a given register class for the
|
||||
// subregister source.
|
||||
|
@ -96,7 +97,6 @@ def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (COPY_TO_REGCLASS SOP:$sr
|
|||
|
||||
// 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,
|
||||
|
@ -118,6 +118,32 @@ def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), (SUBSOME
|
|||
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/1, /*RC DRegs*/1,
|
||||
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/2, /*RC SRegs*/0,
|
||||
|
||||
// Test an EXTRACT_SUBREG that is a sub instruction. The individual
|
||||
// operands should be constrained to specific register classes, and
|
||||
// not use GIR_ConstrainSelectedInstOperands.
|
||||
def : Pat<(i16 (trunc (not DOP:$src))),
|
||||
(SUBSOME_INSN (EXTRACT_SUBREG DOP:$src, sub0))>;
|
||||
// CHECK-LABEL: // (trunc:{ *:[i16] } (xor:{ *:[i32] } DOP:{ *:[i32] }:$src, -1:{ *:[i32] })) => (SUBSOME_INSN:{ *:[i16] } (EXTRACT_SUBREG:{ *:[i16] } DOP:{ *:[i32] }:$src, sub0:{ *:[i32] }))
|
||||
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16,
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::COPY,
|
||||
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define,
|
||||
// CHECK-NEXT: GIR_CopySubReg, /*NewInsnID*/1, /*OldInsnID*/1, /*OpIdx*/1, /*SubRegIdx*/1, // src
|
||||
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, /*RC SRegs*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/1, /*RC DRegs*/1,
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::SUBSOME_INSN,
|
||||
|
||||
// Test an EXTRACT_SUBREG that is the final instruction.
|
||||
def : Pat<(i16 (trunc DOP:$src)),
|
||||
(EXTRACT_SUBREG DOP:$src, sub0)>;
|
||||
// CHECK-LABEL: // (trunc:{ *:[i16] } DOP:{ *:[i32] }:$src) => (EXTRACT_SUBREG:{ *:[i16] } DOP:{ *:[i32] }:$src, sub0:{ *:[i32] })
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::COPY,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_CopySubReg, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, /*SubRegIdx*/1, // src
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC SRegs*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/1, /*RC DRegs*/1,
|
||||
|
||||
|
||||
// Test that we can import SUBREG_TO_REG
|
||||
def : Pat<(i32 (zext SOP:$src)),
|
||||
(SUBREG_TO_REG (i64 0), (SUBSOME_INSN SOP:$src), sub0)>;
|
||||
|
|
|
@ -3203,6 +3203,8 @@ private:
|
|||
inferSuperRegisterClassForNode(const TypeSetByHwMode &Ty,
|
||||
TreePatternNode *SuperRegNode,
|
||||
TreePatternNode *SubRegIdxNode);
|
||||
Optional<CodeGenSubRegIndex *>
|
||||
inferSubRegIndexForNode(TreePatternNode *SubRegIdxNode);
|
||||
|
||||
/// Infer a CodeGenRegisterClass which suppoorts \p Ty and \p SubRegIdxNode.
|
||||
/// Return None if no such class exists.
|
||||
|
@ -3954,6 +3956,33 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer(
|
|||
return InsertPtOrError.get();
|
||||
}
|
||||
|
||||
if (OpName == "EXTRACT_SUBREG") {
|
||||
// EXTRACT_SUBREG selects into a subregister COPY but unlike most
|
||||
// instructions, the result register class is controlled by the
|
||||
// subregisters of the operand. As a result, we must constrain the result
|
||||
// class rather than check that it's already the right one.
|
||||
auto SuperClass = inferRegClassFromPattern(Dst->getChild(0));
|
||||
if (!SuperClass)
|
||||
return failedImport(
|
||||
"Cannot infer register class from EXTRACT_SUBREG operand #0");
|
||||
|
||||
auto SubIdx = inferSubRegIndexForNode(Dst->getChild(1));
|
||||
if (!SubIdx)
|
||||
return failedImport("EXTRACT_SUBREG child #1 is not a subreg index");
|
||||
|
||||
const auto &SrcRCDstRCPair =
|
||||
(*SuperClass)->getMatchingSubClassWithSubRegs(CGRegs, *SubIdx);
|
||||
assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass");
|
||||
M.insertAction<ConstrainOperandToRegClassAction>(
|
||||
InsertPt, DstMIBuilder.getInsnID(), 0, *SrcRCDstRCPair->second);
|
||||
M.insertAction<ConstrainOperandToRegClassAction>(
|
||||
InsertPt, DstMIBuilder.getInsnID(), 1, *SrcRCDstRCPair->first);
|
||||
|
||||
// We're done with this pattern! It's eligible for GISel emission; return
|
||||
// it.
|
||||
return InsertPtOrError.get();
|
||||
}
|
||||
|
||||
// Similar to INSERT_SUBREG, we also have to handle SUBREG_TO_REG as a
|
||||
// subinstruction.
|
||||
if (OpName == "SUBREG_TO_REG") {
|
||||
|
@ -4259,6 +4288,17 @@ GlobalISelEmitter::inferSuperRegisterClassForNode(
|
|||
return inferSuperRegisterClass(Ty, SubRegIdxNode);
|
||||
}
|
||||
|
||||
Optional<CodeGenSubRegIndex *>
|
||||
GlobalISelEmitter::inferSubRegIndexForNode(TreePatternNode *SubRegIdxNode) {
|
||||
if (!SubRegIdxNode->isLeaf())
|
||||
return None;
|
||||
|
||||
DefInit *SubRegInit = dyn_cast<DefInit>(SubRegIdxNode->getLeafValue());
|
||||
if (!SubRegInit)
|
||||
return None;
|
||||
return CGRegs.getSubRegIdx(SubRegInit->getDef());
|
||||
}
|
||||
|
||||
Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
// Keep track of the matchers and actions to emit.
|
||||
int Score = P.getPatternComplexity(CGP);
|
||||
|
@ -4457,27 +4497,15 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
|||
}
|
||||
|
||||
if (DstIName == "EXTRACT_SUBREG") {
|
||||
// EXTRACT_SUBREG selects into a subregister COPY but unlike most
|
||||
// instructions, the result register class is controlled by the
|
||||
// subregisters of the operand. As a result, we must constrain the result
|
||||
// class rather than check that it's already the right one.
|
||||
if (!Dst->getChild(0)->isLeaf())
|
||||
return failedImport("EXTRACT_SUBREG child #1 is not a leaf");
|
||||
auto SuperClass = inferRegClassFromPattern(Dst->getChild(0));
|
||||
if (!SuperClass)
|
||||
return failedImport(
|
||||
"Cannot infer register class from EXTRACT_SUBREG operand #0");
|
||||
|
||||
DefInit *SubRegInit = dyn_cast<DefInit>(Dst->getChild(1)->getLeafValue());
|
||||
if (!SubRegInit)
|
||||
auto SubIdx = inferSubRegIndexForNode(Dst->getChild(1));
|
||||
if (!SubIdx)
|
||||
return failedImport("EXTRACT_SUBREG child #1 is not a subreg index");
|
||||
|
||||
// Constrain the result to the same register bank as the operand.
|
||||
Record *DstIOpRec =
|
||||
getInitValueAsRegClass(Dst->getChild(0)->getLeafValue());
|
||||
|
||||
if (DstIOpRec == nullptr)
|
||||
return failedImport("EXTRACT_SUBREG operand #1 isn't a register class");
|
||||
|
||||
CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef());
|
||||
CodeGenRegisterClass *SrcRC = CGRegs.getRegClass(DstIOpRec);
|
||||
|
||||
// It would be nice to leave this constraint implicit but we're required
|
||||
// to pick a register class so constrain the result to a register class
|
||||
// that can hold the correct MVT.
|
||||
|
@ -4488,7 +4516,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
|||
"Expected Src of EXTRACT_SUBREG to have one result type");
|
||||
|
||||
const auto &SrcRCDstRCPair =
|
||||
SrcRC->getMatchingSubClassWithSubRegs(CGRegs, SubIdx);
|
||||
(*SuperClass)->getMatchingSubClassWithSubRegs(CGRegs, *SubIdx);
|
||||
assert(SrcRCDstRCPair->second && "Couldn't find a matching subclass");
|
||||
M.addAction<ConstrainOperandToRegClassAction>(0, 0, *SrcRCDstRCPair->second);
|
||||
M.addAction<ConstrainOperandToRegClassAction>(0, 1, *SrcRCDstRCPair->first);
|
||||
|
|
Loading…
Reference in New Issue