Skip to content

[TableGen][GISel] Extract common function for determining MI's regclass #120135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def A0 : RegisterClass<"MyTarget", [i32], 32, (add a0)>;
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, GIMT_Encode2(TargetOpcode::G_ANYEXT),
// CHECK-NEXT: // MIs[0] DstI[dst]
// CHECK-NEXT: GIM_RootCheckType, /*Op*/0, /*Type*/GILLT_s16,
// CHECK-NEXT: GIM_RootCheckRegBankForClass, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::A0RegClassID),
// CHECK-NEXT: GIM_RootCheckRegBankForClass, /*Op*/0, /*RC*/GIMT_Encode2(MyTarget::A0wRegClassID),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this is encoded as a class and not the bank directly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess because TableGen cannot infer regbank itself.
There was an attempt to do this at tablegen level, but AFAIK it doesn't cover all cases and targets still need to override getRegBankFromRegClass in some cases.

// CHECK-NEXT: // MIs[0] src
// CHECK-NEXT: GIM_RootCheckType, /*Op*/1, /*Type*/GILLT_s8,
// CHECK-NEXT: // (anyext:{ *:[i16] } i8:{ *:[i8] }:$src) => (EXTRACT_SUBREG:{ *:[i16] } (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), A0b:{ *:[i8] }:$src, lo8:{ *:[i32] }), lo16:{ *:[i32] })
Expand Down
150 changes: 74 additions & 76 deletions llvm/utils/TableGen/GlobalISelEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
const CodeGenRegisterClass *
inferRegClassFromPattern(const TreePatternNode &N);

const CodeGenRegisterClass *
inferRegClassFromInstructionPattern(const TreePatternNode &N,
unsigned ResIdx);

Error constrainOperands(action_iterator InsertPt, RuleMatcher &M,
unsigned InsnID, const TreePatternNode &Dst);

Expand Down Expand Up @@ -1856,46 +1860,85 @@ GlobalISelEmitter::inferRegClassFromPattern(const TreePatternNode &N) {

// Don't want to try and infer things when there could potentially be more
// than one candidate register class.
auto &Inst = Target.getInstruction(OpRec);
return inferRegClassFromInstructionPattern(N, /*ResIdx=*/0);
}

const CodeGenRegisterClass *
GlobalISelEmitter::inferRegClassFromInstructionPattern(const TreePatternNode &N,
unsigned ResIdx) {
const CodeGenInstruction &Inst = Target.getInstruction(N.getOperator());
assert(ResIdx < Inst.Operands.NumDefs &&
"Can only infer register class for explicit defs");

// Handle any special-case instructions which we can safely infer register
// classes from.
StringRef InstName = Inst.TheDef->getName();
bool IsRegSequence = InstName == "REG_SEQUENCE";
if (IsRegSequence || 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.
const TreePatternNode &RCChild = N.getChild(IsRegSequence ? 0 : 1);
if (InstName == "REG_SEQUENCE") {
// (outs $super_dst), (ins $dst_regclass, variable_ops)
// Destination register class is explicitly specified by the first operand.
const TreePatternNode &RCChild = N.getChild(0);
if (!RCChild.isLeaf())
return nullptr;
return getRegClassFromLeaf(RCChild);
}

if (InstName == "COPY_TO_REGCLASS") {
// (outs $dst), (ins $src, $dst_regclass)
// Destination register class is explicitly specified by the second operand.
const TreePatternNode &RCChild = N.getChild(1);
if (!RCChild.isLeaf())
return nullptr;
return getRegClassFromLeaf(RCChild);
}

if (InstName == "INSERT_SUBREG") {
// (outs $super_dst), (ins $super_src, $sub_src, $sub_idx);
// If we can infer the register class for the first operand, use that.
// Otherwise, find a register class that supports both the specified
// sub-register index and the type of the instruction's result.
const TreePatternNode &Child0 = N.getChild(0);
assert(Child0.getNumTypes() == 1 && "Unexpected number of types!");
const TypeSetByHwMode &VTy = Child0.getExtType(0);
return inferSuperRegisterClassForNode(VTy, Child0, N.getChild(2));
return inferSuperRegisterClassForNode(N.getExtType(0), Child0,
N.getChild(2));
}

if (InstName == "EXTRACT_SUBREG") {
assert(N.getNumTypes() == 1 && "Unexpected number of types!");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was actually bugged, but wasn't covered by tests.

const TypeSetByHwMode &VTy = N.getExtType(0);
return inferSuperRegisterClass(VTy, N.getChild(1));
// (outs $sub_dst), (ins $super_src, $sub_idx)
// Find a register class that can be used for a sub-register copy from
// the specified source at the specified sub-register index.
const CodeGenRegisterClass *SuperRC =
inferRegClassFromPattern(N.getChild(0));
if (!SuperRC)
return nullptr;

const CodeGenSubRegIndex *SubIdx = inferSubRegIndexForNode(N.getChild(1));
if (!SubIdx)
return nullptr;

const auto SubRCAndSubRegRC =
SuperRC->getMatchingSubClassWithSubRegs(CGRegs, SubIdx);
if (!SubRCAndSubRegRC)
return nullptr;

return SubRCAndSubRegRC->second;
}

if (InstName == "SUBREG_TO_REG") {
// (outs $super_dst), (ins $super_src, $sub_src, $sub_idx)
// Find a register class that supports both the specified sub-register
// index and the type of the instruction's result.
return inferSuperRegisterClass(N.getExtType(0), N.getChild(2));
}

// Handle destination record types that we can safely infer a register class
// from.
const auto &DstIOperand = Inst.Operands[0];
const auto &DstIOperand = Inst.Operands[ResIdx];
const Record *DstIOpRec = DstIOperand.Rec;
if (DstIOpRec->isSubClassOf("RegisterOperand")) {
DstIOpRec = DstIOpRec->getValueAsDef("RegClass");
const CodeGenRegisterClass &RC = Target.getRegisterClass(DstIOpRec);
return &RC;
}
if (DstIOpRec->isSubClassOf("RegisterOperand"))
return &Target.getRegisterClass(DstIOpRec->getValueAsDef("RegClass"));

if (DstIOpRec->isSubClassOf("RegisterClass")) {
const CodeGenRegisterClass &RC = Target.getRegisterClass(DstIOpRec);
return &RC;
}
if (DstIOpRec->isSubClassOf("RegisterClass"))
return &Target.getRegisterClass(DstIOpRec);

return nullptr;
}
Expand Down Expand Up @@ -2043,8 +2086,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
if (!DstOp->isSubClassOf("Instruction"))
return failedImport("Pattern operator isn't an instruction");

auto &DstI = Target.getInstruction(DstOp);
StringRef DstIName = DstI.TheDef->getName();
const CodeGenInstruction &DstI = Target.getInstruction(DstOp);

// Count both implicit and explicit defs in the dst instruction.
// This avoids errors importing patterns that have inherent implicit defs.
Expand All @@ -2070,68 +2112,24 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {

// The root of the match also has constraints on the register bank so that it
// matches the result instruction.
unsigned OpIdx = 0;
unsigned N = std::min(DstExpDefs, SrcNumDefs);
for (unsigned I = 0; I < N; ++I) {
const TypeSetByHwMode &VTy = Src.getExtType(I);
const auto &DstIOperand = DstI.Operands[I];

const auto &DstIOperand = DstI.Operands[OpIdx];
PointerUnion<const Record *, const CodeGenRegisterClass *> MatchedRC =
DstIOperand.Rec;
if (DstIName == "COPY_TO_REGCLASS") {
MatchedRC = getInitValueAsRegClass(Dst.getChild(1).getLeafValue());

if (MatchedRC.isNull())
return failedImport(
"COPY_TO_REGCLASS operand #1 isn't a register class");
} else if (DstIName == "REG_SEQUENCE") {
MatchedRC = getInitValueAsRegClass(Dst.getChild(0).getLeafValue());
if (MatchedRC.isNull())
return failedImport("REG_SEQUENCE operand #0 isn't a register class");
} else if (DstIName == "EXTRACT_SUBREG") {
const CodeGenRegisterClass *InferredClass =
inferRegClassFromPattern(Dst.getChild(0));
if (!InferredClass)
return failedImport(
"Could not infer class for EXTRACT_SUBREG operand #0");

// We can assume that a subregister is in the same bank as it's super
// register.
MatchedRC = InferredClass->getDef();
Comment on lines -2098 to -2100
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is the difference in behavior: previously we were satisfied with the class of the operand, now we actually try to infer the class of the result. This is a bit slower, but it looks like we would have tried to infer it later anyway (and bailed out if we couldn't).

} else if (DstIName == "INSERT_SUBREG") {
const CodeGenRegisterClass *MaybeSuperClass =
inferSuperRegisterClassForNode(VTy, Dst.getChild(0), Dst.getChild(2));
if (!MaybeSuperClass)
return failedImport(
"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.
MatchedRC = MaybeSuperClass;
} else if (DstIName == "SUBREG_TO_REG") {
const CodeGenRegisterClass *MaybeRegClass =
inferSuperRegisterClass(VTy, Dst.getChild(2));
if (!MaybeRegClass)
return failedImport(
"Cannot infer register class for SUBREG_TO_REG operand #0");
MatchedRC = MaybeRegClass;
} else if (cast<const Record *>(MatchedRC)->isSubClassOf("RegisterOperand"))
MatchedRC = cast<const Record *>(MatchedRC)->getValueAsDef("RegClass");
else if (!cast<const Record *>(MatchedRC)->isSubClassOf("RegisterClass"))
return failedImport("Dst MI def isn't a register class" + to_string(Dst));

OperandMatcher &OM = InsnMatcher.getOperand(OpIdx);
OperandMatcher &OM = InsnMatcher.getOperand(I);
// The operand names declared in the DstI instruction are unrelated to
// those used in pattern's source and destination DAGs, so mangle the
// former to prevent implicitly adding unexpected
// GIM_CheckIsSameOperand predicates by the defineOperand method.
OM.setSymbolicName(getMangledRootDefName(DstIOperand.Name));
M.defineOperand(OM.getSymbolicName(), OM);
if (auto *R = dyn_cast<const Record *>(MatchedRC))
MatchedRC = &Target.getRegisterClass(R);
OM.addPredicate<RegisterBankOperandMatcher>(
*cast<const CodeGenRegisterClass *>(MatchedRC));
++OpIdx;

const CodeGenRegisterClass *RC =
inferRegClassFromInstructionPattern(Dst, I);
if (!RC)
return failedImport("Could not infer register class for result #" +
Twine(I) + " from pattern " + to_string(Dst));
OM.addPredicate<RegisterBankOperandMatcher>(*RC);
}

auto DstMIBuilderOrError =
Expand Down
Loading