Path: blob/main/contrib/llvm-project/llvm/utils/TableGen/Basic/IntrinsicEmitter.cpp
213799 views
//===- IntrinsicEmitter.cpp - Generate intrinsic information --------------===//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//===----------------------------------------------------------------------===//7//8// This tablegen backend emits information about intrinsic functions.9//10//===----------------------------------------------------------------------===//1112#include "CodeGenIntrinsics.h"13#include "SequenceToOffsetTable.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/ADT/SmallVector.h"16#include "llvm/ADT/StringRef.h"17#include "llvm/ADT/Twine.h"18#include "llvm/Support/CommandLine.h"19#include "llvm/Support/ErrorHandling.h"20#include "llvm/Support/FormatVariadic.h"21#include "llvm/Support/ModRef.h"22#include "llvm/Support/raw_ostream.h"23#include "llvm/TableGen/Error.h"24#include "llvm/TableGen/Record.h"25#include "llvm/TableGen/StringToOffsetTable.h"26#include "llvm/TableGen/TableGenBackend.h"27#include <algorithm>28#include <array>29#include <cassert>30#include <cctype>31#include <map>32#include <optional>33#include <string>34#include <utility>35#include <vector>36using namespace llvm;3738static cl::OptionCategory GenIntrinsicCat("Options for -gen-intrinsic-enums");39static cl::opt<std::string>40IntrinsicPrefix("intrinsic-prefix",41cl::desc("Generate intrinsics with this target prefix"),42cl::value_desc("target prefix"), cl::cat(GenIntrinsicCat));4344namespace {45class IntrinsicEmitter {46const RecordKeeper &Records;4748public:49IntrinsicEmitter(const RecordKeeper &R) : Records(R) {}5051void run(raw_ostream &OS, bool Enums);5253void EmitEnumInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);54void EmitArgKind(raw_ostream &OS);55void EmitIITInfo(raw_ostream &OS);56void EmitTargetInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);57void EmitIntrinsicToNameTable(const CodeGenIntrinsicTable &Ints,58raw_ostream &OS);59void EmitIntrinsicToOverloadTable(const CodeGenIntrinsicTable &Ints,60raw_ostream &OS);61void EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);62void EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS);63void EmitIntrinsicToBuiltinMap(const CodeGenIntrinsicTable &Ints,64bool IsClang, raw_ostream &OS);65};6667// Helper class to use with `TableGen::Emitter::OptClass`.68template <bool Enums> class IntrinsicEmitterOpt : public IntrinsicEmitter {69public:70IntrinsicEmitterOpt(const RecordKeeper &R) : IntrinsicEmitter(R) {}71void run(raw_ostream &OS) { IntrinsicEmitter::run(OS, Enums); }72};7374} // End anonymous namespace7576//===----------------------------------------------------------------------===//77// IntrinsicEmitter Implementation78//===----------------------------------------------------------------------===//7980void IntrinsicEmitter::run(raw_ostream &OS, bool Enums) {81emitSourceFileHeader("Intrinsic Function Source Fragment", OS);8283CodeGenIntrinsicTable Ints(Records);8485if (Enums) {86// Emit the enum information.87EmitEnumInfo(Ints, OS);8889// Emit ArgKind for Intrinsics.h.90EmitArgKind(OS);91} else {92// Emit IIT_Info constants.93EmitIITInfo(OS);9495// Emit the target metadata.96EmitTargetInfo(Ints, OS);9798// Emit the intrinsic ID -> name table.99EmitIntrinsicToNameTable(Ints, OS);100101// Emit the intrinsic ID -> overload table.102EmitIntrinsicToOverloadTable(Ints, OS);103104// Emit the intrinsic declaration generator.105EmitGenerator(Ints, OS);106107// Emit the intrinsic parameter attributes.108EmitAttributes(Ints, OS);109110// Emit code to translate Clang builtins into LLVM intrinsics.111EmitIntrinsicToBuiltinMap(Ints, true, OS);112113// Emit code to translate MS builtins into LLVM intrinsics.114EmitIntrinsicToBuiltinMap(Ints, false, OS);115}116}117118void IntrinsicEmitter::EmitEnumInfo(const CodeGenIntrinsicTable &Ints,119raw_ostream &OS) {120// Find the TargetSet for which to generate enums. There will be an initial121// set with an empty target prefix which will include target independent122// intrinsics like dbg.value.123using TargetSet = CodeGenIntrinsicTable::TargetSet;124const TargetSet *Set = nullptr;125for (const auto &Target : Ints.getTargets()) {126if (Target.Name == IntrinsicPrefix) {127Set = &Target;128break;129}130}131if (!Set) {132// The first entry is for target independent intrinsics, so drop it.133auto KnowTargets = Ints.getTargets().drop_front();134PrintFatalError([KnowTargets](raw_ostream &OS) {135OS << "tried to generate intrinsics for unknown target "136<< IntrinsicPrefix << "\nKnown targets are: ";137interleaveComma(KnowTargets, OS,138[&OS](const TargetSet &Target) { OS << Target.Name; });139OS << '\n';140});141}142143// Generate a complete header for target specific intrinsics.144if (IntrinsicPrefix.empty()) {145OS << "#ifdef GET_INTRINSIC_ENUM_VALUES\n";146} else {147std::string UpperPrefix = StringRef(IntrinsicPrefix).upper();148OS << formatv("#ifndef LLVM_IR_INTRINSIC_{}_ENUMS_H\n", UpperPrefix);149OS << formatv("#define LLVM_IR_INTRINSIC_{}_ENUMS_H\n", UpperPrefix);150OS << "namespace llvm::Intrinsic {\n";151OS << formatv("enum {}Intrinsics : unsigned {{\n", UpperPrefix);152}153154OS << "// Enum values for intrinsics.\n";155bool First = true;156for (const auto &Int : Ints[*Set]) {157OS << " " << Int.EnumName;158159// Assign a value to the first intrinsic in this target set so that all160// intrinsic ids are distinct.161if (First) {162OS << " = " << Set->Offset + 1;163First = false;164}165166OS << ", ";167if (Int.EnumName.size() < 40)168OS.indent(40 - Int.EnumName.size());169OS << formatv(" // {}\n", Int.Name);170}171172// Emit num_intrinsics into the target neutral enum.173if (IntrinsicPrefix.empty()) {174OS << formatv(" num_intrinsics = {}\n", Ints.size() + 1);175OS << "#endif\n\n";176} else {177OS << R"(}; // enum178} // namespace llvm::Intrinsic179#endif180181)";182}183}184185void IntrinsicEmitter::EmitArgKind(raw_ostream &OS) {186if (!IntrinsicPrefix.empty())187return;188OS << "// llvm::Intrinsic::IITDescriptor::ArgKind.\n";189OS << "#ifdef GET_INTRINSIC_ARGKIND\n";190if (const auto RecArgKind = Records.getDef("ArgKind")) {191for (const auto &RV : RecArgKind->getValues())192OS << " AK_" << RV.getName() << " = " << *RV.getValue() << ",\n";193} else {194OS << "#error \"ArgKind is not defined\"\n";195}196OS << "#endif\n\n";197}198199void IntrinsicEmitter::EmitIITInfo(raw_ostream &OS) {200OS << "#ifdef GET_INTRINSIC_IITINFO\n";201std::array<StringRef, 256> RecsByNumber;202auto IIT_Base = Records.getAllDerivedDefinitionsIfDefined("IIT_Base");203for (const Record *Rec : IIT_Base) {204auto Number = Rec->getValueAsInt("Number");205assert(0 <= Number && Number < (int)RecsByNumber.size() &&206"IIT_Info.Number should be uint8_t");207assert(RecsByNumber[Number].empty() && "Duplicate IIT_Info.Number");208RecsByNumber[Number] = Rec->getName();209}210if (IIT_Base.size() > 0) {211for (unsigned I = 0, E = RecsByNumber.size(); I < E; ++I)212if (!RecsByNumber[I].empty())213OS << " " << RecsByNumber[I] << " = " << I << ",\n";214} else {215OS << "#error \"class IIT_Base is not defined\"\n";216}217OS << "#endif\n\n";218}219220void IntrinsicEmitter::EmitTargetInfo(const CodeGenIntrinsicTable &Ints,221raw_ostream &OS) {222OS << R"(// Target mapping.223#ifdef GET_INTRINSIC_TARGET_DATA224struct IntrinsicTargetInfo {225StringLiteral Name;226size_t Offset;227size_t Count;228};229static constexpr IntrinsicTargetInfo TargetInfos[] = {230)";231for (const auto [Name, Offset, Count] : Ints.getTargets())232OS << formatv(" {{\"{}\", {}, {}},\n", Name, Offset, Count);233OS << R"(};234#endif235236)";237}238239void IntrinsicEmitter::EmitIntrinsicToNameTable(240const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {241// Built up a table of the intrinsic names.242constexpr StringLiteral NotIntrinsic = "not_intrinsic";243StringToOffsetTable Table;244Table.GetOrAddStringOffset(NotIntrinsic);245for (const auto &Int : Ints)246Table.GetOrAddStringOffset(Int.Name);247248OS << R"(// Intrinsic ID to name table.249#ifdef GET_INTRINSIC_NAME_TABLE250// Note that entry #0 is the invalid intrinsic!251252)";253254Table.EmitStringTableDef(OS, "IntrinsicNameTable");255256OS << R"(257static constexpr unsigned IntrinsicNameOffsetTable[] = {258)";259260OS << formatv(" {}, // {}\n", Table.GetStringOffset(NotIntrinsic),261NotIntrinsic);262for (const auto &Int : Ints)263OS << formatv(" {}, // {}\n", Table.GetStringOffset(Int.Name), Int.Name);264265OS << R"(266}; // IntrinsicNameOffsetTable267268#endif269270)";271}272273void IntrinsicEmitter::EmitIntrinsicToOverloadTable(274const CodeGenIntrinsicTable &Ints, raw_ostream &OS) {275OS << R"(// Intrinsic ID to overload bitset.276#ifdef GET_INTRINSIC_OVERLOAD_TABLE277static constexpr uint8_t OTable[] = {2780279)";280for (auto [I, Int] : enumerate(Ints)) {281// Add one to the index so we emit a null bit for the invalid #0 intrinsic.282size_t Idx = I + 1;283284if (Idx % 8 == 0)285OS << ",\n 0";286if (Int.isOverloaded)287OS << " | (1<<" << Idx % 8 << ')';288}289OS << "\n};\n\n";290// OTable contains a true bit at the position if the intrinsic is overloaded.291OS << "return (OTable[id/8] & (1 << (id%8))) != 0;\n";292OS << "#endif\n\n";293}294295using TypeSigTy = SmallVector<unsigned char>;296297/// Computes type signature of the intrinsic \p Int.298static TypeSigTy ComputeTypeSignature(const CodeGenIntrinsic &Int) {299TypeSigTy TypeSig;300const Record *TypeInfo = Int.TheDef->getValueAsDef("TypeInfo");301const ListInit *TypeList = TypeInfo->getValueAsListInit("TypeSig");302303for (const auto *TypeListEntry : TypeList->getElements())304TypeSig.emplace_back(cast<IntInit>(TypeListEntry)->getValue());305return TypeSig;306}307308// Pack the type signature into 32-bit fixed encoding word.309static std::optional<uint32_t> encodePacked(const TypeSigTy &TypeSig) {310if (TypeSig.size() > 8)311return std::nullopt;312313uint32_t Result = 0;314for (unsigned char C : reverse(TypeSig)) {315if (C > 15)316return std::nullopt;317Result = (Result << 4) | C;318}319return Result;320}321322void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints,323raw_ostream &OS) {324// Note: the code below can be switched to use 32-bit fixed encoding by325// flipping the flag below.326constexpr bool Use16BitFixedEncoding = true;327using FixedEncodingTy =328std::conditional_t<Use16BitFixedEncoding, uint16_t, uint32_t>;329constexpr unsigned FixedEncodingBits = sizeof(FixedEncodingTy) * CHAR_BIT;330// Mask with all bits 1 except the most significant bit.331const unsigned Mask = (1U << (FixedEncodingBits - 1)) - 1;332const unsigned MSBPostion = FixedEncodingBits - 1;333StringRef FixedEncodingTypeName =334Use16BitFixedEncoding ? "uint16_t" : "uint32_t";335336// If we can compute a 16/32-bit fixed encoding for this intrinsic, do so and337// capture it in this vector, otherwise store a ~0U.338std::vector<FixedEncodingTy> FixedEncodings;339SequenceToOffsetTable<TypeSigTy> LongEncodingTable;340341FixedEncodings.reserve(Ints.size());342343// Compute the unique argument type info.344for (const CodeGenIntrinsic &Int : Ints) {345// Get the signature for the intrinsic.346TypeSigTy TypeSig = ComputeTypeSignature(Int);347348// Check to see if we can encode it into a 16/32 bit word.349std::optional<uint32_t> Result = encodePacked(TypeSig);350if (Result && (*Result & Mask) == Result) {351FixedEncodings.push_back(static_cast<FixedEncodingTy>(*Result));352continue;353}354355LongEncodingTable.add(TypeSig);356357// This is a placehold that we'll replace after the table is laid out.358FixedEncodings.push_back(static_cast<FixedEncodingTy>(~0U));359}360361LongEncodingTable.layout();362363OS << formatv(R"(// Global intrinsic function declaration type table.364#ifdef GET_INTRINSIC_GENERATOR_GLOBAL365static constexpr {} IIT_Table[] = {{366)",367FixedEncodingTypeName);368369unsigned MaxOffset = 0;370for (auto [Idx, FixedEncoding, Int] : enumerate(FixedEncodings, Ints)) {371if ((Idx & 7) == 7)372OS << "\n ";373374// If the entry fit in the table, just emit it.375if ((FixedEncoding & Mask) == FixedEncoding) {376OS << "0x" << Twine::utohexstr(FixedEncoding) << ", ";377continue;378}379380TypeSigTy TypeSig = ComputeTypeSignature(Int);381unsigned Offset = LongEncodingTable.get(TypeSig);382MaxOffset = std::max(MaxOffset, Offset);383384// Otherwise, emit the offset into the long encoding table. We emit it this385// way so that it is easier to read the offset in the .def file.386OS << formatv("(1U<<{}) | {}, ", MSBPostion, Offset);387}388389OS << "0\n};\n\n";390391// verify that all offsets will fit in 16/32 bits.392if ((MaxOffset & Mask) != MaxOffset)393PrintFatalError("Offset of long encoding table exceeds encoding bits");394395// Emit the shared table of register lists.396OS << "static constexpr unsigned char IIT_LongEncodingTable[] = {\n";397if (!LongEncodingTable.empty())398LongEncodingTable.emit(399OS, [](raw_ostream &OS, unsigned char C) { OS << (unsigned)C; });400OS << " 255\n};\n";401OS << "#endif\n\n"; // End of GET_INTRINSIC_GENERATOR_GLOBAL402}403404/// Returns the effective MemoryEffects for intrinsic \p Int.405static MemoryEffects getEffectiveME(const CodeGenIntrinsic &Int) {406MemoryEffects ME = Int.ME;407// TODO: IntrHasSideEffects should affect not only readnone intrinsics.408if (ME.doesNotAccessMemory() && Int.hasSideEffects)409ME = MemoryEffects::unknown();410return ME;411}412413static bool compareFnAttributes(const CodeGenIntrinsic *L,414const CodeGenIntrinsic *R) {415auto TieBoolAttributes = [](const CodeGenIntrinsic *I) -> auto {416// Sort throwing intrinsics after non-throwing intrinsics.417return std::tie(I->canThrow, I->isNoDuplicate, I->isNoMerge, I->isNoReturn,418I->isNoCallback, I->isNoSync, I->isNoFree, I->isWillReturn,419I->isCold, I->isConvergent, I->isSpeculatable,420I->hasSideEffects, I->isStrictFP);421};422423auto TieL = TieBoolAttributes(L);424auto TieR = TieBoolAttributes(R);425426if (TieL != TieR)427return TieL < TieR;428429// Try to order by readonly/readnone attribute.430uint32_t LME = getEffectiveME(*L).toIntValue();431uint32_t RME = getEffectiveME(*R).toIntValue();432if (LME != RME)433return LME > RME;434435return false;436}437438/// Returns true if \p Int has a non-empty set of function attributes. Note that439/// NoUnwind = !canThrow, so we need to negate it's sense to test if the440// intrinsic has NoUnwind attribute.441static bool hasFnAttributes(const CodeGenIntrinsic &Int) {442return !Int.canThrow || Int.isNoReturn || Int.isNoCallback || Int.isNoSync ||443Int.isNoFree || Int.isWillReturn || Int.isCold || Int.isNoDuplicate ||444Int.isNoMerge || Int.isConvergent || Int.isSpeculatable ||445Int.isStrictFP || getEffectiveME(Int) != MemoryEffects::unknown();446}447448namespace {449struct FnAttributeComparator {450bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {451return compareFnAttributes(L, R);452}453};454455struct AttributeComparator {456bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const {457// Order all intrinsics with no functiona attributes before all intrinsics458// with function attributes.459bool HasFnAttrLHS = hasFnAttributes(*L);460bool HasFnAttrRHS = hasFnAttributes(*R);461462// Order by argument attributes if function `hasFnAttributes` is equal.463// This is reliable because each side is already sorted internally.464return std::tie(HasFnAttrLHS, L->ArgumentAttributes) <465std::tie(HasFnAttrRHS, R->ArgumentAttributes);466}467};468} // End anonymous namespace469470/// Returns the name of the IR enum for argument attribute kind \p Kind.471static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) {472switch (Kind) {473case CodeGenIntrinsic::NoCapture:474llvm_unreachable("Handled separately");475case CodeGenIntrinsic::NoAlias:476return "NoAlias";477case CodeGenIntrinsic::NoUndef:478return "NoUndef";479case CodeGenIntrinsic::NonNull:480return "NonNull";481case CodeGenIntrinsic::Returned:482return "Returned";483case CodeGenIntrinsic::ReadOnly:484return "ReadOnly";485case CodeGenIntrinsic::WriteOnly:486return "WriteOnly";487case CodeGenIntrinsic::ReadNone:488return "ReadNone";489case CodeGenIntrinsic::ImmArg:490return "ImmArg";491case CodeGenIntrinsic::Alignment:492return "Alignment";493case CodeGenIntrinsic::Dereferenceable:494return "Dereferenceable";495case CodeGenIntrinsic::Range:496return "Range";497}498llvm_unreachable("Unknown CodeGenIntrinsic::ArgAttrKind enum");499}500501/// EmitAttributes - This emits the Intrinsic::getAttributes method.502void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints,503raw_ostream &OS) {504OS << R"(// Add parameter attributes that are not common to all intrinsics.505#ifdef GET_INTRINSIC_ATTRIBUTES506static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID,507Type *ArgType) {508unsigned BitWidth = ArgType->getScalarSizeInBits();509switch (ID) {510default: llvm_unreachable("Invalid attribute set number");)";511// Compute unique argument attribute sets.512std::map<SmallVector<CodeGenIntrinsic::ArgAttribute, 0>, unsigned>513UniqArgAttributes;514for (const CodeGenIntrinsic &Int : Ints) {515for (auto &Attrs : Int.ArgumentAttributes) {516if (Attrs.empty())517continue;518519unsigned ID = UniqArgAttributes.size();520if (!UniqArgAttributes.try_emplace(Attrs, ID).second)521continue;522523assert(is_sorted(Attrs) && "Argument attributes are not sorted");524525OS << formatv(R"(526case {}:527return AttributeSet::get(C, {{528)",529ID);530for (const CodeGenIntrinsic::ArgAttribute &Attr : Attrs) {531if (Attr.Kind == CodeGenIntrinsic::NoCapture) {532OS << " Attribute::getWithCaptureInfo(C, "533"CaptureInfo::none()),\n";534continue;535}536StringRef AttrName = getArgAttrEnumName(Attr.Kind);537if (Attr.Kind == CodeGenIntrinsic::Alignment ||538Attr.Kind == CodeGenIntrinsic::Dereferenceable)539OS << formatv(" Attribute::get(C, Attribute::{}, {}),\n",540AttrName, Attr.Value);541else if (Attr.Kind == CodeGenIntrinsic::Range)542// This allows implicitTrunc because the range may only fit the543// type based on rules implemented in the IR verifier. E.g. the544// [-1, 1] range for ucmp/scmp intrinsics requires a minimum i2 type.545// Give the verifier a chance to diagnose this instead of asserting546// here.547OS << formatv(" Attribute::get(C, Attribute::{}, "548"ConstantRange(APInt(BitWidth, {}, /*isSigned=*/true, "549"/*implicitTrunc=*/true), APInt(BitWidth, {}, "550"/*isSigned=*/true, /*implicitTrunc=*/true))),\n",551AttrName, (int64_t)Attr.Value, (int64_t)Attr.Value2);552else553OS << formatv(" Attribute::get(C, Attribute::{}),\n", AttrName);554}555OS << " });";556}557}558OS << R"(559}560} // getIntrinsicArgAttributeSet561)";562563// Compute unique function attribute sets.564std::map<const CodeGenIntrinsic *, unsigned, FnAttributeComparator>565UniqFnAttributes;566OS << R"(567static AttributeSet getIntrinsicFnAttributeSet(LLVMContext &C, unsigned ID) {568switch (ID) {569default: llvm_unreachable("Invalid attribute set number");)";570571for (const CodeGenIntrinsic &Int : Ints) {572unsigned ID = UniqFnAttributes.size();573if (!UniqFnAttributes.try_emplace(&Int, ID).second)574continue;575OS << formatv(R"(576case {}:577return AttributeSet::get(C, {{578)",579ID);580auto addAttribute = [&OS](StringRef Attr) {581OS << formatv(" Attribute::get(C, Attribute::{}),\n", Attr);582};583if (!Int.canThrow)584addAttribute("NoUnwind");585if (Int.isNoReturn)586addAttribute("NoReturn");587if (Int.isNoCallback)588addAttribute("NoCallback");589if (Int.isNoSync)590addAttribute("NoSync");591if (Int.isNoFree)592addAttribute("NoFree");593if (Int.isWillReturn)594addAttribute("WillReturn");595if (Int.isCold)596addAttribute("Cold");597if (Int.isNoDuplicate)598addAttribute("NoDuplicate");599if (Int.isNoMerge)600addAttribute("NoMerge");601if (Int.isConvergent)602addAttribute("Convergent");603if (Int.isSpeculatable)604addAttribute("Speculatable");605if (Int.isStrictFP)606addAttribute("StrictFP");607608const MemoryEffects ME = getEffectiveME(Int);609if (ME != MemoryEffects::unknown()) {610OS << formatv(" // {}\n", ME);611OS << formatv(" Attribute::getWithMemoryEffects(C, "612"MemoryEffects::createFromIntValue({})),\n",613ME.toIntValue());614}615OS << " });";616}617OS << R"(618}619} // getIntrinsicFnAttributeSet620621static constexpr uint16_t IntrinsicsToAttributesMap[] = {)";622623// Compute the maximum number of attribute arguments and the map. For function624// attributes, we only consider whether the intrinsics has any function625// arguments or not.626std::map<const CodeGenIntrinsic *, unsigned, AttributeComparator>627UniqAttributes;628for (const CodeGenIntrinsic &Int : Ints) {629unsigned ID = UniqAttributes.size();630UniqAttributes.try_emplace(&Int, ID);631}632633// Emit an array of AttributeList. Most intrinsics will have at least one634// entry, for the function itself (index ~1), which is usually nounwind.635for (const CodeGenIntrinsic &Int : Ints) {636uint16_t FnAttrIndex = UniqFnAttributes[&Int];637OS << formatv("\n {} << 8 | {}, // {}", FnAttrIndex,638UniqAttributes[&Int], Int.Name);639}640641// Assign a 16-bit packed ID for each intrinsic. The lower 8-bits will be its642// "argument attribute ID" (index in UniqAttributes) and upper 8 bits will be643// its "function attribute ID" (index in UniqFnAttributes).644if (UniqAttributes.size() > 256)645PrintFatalError("Too many unique argument attributes for table!");646if (UniqFnAttributes.size() > 256)647PrintFatalError("Too many unique function attributes for table!");648649OS << R"(650};651652AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id,653FunctionType *FT) {)";654655OS << formatv(R"(656if (id == 0)657return AttributeList();658659uint16_t PackedID = IntrinsicsToAttributesMap[id - 1];660uint8_t FnAttrID = PackedID >> 8;661switch(PackedID & 0xFF) {{662default: llvm_unreachable("Invalid attribute number");663)");664665for (const auto [IntPtr, UniqueID] : UniqAttributes) {666OS << formatv(" case {}:\n", UniqueID);667const CodeGenIntrinsic &Int = *IntPtr;668669// Keep track of the number of attributes we're writing out.670unsigned NumAttrs =671llvm::count_if(Int.ArgumentAttributes,672[](const auto &Attrs) { return !Attrs.empty(); });673NumAttrs += hasFnAttributes(Int);674if (NumAttrs == 0) {675OS << " return AttributeList();\n";676continue;677}678679OS << " return AttributeList::get(C, {\n";680ListSeparator LS(",\n");681for (const auto &[AttrIdx, Attrs] : enumerate(Int.ArgumentAttributes)) {682if (Attrs.empty())683continue;684685unsigned ArgAttrID = UniqArgAttributes.find(Attrs)->second;686OS << LS687<< formatv(" {{{}, getIntrinsicArgAttributeSet(C, {}, "688"FT->getContainedType({}))}",689AttrIdx, ArgAttrID, AttrIdx);690}691692if (hasFnAttributes(Int)) {693OS << LS694<< " {AttributeList::FunctionIndex, "695"getIntrinsicFnAttributeSet(C, FnAttrID)}";696}697OS << "\n });\n";698}699700OS << R"( }701}702#endif // GET_INTRINSIC_ATTRIBUTES703704)";705}706707void IntrinsicEmitter::EmitIntrinsicToBuiltinMap(708const CodeGenIntrinsicTable &Ints, bool IsClang, raw_ostream &OS) {709StringRef CompilerName = IsClang ? "Clang" : "MS";710StringRef UpperCompilerName = IsClang ? "CLANG" : "MS";711712// map<TargetPrefix, pair<map<BuiltinName, EnumName>, CommonPrefix>.713// Note that we iterate over both the maps in the code below and both714// iterations need to iterate in sorted key order. For the inner map, entries715// need to be emitted in the sorted order of `BuiltinName` with `CommonPrefix`716// rempved, because we use std::lower_bound to search these entries. For the717// outer map as well, entries need to be emitted in sorter order of718// `TargetPrefix` as we use std::lower_bound to search these entries.719using BIMEntryTy =720std::pair<std::map<StringRef, StringRef>, std::optional<StringRef>>;721std::map<StringRef, BIMEntryTy> BuiltinMap;722723for (const CodeGenIntrinsic &Int : Ints) {724StringRef BuiltinName = IsClang ? Int.ClangBuiltinName : Int.MSBuiltinName;725if (BuiltinName.empty())726continue;727// Get the map for this target prefix.728auto &[Map, CommonPrefix] = BuiltinMap[Int.TargetPrefix];729730if (!Map.try_emplace(BuiltinName, Int.EnumName).second)731PrintFatalError(Int.TheDef->getLoc(),732"Intrinsic '" + Int.TheDef->getName() + "': duplicate " +733CompilerName + " builtin name!");734735// Update common prefix.736if (!CommonPrefix) {737// For the first builtin for this target, initialize the common prefix.738CommonPrefix = BuiltinName;739continue;740}741742// Update the common prefix. Note that this assumes that `take_front` will743// never set the `Data` pointer in CommonPrefix to nullptr.744const char *Mismatch = mismatch(*CommonPrefix, BuiltinName).first;745*CommonPrefix = CommonPrefix->take_front(Mismatch - CommonPrefix->begin());746}747748// Populate the string table with the names of all the builtins after749// removing this common prefix.750StringToOffsetTable Table;751for (const auto &[TargetPrefix, Entry] : BuiltinMap) {752auto &[Map, CommonPrefix] = Entry;753for (auto &[BuiltinName, EnumName] : Map) {754StringRef Suffix = BuiltinName.substr(CommonPrefix->size());755Table.GetOrAddStringOffset(Suffix);756}757}758759OS << formatv(R"(760// Get the LLVM intrinsic that corresponds to a builtin. This is used by the761// C front-end. The builtin name is passed in as BuiltinName, and a target762// prefix (e.g. 'ppc') is passed in as TargetPrefix.763#ifdef GET_LLVM_INTRINSIC_FOR_{}_BUILTIN764Intrinsic::ID765Intrinsic::getIntrinsicFor{}Builtin(StringRef TargetPrefix,766StringRef BuiltinName) {{767using namespace Intrinsic;768)",769UpperCompilerName, CompilerName);770771if (BuiltinMap.empty()) {772OS << formatv(R"(773return not_intrinsic;774}775#endif // GET_LLVM_INTRINSIC_FOR_{}_BUILTIN776)",777UpperCompilerName);778return;779}780781if (!Table.empty()) {782Table.EmitStringTableDef(OS, "BuiltinNames");783784OS << R"(785struct BuiltinEntry {786ID IntrinsicID;787unsigned StrTabOffset;788const char *getName() const { return BuiltinNames[StrTabOffset].data(); }789bool operator<(StringRef RHS) const {790return strncmp(getName(), RHS.data(), RHS.size()) < 0;791}792};793794)";795}796797// Emit a per target table of bultin names.798bool HasTargetIndependentBuiltins = false;799StringRef TargetIndepndentCommonPrefix;800for (const auto &[TargetPrefix, Entry] : BuiltinMap) {801const auto &[Map, CommonPrefix] = Entry;802if (!TargetPrefix.empty()) {803OS << formatv(" // Builtins for {0}.\n", TargetPrefix);804} else {805OS << " // Target independent builtins.\n";806HasTargetIndependentBuiltins = true;807TargetIndepndentCommonPrefix = *CommonPrefix;808}809810// Emit the builtin table for this target prefix.811OS << formatv(" static constexpr BuiltinEntry {}Names[] = {{\n",812TargetPrefix);813for (const auto &[BuiltinName, EnumName] : Map) {814StringRef Suffix = BuiltinName.substr(CommonPrefix->size());815OS << formatv(" {{{}, {}}, // {}\n", EnumName,816*Table.GetStringOffset(Suffix), BuiltinName);817}818OS << formatv(" }; // {}Names\n\n", TargetPrefix);819}820821// After emitting the builtin tables for all targets, emit a lookup table for822// all targets. We will use binary search, similar to the table for builtin823// names to lookup into this table.824OS << R"(825struct TargetEntry {826StringLiteral TargetPrefix;827ArrayRef<BuiltinEntry> Names;828StringLiteral CommonPrefix;829bool operator<(StringRef RHS) const {830return TargetPrefix < RHS;831};832};833static constexpr TargetEntry TargetTable[] = {834)";835836for (const auto &[TargetPrefix, Entry] : BuiltinMap) {837const auto &[Map, CommonPrefix] = Entry;838if (TargetPrefix.empty())839continue;840OS << formatv(R"( {{"{0}", {0}Names, "{1}"},)", TargetPrefix,841CommonPrefix)842<< "\n";843}844OS << " };\n";845846// Now for the actual lookup, first check the target independent table if847// we emitted one.848if (HasTargetIndependentBuiltins) {849OS << formatv(R"(850// Check if it's a target independent builtin.851// Copy the builtin name so we can use it in consume_front without clobbering852// if for the lookup in the target specific table.853StringRef Suffix = BuiltinName;854if (Suffix.consume_front("{}")) {{855auto II = lower_bound(Names, Suffix);856if (II != std::end(Names) && II->getName() == Suffix)857return II->IntrinsicID;858}859)",860TargetIndepndentCommonPrefix);861}862863// If a target independent builtin was not found, lookup the target specific.864OS << formatv(R"(865auto TI = lower_bound(TargetTable, TargetPrefix);866if (TI == std::end(TargetTable) || TI->TargetPrefix != TargetPrefix)867return not_intrinsic;868// This is the last use of BuiltinName, so no need to copy before using it in869// consume_front.870if (!BuiltinName.consume_front(TI->CommonPrefix))871return not_intrinsic;872auto II = lower_bound(TI->Names, BuiltinName);873if (II == std::end(TI->Names) || II->getName() != BuiltinName)874return not_intrinsic;875return II->IntrinsicID;876}877#endif // GET_LLVM_INTRINSIC_FOR_{}_BUILTIN878879)",880UpperCompilerName);881}882883static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/true>>884X("gen-intrinsic-enums", "Generate intrinsic enums");885886static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/false>>887Y("gen-intrinsic-impl", "Generate intrinsic implementation code");888889890