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.
405 lines
15 KiB
C++
405 lines
15 KiB
C++
//===- CallingConvEmitter.cpp - Generate calling conventions --------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This tablegen backend is responsible for emitting descriptions of the calling
|
|
// conventions supported by this target.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Common/CodeGenRegisters.h"
|
|
#include "Common/CodeGenTarget.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
#include "llvm/Support/InterleavedRange.h"
|
|
#include "llvm/TableGen/Error.h"
|
|
#include "llvm/TableGen/Record.h"
|
|
#include "llvm/TableGen/TGTimer.h"
|
|
#include "llvm/TableGen/TableGenBackend.h"
|
|
#include <deque>
|
|
#include <set>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
class CallingConvEmitter {
|
|
const RecordKeeper &Records;
|
|
const CodeGenTarget Target;
|
|
unsigned Counter = 0u;
|
|
std::string CurrentAction;
|
|
bool SwiftAction = false;
|
|
|
|
std::map<std::string, std::set<std::string>> AssignedRegsMap;
|
|
std::map<std::string, std::set<std::string>> AssignedSwiftRegsMap;
|
|
std::map<std::string, std::set<std::string>> DelegateToMap;
|
|
|
|
public:
|
|
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);
|
|
|
|
private:
|
|
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
|
|
|
|
void CallingConvEmitter::run(raw_ostream &O) {
|
|
emitSourceFileHeader("Calling Convention Implementation Fragment", O);
|
|
|
|
ArrayRef<const Record *> CCs =
|
|
Records.getAllDerivedDefinitions("CallingConv");
|
|
|
|
// Emit prototypes for all of the non-custom CC's so that they can forward ref
|
|
// each other.
|
|
Records.getTimer().startTimer("Emit prototypes");
|
|
O << "#ifndef GET_CC_REGISTER_LISTS\n\n";
|
|
for (const Record *CC : CCs) {
|
|
if (!CC->getValueAsBit("Custom")) {
|
|
unsigned Pad = CC->getName().size();
|
|
if (CC->getValueAsBit("Entry")) {
|
|
O << "bool llvm::";
|
|
Pad += 12;
|
|
} else {
|
|
O << "static bool ";
|
|
Pad += 13;
|
|
}
|
|
O << CC->getName() << "(unsigned ValNo, MVT ValVT,\n"
|
|
<< std::string(Pad, ' ') << "MVT LocVT, CCValAssign::LocInfo LocInfo,\n"
|
|
<< std::string(Pad, ' ')
|
|
<< "ISD::ArgFlagsTy ArgFlags, CCState &State);\n";
|
|
}
|
|
}
|
|
|
|
// Emit each non-custom calling convention description in full.
|
|
Records.getTimer().startTimer("Emit full descriptions");
|
|
for (const Record *CC : CCs) {
|
|
if (!CC->getValueAsBit("Custom")) {
|
|
emitCallingConv(CC, O);
|
|
}
|
|
}
|
|
|
|
emitArgRegisterLists(O);
|
|
|
|
O << "\n#endif // CC_REGISTER_LIST\n";
|
|
}
|
|
|
|
void CallingConvEmitter::emitCallingConv(const Record *CC, raw_ostream &O) {
|
|
const ListInit *CCActions = CC->getValueAsListInit("Actions");
|
|
Counter = 0;
|
|
|
|
CurrentAction = CC->getName().str();
|
|
// Call upon the creation of a map entry from the void!
|
|
// We want an entry in AssignedRegsMap for every action, even if that
|
|
// entry is empty.
|
|
AssignedRegsMap[CurrentAction] = {};
|
|
|
|
O << "\n\n";
|
|
unsigned Pad = CurrentAction.size();
|
|
if (CC->getValueAsBit("Entry")) {
|
|
O << "bool llvm::";
|
|
Pad += 12;
|
|
} else {
|
|
O << "static bool ";
|
|
Pad += 13;
|
|
}
|
|
O << CurrentAction << "(unsigned ValNo, MVT ValVT,\n"
|
|
<< std::string(Pad, ' ') << "MVT LocVT, CCValAssign::LocInfo LocInfo,\n"
|
|
<< std::string(Pad, ' ') << "ISD::ArgFlagsTy ArgFlags, CCState &State) {\n";
|
|
// Emit all of the actions, in order.
|
|
for (unsigned I = 0, E = CCActions->size(); I != E; ++I) {
|
|
const Record *Action = CCActions->getElementAsRecord(I);
|
|
SwiftAction =
|
|
llvm::any_of(Action->getSuperClasses(), [](const Record *Class) {
|
|
std::string Name = Class->getNameInitAsString();
|
|
return StringRef(Name).starts_with("CCIfSwift");
|
|
});
|
|
|
|
O << "\n";
|
|
emitAction(Action, indent(2), O);
|
|
}
|
|
|
|
O << "\n return true; // CC didn't match.\n";
|
|
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) {
|
|
|
|
auto EmitRegList = [&](const ListInit *RL, const StringRef RLName) {
|
|
O << Indent << "static const MCPhysReg " << RLName << "[] = {\n";
|
|
O << Indent << " ";
|
|
ListSeparator LS;
|
|
for (const Init *V : RL->getValues())
|
|
O << LS << getQualifiedRegisterName(V);
|
|
O << "\n" << Indent << "};\n";
|
|
};
|
|
|
|
auto EmitAllocateReg = [&](ArrayRef<const ListInit *> RegLists,
|
|
ArrayRef<std::string> RLNames) {
|
|
SmallVector<std::string> Parms;
|
|
if (RegLists[0]->size() == 1) {
|
|
for (const ListInit *LI : RegLists)
|
|
Parms.push_back(getQualifiedRegisterName(LI->getElement(0)));
|
|
} else {
|
|
for (const std::string &S : RLNames)
|
|
Parms.push_back(S + utostr(++Counter));
|
|
for (const auto [Idx, LI] : enumerate(RegLists))
|
|
EmitRegList(LI, Parms[Idx]);
|
|
}
|
|
O << formatv("{0}if (MCRegister Reg = State.AllocateReg({1})) {{\n", Indent,
|
|
make_range(Parms.begin(), Parms.end()));
|
|
O << Indent << " State.addLoc(CCValAssign::getReg(ValNo, ValVT, "
|
|
<< "Reg, LocVT, LocInfo));\n";
|
|
};
|
|
|
|
auto EmitAllocateStack = [&](bool EmitOffset = false) {
|
|
int Size = Action->getValueAsInt("Size");
|
|
int Align = Action->getValueAsInt("Align");
|
|
if (EmitOffset)
|
|
O << Indent << "int64_t Offset" << ++Counter << " = ";
|
|
else
|
|
O << Indent << " (void)";
|
|
O << "State.AllocateStack(";
|
|
|
|
const char *Fmt = " State.getMachineFunction().getDataLayout()."
|
|
"{0}(EVT(LocVT).getTypeForEVT(State.getContext()))";
|
|
if (Size)
|
|
O << Size << ", ";
|
|
else
|
|
O << "\n" << Indent << formatv(Fmt, "getTypeAllocSize") << ", ";
|
|
if (Align)
|
|
O << "Align(" << Align << ")";
|
|
else
|
|
O << "\n" << Indent << formatv(Fmt, "getABITypeAlign");
|
|
O << ");\n";
|
|
};
|
|
|
|
if (Action->isSubClassOf("CCPredicateAction")) {
|
|
O << Indent << "if (";
|
|
|
|
if (Action->isSubClassOf("CCIfType")) {
|
|
const ListInit *VTs = Action->getValueAsListInit("VTs");
|
|
for (unsigned I = 0, E = VTs->size(); I != E; ++I) {
|
|
const Record *VT = VTs->getElementAsRecord(I);
|
|
if (I != 0)
|
|
O << " ||\n " << Indent;
|
|
O << "LocVT == " << getEnumName(getValueType(VT));
|
|
}
|
|
|
|
} else if (Action->isSubClassOf("CCIf")) {
|
|
O << Action->getValueAsString("Predicate");
|
|
} else {
|
|
errs() << *Action;
|
|
PrintFatalError(Action->getLoc(), "Unknown CCPredicateAction!");
|
|
}
|
|
|
|
O << ") {\n";
|
|
emitAction(Action->getValueAsDef("SubAction"), Indent + 2, O);
|
|
O << Indent << "}\n";
|
|
} else {
|
|
if (Action->isSubClassOf("CCDelegateTo")) {
|
|
const Record *CC = Action->getValueAsDef("CC");
|
|
O << Indent << "if (!" << CC->getName()
|
|
<< "(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State))\n"
|
|
<< 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 = getQualifiedRegisterName(RegList->getElement(I));
|
|
if (SwiftAction)
|
|
AssignedSwiftRegsMap[CurrentAction].insert(std::move(Name));
|
|
else
|
|
AssignedRegsMap[CurrentAction].insert(std::move(Name));
|
|
}
|
|
EmitAllocateReg({RegList}, {"RegList"});
|
|
|
|
if (Action->isSubClassOf("CCAssignToRegAndStack"))
|
|
EmitAllocateStack();
|
|
|
|
O << Indent << " return false;\n";
|
|
O << Indent << "}\n";
|
|
} else if (Action->isSubClassOf("CCAssignToRegWithShadow")) {
|
|
const ListInit *RegList = Action->getValueAsListInit("RegList");
|
|
const ListInit *ShadowRegList =
|
|
Action->getValueAsListInit("ShadowRegList");
|
|
if (!ShadowRegList->empty() && ShadowRegList->size() != RegList->size())
|
|
PrintFatalError(Action->getLoc(),
|
|
"Invalid length of list of shadowed registers");
|
|
|
|
EmitAllocateReg({RegList, ShadowRegList}, {"RegList", "RegList"});
|
|
|
|
O << Indent << " return false;\n";
|
|
O << Indent << "}\n";
|
|
} else if (Action->isSubClassOf("CCAssignToStack")) {
|
|
EmitAllocateStack(/*EmitOffset=*/true);
|
|
O << Indent << "State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset"
|
|
<< Counter << ", LocVT, LocInfo));\n";
|
|
O << Indent << "return false;\n";
|
|
} else if (Action->isSubClassOf("CCAssignToStackWithShadow")) {
|
|
int Size = Action->getValueAsInt("Size");
|
|
int Align = Action->getValueAsInt("Align");
|
|
const ListInit *ShadowRegList =
|
|
Action->getValueAsListInit("ShadowRegList");
|
|
|
|
unsigned ShadowRegListNumber = ++Counter;
|
|
EmitRegList(ShadowRegList, "ShadowRegList" + utostr(ShadowRegListNumber));
|
|
|
|
O << Indent << "int64_t Offset" << ++Counter << " = State.AllocateStack("
|
|
<< Size << ", Align(" << Align << "), "
|
|
<< "ShadowRegList" << ShadowRegListNumber << ");\n";
|
|
O << Indent << "State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset"
|
|
<< Counter << ", LocVT, LocInfo));\n";
|
|
O << Indent << "return false;\n";
|
|
} else if (Action->isSubClassOf("CCPromoteToType")) {
|
|
const Record *DestTy = Action->getValueAsDef("DestTy");
|
|
MVT::SimpleValueType DestVT = getValueType(DestTy);
|
|
O << Indent << "LocVT = " << getEnumName(DestVT) << ";\n";
|
|
if (MVT(DestVT).isFloatingPoint()) {
|
|
O << Indent << "LocInfo = CCValAssign::FPExt;\n";
|
|
} else {
|
|
O << Indent << "if (ArgFlags.isSExt())\n"
|
|
<< Indent << " LocInfo = CCValAssign::SExt;\n"
|
|
<< Indent << "else if (ArgFlags.isZExt())\n"
|
|
<< Indent << " LocInfo = CCValAssign::ZExt;\n"
|
|
<< Indent << "else\n"
|
|
<< Indent << " LocInfo = CCValAssign::AExt;\n";
|
|
}
|
|
} else if (Action->isSubClassOf("CCPromoteToUpperBitsInType")) {
|
|
const Record *DestTy = Action->getValueAsDef("DestTy");
|
|
MVT::SimpleValueType DestVT = getValueType(DestTy);
|
|
O << Indent << "LocVT = " << getEnumName(DestVT) << ";\n";
|
|
if (MVT(DestVT).isFloatingPoint()) {
|
|
PrintFatalError(Action->getLoc(),
|
|
"CCPromoteToUpperBitsInType does not handle floating "
|
|
"point");
|
|
} else {
|
|
O << Indent << "if (ArgFlags.isSExt())\n"
|
|
<< Indent << " LocInfo = CCValAssign::SExtUpper;\n"
|
|
<< Indent << "else if (ArgFlags.isZExt())\n"
|
|
<< Indent << " LocInfo = CCValAssign::ZExtUpper;\n"
|
|
<< Indent << "else\n"
|
|
<< Indent << " LocInfo = CCValAssign::AExtUpper;\n";
|
|
}
|
|
} else if (Action->isSubClassOf("CCBitConvertToType")) {
|
|
const Record *DestTy = Action->getValueAsDef("DestTy");
|
|
O << Indent << "LocVT = " << getEnumName(getValueType(DestTy)) << ";\n";
|
|
O << Indent << "LocInfo = CCValAssign::BCvt;\n";
|
|
} else if (Action->isSubClassOf("CCTruncToType")) {
|
|
const Record *DestTy = Action->getValueAsDef("DestTy");
|
|
O << Indent << "LocVT = " << getEnumName(getValueType(DestTy)) << ";\n";
|
|
O << Indent << "LocInfo = CCValAssign::Trunc;\n";
|
|
} else if (Action->isSubClassOf("CCPassIndirect")) {
|
|
const Record *DestTy = Action->getValueAsDef("DestTy");
|
|
O << Indent << "LocVT = " << getEnumName(getValueType(DestTy)) << ";\n";
|
|
O << Indent << "LocInfo = CCValAssign::Indirect;\n";
|
|
} else if (Action->isSubClassOf("CCPassByVal")) {
|
|
int Size = Action->getValueAsInt("Size");
|
|
int Align = Action->getValueAsInt("Align");
|
|
O << Indent << "State.HandleByVal(ValNo, ValVT, LocVT, LocInfo, " << Size
|
|
<< ", Align(" << Align << "), ArgFlags);\n";
|
|
O << Indent << "return false;\n";
|
|
} else if (Action->isSubClassOf("CCCustom")) {
|
|
O << Indent << "if (" << Action->getValueAsString("FuncName")
|
|
<< "(ValNo, ValVT, "
|
|
<< "LocVT, LocInfo, ArgFlags, State))\n";
|
|
O << Indent << " return false;\n";
|
|
} else {
|
|
errs() << *Action;
|
|
PrintFatalError(Action->getLoc(), "Unknown CCAction!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void CallingConvEmitter::emitArgRegisterLists(raw_ostream &O) {
|
|
// Transitively merge all delegated CCs into AssignedRegsMap.
|
|
using EntryTy = std::pair<std::string, std::set<std::string>>;
|
|
bool Redo;
|
|
do {
|
|
Redo = false;
|
|
std::deque<EntryTy> Worklist(DelegateToMap.begin(), DelegateToMap.end());
|
|
|
|
while (!Worklist.empty()) {
|
|
EntryTy Entry = Worklist.front();
|
|
Worklist.pop_front();
|
|
|
|
const std::string &CCName = Entry.first;
|
|
std::set<std::string> &Registers = Entry.second;
|
|
if (!Registers.empty())
|
|
continue;
|
|
|
|
for (auto &InnerEntry : Worklist) {
|
|
const std::string &InnerCCName = InnerEntry.first;
|
|
std::set<std::string> &InnerRegisters = InnerEntry.second;
|
|
|
|
auto It = InnerRegisters.find(CCName);
|
|
if (It != InnerRegisters.end()) {
|
|
const auto &Src = AssignedRegsMap[CCName];
|
|
AssignedRegsMap[InnerCCName].insert(Src.begin(), Src.end());
|
|
InnerRegisters.erase(It);
|
|
}
|
|
}
|
|
|
|
DelegateToMap.erase(CCName);
|
|
Redo = true;
|
|
}
|
|
} while (Redo);
|
|
|
|
if (AssignedRegsMap.empty())
|
|
return;
|
|
|
|
O << "\n#else\n\n";
|
|
|
|
for (const auto &[RegName, Registers] : AssignedRegsMap) {
|
|
if (RegName.empty())
|
|
continue;
|
|
|
|
O << "const MCRegister " << RegName << "_ArgRegs[] = { ";
|
|
|
|
if (Registers.empty())
|
|
O << "0";
|
|
else
|
|
O << llvm::interleaved(Registers);
|
|
|
|
O << " };\n";
|
|
}
|
|
|
|
if (AssignedSwiftRegsMap.empty())
|
|
return;
|
|
|
|
O << "\n// Registers used by Swift.\n";
|
|
for (const auto &[RegName, Registers] : AssignedSwiftRegsMap)
|
|
O << "const MCRegister " << RegName << "_Swift_ArgRegs[] = { "
|
|
<< llvm::interleaved(Registers) << " };\n";
|
|
}
|
|
|
|
static TableGen::Emitter::OptClass<CallingConvEmitter>
|
|
X("gen-callingconv", "Generate calling convention descriptions");
|