llvm-project/llvm/utils/TableGen/Basic/RuntimeLibcallsEmitter.cpp
Matt Arsenault 3e5d8a1439 Reapply "RuntimeLibcalls: Generate table of libcall name lengths (#153… (#153864)
This reverts commit 334e9bf2dd01fbbfe785624c0de477b725cde6f2.

Check if llvm-nm exists before building the benchmark.
2025-08-16 09:53:50 +09:00

771 lines
25 KiB
C++

//===- RuntimeLibcallEmitter.cpp - Properties from RuntimeLibcalls.td -----===//
//
// 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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "runtime-libcall-emitter"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/xxhash.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/SetTheory.h"
#include "llvm/TableGen/StringToOffsetTable.h"
#include "llvm/TableGen/TableGenBackend.h"
using namespace llvm;
namespace {
// Pair of a RuntimeLibcallPredicate and LibcallCallingConv to use as a map key.
struct PredicateWithCC {
const Record *Predicate = nullptr;
const Record *CallingConv = nullptr;
PredicateWithCC() = default;
PredicateWithCC(std::pair<const Record *, const Record *> P)
: Predicate(P.first), CallingConv(P.second) {}
PredicateWithCC(const Record *P, const Record *C)
: Predicate(P), CallingConv(C) {}
};
inline bool operator==(PredicateWithCC LHS, PredicateWithCC RHS) {
return LHS.Predicate == RHS.Predicate && LHS.CallingConv == RHS.CallingConv;
}
} // namespace
namespace llvm {
template <> struct DenseMapInfo<PredicateWithCC, void> {
static inline PredicateWithCC getEmptyKey() {
return DenseMapInfo<
std::pair<const Record *, const Record *>>::getEmptyKey();
}
static inline PredicateWithCC getTombstoneKey() {
return DenseMapInfo<
std::pair<const Record *, const Record *>>::getTombstoneKey();
}
static unsigned getHashValue(const PredicateWithCC Val) {
auto Pair = std::make_pair(Val.Predicate, Val.CallingConv);
return DenseMapInfo<
std::pair<const Record *, const Record *>>::getHashValue(Pair);
}
static bool isEqual(PredicateWithCC LHS, PredicateWithCC RHS) {
return LHS == RHS;
}
};
} // namespace llvm
namespace {
class AvailabilityPredicate {
const Record *TheDef;
StringRef PredicateString;
public:
AvailabilityPredicate(const Record *Def) : TheDef(Def) {
if (TheDef)
PredicateString = TheDef->getValueAsString("Cond");
}
const Record *getDef() const { return TheDef; }
bool isAlwaysAvailable() const { return PredicateString.empty(); }
void emitIf(raw_ostream &OS) const {
OS << "if (" << PredicateString << ") {\n";
}
void emitEndIf(raw_ostream &OS) const { OS << "}\n"; }
void emitTableVariableNameSuffix(raw_ostream &OS) const {
if (TheDef)
OS << '_' << TheDef->getName();
}
};
class RuntimeLibcallEmitter;
class RuntimeLibcallImpl;
/// Used to apply predicates to nested sets of libcalls.
struct LibcallPredicateExpander : SetTheory::Expander {
const RuntimeLibcallEmitter &LibcallEmitter;
DenseMap<const RuntimeLibcallImpl *,
std::pair<std::vector<const Record *>, const Record *>> &Func2Preds;
LibcallPredicateExpander(
const RuntimeLibcallEmitter &LibcallEmitter,
DenseMap<const RuntimeLibcallImpl *,
std::pair<std::vector<const Record *>, const Record *>>
&Func2Preds)
: LibcallEmitter(LibcallEmitter), Func2Preds(Func2Preds) {}
void expand(SetTheory &ST, const Record *Def,
SetTheory::RecSet &Elts) override;
};
class RuntimeLibcall {
const Record *TheDef = nullptr;
const size_t EnumVal;
public:
RuntimeLibcall() = delete;
RuntimeLibcall(const Record *Def, size_t EnumVal)
: TheDef(Def), EnumVal(EnumVal) {
assert(Def);
}
~RuntimeLibcall() { assert(TheDef); }
const Record *getDef() const { return TheDef; }
StringRef getName() const { return TheDef->getName(); }
size_t getEnumVal() const { return EnumVal; }
void emitEnumEntry(raw_ostream &OS) const {
OS << "RTLIB::" << TheDef->getValueAsString("Name");
}
};
class RuntimeLibcallImpl {
const Record *TheDef;
const RuntimeLibcall *Provides = nullptr;
const size_t EnumVal;
public:
RuntimeLibcallImpl(
const Record *Def,
const DenseMap<const Record *, const RuntimeLibcall *> &ProvideMap,
size_t EnumVal)
: TheDef(Def), EnumVal(EnumVal) {
if (const Record *ProvidesDef = Def->getValueAsDef("Provides"))
Provides = ProvideMap.lookup(ProvidesDef);
}
~RuntimeLibcallImpl() {}
const Record *getDef() const { return TheDef; }
StringRef getName() const { return TheDef->getName(); }
size_t getEnumVal() const { return EnumVal; }
const RuntimeLibcall *getProvides() const { return Provides; }
StringRef getLibcallFuncName() const {
return TheDef->getValueAsString("LibCallFuncName");
}
const Record *getCallingConv() const {
return TheDef->getValueAsOptionalDef("CallingConv");
}
void emitQuotedLibcallFuncName(raw_ostream &OS) const {
OS << '\"' << getLibcallFuncName() << '\"';
}
bool isDefault() const { return TheDef->getValueAsBit("IsDefault"); }
void emitEnumEntry(raw_ostream &OS) const {
OS << "RTLIB::" << TheDef->getName();
}
void emitSetImplCall(raw_ostream &OS) const {
OS << "setLibcallImpl(";
Provides->emitEnumEntry(OS);
OS << ", ";
emitEnumEntry(OS);
OS << "); // " << getLibcallFuncName() << '\n';
}
void emitTableEntry(raw_ostream &OS) const {
OS << '{';
Provides->emitEnumEntry(OS);
OS << ", ";
emitEnumEntry(OS);
OS << "}, // " << getLibcallFuncName() << '\n';
}
void emitSetCallingConv(raw_ostream &OS) const {}
};
struct LibcallsWithCC {
std::vector<const RuntimeLibcallImpl *> LibcallImpls;
const Record *CallingConv = nullptr;
};
class RuntimeLibcallEmitter {
private:
const RecordKeeper &Records;
DenseMap<const Record *, const RuntimeLibcall *> Def2RuntimeLibcall;
DenseMap<const Record *, const RuntimeLibcallImpl *> Def2RuntimeLibcallImpl;
std::vector<RuntimeLibcall> RuntimeLibcallDefList;
std::vector<RuntimeLibcallImpl> RuntimeLibcallImplDefList;
DenseMap<const RuntimeLibcall *, const RuntimeLibcallImpl *>
LibCallToDefaultImpl;
private:
void emitGetRuntimeLibcallEnum(raw_ostream &OS) const;
void emitNameMatchHashTable(raw_ostream &OS,
StringToOffsetTable &OffsetTable) const;
void emitGetInitRuntimeLibcallNames(raw_ostream &OS) const;
void emitSystemRuntimeLibrarySetCalls(raw_ostream &OS) const;
public:
RuntimeLibcallEmitter(const RecordKeeper &R) : Records(R) {
ArrayRef<const Record *> AllRuntimeLibcalls =
Records.getAllDerivedDefinitions("RuntimeLibcall");
RuntimeLibcallDefList.reserve(AllRuntimeLibcalls.size());
size_t CallTypeEnumVal = 0;
for (const Record *RuntimeLibcallDef : AllRuntimeLibcalls) {
RuntimeLibcallDefList.emplace_back(RuntimeLibcallDef, CallTypeEnumVal++);
Def2RuntimeLibcall[RuntimeLibcallDef] = &RuntimeLibcallDefList.back();
}
for (RuntimeLibcall &LibCall : RuntimeLibcallDefList)
Def2RuntimeLibcall[LibCall.getDef()] = &LibCall;
ArrayRef<const Record *> AllRuntimeLibcallImplsRaw =
Records.getAllDerivedDefinitions("RuntimeLibcallImpl");
SmallVector<const Record *, 1024> AllRuntimeLibcallImpls(
AllRuntimeLibcallImplsRaw);
// Sort by libcall impl name and secondarily by the enum name.
sort(AllRuntimeLibcallImpls, [](const Record *A, const Record *B) {
return std::pair(A->getValueAsString("LibCallFuncName"), A->getName()) <
std::pair(B->getValueAsString("LibCallFuncName"), B->getName());
});
RuntimeLibcallImplDefList.reserve(AllRuntimeLibcallImpls.size());
size_t LibCallImplEnumVal = 1;
for (const Record *LibCallImplDef : AllRuntimeLibcallImpls) {
RuntimeLibcallImplDefList.emplace_back(LibCallImplDef, Def2RuntimeLibcall,
LibCallImplEnumVal++);
const RuntimeLibcallImpl &LibCallImpl = RuntimeLibcallImplDefList.back();
Def2RuntimeLibcallImpl[LibCallImplDef] = &LibCallImpl;
if (LibCallImpl.isDefault()) {
const RuntimeLibcall *Provides = LibCallImpl.getProvides();
if (!Provides)
PrintFatalError(LibCallImplDef->getLoc(),
"default implementations must provide a libcall");
LibCallToDefaultImpl[Provides] = &LibCallImpl;
}
}
}
const RuntimeLibcall *getRuntimeLibcall(const Record *Def) const {
return Def2RuntimeLibcall.lookup(Def);
}
const RuntimeLibcallImpl *getRuntimeLibcallImpl(const Record *Def) const {
return Def2RuntimeLibcallImpl.lookup(Def);
}
void run(raw_ostream &OS);
};
/// Helper struct for the name hash table.
struct LookupEntry {
StringRef FuncName;
uint64_t Hash = 0;
unsigned TableValue = 0;
};
} // End anonymous namespace.
void RuntimeLibcallEmitter::emitGetRuntimeLibcallEnum(raw_ostream &OS) const {
OS << "#ifdef GET_RUNTIME_LIBCALL_ENUM\n"
"namespace llvm {\n"
"namespace RTLIB {\n"
"enum Libcall : unsigned short {\n";
for (const RuntimeLibcall &LibCall : RuntimeLibcallDefList) {
StringRef Name = LibCall.getName();
OS << " " << Name << " = " << LibCall.getEnumVal() << ",\n";
}
OS << " UNKNOWN_LIBCALL = " << RuntimeLibcallDefList.size()
<< "\n};\n\n"
"enum LibcallImpl : unsigned short {\n"
" Unsupported = 0,\n";
// FIXME: Emit this in a different namespace. And maybe use enum class.
for (const RuntimeLibcallImpl &LibCall : RuntimeLibcallImplDefList) {
OS << " " << LibCall.getName() << " = " << LibCall.getEnumVal() << ", // "
<< LibCall.getLibcallFuncName() << '\n';
}
OS << " NumLibcallImpls = " << RuntimeLibcallImplDefList.size() + 1
<< "\n};\n"
"} // End namespace RTLIB\n"
"} // End namespace llvm\n"
"#endif\n\n";
}
// StringMap uses xxh3_64bits, truncated to uint32_t.
static uint64_t hash(StringRef Str) {
return static_cast<uint32_t>(xxh3_64bits(Str));
}
static void emitHashFunction(raw_ostream &OS) {
OS << "static inline uint64_t hash(StringRef Str) {\n"
" return static_cast<uint32_t>(xxh3_64bits(Str));\n"
"}\n\n";
}
/// Return the table size, maximum number of collisions for the set of hashes
static std::pair<int, int>
computePerfectHashParameters(ArrayRef<uint64_t> Hashes) {
const int SizeOverhead = 10;
const int NumHashes = Hashes.size();
// Index derived from hash -> number of collisions.
DenseMap<uint64_t, int> Table;
for (int MaxCollisions = 1;; ++MaxCollisions) {
for (int N = NumHashes; N < SizeOverhead * NumHashes; ++N) {
Table.clear();
bool NeedResize = false;
for (uint64_t H : Hashes) {
uint64_t Idx = H % static_cast<uint64_t>(N);
if (++Table[Idx] > MaxCollisions) {
// Need to resize the final table if we increased the collision count.
NeedResize = true;
break;
}
}
if (!NeedResize)
return {N, MaxCollisions};
}
}
}
static std::vector<LookupEntry>
constructPerfectHashTable(ArrayRef<RuntimeLibcallImpl> Keywords,
ArrayRef<uint64_t> Hashes, int Size, int Collisions,
StringToOffsetTable &OffsetTable) {
DenseSet<StringRef> Seen;
std::vector<LookupEntry> Lookup(Size * Collisions);
for (const RuntimeLibcallImpl &LibCallImpl : Keywords) {
StringRef ImplName = LibCallImpl.getLibcallFuncName();
// We do not want to add repeated entries for cases with the same name, only
// an entry for the first, with the name collision enum values immediately
// following.
if (!Seen.insert(ImplName).second)
continue;
uint64_t HashValue = Hashes[LibCallImpl.getEnumVal() - 1];
uint64_t Idx = (HashValue % static_cast<uint64_t>(Size)) *
static_cast<uint64_t>(Collisions);
bool Found = false;
for (int J = 0; J < Collisions; ++J) {
LookupEntry &Entry = Lookup[Idx + J];
if (Entry.TableValue == 0) {
Entry.FuncName = ImplName;
Entry.TableValue = LibCallImpl.getEnumVal();
Entry.Hash = HashValue;
Found = true;
break;
}
}
if (!Found)
reportFatalInternalError("failure to hash " + ImplName);
}
return Lookup;
}
/// Generate hash table based lookup by name.
void RuntimeLibcallEmitter::emitNameMatchHashTable(
raw_ostream &OS, StringToOffsetTable &OffsetTable) const {
std::vector<uint64_t> Hashes(RuntimeLibcallImplDefList.size());
size_t MaxFuncNameSize = 0;
size_t Index = 0;
for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) {
StringRef ImplName = LibCallImpl.getLibcallFuncName();
MaxFuncNameSize = std::max(MaxFuncNameSize, ImplName.size());
Hashes[Index++] = hash(ImplName);
}
LLVM_DEBUG({
for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) {
StringRef ImplName = LibCallImpl.getLibcallFuncName();
if (ImplName.size() == MaxFuncNameSize) {
dbgs() << "Maximum runtime libcall name size: " << ImplName << '('
<< MaxFuncNameSize << ")\n";
}
}
});
// Early exiting on the symbol name provides a significant speedup in the miss
// case on the set of symbols in a clang binary. Emit this as an inlinable
// precondition in the header.
//
// The empty check is also used to get sensible behavior on anonymous
// functions.
//
// TODO: It may make more sense to split the search by string size more. There
// are a few outliers, most call names are small.
OS << "#ifdef GET_LOOKUP_LIBCALL_IMPL_NAME_BODY\n"
" size_t Size = Name.size();\n"
" if (Size == 0 || Size > "
<< MaxFuncNameSize
<< ")\n"
" return enum_seq(RTLIB::Unsupported, RTLIB::Unsupported);\n"
" return lookupLibcallImplNameImpl(Name);\n"
"#endif\n";
auto [Size, Collisions] = computePerfectHashParameters(Hashes);
std::vector<LookupEntry> Lookup = constructPerfectHashTable(
RuntimeLibcallImplDefList, Hashes, Size, Collisions, OffsetTable);
LLVM_DEBUG(dbgs() << "Runtime libcall perfect hashing parameters: Size = "
<< Size << ", maximum collisions = " << Collisions << '\n');
OS << "#ifdef DEFINE_GET_LOOKUP_LIBCALL_IMPL_NAME\n";
emitHashFunction(OS);
OS << "iota_range<RTLIB::LibcallImpl> RTLIB::RuntimeLibcallsInfo::"
"lookupLibcallImplNameImpl(StringRef Name) {\n";
// Emit RTLIB::LibcallImpl values
OS << " static constexpr uint16_t HashTableNameToEnum[" << Lookup.size()
<< "] = {\n";
for (auto [FuncName, Hash, TableVal] : Lookup) {
OS << " " << TableVal << ',';
if (TableVal != 0)
OS << " // " << format_hex(Hash, 16) << ", " << FuncName;
OS << '\n';
}
OS << " };\n\n";
OS << " unsigned Idx = (hash(Name) % " << Size << ") * " << Collisions
<< ";\n\n"
" for (int I = 0; I != "
<< Collisions << R"(; ++I) {
const uint16_t Entry = HashTableNameToEnum[Idx + I];
const uint16_t StrOffset = RuntimeLibcallNameOffsetTable[Entry];
const uint8_t StrSize = RuntimeLibcallNameSizeTable[Entry];
StringRef Str(
&RTLIB::RuntimeLibcallsInfo::RuntimeLibcallImplNameTableStorage[StrOffset],
StrSize);
if (Str == Name)
return libcallImplNameHit(Entry, StrOffset);
}
return enum_seq(RTLIB::Unsupported, RTLIB::Unsupported);
}
)";
OS << "#endif\n\n";
}
void RuntimeLibcallEmitter::emitGetInitRuntimeLibcallNames(
raw_ostream &OS) const {
OS << "#ifdef GET_INIT_RUNTIME_LIBCALL_NAMES\n";
// Emit the implementation names
StringToOffsetTable Table(/*AppendZero=*/true,
"RTLIB::RuntimeLibcallsInfo::");
for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList)
Table.GetOrAddStringOffset(LibCallImpl.getLibcallFuncName());
Table.EmitStringTableDef(OS, "RuntimeLibcallImplNameTable");
OS << R"(
const uint16_t RTLIB::RuntimeLibcallsInfo::RuntimeLibcallNameOffsetTable[] = {
)";
OS << formatv(" {}, // {}\n", Table.GetStringOffset(""),
""); // Unsupported entry
for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) {
StringRef ImplName = LibCallImpl.getLibcallFuncName();
OS << formatv(" {}, // {}\n", Table.GetStringOffset(ImplName), ImplName);
}
OS << "};\n";
OS << R"(
const uint8_t RTLIB::RuntimeLibcallsInfo::RuntimeLibcallNameSizeTable[] = {
)";
OS << " 0,\n";
for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList)
OS << " " << LibCallImpl.getLibcallFuncName().size() << ",\n";
OS << "};\n\n";
// Emit the reverse mapping from implementation libraries to RTLIB::Libcall
OS << "const RTLIB::Libcall llvm::RTLIB::RuntimeLibcallsInfo::"
"ImplToLibcall[RTLIB::NumLibcallImpls] = {\n"
" RTLIB::UNKNOWN_LIBCALL, // RTLIB::Unsupported\n";
for (const RuntimeLibcallImpl &LibCallImpl : RuntimeLibcallImplDefList) {
const RuntimeLibcall *Provides = LibCallImpl.getProvides();
OS << " ";
Provides->emitEnumEntry(OS);
OS << ", // ";
LibCallImpl.emitEnumEntry(OS);
OS << '\n';
}
OS << "};\n\n";
OS << "#endif\n\n";
emitNameMatchHashTable(OS, Table);
}
void RuntimeLibcallEmitter::emitSystemRuntimeLibrarySetCalls(
raw_ostream &OS) const {
OS << "void llvm::RTLIB::RuntimeLibcallsInfo::setTargetRuntimeLibcallSets("
"const llvm::Triple &TT, FloatABI::ABIType FloatABI, EABI EABIVersion, "
"StringRef ABIName) {\n"
" struct LibcallImplPair {\n"
" RTLIB::Libcall Func;\n"
" RTLIB::LibcallImpl Impl;\n"
" };\n"
" auto setLibcallsImpl = [this](\n"
" ArrayRef<LibcallImplPair> Libcalls,\n"
" std::optional<llvm::CallingConv::ID> CC = {})\n"
" {\n"
" for (const auto [Func, Impl] : Libcalls) {\n"
" setLibcallImpl(Func, Impl);\n"
" if (CC)\n"
" setLibcallImplCallingConv(Impl, *CC);\n"
" }\n"
" };\n";
ArrayRef<const Record *> AllLibs =
Records.getAllDerivedDefinitions("SystemRuntimeLibrary");
for (const Record *R : AllLibs) {
OS << '\n';
AvailabilityPredicate TopLevelPredicate(R->getValueAsDef("TriplePred"));
OS << indent(2);
TopLevelPredicate.emitIf(OS);
if (const Record *DefaultCCClass =
R->getValueAsDef("DefaultLibcallCallingConv")) {
StringRef DefaultCC =
DefaultCCClass->getValueAsString("CallingConv").trim();
if (!DefaultCC.empty()) {
OS << " const CallingConv::ID DefaultCC = " << DefaultCC << ";\n"
<< " for (CallingConv::ID &Entry : LibcallImplCallingConvs) {\n"
" Entry = DefaultCC;\n"
" }\n\n";
}
}
SetTheory Sets;
DenseMap<const RuntimeLibcallImpl *,
std::pair<std::vector<const Record *>, const Record *>>
Func2Preds;
Sets.addExpander("LibcallImpls", std::make_unique<LibcallPredicateExpander>(
*this, Func2Preds));
const SetTheory::RecVec *Elements =
Sets.expand(R->getValueAsDef("MemberList"));
// Sort to get deterministic output
SetVector<PredicateWithCC> PredicateSorter;
PredicateSorter.insert(
PredicateWithCC()); // No predicate or CC override first.
DenseMap<PredicateWithCC, LibcallsWithCC> Pred2Funcs;
for (const Record *Elt : *Elements) {
const RuntimeLibcallImpl *LibCallImpl = getRuntimeLibcallImpl(Elt);
if (!LibCallImpl) {
PrintError(R, "entry for SystemLibrary is not a RuntimeLibcallImpl");
PrintNote(Elt->getLoc(), "invalid entry `" + Elt->getName() + "`");
continue;
}
auto It = Func2Preds.find(LibCallImpl);
if (It == Func2Preds.end()) {
Pred2Funcs[PredicateWithCC()].LibcallImpls.push_back(LibCallImpl);
continue;
}
for (const Record *Pred : It->second.first) {
const Record *CC = It->second.second;
PredicateWithCC Key(Pred, CC);
auto &Entry = Pred2Funcs[Key];
Entry.LibcallImpls.push_back(LibCallImpl);
Entry.CallingConv = It->second.second;
PredicateSorter.insert(Key);
}
}
SmallVector<PredicateWithCC, 0> SortedPredicates =
PredicateSorter.takeVector();
llvm::sort(SortedPredicates, [](PredicateWithCC A, PredicateWithCC B) {
StringRef AName = A.Predicate ? A.Predicate->getName() : "";
StringRef BName = B.Predicate ? B.Predicate->getName() : "";
return AName < BName;
});
for (PredicateWithCC Entry : SortedPredicates) {
AvailabilityPredicate SubsetPredicate(Entry.Predicate);
unsigned IndentDepth = 2;
auto It = Pred2Funcs.find(Entry);
if (It == Pred2Funcs.end())
continue;
if (!SubsetPredicate.isAlwaysAvailable()) {
IndentDepth = 4;
OS << indent(IndentDepth);
SubsetPredicate.emitIf(OS);
}
LibcallsWithCC &FuncsWithCC = It->second;
std::vector<const RuntimeLibcallImpl *> &Funcs = FuncsWithCC.LibcallImpls;
// Ensure we only emit a unique implementation per libcall in the
// selection table.
//
// FIXME: We need to generate separate functions for
// is-libcall-available and should-libcall-be-used to avoid this.
//
// This also makes it annoying to make use of the default set, since the
// entries from the default set may win over the replacements unless
// they are explicitly removed.
stable_sort(Funcs, [](const RuntimeLibcallImpl *A,
const RuntimeLibcallImpl *B) {
return A->getProvides()->getEnumVal() < B->getProvides()->getEnumVal();
});
auto UniqueI = llvm::unique(
Funcs, [&](const RuntimeLibcallImpl *A, const RuntimeLibcallImpl *B) {
if (A->getProvides() == B->getProvides()) {
PrintWarning(R->getLoc(),
Twine("conflicting implementations for libcall " +
A->getProvides()->getName() + ": " +
A->getLibcallFuncName() + ", " +
B->getLibcallFuncName()));
return true;
}
return false;
});
Funcs.erase(UniqueI, Funcs.end());
OS << indent(IndentDepth + 2) << "setLibcallsImpl({\n";
for (const RuntimeLibcallImpl *LibCallImpl : Funcs) {
OS << indent(IndentDepth + 4);
LibCallImpl->emitTableEntry(OS);
}
OS << indent(IndentDepth + 2) << "}";
if (FuncsWithCC.CallingConv) {
StringRef CCEnum =
FuncsWithCC.CallingConv->getValueAsString("CallingConv");
OS << ", " << CCEnum;
}
OS << ");\n\n";
if (!SubsetPredicate.isAlwaysAvailable()) {
OS << indent(IndentDepth);
SubsetPredicate.emitEndIf(OS);
OS << '\n';
}
}
OS << indent(4) << "return;\n" << indent(2);
TopLevelPredicate.emitEndIf(OS);
}
// FIXME: This should be a fatal error. A few contexts are improperly relying
// on RuntimeLibcalls constructed with fully unknown triples.
OS << " LLVM_DEBUG(dbgs() << \"no system runtime library applied to target "
"\\'\" << TT.str() << \"\\'\\n\");\n"
"}\n\n";
}
void RuntimeLibcallEmitter::run(raw_ostream &OS) {
emitSourceFileHeader("Runtime LibCalls Source Fragment", OS, Records);
emitGetRuntimeLibcallEnum(OS);
emitGetInitRuntimeLibcallNames(OS);
OS << "#ifdef GET_SET_TARGET_RUNTIME_LIBCALL_SETS\n";
emitSystemRuntimeLibrarySetCalls(OS);
OS << "#endif\n\n";
}
void LibcallPredicateExpander::expand(SetTheory &ST, const Record *Def,
SetTheory::RecSet &Elts) {
assert(Def->isSubClassOf("LibcallImpls"));
SetTheory::RecSet TmpElts;
ST.evaluate(Def->getValueInit("MemberList"), TmpElts, Def->getLoc());
Elts.insert(TmpElts.begin(), TmpElts.end());
AvailabilityPredicate AP(Def->getValueAsDef("AvailabilityPredicate"));
const Record *CCClass = Def->getValueAsOptionalDef("CallingConv");
// This is assuming we aren't conditionally applying a calling convention to
// some subsets, and not another, but this doesn't appear to be used.
for (const Record *LibcallImplDef : TmpElts) {
const RuntimeLibcallImpl *LibcallImpl =
LibcallEmitter.getRuntimeLibcallImpl(LibcallImplDef);
if (!AP.isAlwaysAvailable() || CCClass) {
auto [It, Inserted] = Func2Preds.insert({LibcallImpl, {{}, CCClass}});
if (!Inserted) {
PrintError(
Def,
"combining nested libcall set predicates currently unhandled: '" +
LibcallImpl->getLibcallFuncName() + "'");
}
It->second.first.push_back(AP.getDef());
It->second.second = CCClass;
}
}
}
static TableGen::Emitter::OptClass<RuntimeLibcallEmitter>
X("gen-runtime-libcalls", "Generate RuntimeLibcalls");