aboutsummaryrefslogtreecommitdiff
path: root/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
diff options
context:
space:
mode:
authorNAKAMURA Takumi <geek4civic@gmail.com>2025-01-09 17:16:04 +0900
committerNAKAMURA Takumi <geek4civic@gmail.com>2025-01-09 17:16:04 +0900
commit0aa930a41f2d1ebf1fa90ec42da8f96d15a4dcbb (patch)
tree6a77b463f700e090df586672c26b9fe765fd115b /llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
parentec6892d1c979ce0b84c86918d5cdbb03037b409a (diff)
parent6d16b1c5c468a79ecf867293023c89ac518ecdda (diff)
downloadllvm-users/chapuni/cov/single/nextcount-base.zip
llvm-users/chapuni/cov/single/nextcount-base.tar.gz
llvm-users/chapuni/cov/single/nextcount-base.tar.bz2
Merge branch 'users/chapuni/cov/single/pair' into users/chapuni/cov/single/nextcount-baseusers/chapuni/cov/single/nextcount-base
Diffstat (limited to 'llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp')
-rw-r--r--llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp867
1 files changed, 867 insertions, 0 deletions
diff --git a/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
new file mode 100644
index 0000000..fc2b890
--- /dev/null
+++ b/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
@@ -0,0 +1,867 @@
+//===- IntrinsicEmitter.cpp - Generate intrinsic information --------------===//
+//
+// 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 emits information about intrinsic functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeGenIntrinsics.h"
+#include "SequenceToOffsetTable.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/ModRef.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/StringToOffsetTable.h"
+#include "llvm/TableGen/TableGenBackend.h"
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cctype>
+#include <map>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+using namespace llvm;
+
+static cl::OptionCategory GenIntrinsicCat("Options for -gen-intrinsic-enums");
+static cl::opt<std::string>
+ IntrinsicPrefix("intrinsic-prefix",
+ cl::desc("Generate intrinsics with this target prefix"),
+ cl::value_desc("target prefix"), cl::cat(GenIntrinsicCat));
+
+namespace {
+class IntrinsicEmitter {
+ const RecordKeeper &Records;
+
+public:
+ IntrinsicEmitter(const RecordKeeper &R) : Records(R) {}
+
+ void run(raw_ostream &OS, bool Enums);
+
+ void EmitEnumInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
+ void EmitArgKind(raw_ostream &OS);
+ void EmitIITInfo(raw_ostream &OS);
+ void EmitTargetInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
+ void EmitIntrinsicToNameTable(const CodeGenIntrinsicTable &Ints,
+ raw_ostream &OS);
+ void EmitIntrinsicToOverloadTable(const CodeGenIntrinsicTable &Ints,
+ raw_ostream &OS);
+ void EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
+ void EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);
+ void EmitIntrinsicToBuiltinMap(const CodeGenIntrinsicTable &Ints,
+ bool IsClang, raw_ostream &OS);
+};
+
+// Helper class to use with `TableGen::Emitter::OptClass`.
+template <bool Enums> class IntrinsicEmitterOpt : public IntrinsicEmitter {
+public:
+ IntrinsicEmitterOpt(const RecordKeeper &R) : IntrinsicEmitter(R) {}
+ void run(raw_ostream &OS) { IntrinsicEmitter::run(OS, Enums); }
+};
+
+} // End anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// IntrinsicEmitter Implementation
+//===----------------------------------------------------------------------===//
+
+void IntrinsicEmitter::run(raw_ostream &OS, bool Enums) {
+ emitSourceFileHeader("Intrinsic Function Source Fragment", OS);
+
+ CodeGenIntrinsicTable Ints(Records);
+
+ if (Enums) {
+ // Emit the enum information.
+ EmitEnumInfo(Ints, OS);
+
+ // Emit ArgKind for Intrinsics.h.
+ EmitArgKind(OS);
+ } else {
+ // Emit IIT_Info constants.
+ EmitIITInfo(OS);
+
+ // Emit the target metadata.
+ EmitTargetInfo(Ints, OS);
+
+ // Emit the intrinsic ID -> name table.
+ EmitIntrinsicToNameTable(Ints, OS);
+
+ // Emit the intrinsic ID -> overload table.
+ EmitIntrinsicToOverloadTable(Ints, OS);
+
+ // Emit the intrinsic declaration generator.
+ EmitGenerator(Ints, OS);
+
+ // Emit the intrinsic parameter attributes.
+ EmitAttributes(Ints, OS);
+
+ // Emit code to translate Clang builtins into LLVM intrinsics.
+ EmitIntrinsicToBuiltinMap(Ints, true, OS);
+
+ // Emit code to translate MS builtins into LLVM intrinsics.
+ EmitIntrinsicToBuiltinMap(Ints, false, OS);
+ }
+}
+
+void IntrinsicEmitter::EmitEnumInfo(const CodeGenIntrinsicTable &Ints,
+ raw_ostream &OS) {
+ // Find the TargetSet for which to generate enums. There will be an initial
+ // set with an empty target prefix which will include target independent
+ // intrinsics like dbg.value.
+ using TargetSet = CodeGenIntrinsicTable::TargetSet;
+ const TargetSet *Set = nullptr;
+ for (const auto &Target : Ints.getTargets()) {
+ if (Target.Name == IntrinsicPrefix) {
+ Set = &Target;
+ break;
+ }
+ }
+ if (!Set) {
+ // The first entry is for target independent intrinsics, so drop it.
+ auto KnowTargets = Ints.getTargets().drop_front();
+ PrintFatalError([KnowTargets](raw_ostream &OS) {
+ OS << "tried to generate intrinsics for unknown target "
+ << IntrinsicPrefix << "\nKnown targets are: ";
+ interleaveComma(KnowTargets, OS,
+ [&OS](const TargetSet &Target) { OS << Target.Name; });
+ OS << '\n';
+ });
+ }
+
+ // Generate a complete header for target specific intrinsics.
+ if (IntrinsicPrefix.empty()) {
+ OS << "#ifdef GET_INTRINSIC_ENUM_VALUES\n";
+ } else {
+ std::string UpperPrefix = StringRef(IntrinsicPrefix).upper();
+ OS << formatv("#ifndef LLVM_IR_INTRINSIC_{}_ENUMS_H\n", UpperPrefix);
+ OS << formatv("#define LLVM_IR_INTRINSIC_{}_ENUMS_H\n", UpperPrefix);
+ OS << "namespace llvm::Intrinsic {\n";
+ OS << formatv("enum {}Intrinsics : unsigned {{\n", UpperPrefix);
+ }
+
+ OS << "// Enum values for intrinsics.\n";
+ bool First = true;
+ for (const auto &Int : Ints[*Set]) {
+ OS << " " << Int.EnumName;
+
+ // Assign a value to the first intrinsic in this target set so that all
+ // intrinsic ids are distinct.
+ if (First) {
+ OS << " = " << Set->Offset + 1;
+ First = false;
+ }
+
+ OS << ", ";
+ if (Int.EnumName.size() < 40)
+ OS.indent(40 - Int.EnumName.size());
+ OS << formatv(" // {}\n", Int.Name);
+ }
+
+ // Emit num_intrinsics into the target neutral enum.
+ if (IntrinsicPrefix.empty()) {
+ OS << formatv(" num_intrinsics = {}\n", Ints.size() + 1);
+ OS << "#endif\n\n";
+ } else {
+ OS << R"(}; // enum
+} // namespace llvm::Intrinsic
+#endif
+
+)";
+ }
+}
+
+void IntrinsicEmitter::EmitArgKind(raw_ostream &OS) {
+ if (!IntrinsicPrefix.empty())
+ return;
+ OS << "// llvm::Intrinsic::IITDescriptor::ArgKind.\n";
+ OS << "#ifdef GET_INTRINSIC_ARGKIND\n";
+ if (const auto RecArgKind = Records.getDef("ArgKind")) {
+ for (const auto &RV : RecArgKind->getValues())
+ OS << " AK_" << RV.getName() << " = " << *RV.getValue() << ",\n";
+ } else {
+ OS << "#error \"ArgKind is not defined\"\n";
+ }
+ OS << "#endif\n\n";
+}
+
+void IntrinsicEmitter::EmitIITInfo(raw_ostream &OS) {
+ OS << "#ifdef GET_INTRINSIC_IITINFO\n";
+ std::array<StringRef, 256> RecsByNumber;
+ auto IIT_Base = Records.getAllDerivedDefinitionsIfDefined("IIT_Base");
+ for (const Record *Rec : IIT_Base) {
+ auto Number = Rec->getValueAsInt("Number");
+ assert(0 <= Number && Number < (int)RecsByNumber.size() &&
+ "IIT_Info.Number should be uint8_t");
+ assert(RecsByNumber[Number].empty() && "Duplicate IIT_Info.Number");
+ RecsByNumber[Number] = Rec->getName();
+ }
+ if (IIT_Base.size() > 0) {
+ for (unsigned I = 0, E = RecsByNumber.size(); I < E; ++I)
+ if (!RecsByNumber[I].empty())
+ OS << " " << RecsByNumber[I] << " = " << I << ",\n";
+ } else {
+ OS << "#error \"class IIT_Base is not defined\"\n";
+ }
+ OS << "#endif\n\n";
+}
+
+void IntrinsicEmitter::EmitTargetInfo(const CodeGenIntrinsicTable &Ints,
+ raw_ostream &OS) {
+ OS << R"(// Target mapping.
+#ifdef GET_INTRINSIC_TARGET_DATA
+struct IntrinsicTargetInfo {
+ StringLiteral Name;
+ size_t Offset;
+ size_t Count;
+};
+static constexpr IntrinsicTargetInfo TargetInfos[] = {
+)";
+ for (const auto [Name, Offset, Count] : Ints.getTargets())
+ OS << formatv(" {{\"{}\", {}, {}},\n", Name, Offset, Count);
+ OS << R"(};
+#endif
+
+)";
+}
+
+void IntrinsicEmitter::EmitIntrinsicToNameTable(
+ const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
+ // Built up a table of the intrinsic names.
+ constexpr StringLiteral NotIntrinsic = "not_intrinsic";
+ StringToOffsetTable Table;
+ Table.GetOrAddStringOffset(NotIntrinsic);
+ for (const auto &Int : Ints)
+ Table.GetOrAddStringOffset(Int.Name);
+
+ OS << R"(// Intrinsic ID to name table.
+#ifdef GET_INTRINSIC_NAME_TABLE
+// Note that entry #0 is the invalid intrinsic!
+
+)";
+
+ Table.EmitStringLiteralDef(OS, "static constexpr char IntrinsicNameTable[]",
+ /*Indent=*/"");
+
+ OS << R"(
+static constexpr unsigned IntrinsicNameOffsetTable[] = {
+)";
+
+ OS << formatv(" {}, // {}\n", Table.GetStringOffset(NotIntrinsic),
+ NotIntrinsic);
+ for (const auto &Int : Ints)
+ OS << formatv(" {}, // {}\n", Table.GetStringOffset(Int.Name), Int.Name);
+
+ OS << R"(
+}; // IntrinsicNameOffsetTable
+
+#endif
+
+)";
+}
+
+void IntrinsicEmitter::EmitIntrinsicToOverloadTable(
+ const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {
+ OS << R"(// Intrinsic ID to overload bitset.
+#ifdef GET_INTRINSIC_OVERLOAD_TABLE
+static constexpr uint8_t OTable[] = {
+ 0
+ )";
+ for (auto [I, Int] : enumerate(Ints)) {
+ // Add one to the index so we emit a null bit for the invalid #0 intrinsic.
+ size_t Idx = I + 1;
+
+ if (Idx % 8 == 0)
+ OS << ",\n 0";
+ if (Int.isOverloaded)
+ OS << " | (1<<" << Idx % 8 << ')';
+ }
+ OS << "\n};\n\n";
+ // OTable contains a true bit at the position if the intrinsic is overloaded.
+ OS << "return (OTable[id/8] & (1 << (id%8))) != 0;\n";
+ OS << "#endif\n\n";
+}
+
+using TypeSigTy = SmallVector<unsigned char>;
+
+/// Computes type signature of the intrinsic \p Int.
+static TypeSigTy ComputeTypeSignature(const CodeGenIntrinsic &Int) {
+ TypeSigTy TypeSig;
+ const Record *TypeInfo = Int.TheDef->getValueAsDef("TypeInfo");
+ const ListInit *TypeList = TypeInfo->getValueAsListInit("TypeSig");
+
+ for (const auto *TypeListEntry : TypeList->getValues())
+ TypeSig.emplace_back(cast<IntInit>(TypeListEntry)->getValue());
+ return TypeSig;
+}
+
+// Pack the type signature into 32-bit fixed encoding word.
+static std::optional<uint32_t> encodePacked(const TypeSigTy &TypeSig) {
+ if (TypeSig.size() > 8)
+ return std::nullopt;
+
+ uint32_t Result = 0;
+ for (unsigned char C : reverse(TypeSig)) {
+ if (C > 15)
+ return std::nullopt;
+ Result = (Result << 4) | C;
+ }
+ return Result;
+}
+
+void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints,
+ raw_ostream &OS) {
+ // Note: the code below can be switched to use 32-bit fixed encoding by
+ // flipping the flag below.
+ constexpr bool Use16BitFixedEncoding = true;
+ using FixedEncodingTy =
+ std::conditional_t<Use16BitFixedEncoding, uint16_t, uint32_t>;
+ constexpr unsigned FixedEncodingBits = sizeof(FixedEncodingTy) * CHAR_BIT;
+ // Mask with all bits 1 except the most significant bit.
+ const unsigned Mask = (1U << (FixedEncodingBits - 1)) - 1;
+ const unsigned MSBPostion = FixedEncodingBits - 1;
+ StringRef FixedEncodingTypeName =
+ Use16BitFixedEncoding ? "uint16_t" : "uint32_t";
+
+ // If we can compute a 16/32-bit fixed encoding for this intrinsic, do so and
+ // capture it in this vector, otherwise store a ~0U.
+ std::vector<FixedEncodingTy> FixedEncodings;
+ SequenceToOffsetTable<TypeSigTy> LongEncodingTable;
+
+ FixedEncodings.reserve(Ints.size());
+
+ // Compute the unique argument type info.
+ for (const CodeGenIntrinsic &Int : Ints) {
+ // Get the signature for the intrinsic.
+ TypeSigTy TypeSig = ComputeTypeSignature(Int);
+
+ // Check to see if we can encode it into a 16/32 bit word.
+ std::optional<uint32_t> Result = encodePacked(TypeSig);
+ if (Result && (*Result & Mask) == Result) {
+ FixedEncodings.push_back(static_cast<FixedEncodingTy>(*Result));
+ continue;
+ }
+
+ LongEncodingTable.add(TypeSig);
+
+ // This is a placehold that we'll replace after the table is laid out.
+ FixedEncodings.push_back(static_cast<FixedEncodingTy>(~0U));
+ }
+
+ LongEncodingTable.layout();
+
+ OS << formatv(R"(// Global intrinsic function declaration type table.
+#ifdef GET_INTRINSIC_GENERATOR_GLOBAL
+static constexpr {} IIT_Table[] = {{
+ )",
+ FixedEncodingTypeName);
+
+ unsigned MaxOffset = 0;
+ for (auto [Idx, FixedEncoding, Int] : enumerate(FixedEncodings, Ints)) {
+ if ((Idx & 7) == 7)
+ OS << "\n ";
+
+ // If the entry fit in the table, just emit it.
+ if ((FixedEncoding & Mask) == FixedEncoding) {
+ OS << "0x" << Twine::utohexstr(FixedEncoding) << ", ";
+ continue;
+ }
+
+ TypeSigTy TypeSig = ComputeTypeSignature(Int);
+ unsigned Offset = LongEncodingTable.get(TypeSig);
+ MaxOffset = std::max(MaxOffset, Offset);
+
+ // Otherwise, emit the offset into the long encoding table. We emit it this
+ // way so that it is easier to read the offset in the .def file.
+ OS << formatv("(1U<<{}) | {}, ", MSBPostion, Offset);
+ }
+
+ OS << "0\n};\n\n";
+
+ // verify that all offsets will fit in 16/32 bits.
+ if ((MaxOffset & Mask) != MaxOffset)
+ PrintFatalError("Offset of long encoding table exceeds encoding bits");
+
+ // Emit the shared table of register lists.
+ OS << "static constexpr unsigned char IIT_LongEncodingTable[] = {\n";
+ if (!LongEncodingTable.empty())
+ LongEncodingTable.emit(
+ OS, [](raw_ostream &OS, unsigned char C) { OS << (unsigned)C; });
+ OS << " 255\n};\n";
+ OS << "#endif\n\n"; // End of GET_INTRINSIC_GENERATOR_GLOBAL
+}
+
+/// Returns the effective MemoryEffects for intrinsic \p Int.
+static MemoryEffects getEffectiveME(const CodeGenIntrinsic &Int) {
+ MemoryEffects ME = Int.ME;
+ // TODO: IntrHasSideEffects should affect not only readnone intrinsics.
+ if (ME.doesNotAccessMemory() && Int.hasSideEffects)
+ ME = MemoryEffects::unknown();
+ return ME;
+}
+
+static bool compareFnAttributes(const CodeGenIntrinsic *L,
+ const CodeGenIntrinsic *R) {
+ auto TieBoolAttributes = [](const CodeGenIntrinsic *I) -> auto {
+ // Sort throwing intrinsics after non-throwing intrinsics.
+ return std::tie(I->canThrow, I->isNoDuplicate, I->isNoMerge, I->isNoReturn,
+ I->isNoCallback, I->isNoSync, I->isNoFree, I->isWillReturn,
+ I->isCold, I->isConvergent, I->isSpeculatable,
+ I->hasSideEffects, I->isStrictFP);
+ };
+
+ auto TieL = TieBoolAttributes(L);
+ auto TieR = TieBoolAttributes(R);
+
+ if (TieL != TieR)
+ return TieL < TieR;
+
+ // Try to order by readonly/readnone attribute.
+ uint32_t LME = getEffectiveME(*L).toIntValue();
+ uint32_t RME = getEffectiveME(*R).toIntValue();
+ if (LME != RME)
+ return LME > RME;
+
+ return false;
+}
+
+/// Returns true if \p Int has a non-empty set of function attributes. Note that
+/// NoUnwind = !canThrow, so we need to negate it's sense to test if the
+// intrinsic has NoUnwind attribute.
+static bool hasFnAttributes(const CodeGenIntrinsic &Int) {
+ return !Int.canThrow || Int.isNoReturn || Int.isNoCallback || Int.isNoSync ||
+ Int.isNoFree || Int.isWillReturn || Int.isCold || Int.isNoDuplicate ||
+ Int.isNoMerge || Int.isConvergent || Int.isSpeculatable ||
+ Int.isStrictFP || getEffectiveME(Int) != MemoryEffects::unknown();
+}
+
+namespace {
+struct FnAttributeComparator {
+ bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {
+ return compareFnAttributes(L, R);
+ }
+};
+
+struct AttributeComparator {
+ bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {
+ // Order all intrinsics with no functiona attributes before all intrinsics
+ // with function attributes.
+ bool HasFnAttrLHS = hasFnAttributes(*L);
+ bool HasFnAttrRHS = hasFnAttributes(*R);
+
+ // Order by argument attributes if function `hasFnAttributes` is equal.
+ // This is reliable because each side is already sorted internally.
+ return std::tie(HasFnAttrLHS, L->ArgumentAttributes) <
+ std::tie(HasFnAttrRHS, R->ArgumentAttributes);
+ }
+};
+} // End anonymous namespace
+
+/// Returns the name of the IR enum for argument attribute kind \p Kind.
+static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) {
+ switch (Kind) {
+ case CodeGenIntrinsic::NoCapture:
+ return "NoCapture";
+ case CodeGenIntrinsic::NoAlias:
+ return "NoAlias";
+ case CodeGenIntrinsic::NoUndef:
+ return "NoUndef";
+ case CodeGenIntrinsic::NonNull:
+ return "NonNull";
+ case CodeGenIntrinsic::Returned:
+ return "Returned";
+ case CodeGenIntrinsic::ReadOnly:
+ return "ReadOnly";
+ case CodeGenIntrinsic::WriteOnly:
+ return "WriteOnly";
+ case CodeGenIntrinsic::ReadNone:
+ return "ReadNone";
+ case CodeGenIntrinsic::ImmArg:
+ return "ImmArg";
+ case CodeGenIntrinsic::Alignment:
+ return "Alignment";
+ case CodeGenIntrinsic::Dereferenceable:
+ return "Dereferenceable";
+ }
+ llvm_unreachable("Unknown CodeGenIntrinsic::ArgAttrKind enum");
+}
+
+/// EmitAttributes - This emits the Intrinsic::getAttributes method.
+void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints,
+ raw_ostream &OS) {
+ OS << R"(// Add parameter attributes that are not common to all intrinsics.
+#ifdef GET_INTRINSIC_ATTRIBUTES
+static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID) {
+ switch (ID) {
+ default: llvm_unreachable("Invalid attribute set number");)";
+ // Compute unique argument attribute sets.
+ std::map<SmallVector<CodeGenIntrinsic::ArgAttribute, 0>, unsigned>
+ UniqArgAttributes;
+ for (const CodeGenIntrinsic &Int : Ints) {
+ for (auto &Attrs : Int.ArgumentAttributes) {
+ if (Attrs.empty())
+ continue;
+
+ unsigned ID = UniqArgAttributes.size();
+ if (!UniqArgAttributes.try_emplace(Attrs, ID).second)
+ continue;
+
+ assert(is_sorted(Attrs) && "Argument attributes are not sorted");
+
+ OS << formatv(R"(
+ case {}:
+ return AttributeSet::get(C, {{
+)",
+ ID);
+ for (const CodeGenIntrinsic::ArgAttribute &Attr : Attrs) {
+ StringRef AttrName = getArgAttrEnumName(Attr.Kind);
+ if (Attr.Kind == CodeGenIntrinsic::Alignment ||
+ Attr.Kind == CodeGenIntrinsic::Dereferenceable)
+ OS << formatv(" Attribute::get(C, Attribute::{}, {}),\n",
+ AttrName, Attr.Value);
+ else
+ OS << formatv(" Attribute::get(C, Attribute::{}),\n", AttrName);
+ }
+ OS << " });";
+ }
+ }
+ OS << R"(
+ }
+} // getIntrinsicArgAttributeSet
+)";
+
+ // Compute unique function attribute sets.
+ std::map<const CodeGenIntrinsic *, unsigned, FnAttributeComparator>
+ UniqFnAttributes;
+ OS << R"(
+static AttributeSet getIntrinsicFnAttributeSet(LLVMContext &C, unsigned ID) {
+ switch (ID) {
+ default: llvm_unreachable("Invalid attribute set number");)";
+
+ for (const CodeGenIntrinsic &Int : Ints) {
+ if (!hasFnAttributes(Int))
+ continue;
+ unsigned ID = UniqFnAttributes.size();
+ if (!UniqFnAttributes.try_emplace(&Int, ID).second)
+ continue;
+ OS << formatv(R"(
+ case {}:
+ return AttributeSet::get(C, {{
+)",
+ ID);
+ auto addAttribute = [&OS](StringRef Attr) {
+ OS << formatv(" Attribute::get(C, Attribute::{}),\n", Attr);
+ };
+ if (!Int.canThrow)
+ addAttribute("NoUnwind");
+ if (Int.isNoReturn)
+ addAttribute("NoReturn");
+ if (Int.isNoCallback)
+ addAttribute("NoCallback");
+ if (Int.isNoSync)
+ addAttribute("NoSync");
+ if (Int.isNoFree)
+ addAttribute("NoFree");
+ if (Int.isWillReturn)
+ addAttribute("WillReturn");
+ if (Int.isCold)
+ addAttribute("Cold");
+ if (Int.isNoDuplicate)
+ addAttribute("NoDuplicate");
+ if (Int.isNoMerge)
+ addAttribute("NoMerge");
+ if (Int.isConvergent)
+ addAttribute("Convergent");
+ if (Int.isSpeculatable)
+ addAttribute("Speculatable");
+ if (Int.isStrictFP)
+ addAttribute("StrictFP");
+
+ const MemoryEffects ME = getEffectiveME(Int);
+ if (ME != MemoryEffects::unknown()) {
+ OS << formatv(" // {}\n", ME);
+ OS << formatv(" Attribute::getWithMemoryEffects(C, "
+ "MemoryEffects::createFromIntValue({})),\n",
+ ME.toIntValue());
+ }
+ OS << " });";
+ }
+ OS << R"(
+ }
+} // getIntrinsicFnAttributeSet
+
+AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id) {
+)";
+
+ // Compute the maximum number of attribute arguments and the map. For function
+ // attributes, we only consider whether the intrinsics has any function
+ // arguments or not.
+ std::map<const CodeGenIntrinsic *, unsigned, AttributeComparator>
+ UniqAttributes;
+ for (const CodeGenIntrinsic &Int : Ints) {
+ unsigned ID = UniqAttributes.size();
+ UniqAttributes.try_emplace(&Int, ID);
+ }
+
+ // Assign a 16-bit packed ID for each intrinsic. The lower 8-bits will be its
+ // "argument attribute ID" (index in UniqAttributes) and upper 8 bits will be
+ // its "function attribute ID" (index in UniqFnAttributes).
+ if (UniqAttributes.size() > 256)
+ PrintFatalError("Too many unique argument attributes for table!");
+ if (UniqFnAttributes.size() > 256)
+ PrintFatalError("Too many unique function attributes for table!");
+
+ // Emit an array of AttributeList. Most intrinsics will have at least one
+ // entry, for the function itself (index ~1), which is usually nounwind.
+ OS << " static constexpr uint16_t IntrinsicsToAttributesMap[] = {";
+ for (const CodeGenIntrinsic &Int : Ints) {
+ uint16_t FnAttrIndex = hasFnAttributes(Int) ? UniqFnAttributes[&Int] : 0;
+ OS << formatv("\n {} << 8 | {}, // {}", FnAttrIndex,
+ UniqAttributes[&Int], Int.Name);
+ }
+
+ OS << formatv(R"(
+ };
+ if (id == 0)
+ return AttributeList();
+
+ uint16_t PackedID = IntrinsicsToAttributesMap[id - 1];
+ uint8_t FnAttrID = PackedID >> 8;
+ switch(PackedID & 0xFF) {{
+ default: llvm_unreachable("Invalid attribute number");
+)");
+
+ for (const auto [IntPtr, UniqueID] : UniqAttributes) {
+ OS << formatv(" case {}:\n", UniqueID);
+ const CodeGenIntrinsic &Int = *IntPtr;
+
+ // Keep track of the number of attributes we're writing out.
+ unsigned NumAttrs =
+ llvm::count_if(Int.ArgumentAttributes,
+ [](const auto &Attrs) { return !Attrs.empty(); });
+ NumAttrs += hasFnAttributes(Int);
+ if (NumAttrs == 0) {
+ OS << " return AttributeList();\n";
+ continue;
+ }
+
+ OS << " return AttributeList::get(C, {\n";
+ ListSeparator LS(",\n");
+ for (const auto &[AttrIdx, Attrs] : enumerate(Int.ArgumentAttributes)) {
+ if (Attrs.empty())
+ continue;
+
+ unsigned ArgAttrID = UniqArgAttributes.find(Attrs)->second;
+ OS << LS
+ << formatv(" {{{}, getIntrinsicArgAttributeSet(C, {})}", AttrIdx,
+ ArgAttrID);
+ }
+
+ if (hasFnAttributes(Int)) {
+ OS << LS
+ << " {AttributeList::FunctionIndex, "
+ "getIntrinsicFnAttributeSet(C, FnAttrID)}";
+ }
+ OS << "\n });\n";
+ }
+
+ OS << R"( }
+}
+#endif // GET_INTRINSIC_ATTRIBUTES
+
+)";
+}
+
+void IntrinsicEmitter::EmitIntrinsicToBuiltinMap(
+ const CodeGenIntrinsicTable &Ints, bool IsClang, raw_ostream &OS) {
+ StringRef CompilerName = IsClang ? "Clang" : "MS";
+ StringRef UpperCompilerName = IsClang ? "CLANG" : "MS";
+
+ // map<TargetPrefix, pair<map<BuiltinName, EnumName>, CommonPrefix>.
+ // Note that we iterate over both the maps in the code below and both
+ // iterations need to iterate in sorted key order. For the inner map, entries
+ // need to be emitted in the sorted order of `BuiltinName` with `CommonPrefix`
+ // rempved, because we use std::lower_bound to search these entries. For the
+ // outer map as well, entries need to be emitted in sorter order of
+ // `TargetPrefix` as we use std::lower_bound to search these entries.
+ using BIMEntryTy =
+ std::pair<std::map<StringRef, StringRef>, std::optional<StringRef>>;
+ std::map<StringRef, BIMEntryTy> BuiltinMap;
+
+ for (const CodeGenIntrinsic &Int : Ints) {
+ StringRef BuiltinName = IsClang ? Int.ClangBuiltinName : Int.MSBuiltinName;
+ if (BuiltinName.empty())
+ continue;
+ // Get the map for this target prefix.
+ auto &[Map, CommonPrefix] = BuiltinMap[Int.TargetPrefix];
+
+ if (!Map.insert({BuiltinName, Int.EnumName}).second)
+ PrintFatalError(Int.TheDef->getLoc(),
+ "Intrinsic '" + Int.TheDef->getName() + "': duplicate " +
+ CompilerName + " builtin name!");
+
+ // Update common prefix.
+ if (!CommonPrefix) {
+ // For the first builtin for this target, initialize the common prefix.
+ CommonPrefix = BuiltinName;
+ continue;
+ }
+
+ // Update the common prefix. Note that this assumes that `take_front` will
+ // never set the `Data` pointer in CommonPrefix to nullptr.
+ const char *Mismatch = mismatch(*CommonPrefix, BuiltinName).first;
+ *CommonPrefix = CommonPrefix->take_front(Mismatch - CommonPrefix->begin());
+ }
+
+ // Populate the string table with the names of all the builtins after
+ // removing this common prefix.
+ StringToOffsetTable Table;
+ for (const auto &[TargetPrefix, Entry] : BuiltinMap) {
+ auto &[Map, CommonPrefix] = Entry;
+ for (auto &[BuiltinName, EnumName] : Map) {
+ StringRef Suffix = BuiltinName.substr(CommonPrefix->size());
+ Table.GetOrAddStringOffset(Suffix);
+ }
+ }
+
+ OS << formatv(R"(
+// Get the LLVM intrinsic that corresponds to a builtin. This is used by the
+// C front-end. The builtin name is passed in as BuiltinName, and a target
+// prefix (e.g. 'ppc') is passed in as TargetPrefix.
+#ifdef GET_LLVM_INTRINSIC_FOR_{}_BUILTIN
+Intrinsic::ID
+Intrinsic::getIntrinsicFor{}Builtin(StringRef TargetPrefix,
+ StringRef BuiltinName) {{
+ using namespace Intrinsic;
+)",
+ UpperCompilerName, CompilerName);
+
+ if (BuiltinMap.empty()) {
+ OS << formatv(R"(
+ return not_intrinsic;
+ }
+#endif // GET_LLVM_INTRINSIC_FOR_{}_BUILTIN
+)",
+ UpperCompilerName);
+ return;
+ }
+
+ if (!Table.empty()) {
+ Table.EmitStringLiteralDef(OS, "static constexpr char BuiltinNames[]");
+
+ OS << R"(
+ struct BuiltinEntry {
+ ID IntrinsicID;
+ unsigned StrTabOffset;
+ const char *getName() const { return &BuiltinNames[StrTabOffset]; }
+ bool operator<(StringRef RHS) const {
+ return strncmp(getName(), RHS.data(), RHS.size()) < 0;
+ }
+ };
+
+)";
+ }
+
+ // Emit a per target table of bultin names.
+ bool HasTargetIndependentBuiltins = false;
+ StringRef TargetIndepndentCommonPrefix;
+ for (const auto &[TargetPrefix, Entry] : BuiltinMap) {
+ const auto &[Map, CommonPrefix] = Entry;
+ if (!TargetPrefix.empty()) {
+ OS << formatv(" // Builtins for {0}.\n", TargetPrefix);
+ } else {
+ OS << " // Target independent builtins.\n";
+ HasTargetIndependentBuiltins = true;
+ TargetIndepndentCommonPrefix = *CommonPrefix;
+ }
+
+ // Emit the builtin table for this target prefix.
+ OS << formatv(" static constexpr BuiltinEntry {}Names[] = {{\n",
+ TargetPrefix);
+ for (const auto &[BuiltinName, EnumName] : Map) {
+ StringRef Suffix = BuiltinName.substr(CommonPrefix->size());
+ OS << formatv(" {{{}, {}}, // {}\n", EnumName,
+ *Table.GetStringOffset(Suffix), BuiltinName);
+ }
+ OS << formatv(" }; // {}Names\n\n", TargetPrefix);
+ }
+
+ // After emitting the builtin tables for all targets, emit a lookup table for
+ // all targets. We will use binary search, similar to the table for builtin
+ // names to lookup into this table.
+ OS << R"(
+ struct TargetEntry {
+ StringLiteral TargetPrefix;
+ ArrayRef<BuiltinEntry> Names;
+ StringLiteral CommonPrefix;
+ bool operator<(StringRef RHS) const {
+ return TargetPrefix < RHS;
+ };
+ };
+ static constexpr TargetEntry TargetTable[] = {
+)";
+
+ for (const auto &[TargetPrefix, Entry] : BuiltinMap) {
+ const auto &[Map, CommonPrefix] = Entry;
+ if (TargetPrefix.empty())
+ continue;
+ OS << formatv(R"( {{"{0}", {0}Names, "{1}"},)", TargetPrefix,
+ CommonPrefix)
+ << "\n";
+ }
+ OS << " };\n";
+
+ // Now for the actual lookup, first check the target independent table if
+ // we emitted one.
+ if (HasTargetIndependentBuiltins) {
+ OS << formatv(R"(
+ // Check if it's a target independent builtin.
+ // Copy the builtin name so we can use it in consume_front without clobbering
+ // if for the lookup in the target specific table.
+ StringRef Suffix = BuiltinName;
+ if (Suffix.consume_front("{}")) {{
+ auto II = lower_bound(Names, Suffix);
+ if (II != std::end(Names) && II->getName() == Suffix)
+ return II->IntrinsicID;
+ }
+)",
+ TargetIndepndentCommonPrefix);
+ }
+
+ // If a target independent builtin was not found, lookup the target specific.
+ OS << formatv(R"(
+ auto TI = lower_bound(TargetTable, TargetPrefix);
+ if (TI == std::end(TargetTable) || TI->TargetPrefix != TargetPrefix)
+ return not_intrinsic;
+ // This is the last use of BuiltinName, so no need to copy before using it in
+ // consume_front.
+ if (!BuiltinName.consume_front(TI->CommonPrefix))
+ return not_intrinsic;
+ auto II = lower_bound(TI->Names, BuiltinName);
+ if (II == std::end(TI->Names) || II->getName() != BuiltinName)
+ return not_intrinsic;
+ return II->IntrinsicID;
+}
+#endif // GET_LLVM_INTRINSIC_FOR_{}_BUILTIN
+
+)",
+ UpperCompilerName);
+}
+
+static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/true>>
+ X("gen-intrinsic-enums", "Generate intrinsic enums");
+
+static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/false>>
+ Y("gen-intrinsic-impl", "Generate intrinsic implementation code");