[llvm] [TableGen][CallingConv] Add CCAssignToRegTuple for synthetic registers. (PR #137826)
Jason Eckhardt via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 29 14:22:08 PDT 2025
https://github.com/nvjle updated https://github.com/llvm/llvm-project/pull/137826
>From 0e8618a06c231a3a436827accc8f2d26ca04ac92 Mon Sep 17 00:00:00 2001
From: Jason Eckhardt <jeckhardt at nvidia.com>
Date: Tue, 29 Apr 2025 09:48:15 -0500
Subject: [PATCH 1/2] [TableGen][CallingConv] Add CCAssignToRegTuple for
synthetic registers.
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.
---
llvm/include/llvm/Target/TargetCallingConv.td | 7 ++
llvm/test/TableGen/cc-assign-to-reg-tuple.td | 78 +++++++++++++++++++
llvm/utils/TableGen/CallingConvEmitter.cpp | 32 +++++++-
3 files changed, 113 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/TableGen/cc-assign-to-reg-tuple.td
diff --git a/llvm/include/llvm/Target/TargetCallingConv.td b/llvm/include/llvm/Target/TargetCallingConv.td
index 18b7ff4aec95f..d0533cad927a7 100644
--- a/llvm/include/llvm/Target/TargetCallingConv.td
+++ b/llvm/include/llvm/Target/TargetCallingConv.td
@@ -113,6 +113,13 @@ class CCAssignToReg<list<Register> regList> : CCAction {
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 0000000000000..624aa1d5f5f0b
--- /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 57b1622752613..fd7280a9f2b42 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 @@ using namespace llvm;
namespace {
class CallingConvEmitter {
const RecordKeeper &Records;
+ const CodeGenTarget Target;
unsigned Counter = 0u;
std::string CurrentAction;
bool SwiftAction = false;
@@ -35,7 +37,10 @@ class CallingConvEmitter {
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 @@ class CallingConvEmitter {
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 getQualifiedNameFromInit(const Init *I);
};
} // End anonymous namespace
@@ -125,6 +133,21 @@ void CallingConvEmitter::emitCallingConv(const Record *CC, raw_ostream &O) {
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::getQualifiedNameFromInit(const Init *I) {
+ if (const auto *DI = dyn_cast<DefInit>(I))
+ return getQualifiedName(DI->getDef());
+
+ const auto *SI = dyn_cast<StringInit>(I);
+ assert(SI && "unexpected Init kind");
+ 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 +156,7 @@ void CallingConvEmitter::emitAction(const Record *Action, indent Indent,
O << Indent << " ";
ListSeparator LS;
for (const Init *V : RL->getValues())
- O << LS << getQualifiedName(cast<DefInit>(V)->getDef());
+ O << LS << getQualifiedNameFromInit(V);
O << "\n" << Indent << "};\n";
};
@@ -142,7 +165,7 @@ void CallingConvEmitter::emitAction(const Record *Action, indent Indent,
SmallVector<std::string> Parms;
if (RegLists[0]->size() == 1) {
for (const ListInit *LI : RegLists)
- Parms.push_back(getQualifiedName(LI->getElementAsRecord(0)));
+ Parms.push_back(getQualifiedNameFromInit(LI->getElement(0)));
} else {
for (const std::string &S : RLNames)
Parms.push_back(S + utostr(++Counter));
@@ -207,10 +230,11 @@ void CallingConvEmitter::emitAction(const Record *Action, indent Indent,
<< 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 = getQualifiedNameFromInit(RegList->getElement(I));
if (SwiftAction)
AssignedSwiftRegsMap[CurrentAction].insert(std::move(Name));
else
>From c201a9e25cce0a9fd9be6f116c7a936b792a8407 Mon Sep 17 00:00:00 2001
From: Jason Eckhardt <jeckhardt at nvidia.com>
Date: Tue, 29 Apr 2025 16:20:19 -0500
Subject: [PATCH 2/2] Rename getQualifiedNameFromInit to
getQualifiedRegisterName.
---
llvm/utils/TableGen/CallingConvEmitter.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/llvm/utils/TableGen/CallingConvEmitter.cpp b/llvm/utils/TableGen/CallingConvEmitter.cpp
index fd7280a9f2b42..9225b587723a3 100644
--- a/llvm/utils/TableGen/CallingConvEmitter.cpp
+++ b/llvm/utils/TableGen/CallingConvEmitter.cpp
@@ -50,7 +50,7 @@ class CallingConvEmitter {
void emitArgRegisterLists(raw_ostream &O);
StringMap<const CodeGenRegister *> RegistersByDefName;
- std::string getQualifiedNameFromInit(const Init *I);
+ std::string getQualifiedRegisterName(const Init *I);
};
} // End anonymous namespace
@@ -135,7 +135,7 @@ void CallingConvEmitter::emitCallingConv(const Record *CC, raw_ostream &O) {
// Return the name of the specified Init (DefInit or StringInit), with a
// namespace qualifier if the corresponding record contains one.
-std::string CallingConvEmitter::getQualifiedNameFromInit(const Init *I) {
+std::string CallingConvEmitter::getQualifiedRegisterName(const Init *I) {
if (const auto *DI = dyn_cast<DefInit>(I))
return getQualifiedName(DI->getDef());
@@ -156,7 +156,7 @@ void CallingConvEmitter::emitAction(const Record *Action, indent Indent,
O << Indent << " ";
ListSeparator LS;
for (const Init *V : RL->getValues())
- O << LS << getQualifiedNameFromInit(V);
+ O << LS << getQualifiedRegisterName(V);
O << "\n" << Indent << "};\n";
};
@@ -165,7 +165,7 @@ void CallingConvEmitter::emitAction(const Record *Action, indent Indent,
SmallVector<std::string> Parms;
if (RegLists[0]->size() == 1) {
for (const ListInit *LI : RegLists)
- Parms.push_back(getQualifiedNameFromInit(LI->getElement(0)));
+ Parms.push_back(getQualifiedRegisterName(LI->getElement(0)));
} else {
for (const std::string &S : RLNames)
Parms.push_back(S + utostr(++Counter));
@@ -234,7 +234,7 @@ void CallingConvEmitter::emitAction(const Record *Action, indent Indent,
Action->isSubClassOf("CCAssignToRegAndStack")) {
const ListInit *RegList = Action->getValueAsListInit("RegList");
for (unsigned I = 0, E = RegList->size(); I != E; ++I) {
- std::string Name = getQualifiedNameFromInit(RegList->getElement(I));
+ std::string Name = getQualifiedRegisterName(RegList->getElement(I));
if (SwiftAction)
AssignedSwiftRegsMap[CurrentAction].insert(std::move(Name));
else
More information about the llvm-commits
mailing list