Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
213799 views
//===- RuntimeLibcallEmitter.cpp - Properties from RuntimeLibcalls.td -----===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/ADT/StringRef.h"9#include "llvm/Support/Debug.h"10#include "llvm/Support/raw_ostream.h"11#include "llvm/TableGen/Error.h"12#include "llvm/TableGen/Record.h"13#include "llvm/TableGen/SetTheory.h"14#include "llvm/TableGen/TableGenBackend.h"1516using namespace llvm;1718namespace {19// Pair of a RuntimeLibcallPredicate and LibcallCallingConv to use as a map key.20struct PredicateWithCC {21const Record *Predicate = nullptr;22const Record *CallingConv = nullptr;2324PredicateWithCC() = default;25PredicateWithCC(std::pair<const Record *, const Record *> P)26: Predicate(P.first), CallingConv(P.second) {}2728PredicateWithCC(const Record *P, const Record *C)29: Predicate(P), CallingConv(C) {}30};3132inline bool operator==(PredicateWithCC LHS, PredicateWithCC RHS) {33return LHS.Predicate == RHS.Predicate && LHS.CallingConv == RHS.CallingConv;34}35} // namespace3637namespace llvm {38template <> struct DenseMapInfo<PredicateWithCC, void> {39static inline PredicateWithCC getEmptyKey() {40return DenseMapInfo<41std::pair<const Record *, const Record *>>::getEmptyKey();42}4344static inline PredicateWithCC getTombstoneKey() {45return DenseMapInfo<46std::pair<const Record *, const Record *>>::getTombstoneKey();47}4849static unsigned getHashValue(const PredicateWithCC Val) {50auto Pair = std::make_pair(Val.Predicate, Val.CallingConv);51return DenseMapInfo<52std::pair<const Record *, const Record *>>::getHashValue(Pair);53}5455static bool isEqual(PredicateWithCC LHS, PredicateWithCC RHS) {56return LHS == RHS;57}58};59} // namespace llvm6061namespace {6263class AvailabilityPredicate {64const Record *TheDef;65StringRef PredicateString;6667public:68AvailabilityPredicate(const Record *Def) : TheDef(Def) {69if (TheDef)70PredicateString = TheDef->getValueAsString("Cond");71}7273const Record *getDef() const { return TheDef; }7475bool isAlwaysAvailable() const { return PredicateString.empty(); }7677void emitIf(raw_ostream &OS) const {78OS << "if (" << PredicateString << ") {\n";79}8081void emitEndIf(raw_ostream &OS) const { OS << "}\n"; }8283void emitTableVariableNameSuffix(raw_ostream &OS) const {84if (TheDef)85OS << '_' << TheDef->getName();86}87};8889class RuntimeLibcallEmitter;90class RuntimeLibcallImpl;9192/// Used to apply predicates to nested sets of libcalls.93struct LibcallPredicateExpander : SetTheory::Expander {94const RuntimeLibcallEmitter &LibcallEmitter;95DenseMap<const RuntimeLibcallImpl *,96std::pair<std::vector<const Record *>, const Record *>> &Func2Preds;9798LibcallPredicateExpander(99const RuntimeLibcallEmitter &LibcallEmitter,100DenseMap<const RuntimeLibcallImpl *,101std::pair<std::vector<const Record *>, const Record *>>102&Func2Preds)103: LibcallEmitter(LibcallEmitter), Func2Preds(Func2Preds) {}104105void expand(SetTheory &ST, const Record *Def,106SetTheory::RecSet &Elts) override;107};108109class RuntimeLibcall {110const Record *TheDef = nullptr;111const size_t EnumVal;112113public:114RuntimeLibcall() = delete;115RuntimeLibcall(const Record *Def, size_t EnumVal)116: TheDef(Def), EnumVal(EnumVal) {117assert(Def);118}119120~RuntimeLibcall() { assert(TheDef); }121122const Record *getDef() const { return TheDef; }123124StringRef getName() const { return TheDef->getName(); }125126size_t getEnumVal() const { return EnumVal; }127128void emitEnumEntry(raw_ostream &OS) const {129OS << "RTLIB::" << TheDef->getValueAsString("Name");130}131};132133class RuntimeLibcallImpl {134const Record *TheDef;135const RuntimeLibcall *Provides = nullptr;136const size_t EnumVal;137138public:139RuntimeLibcallImpl(140const Record *Def,141const DenseMap<const Record *, const RuntimeLibcall *> &ProvideMap,142size_t EnumVal)143: TheDef(Def), EnumVal(EnumVal) {144if (const Record *ProvidesDef = Def->getValueAsDef("Provides"))145Provides = ProvideMap.lookup(ProvidesDef);146}147148~RuntimeLibcallImpl() {}149150const Record *getDef() const { return TheDef; }151152StringRef getName() const { return TheDef->getName(); }153154size_t getEnumVal() const { return EnumVal; }155156const RuntimeLibcall *getProvides() const { return Provides; }157158StringRef getLibcallFuncName() const {159return TheDef->getValueAsString("LibCallFuncName");160}161162const Record *getCallingConv() const {163return TheDef->getValueAsOptionalDef("CallingConv");164}165166void emitQuotedLibcallFuncName(raw_ostream &OS) const {167OS << '\"' << getLibcallFuncName() << '\"';168}169170bool isDefault() const { return TheDef->getValueAsBit("IsDefault"); }171172void emitEnumEntry(raw_ostream &OS) const {173OS << "RTLIB::" << TheDef->getName();174}175176void emitSetImplCall(raw_ostream &OS) const {177OS << "setLibcallImpl(";178Provides->emitEnumEntry(OS);179OS << ", ";180emitEnumEntry(OS);181OS << "); // " << getLibcallFuncName() << '\n';182}183184void emitTableEntry(raw_ostream &OS) const {185OS << '{';186Provides->emitEnumEntry(OS);187OS << ", ";188emitEnumEntry(OS);189OS << "}, // " << getLibcallFuncName() << '\n';190}191192void emitSetCallingConv(raw_ostream &OS) const {}193};194195struct LibcallsWithCC {196std::vector<const RuntimeLibcallImpl *> LibcallImpls;197const Record *CallingConv = nullptr;198};199200class RuntimeLibcallEmitter {201private:202const RecordKeeper &Records;203DenseMap<const Record *, const RuntimeLibcall *> Def2RuntimeLibcall;204DenseMap<const Record *, const RuntimeLibcallImpl *> Def2RuntimeLibcallImpl;205206std::vector<RuntimeLibcall> RuntimeLibcallDefList;207std::vector<RuntimeLibcallImpl> RuntimeLibcallImplDefList;208209DenseMap<const RuntimeLibcall *, const RuntimeLibcallImpl *>210LibCallToDefaultImpl;211212private:213void emitGetRuntimeLibcallEnum(raw_ostream &OS) const;214215void emitGetInitRuntimeLibcallNames(raw_ostream &OS) const;216217void emitSystemRuntimeLibrarySetCalls(raw_ostream &OS) const;218219public:220RuntimeLibcallEmitter(const RecordKeeper &R) : Records(R) {221222ArrayRef<const Record *> AllRuntimeLibcalls =223Records.getAllDerivedDefinitions("RuntimeLibcall");224225RuntimeLibcallDefList.reserve(AllRuntimeLibcalls.size());226227size_t CallTypeEnumVal = 0;228for (const Record *RuntimeLibcallDef : AllRuntimeLibcalls) {229RuntimeLibcallDefList.emplace_back(RuntimeLibcallDef, CallTypeEnumVal++);230Def2RuntimeLibcall[RuntimeLibcallDef] = &RuntimeLibcallDefList.back();231}232233for (RuntimeLibcall &LibCall : RuntimeLibcallDefList)234Def2RuntimeLibcall[LibCall.getDef()] = &LibCall;235236ArrayRef<const Record *> AllRuntimeLibcallImpls =237Records.getAllDerivedDefinitions("RuntimeLibcallImpl");238RuntimeLibcallImplDefList.reserve(AllRuntimeLibcallImpls.size());239240size_t LibCallImplEnumVal = 1;241for (const Record *LibCallImplDef : AllRuntimeLibcallImpls) {242RuntimeLibcallImplDefList.emplace_back(LibCallImplDef, Def2RuntimeLibcall,243LibCallImplEnumVal++);244245RuntimeLibcallImpl &LibCallImpl = RuntimeLibcallImplDefList.back();246247Def2RuntimeLibcallImpl[LibCallImplDef] = &LibCallImpl;248249// const RuntimeLibcallImpl &LibCallImpl =250// RuntimeLibcallImplDefList.back();251if (LibCallImpl.isDefault()) {252const RuntimeLibcall *Provides = LibCallImpl.getProvides();253if (!Provides)254PrintFatalError(LibCallImplDef->getLoc(),255"default implementations must provide a libcall");256LibCallToDefaultImpl[Provides] = &LibCallImpl;257}258}259}260261const RuntimeLibcall *getRuntimeLibcall(const Record *Def) const {262return Def2RuntimeLibcall.lookup(Def);263}264265const RuntimeLibcallImpl *getRuntimeLibcallImpl(const Record *Def) const {266return Def2RuntimeLibcallImpl.lookup(Def);267}268269void run(raw_ostream &OS);270};271272} // End anonymous namespace.273274void RuntimeLibcallEmitter::emitGetRuntimeLibcallEnum(raw_ostream &OS) const {275OS << "#ifdef GET_RUNTIME_LIBCALL_ENUM\n"276"namespace llvm {\n"277"namespace RTLIB {\n"278"enum Libcall : unsigned short {\n";279280for (const RuntimeLibcall &LibCall : RuntimeLibcallDefList) {281StringRef Name = LibCall.getName();282OS << " " << Name << " = " << LibCall.getEnumVal() << ",\n";283}284285// TODO: Emit libcall names as string offset table.286287OS << " UNKNOWN_LIBCALL = " << RuntimeLibcallDefList.size()288<< "\n};\n\n"289"enum LibcallImpl : unsigned short {\n"290" Unsupported = 0,\n";291292// FIXME: Emit this in a different namespace. And maybe use enum class.293for (const RuntimeLibcallImpl &LibCall : RuntimeLibcallImplDefList) {294OS << " " << LibCall.getName() << " = " << LibCall.getEnumVal() << ", // "295<< LibCall.getLibcallFuncName() << '\n';296}297298OS << " NumLibcallImpls = " << RuntimeLibcallImplDefList.size() + 1299<< "\n};\n"300"} // End namespace RTLIB\n"301"} // End namespace llvm\n"302"#endif\n\n";303}304305void RuntimeLibcallEmitter::emitGetInitRuntimeLibcallNames(306raw_ostream &OS) const {307// TODO: Emit libcall names as string offset table.308309OS << "const RTLIB::LibcallImpl "310"llvm::RTLIB::RuntimeLibcallsInfo::"311"DefaultLibcallImpls[RTLIB::UNKNOWN_LIBCALL + 1] = {\n";312313for (const RuntimeLibcall &LibCall : RuntimeLibcallDefList) {314auto I = LibCallToDefaultImpl.find(&LibCall);315if (I == LibCallToDefaultImpl.end()) {316OS << " RTLIB::Unsupported,";317} else {318const RuntimeLibcallImpl *LibCallImpl = I->second;319OS << " ";320LibCallImpl->emitEnumEntry(OS);321OS << ',';322}323324OS << " // ";325LibCall.emitEnumEntry(OS);326OS << '\n';327}328329OS << " RTLIB::Unsupported\n"330"};\n\n";331332// Emit the implementation names333OS << "const char *const llvm::RTLIB::RuntimeLibcallsInfo::"334"LibCallImplNames[RTLIB::NumLibcallImpls] = {\n"335" nullptr, // RTLIB::Unsupported\n";336337for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) {338OS << " \"" << LibCallImpl.getLibcallFuncName() << "\", // ";339LibCallImpl.emitEnumEntry(OS);340OS << '\n';341}342343OS << "};\n\n";344345// Emit the reverse mapping from implementation libraries to RTLIB::Libcall346OS << "const RTLIB::Libcall llvm::RTLIB::RuntimeLibcallsInfo::"347"ImplToLibcall[RTLIB::NumLibcallImpls] = {\n"348" RTLIB::UNKNOWN_LIBCALL, // RTLIB::Unsupported\n";349350for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) {351const RuntimeLibcall *Provides = LibCallImpl.getProvides();352OS << " ";353Provides->emitEnumEntry(OS);354OS << ", // ";355LibCallImpl.emitEnumEntry(OS);356OS << '\n';357}358OS << "};\n\n";359}360361void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(362raw_ostream &OS) const {363OS << "void llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets("364"const llvm::Triple &TT, FloatABI::ABIType FloatABI) {\n"365" struct LibcallImplPair {\n"366" RTLIB::Libcall Func;\n"367" RTLIB::LibcallImpl Impl;\n"368" };\n";369ArrayRef<const Record *> AllLibs =370Records.getAllDerivedDefinitions("SystemRuntimeLibrary");371372for (const Record *R : AllLibs) {373OS << '\n';374375AvailabilityPredicate TopLevelPredicate(R->getValueAsDef("TriplePred"));376377OS << indent(2);378TopLevelPredicate.emitIf(OS);379380if (const Record *DefaultCCClass =381R->getValueAsDef("DefaultLibcallCallingConv")) {382StringRef DefaultCC =383DefaultCCClass->getValueAsString("CallingConv").trim();384385if (!DefaultCC.empty()) {386OS << " const CallingConv::ID DefaultCC = " << DefaultCC << ";\n"387<< " for (CallingConv::ID &Entry : LibcallImplCallingConvs) {\n"388" Entry = DefaultCC;\n"389" }\n\n";390}391}392393SetTheory Sets;394395DenseMap<const RuntimeLibcallImpl *,396std::pair<std::vector<const Record *>, const Record *>>397Func2Preds;398Sets.addExpander("LibcallImpls", std::make_unique<LibcallPredicateExpander>(399*this, Func2Preds));400401const SetTheory::RecVec *Elements =402Sets.expand(R->getValueAsDef("MemberList"));403404// Sort to get deterministic output405SetVector<PredicateWithCC> PredicateSorter;406PredicateSorter.insert(407PredicateWithCC()); // No predicate or CC override first.408409DenseMap<PredicateWithCC, LibcallsWithCC> Pred2Funcs;410for (const Record *Elt : *Elements) {411const RuntimeLibcallImpl *LibCallImpl = getRuntimeLibcallImpl(Elt);412if (!LibCallImpl) {413PrintError(R, "entry for SystemLibrary is not a RuntimeLibcallImpl");414PrintNote(Elt->getLoc(), "invalid entry `" + Elt->getName() + "`");415continue;416}417418auto It = Func2Preds.find(LibCallImpl);419if (It == Func2Preds.end()) {420Pred2Funcs[PredicateWithCC()].LibcallImpls.push_back(LibCallImpl);421continue;422}423424for (const Record *Pred : It->second.first) {425const Record *CC = It->second.second;426PredicateWithCC Key(Pred, CC);427428auto &Entry = Pred2Funcs[Key];429Entry.LibcallImpls.push_back(LibCallImpl);430Entry.CallingConv = It->second.second;431PredicateSorter.insert(Key);432}433}434435SmallVector<PredicateWithCC, 0> SortedPredicates =436PredicateSorter.takeVector();437438llvm::sort(SortedPredicates, [](PredicateWithCC A, PredicateWithCC B) {439StringRef AName = A.Predicate ? A.Predicate->getName() : "";440StringRef BName = B.Predicate ? B.Predicate->getName() : "";441return AName < BName;442});443444for (PredicateWithCC Entry : SortedPredicates) {445AvailabilityPredicate SubsetPredicate(Entry.Predicate);446unsigned IndentDepth = 2;447448auto It = Pred2Funcs.find(Entry);449if (It == Pred2Funcs.end())450continue;451452if (!SubsetPredicate.isAlwaysAvailable()) {453IndentDepth = 4;454455OS << indent(IndentDepth);456SubsetPredicate.emitIf(OS);457}458459LibcallsWithCC &FuncsWithCC = It->second;460461std::vector<const RuntimeLibcallImpl *> &Funcs = FuncsWithCC.LibcallImpls;462463// Ensure we only emit a unique implementation per libcall in the464// selection table.465//466// FIXME: We need to generate separate functions for467// is-libcall-available and should-libcall-be-used to avoid this.468//469// This also makes it annoying to make use of the default set, since the470// entries from the default set may win over the replacements unless471// they are explicitly removed.472stable_sort(Funcs, [](const RuntimeLibcallImpl *A,473const RuntimeLibcallImpl *B) {474return A->getProvides()->getEnumVal() < B->getProvides()->getEnumVal();475});476477auto UniqueI = llvm::unique(478Funcs, [&](const RuntimeLibcallImpl *A, const RuntimeLibcallImpl *B) {479if (A->getProvides() == B->getProvides()) {480PrintWarning(R->getLoc(),481Twine("conflicting implementations for libcall " +482A->getProvides()->getName() + ": " +483A->getLibcallFuncName() + ", " +484B->getLibcallFuncName()));485return true;486}487488return false;489});490491Funcs.erase(UniqueI, Funcs.end());492493OS << indent(IndentDepth + 2)494<< "static const LibcallImplPair LibraryCalls";495SubsetPredicate.emitTableVariableNameSuffix(OS);496OS << "[] = {\n";497for (const RuntimeLibcallImpl *LibCallImpl : Funcs) {498OS << indent(IndentDepth + 6);499LibCallImpl->emitTableEntry(OS);500}501502OS << indent(IndentDepth + 2) << "};\n\n"503<< indent(IndentDepth + 2)504<< "for (const auto [Func, Impl] : LibraryCalls";505SubsetPredicate.emitTableVariableNameSuffix(OS);506OS << ") {\n"507<< indent(IndentDepth + 4) << "setLibcallImpl(Func, Impl);\n";508509if (FuncsWithCC.CallingConv) {510StringRef CCEnum =511FuncsWithCC.CallingConv->getValueAsString("CallingConv");512OS << indent(IndentDepth + 4) << "setLibcallImplCallingConv(Impl, "513<< CCEnum << ");\n";514}515516OS << indent(IndentDepth + 2) << "}\n";517OS << '\n';518519if (!SubsetPredicate.isAlwaysAvailable()) {520OS << indent(IndentDepth);521SubsetPredicate.emitEndIf(OS);522OS << '\n';523}524}525526OS << indent(4) << "return;\n" << indent(2);527TopLevelPredicate.emitEndIf(OS);528}529530// Fallback to the old default set for manual table entries.531//532// TODO: Remove this when targets have switched to using generated tables by533// default.534OS << " initDefaultLibCallImpls();\n";535536OS << "}\n\n";537}538539void RuntimeLibcallEmitter::run(raw_ostream &OS) {540emitSourceFileHeader("Runtime LibCalls Source Fragment", OS, Records);541emitGetRuntimeLibcallEnum(OS);542543OS << "#ifdef GET_INIT_RUNTIME_LIBCALL_NAMES\n";544emitGetInitRuntimeLibcallNames(OS);545OS << "#endif\n\n";546547OS << "#ifdef GET_SET_TARGET_RUNTIME_LIBCALL_SETS\n";548emitSystemRuntimeLibrarySetCalls(OS);549OS << "#endif\n\n";550}551552void LibcallPredicateExpander::expand(SetTheory &ST, const Record *Def,553SetTheory::RecSet &Elts) {554assert(Def->isSubClassOf("LibcallImpls"));555556SetTheory::RecSet TmpElts;557558ST.evaluate(Def->getValueInit("MemberList"), TmpElts, Def->getLoc());559560Elts.insert(TmpElts.begin(), TmpElts.end());561562AvailabilityPredicate AP(Def->getValueAsDef("AvailabilityPredicate"));563const Record *CCClass = Def->getValueAsOptionalDef("CallingConv");564565// This is assuming we aren't conditionally applying a calling convention to566// some subsets, and not another, but this doesn't appear to be used.567568for (const Record *LibcallImplDef : TmpElts) {569const RuntimeLibcallImpl *LibcallImpl =570LibcallEmitter.getRuntimeLibcallImpl(LibcallImplDef);571if (!AP.isAlwaysAvailable() || CCClass) {572auto [It, Inserted] = Func2Preds.insert({LibcallImpl, {{}, CCClass}});573if (!Inserted) {574PrintError(575Def, "combining nested libcall set predicates currently unhandled");576}577578It->second.first.push_back(AP.getDef());579It->second.second = CCClass;580}581}582}583584static TableGen::Emitter::OptClass<RuntimeLibcallEmitter>585X("gen-runtime-libcalls", "Generate RuntimeLibcalls");586587588