[TableGen][CallingConv] Add CCAssignToRegTuple for synthetic registers. (#137826)
Currently CCAssignToReg takes a list<Register>. There are tuple-heavy
back-ends where we would like to reference any register-- whether those
are singletons or those defined by RegisterTuples. However, the latter
are synthesized during tuple expansion and are not visible outside of
the register info emitter.
The problem is that the parser will see tuple registers as undefined
variables before the calling convention emitter is ever reached. To
defer evaluation of the symbol, we introduce CCAssignToRegTuple which
takes list<string> instead. This allows us to defer the actual
register name lookup until the emitter runs-- where we also validate
that the register actually exists.
This is currently used in a downstream back-end which will be upstreamed
very soon. In the meantime, a unit test is provided to exercise the
feature.
diff --git a/llvm/include/llvm/Target/TargetCallingConv.td b/llvm/include/llvm/Target/TargetCallingConv.td
index 18b7ff4..d0533ca 100644
--- a/llvm/include/llvm/Target/TargetCallingConv.td
+++ b/llvm/include/llvm/Target/TargetCallingConv.td
@@ -113,6 +113,13 @@
list<Register> RegList = regList;
}
+/// CCAssignToRegTuple - Same as CCAssignToReg, but with a list of registers as
+/// strings. This is needed because records synthesized during tuple expansion
+/// are not visible outside of the register info emitter.
+class CCAssignToRegTuple<list<string> regList> : CCAction {
+ list<string> RegList = regList;
+}
+
/// CCAssignToRegWithShadow - Same as CCAssignToReg, but with list of registers
/// which became shadowed, when some register is used.
class CCAssignToRegWithShadow<list<Register> regList,
diff --git a/llvm/test/TableGen/cc-assign-to-reg-tuple.td b/llvm/test/TableGen/cc-assign-to-reg-tuple.td
new file mode 100644
index 0000000..624aa1d
--- /dev/null
+++ b/llvm/test/TableGen/cc-assign-to-reg-tuple.td
@@ -0,0 +1,78 @@
+// RUN: llvm-tblgen --gen-callingconv -I %p/../../include -I %p/Common %s 2>&1 | FileCheck %s
+// RUN: not llvm-tblgen -DERROR1 --gen-callingconv -I %p/../../include -I %p/Common %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s
+// RUN: not llvm-tblgen -DERROR2 --gen-callingconv -I %p/../../include -I %p/Common %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR2 %s
+
+include "reg-with-subregs-common.td"
+
+def CC_ABI1 : CallingConv<[
+ // Use singleton definitions directly.
+ CCIfType<[i32, f32],
+ CCAssignToReg<[R8, R9, R10, R11, R12, R13, R14, R15]>>,
+
+ // Use tuple definitions indirectly as strings.
+ CCIfType<[i64, f64],
+ CCAssignToRegTuple<["R8_R9", "R10_R11", "R12_R13", "R14_R15"]>>,
+
+ CCIfType<[i128],
+ CCAssignToRegTuple<["R8_R9_R10_R11", "R12_R13_R14_R15"]>>,
+
+ CCIfType<[v8i32],
+ CCAssignToRegTuple<["R8_R9_R10_R11_R12_R13_R14_R15"]>>,
+]>;
+
+// CHECK: if (LocVT == MVT::i32 ||
+// CHECK: LocVT == MVT::f32) {
+// CHECK: static const MCPhysReg RegList1[] = {
+// CHECK: R8, R9, R10, R11, R12, R13, R14, R15
+// CHECK: };
+// CHECK: if (MCRegister Reg = State.AllocateReg(RegList1)) {
+// CHECK: State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+// CHECK: return false;
+// CHECK: }
+// CHECK: }
+
+// CHECK: if (LocVT == MVT::i64 ||
+// CHECK: LocVT == MVT::f64) {
+// CHECK: static const MCPhysReg RegList2[] = {
+// CHECK: R8_R9, R10_R11, R12_R13, R14_R15
+// CHECK: };
+// CHECK: if (MCRegister Reg = State.AllocateReg(RegList2)) {
+// CHECK: State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+// CHECK: return false;
+// CHECK: }
+// CHECK: }
+
+// CHECK: if (LocVT == MVT::i128) {
+// CHECK: static const MCPhysReg RegList3[] = {
+// CHECK: R8_R9_R10_R11, R12_R13_R14_R15
+// CHECK: };
+// CHECK: if (MCRegister Reg = State.AllocateReg(RegList3)) {
+// CHECK: State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+// CHECK: return false;
+// CHECK: }
+// CHECK: }
+
+// CHECK: if (LocVT == MVT::v8i32) {
+// CHECK: if (MCRegister Reg = State.AllocateReg(R8_R9_R10_R11_R12_R13_R14_R15)) {
+// CHECK: State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo));
+// CHECK: return false;
+// CHECK: }
+// CHECK: }
+
+#ifdef ERROR1
+def CC_ABI2 : CallingConv<[
+ // Test that referencing an undefined tuple is diagnosed as an error.
+ // CHECK-ERROR1: error: register not defined: "R89_R33"
+ CCIfType<[i64, f64],
+ CCAssignToRegTuple<["R89_R33", "R12_R13", "R14_R15"]>>,
+]>;
+#endif
+
+#ifdef ERROR2
+def CC_ABI3 : CallingConv<[
+ // Currently an error: Use tuple definitions directly.
+ // CHECK-ERROR2: error: Variable not defined: 'R8_R9_R10_R11'
+ CCIfType<[i128],
+ CCAssignToRegTuple<[R8_R9_R10_R11, R12_R13_R14_R15]>>,
+]>;
+#endif
diff --git a/llvm/utils/TableGen/CallingConvEmitter.cpp b/llvm/utils/TableGen/CallingConvEmitter.cpp
index 57b1622..20dc1ff 100644
--- a/llvm/utils/TableGen/CallingConvEmitter.cpp
+++ b/llvm/utils/TableGen/CallingConvEmitter.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "Common/CodeGenRegisters.h"
#include "Common/CodeGenTarget.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/InterleavedRange.h"
@@ -26,6 +27,7 @@
namespace {
class CallingConvEmitter {
const RecordKeeper &Records;
+ const CodeGenTarget Target;
unsigned Counter = 0u;
std::string CurrentAction;
bool SwiftAction = false;
@@ -35,7 +37,10 @@
std::map<std::string, std::set<std::string>> DelegateToMap;
public:
- explicit CallingConvEmitter(const RecordKeeper &R) : Records(R) {}
+ explicit CallingConvEmitter(const RecordKeeper &R) : Records(R), Target(R) {
+ for (const CodeGenRegister &Reg : Target.getRegBank().getRegisters())
+ RegistersByDefName.try_emplace(Reg.getName(), &Reg);
+ }
void run(raw_ostream &O);
@@ -43,6 +48,9 @@
void emitCallingConv(const Record *CC, raw_ostream &O);
void emitAction(const Record *Action, indent Indent, raw_ostream &O);
void emitArgRegisterLists(raw_ostream &O);
+
+ StringMap<const CodeGenRegister *> RegistersByDefName;
+ std::string getQualifiedRegisterName(const Init *I);
};
} // End anonymous namespace
@@ -125,6 +133,20 @@
O << "}\n";
}
+// Return the name of the specified Init (DefInit or StringInit), with a
+// namespace qualifier if the corresponding record contains one.
+std::string CallingConvEmitter::getQualifiedRegisterName(const Init *I) {
+ if (const auto *DI = dyn_cast<DefInit>(I))
+ return getQualifiedName(DI->getDef());
+
+ const auto *SI = cast<StringInit>(I);
+ if (const CodeGenRegister *CGR = RegistersByDefName.lookup(SI->getValue()))
+ return getQualifiedName(CGR->TheDef);
+
+ PrintFatalError("register not defined: " + SI->getAsString());
+ return "";
+}
+
void CallingConvEmitter::emitAction(const Record *Action, indent Indent,
raw_ostream &O) {
@@ -133,7 +155,7 @@
O << Indent << " ";
ListSeparator LS;
for (const Init *V : RL->getValues())
- O << LS << getQualifiedName(cast<DefInit>(V)->getDef());
+ O << LS << getQualifiedRegisterName(V);
O << "\n" << Indent << "};\n";
};
@@ -142,7 +164,7 @@
SmallVector<std::string> Parms;
if (RegLists[0]->size() == 1) {
for (const ListInit *LI : RegLists)
- Parms.push_back(getQualifiedName(LI->getElementAsRecord(0)));
+ Parms.push_back(getQualifiedRegisterName(LI->getElement(0)));
} else {
for (const std::string &S : RLNames)
Parms.push_back(S + utostr(++Counter));
@@ -207,10 +229,11 @@
<< Indent + 2 << "return false;\n";
DelegateToMap[CurrentAction].insert(CC->getName().str());
} else if (Action->isSubClassOf("CCAssignToReg") ||
+ Action->isSubClassOf("CCAssignToRegTuple") ||
Action->isSubClassOf("CCAssignToRegAndStack")) {
const ListInit *RegList = Action->getValueAsListInit("RegList");
for (unsigned I = 0, E = RegList->size(); I != E; ++I) {
- std::string Name = getQualifiedName(RegList->getElementAsRecord(I));
+ std::string Name = getQualifiedRegisterName(RegList->getElement(I));
if (SwiftAction)
AssignedSwiftRegsMap[CurrentAction].insert(std::move(Name));
else