| 1 | //===- IntrinsicEmitter.cpp - Generate intrinsic information --------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This tablegen backend emits information about intrinsic functions. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "CodeGenIntrinsics.h" |
| 14 | #include "SequenceToOffsetTable.h" |
| 15 | #include "llvm/ADT/STLExtras.h" |
| 16 | #include "llvm/ADT/SmallVector.h" |
| 17 | #include "llvm/ADT/StringRef.h" |
| 18 | #include "llvm/ADT/Twine.h" |
| 19 | #include "llvm/Support/CommandLine.h" |
| 20 | #include "llvm/Support/ErrorHandling.h" |
| 21 | #include "llvm/Support/FormatVariadic.h" |
| 22 | #include "llvm/Support/ModRef.h" |
| 23 | #include "llvm/Support/raw_ostream.h" |
| 24 | #include "llvm/TableGen/Error.h" |
| 25 | #include "llvm/TableGen/Record.h" |
| 26 | #include "llvm/TableGen/StringToOffsetTable.h" |
| 27 | #include "llvm/TableGen/TableGenBackend.h" |
| 28 | #include <algorithm> |
| 29 | #include <array> |
| 30 | #include <cassert> |
| 31 | #include <cctype> |
| 32 | #include <map> |
| 33 | #include <optional> |
| 34 | #include <string> |
| 35 | #include <utility> |
| 36 | #include <vector> |
| 37 | using namespace llvm; |
| 38 | |
| 39 | static cl::OptionCategory GenIntrinsicCat("Options for -gen-intrinsic-enums" ); |
| 40 | static cl::opt<std::string> |
| 41 | IntrinsicPrefix("intrinsic-prefix" , |
| 42 | cl::desc("Generate intrinsics with this target prefix" ), |
| 43 | cl::value_desc("target prefix" ), cl::cat(GenIntrinsicCat)); |
| 44 | |
| 45 | namespace { |
| 46 | class IntrinsicEmitter { |
| 47 | const RecordKeeper &Records; |
| 48 | |
| 49 | public: |
| 50 | IntrinsicEmitter(const RecordKeeper &R) : Records(R) {} |
| 51 | |
| 52 | void run(raw_ostream &OS, bool Enums); |
| 53 | |
| 54 | void EmitEnumInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); |
| 55 | void EmitArgKind(raw_ostream &OS); |
| 56 | void EmitIITInfo(raw_ostream &OS); |
| 57 | void EmitTargetInfo(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); |
| 58 | void EmitIntrinsicToNameTable(const CodeGenIntrinsicTable &Ints, |
| 59 | raw_ostream &OS); |
| 60 | void EmitIntrinsicToOverloadTable(const CodeGenIntrinsicTable &Ints, |
| 61 | raw_ostream &OS); |
| 62 | void EmitGenerator(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); |
| 63 | void EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS); |
| 64 | void EmitIntrinsicToBuiltinMap(const CodeGenIntrinsicTable &Ints, |
| 65 | bool IsClang, raw_ostream &OS); |
| 66 | }; |
| 67 | |
| 68 | // Helper class to use with `TableGen::Emitter::OptClass`. |
| 69 | template <bool Enums> class IntrinsicEmitterOpt : public IntrinsicEmitter { |
| 70 | public: |
| 71 | IntrinsicEmitterOpt(const RecordKeeper &R) : IntrinsicEmitter(R) {} |
| 72 | void run(raw_ostream &OS) { IntrinsicEmitter::run(OS, Enums); } |
| 73 | }; |
| 74 | |
| 75 | } // End anonymous namespace |
| 76 | |
| 77 | //===----------------------------------------------------------------------===// |
| 78 | // IntrinsicEmitter Implementation |
| 79 | //===----------------------------------------------------------------------===// |
| 80 | |
| 81 | void IntrinsicEmitter::run(raw_ostream &OS, bool Enums) { |
| 82 | emitSourceFileHeader(Desc: "Intrinsic Function Source Fragment" , OS); |
| 83 | |
| 84 | CodeGenIntrinsicTable Ints(Records); |
| 85 | |
| 86 | if (Enums) { |
| 87 | // Emit the enum information. |
| 88 | EmitEnumInfo(Ints, OS); |
| 89 | |
| 90 | // Emit ArgKind for Intrinsics.h. |
| 91 | EmitArgKind(OS); |
| 92 | } else { |
| 93 | // Emit IIT_Info constants. |
| 94 | EmitIITInfo(OS); |
| 95 | |
| 96 | // Emit the target metadata. |
| 97 | EmitTargetInfo(Ints, OS); |
| 98 | |
| 99 | // Emit the intrinsic ID -> name table. |
| 100 | EmitIntrinsicToNameTable(Ints, OS); |
| 101 | |
| 102 | // Emit the intrinsic ID -> overload table. |
| 103 | EmitIntrinsicToOverloadTable(Ints, OS); |
| 104 | |
| 105 | // Emit the intrinsic declaration generator. |
| 106 | EmitGenerator(Ints, OS); |
| 107 | |
| 108 | // Emit the intrinsic parameter attributes. |
| 109 | EmitAttributes(Ints, OS); |
| 110 | |
| 111 | // Emit code to translate Clang builtins into LLVM intrinsics. |
| 112 | EmitIntrinsicToBuiltinMap(Ints, IsClang: true, OS); |
| 113 | |
| 114 | // Emit code to translate MS builtins into LLVM intrinsics. |
| 115 | EmitIntrinsicToBuiltinMap(Ints, IsClang: false, OS); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | void IntrinsicEmitter::EmitEnumInfo(const CodeGenIntrinsicTable &Ints, |
| 120 | raw_ostream &OS) { |
| 121 | // Find the TargetSet for which to generate enums. There will be an initial |
| 122 | // set with an empty target prefix which will include target independent |
| 123 | // intrinsics like dbg.value. |
| 124 | using TargetSet = CodeGenIntrinsicTable::TargetSet; |
| 125 | const TargetSet *Set = nullptr; |
| 126 | for (const auto &Target : Ints.getTargets()) { |
| 127 | if (Target.Name == IntrinsicPrefix) { |
| 128 | Set = &Target; |
| 129 | break; |
| 130 | } |
| 131 | } |
| 132 | if (!Set) { |
| 133 | // The first entry is for target independent intrinsics, so drop it. |
| 134 | auto KnowTargets = Ints.getTargets().drop_front(); |
| 135 | PrintFatalError(PrintMsg: [KnowTargets](raw_ostream &OS) { |
| 136 | OS << "tried to generate intrinsics for unknown target " |
| 137 | << IntrinsicPrefix << "\nKnown targets are: " ; |
| 138 | interleaveComma(c: KnowTargets, os&: OS, |
| 139 | each_fn: [&OS](const TargetSet &Target) { OS << Target.Name; }); |
| 140 | OS << '\n'; |
| 141 | }); |
| 142 | } |
| 143 | |
| 144 | // Generate a complete header for target specific intrinsics. |
| 145 | if (IntrinsicPrefix.empty()) { |
| 146 | OS << "#ifdef GET_INTRINSIC_ENUM_VALUES\n" ; |
| 147 | } else { |
| 148 | std::string UpperPrefix = StringRef(IntrinsicPrefix).upper(); |
| 149 | OS << formatv(Fmt: "#ifndef LLVM_IR_INTRINSIC_{}_ENUMS_H\n" , Vals&: UpperPrefix); |
| 150 | OS << formatv(Fmt: "#define LLVM_IR_INTRINSIC_{}_ENUMS_H\n" , Vals&: UpperPrefix); |
| 151 | OS << "namespace llvm::Intrinsic {\n" ; |
| 152 | OS << formatv(Fmt: "enum {}Intrinsics : unsigned {{\n" , Vals&: UpperPrefix); |
| 153 | } |
| 154 | |
| 155 | OS << "// Enum values for intrinsics.\n" ; |
| 156 | bool First = true; |
| 157 | for (const auto &Int : Ints[*Set]) { |
| 158 | OS << " " << Int.EnumName; |
| 159 | |
| 160 | // Assign a value to the first intrinsic in this target set so that all |
| 161 | // intrinsic ids are distinct. |
| 162 | if (First) { |
| 163 | OS << " = " << Set->Offset + 1; |
| 164 | First = false; |
| 165 | } |
| 166 | |
| 167 | OS << ", " ; |
| 168 | if (Int.EnumName.size() < 40) |
| 169 | OS.indent(NumSpaces: 40 - Int.EnumName.size()); |
| 170 | OS << formatv(Fmt: " // {}\n" , Vals: Int.Name); |
| 171 | } |
| 172 | |
| 173 | // Emit num_intrinsics into the target neutral enum. |
| 174 | if (IntrinsicPrefix.empty()) { |
| 175 | OS << formatv(Fmt: " num_intrinsics = {}\n" , Vals: Ints.size() + 1); |
| 176 | OS << "#endif\n\n" ; |
| 177 | } else { |
| 178 | OS << R"(}; // enum |
| 179 | } // namespace llvm::Intrinsic |
| 180 | #endif |
| 181 | |
| 182 | )" ; |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | void IntrinsicEmitter::EmitArgKind(raw_ostream &OS) { |
| 187 | if (!IntrinsicPrefix.empty()) |
| 188 | return; |
| 189 | OS << "// llvm::Intrinsic::IITDescriptor::ArgKind.\n" ; |
| 190 | OS << "#ifdef GET_INTRINSIC_ARGKIND\n" ; |
| 191 | if (const auto RecArgKind = Records.getDef(Name: "ArgKind" )) { |
| 192 | for (const auto &RV : RecArgKind->getValues()) |
| 193 | OS << " AK_" << RV.getName() << " = " << *RV.getValue() << ",\n" ; |
| 194 | } else { |
| 195 | OS << "#error \"ArgKind is not defined\"\n" ; |
| 196 | } |
| 197 | OS << "#endif\n\n" ; |
| 198 | } |
| 199 | |
| 200 | void IntrinsicEmitter::EmitIITInfo(raw_ostream &OS) { |
| 201 | OS << "#ifdef GET_INTRINSIC_IITINFO\n" ; |
| 202 | std::array<StringRef, 256> RecsByNumber; |
| 203 | auto IIT_Base = Records.getAllDerivedDefinitionsIfDefined(ClassName: "IIT_Base" ); |
| 204 | for (const Record *Rec : IIT_Base) { |
| 205 | auto Number = Rec->getValueAsInt(FieldName: "Number" ); |
| 206 | assert(0 <= Number && Number < (int)RecsByNumber.size() && |
| 207 | "IIT_Info.Number should be uint8_t" ); |
| 208 | assert(RecsByNumber[Number].empty() && "Duplicate IIT_Info.Number" ); |
| 209 | RecsByNumber[Number] = Rec->getName(); |
| 210 | } |
| 211 | if (IIT_Base.size() > 0) { |
| 212 | for (unsigned I = 0, E = RecsByNumber.size(); I < E; ++I) |
| 213 | if (!RecsByNumber[I].empty()) |
| 214 | OS << " " << RecsByNumber[I] << " = " << I << ",\n" ; |
| 215 | } else { |
| 216 | OS << "#error \"class IIT_Base is not defined\"\n" ; |
| 217 | } |
| 218 | OS << "#endif\n\n" ; |
| 219 | } |
| 220 | |
| 221 | void IntrinsicEmitter::EmitTargetInfo(const CodeGenIntrinsicTable &Ints, |
| 222 | raw_ostream &OS) { |
| 223 | OS << R"(// Target mapping. |
| 224 | #ifdef GET_INTRINSIC_TARGET_DATA |
| 225 | struct IntrinsicTargetInfo { |
| 226 | StringLiteral Name; |
| 227 | size_t Offset; |
| 228 | size_t Count; |
| 229 | }; |
| 230 | static constexpr IntrinsicTargetInfo TargetInfos[] = { |
| 231 | )" ; |
| 232 | for (const auto [Name, Offset, Count] : Ints.getTargets()) |
| 233 | OS << formatv(Fmt: " {{\"{}\", {}, {}},\n" , Vals: Name, Vals: Offset, Vals: Count); |
| 234 | OS << R"(}; |
| 235 | #endif |
| 236 | |
| 237 | )" ; |
| 238 | } |
| 239 | |
| 240 | void IntrinsicEmitter::EmitIntrinsicToNameTable( |
| 241 | const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { |
| 242 | // Built up a table of the intrinsic names. |
| 243 | constexpr StringLiteral NotIntrinsic = "not_intrinsic" ; |
| 244 | StringToOffsetTable Table; |
| 245 | Table.GetOrAddStringOffset(Str: NotIntrinsic); |
| 246 | for (const auto &Int : Ints) |
| 247 | Table.GetOrAddStringOffset(Str: Int.Name); |
| 248 | |
| 249 | OS << R"(// Intrinsic ID to name table. |
| 250 | #ifdef GET_INTRINSIC_NAME_TABLE |
| 251 | // Note that entry #0 is the invalid intrinsic! |
| 252 | |
| 253 | )" ; |
| 254 | |
| 255 | Table.EmitStringTableDef(OS, Name: "IntrinsicNameTable" , /*Indent=*/"" ); |
| 256 | |
| 257 | OS << R"( |
| 258 | static constexpr unsigned IntrinsicNameOffsetTable[] = { |
| 259 | )" ; |
| 260 | |
| 261 | OS << formatv(Fmt: " {}, // {}\n" , Vals: Table.GetStringOffset(Str: NotIntrinsic), |
| 262 | Vals: NotIntrinsic); |
| 263 | for (const auto &Int : Ints) |
| 264 | OS << formatv(Fmt: " {}, // {}\n" , Vals: Table.GetStringOffset(Str: Int.Name), Vals: Int.Name); |
| 265 | |
| 266 | OS << R"( |
| 267 | }; // IntrinsicNameOffsetTable |
| 268 | |
| 269 | #endif |
| 270 | |
| 271 | )" ; |
| 272 | } |
| 273 | |
| 274 | void IntrinsicEmitter::EmitIntrinsicToOverloadTable( |
| 275 | const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { |
| 276 | OS << R"(// Intrinsic ID to overload bitset. |
| 277 | #ifdef GET_INTRINSIC_OVERLOAD_TABLE |
| 278 | static constexpr uint8_t OTable[] = { |
| 279 | 0 |
| 280 | )" ; |
| 281 | for (auto [I, Int] : enumerate(First: Ints)) { |
| 282 | // Add one to the index so we emit a null bit for the invalid #0 intrinsic. |
| 283 | size_t Idx = I + 1; |
| 284 | |
| 285 | if (Idx % 8 == 0) |
| 286 | OS << ",\n 0" ; |
| 287 | if (Int.isOverloaded) |
| 288 | OS << " | (1<<" << Idx % 8 << ')'; |
| 289 | } |
| 290 | OS << "\n};\n\n" ; |
| 291 | // OTable contains a true bit at the position if the intrinsic is overloaded. |
| 292 | OS << "return (OTable[id/8] & (1 << (id%8))) != 0;\n" ; |
| 293 | OS << "#endif\n\n" ; |
| 294 | } |
| 295 | |
| 296 | using TypeSigTy = SmallVector<unsigned char>; |
| 297 | |
| 298 | /// Computes type signature of the intrinsic \p Int. |
| 299 | static TypeSigTy ComputeTypeSignature(const CodeGenIntrinsic &Int) { |
| 300 | TypeSigTy TypeSig; |
| 301 | const Record *TypeInfo = Int.TheDef->getValueAsDef(FieldName: "TypeInfo" ); |
| 302 | const ListInit *TypeList = TypeInfo->getValueAsListInit(FieldName: "TypeSig" ); |
| 303 | |
| 304 | for (const auto *TypeListEntry : TypeList->getElements()) |
| 305 | TypeSig.emplace_back(Args: cast<IntInit>(Val: TypeListEntry)->getValue()); |
| 306 | return TypeSig; |
| 307 | } |
| 308 | |
| 309 | // Pack the type signature into 32-bit fixed encoding word. |
| 310 | static std::optional<uint32_t> encodePacked(const TypeSigTy &TypeSig) { |
| 311 | if (TypeSig.size() > 8) |
| 312 | return std::nullopt; |
| 313 | |
| 314 | uint32_t Result = 0; |
| 315 | for (unsigned char C : reverse(C: TypeSig)) { |
| 316 | if (C > 15) |
| 317 | return std::nullopt; |
| 318 | Result = (Result << 4) | C; |
| 319 | } |
| 320 | return Result; |
| 321 | } |
| 322 | |
| 323 | void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints, |
| 324 | raw_ostream &OS) { |
| 325 | // Note: the code below can be switched to use 32-bit fixed encoding by |
| 326 | // flipping the flag below. |
| 327 | constexpr bool Use16BitFixedEncoding = true; |
| 328 | using FixedEncodingTy = |
| 329 | std::conditional_t<Use16BitFixedEncoding, uint16_t, uint32_t>; |
| 330 | constexpr unsigned FixedEncodingBits = sizeof(FixedEncodingTy) * CHAR_BIT; |
| 331 | // Mask with all bits 1 except the most significant bit. |
| 332 | const unsigned Mask = (1U << (FixedEncodingBits - 1)) - 1; |
| 333 | const unsigned MSBPostion = FixedEncodingBits - 1; |
| 334 | StringRef FixedEncodingTypeName = |
| 335 | Use16BitFixedEncoding ? "uint16_t" : "uint32_t" ; |
| 336 | |
| 337 | // If we can compute a 16/32-bit fixed encoding for this intrinsic, do so and |
| 338 | // capture it in this vector, otherwise store a ~0U. |
| 339 | std::vector<FixedEncodingTy> FixedEncodings; |
| 340 | SequenceToOffsetTable<TypeSigTy> LongEncodingTable; |
| 341 | |
| 342 | FixedEncodings.reserve(n: Ints.size()); |
| 343 | |
| 344 | // Compute the unique argument type info. |
| 345 | for (const CodeGenIntrinsic &Int : Ints) { |
| 346 | // Get the signature for the intrinsic. |
| 347 | TypeSigTy TypeSig = ComputeTypeSignature(Int); |
| 348 | |
| 349 | // Check to see if we can encode it into a 16/32 bit word. |
| 350 | std::optional<uint32_t> Result = encodePacked(TypeSig); |
| 351 | if (Result && (*Result & Mask) == Result) { |
| 352 | FixedEncodings.push_back(x: static_cast<FixedEncodingTy>(*Result)); |
| 353 | continue; |
| 354 | } |
| 355 | |
| 356 | LongEncodingTable.add(Seq: TypeSig); |
| 357 | |
| 358 | // This is a placehold that we'll replace after the table is laid out. |
| 359 | FixedEncodings.push_back(x: static_cast<FixedEncodingTy>(~0U)); |
| 360 | } |
| 361 | |
| 362 | LongEncodingTable.layout(); |
| 363 | |
| 364 | OS << formatv(Fmt: R"(// Global intrinsic function declaration type table. |
| 365 | #ifdef GET_INTRINSIC_GENERATOR_GLOBAL |
| 366 | static constexpr {} IIT_Table[] = {{ |
| 367 | )" , |
| 368 | Vals&: FixedEncodingTypeName); |
| 369 | |
| 370 | unsigned MaxOffset = 0; |
| 371 | for (auto [Idx, FixedEncoding, Int] : enumerate(First&: FixedEncodings, Rest: Ints)) { |
| 372 | if ((Idx & 7) == 7) |
| 373 | OS << "\n " ; |
| 374 | |
| 375 | // If the entry fit in the table, just emit it. |
| 376 | if ((FixedEncoding & Mask) == FixedEncoding) { |
| 377 | OS << "0x" << Twine::utohexstr(Val: FixedEncoding) << ", " ; |
| 378 | continue; |
| 379 | } |
| 380 | |
| 381 | TypeSigTy TypeSig = ComputeTypeSignature(Int); |
| 382 | unsigned Offset = LongEncodingTable.get(Seq: TypeSig); |
| 383 | MaxOffset = std::max(a: MaxOffset, b: Offset); |
| 384 | |
| 385 | // Otherwise, emit the offset into the long encoding table. We emit it this |
| 386 | // way so that it is easier to read the offset in the .def file. |
| 387 | OS << formatv(Fmt: "(1U<<{}) | {}, " , Vals: MSBPostion, Vals&: Offset); |
| 388 | } |
| 389 | |
| 390 | OS << "0\n};\n\n" ; |
| 391 | |
| 392 | // verify that all offsets will fit in 16/32 bits. |
| 393 | if ((MaxOffset & Mask) != MaxOffset) |
| 394 | PrintFatalError(Msg: "Offset of long encoding table exceeds encoding bits" ); |
| 395 | |
| 396 | // Emit the shared table of register lists. |
| 397 | OS << "static constexpr unsigned char IIT_LongEncodingTable[] = {\n" ; |
| 398 | if (!LongEncodingTable.empty()) |
| 399 | LongEncodingTable.emit( |
| 400 | OS, Print: [](raw_ostream &OS, unsigned char C) { OS << (unsigned)C; }); |
| 401 | OS << " 255\n};\n" ; |
| 402 | OS << "#endif\n\n" ; // End of GET_INTRINSIC_GENERATOR_GLOBAL |
| 403 | } |
| 404 | |
| 405 | /// Returns the effective MemoryEffects for intrinsic \p Int. |
| 406 | static MemoryEffects getEffectiveME(const CodeGenIntrinsic &Int) { |
| 407 | MemoryEffects ME = Int.ME; |
| 408 | // TODO: IntrHasSideEffects should affect not only readnone intrinsics. |
| 409 | if (ME.doesNotAccessMemory() && Int.hasSideEffects) |
| 410 | ME = MemoryEffects::unknown(); |
| 411 | return ME; |
| 412 | } |
| 413 | |
| 414 | static bool compareFnAttributes(const CodeGenIntrinsic *L, |
| 415 | const CodeGenIntrinsic *R) { |
| 416 | auto TieBoolAttributes = [](const CodeGenIntrinsic *I) -> auto { |
| 417 | // Sort throwing intrinsics after non-throwing intrinsics. |
| 418 | return std::tie(args: I->canThrow, args: I->isNoDuplicate, args: I->isNoMerge, args: I->isNoReturn, |
| 419 | args: I->isNoCallback, args: I->isNoSync, args: I->isNoFree, args: I->isWillReturn, |
| 420 | args: I->isCold, args: I->isConvergent, args: I->isSpeculatable, |
| 421 | args: I->hasSideEffects, args: I->isStrictFP); |
| 422 | }; |
| 423 | |
| 424 | auto TieL = TieBoolAttributes(L); |
| 425 | auto TieR = TieBoolAttributes(R); |
| 426 | |
| 427 | if (TieL != TieR) |
| 428 | return TieL < TieR; |
| 429 | |
| 430 | // Try to order by readonly/readnone attribute. |
| 431 | uint32_t LME = getEffectiveME(Int: *L).toIntValue(); |
| 432 | uint32_t RME = getEffectiveME(Int: *R).toIntValue(); |
| 433 | if (LME != RME) |
| 434 | return LME > RME; |
| 435 | |
| 436 | return false; |
| 437 | } |
| 438 | |
| 439 | /// Returns true if \p Int has a non-empty set of function attributes. Note that |
| 440 | /// NoUnwind = !canThrow, so we need to negate it's sense to test if the |
| 441 | // intrinsic has NoUnwind attribute. |
| 442 | static bool hasFnAttributes(const CodeGenIntrinsic &Int) { |
| 443 | return !Int.canThrow || Int.isNoReturn || Int.isNoCallback || Int.isNoSync || |
| 444 | Int.isNoFree || Int.isWillReturn || Int.isCold || Int.isNoDuplicate || |
| 445 | Int.isNoMerge || Int.isConvergent || Int.isSpeculatable || |
| 446 | Int.isStrictFP || getEffectiveME(Int) != MemoryEffects::unknown(); |
| 447 | } |
| 448 | |
| 449 | namespace { |
| 450 | struct FnAttributeComparator { |
| 451 | bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const { |
| 452 | return compareFnAttributes(L, R); |
| 453 | } |
| 454 | }; |
| 455 | |
| 456 | struct AttributeComparator { |
| 457 | bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const { |
| 458 | // Order all intrinsics with no functiona attributes before all intrinsics |
| 459 | // with function attributes. |
| 460 | bool HasFnAttrLHS = hasFnAttributes(Int: *L); |
| 461 | bool HasFnAttrRHS = hasFnAttributes(Int: *R); |
| 462 | |
| 463 | // Order by argument attributes if function `hasFnAttributes` is equal. |
| 464 | // This is reliable because each side is already sorted internally. |
| 465 | return std::tie(args&: HasFnAttrLHS, args: L->ArgumentAttributes) < |
| 466 | std::tie(args&: HasFnAttrRHS, args: R->ArgumentAttributes); |
| 467 | } |
| 468 | }; |
| 469 | } // End anonymous namespace |
| 470 | |
| 471 | /// Returns the name of the IR enum for argument attribute kind \p Kind. |
| 472 | static StringRef getArgAttrEnumName(CodeGenIntrinsic::ArgAttrKind Kind) { |
| 473 | switch (Kind) { |
| 474 | case CodeGenIntrinsic::NoCapture: |
| 475 | llvm_unreachable("Handled separately" ); |
| 476 | case CodeGenIntrinsic::NoAlias: |
| 477 | return "NoAlias" ; |
| 478 | case CodeGenIntrinsic::NoUndef: |
| 479 | return "NoUndef" ; |
| 480 | case CodeGenIntrinsic::NonNull: |
| 481 | return "NonNull" ; |
| 482 | case CodeGenIntrinsic::Returned: |
| 483 | return "Returned" ; |
| 484 | case CodeGenIntrinsic::ReadOnly: |
| 485 | return "ReadOnly" ; |
| 486 | case CodeGenIntrinsic::WriteOnly: |
| 487 | return "WriteOnly" ; |
| 488 | case CodeGenIntrinsic::ReadNone: |
| 489 | return "ReadNone" ; |
| 490 | case CodeGenIntrinsic::ImmArg: |
| 491 | return "ImmArg" ; |
| 492 | case CodeGenIntrinsic::Alignment: |
| 493 | return "Alignment" ; |
| 494 | case CodeGenIntrinsic::Dereferenceable: |
| 495 | return "Dereferenceable" ; |
| 496 | case CodeGenIntrinsic::Range: |
| 497 | return "Range" ; |
| 498 | } |
| 499 | llvm_unreachable("Unknown CodeGenIntrinsic::ArgAttrKind enum" ); |
| 500 | } |
| 501 | |
| 502 | /// EmitAttributes - This emits the Intrinsic::getAttributes method. |
| 503 | void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, |
| 504 | raw_ostream &OS) { |
| 505 | OS << R"(// Add parameter attributes that are not common to all intrinsics. |
| 506 | #ifdef GET_INTRINSIC_ATTRIBUTES |
| 507 | static AttributeSet getIntrinsicArgAttributeSet(LLVMContext &C, unsigned ID, |
| 508 | Type *ArgType) { |
| 509 | unsigned BitWidth = ArgType->getScalarSizeInBits(); |
| 510 | switch (ID) { |
| 511 | default: llvm_unreachable("Invalid attribute set number");)" ; |
| 512 | // Compute unique argument attribute sets. |
| 513 | std::map<SmallVector<CodeGenIntrinsic::ArgAttribute, 0>, unsigned> |
| 514 | UniqArgAttributes; |
| 515 | for (const CodeGenIntrinsic &Int : Ints) { |
| 516 | for (auto &Attrs : Int.ArgumentAttributes) { |
| 517 | if (Attrs.empty()) |
| 518 | continue; |
| 519 | |
| 520 | unsigned ID = UniqArgAttributes.size(); |
| 521 | if (!UniqArgAttributes.try_emplace(k: Attrs, args&: ID).second) |
| 522 | continue; |
| 523 | |
| 524 | assert(is_sorted(Attrs) && "Argument attributes are not sorted" ); |
| 525 | |
| 526 | OS << formatv(Fmt: R"( |
| 527 | case {}: |
| 528 | return AttributeSet::get(C, {{ |
| 529 | )" , |
| 530 | Vals&: ID); |
| 531 | for (const CodeGenIntrinsic::ArgAttribute &Attr : Attrs) { |
| 532 | if (Attr.Kind == CodeGenIntrinsic::NoCapture) { |
| 533 | OS << " Attribute::getWithCaptureInfo(C, " |
| 534 | "CaptureInfo::none()),\n" ; |
| 535 | continue; |
| 536 | } |
| 537 | StringRef AttrName = getArgAttrEnumName(Kind: Attr.Kind); |
| 538 | if (Attr.Kind == CodeGenIntrinsic::Alignment || |
| 539 | Attr.Kind == CodeGenIntrinsic::Dereferenceable) |
| 540 | OS << formatv(Fmt: " Attribute::get(C, Attribute::{}, {}),\n" , |
| 541 | Vals&: AttrName, Vals: Attr.Value); |
| 542 | else if (Attr.Kind == CodeGenIntrinsic::Range) |
| 543 | // This allows implicitTrunc because the range may only fit the |
| 544 | // type based on rules implemented in the IR verifier. E.g. the |
| 545 | // [-1, 1] range for ucmp/scmp intrinsics requires a minimum i2 type. |
| 546 | // Give the verifier a chance to diagnose this instead of asserting |
| 547 | // here. |
| 548 | OS << formatv(Fmt: " Attribute::get(C, Attribute::{}, " |
| 549 | "ConstantRange(APInt(BitWidth, {}, /*isSigned=*/true, " |
| 550 | "/*implicitTrunc=*/true), APInt(BitWidth, {}, " |
| 551 | "/*isSigned=*/true, /*implicitTrunc=*/true))),\n" , |
| 552 | Vals&: AttrName, Vals: (int64_t)Attr.Value, Vals: (int64_t)Attr.Value2); |
| 553 | else |
| 554 | OS << formatv(Fmt: " Attribute::get(C, Attribute::{}),\n" , Vals&: AttrName); |
| 555 | } |
| 556 | OS << " });" ; |
| 557 | } |
| 558 | } |
| 559 | OS << R"( |
| 560 | } |
| 561 | } // getIntrinsicArgAttributeSet |
| 562 | )" ; |
| 563 | |
| 564 | // Compute unique function attribute sets. |
| 565 | std::map<const CodeGenIntrinsic *, unsigned, FnAttributeComparator> |
| 566 | UniqFnAttributes; |
| 567 | OS << R"( |
| 568 | static AttributeSet getIntrinsicFnAttributeSet(LLVMContext &C, unsigned ID) { |
| 569 | switch (ID) { |
| 570 | default: llvm_unreachable("Invalid attribute set number");)" ; |
| 571 | |
| 572 | for (const CodeGenIntrinsic &Int : Ints) { |
| 573 | unsigned ID = UniqFnAttributes.size(); |
| 574 | if (!UniqFnAttributes.try_emplace(k: &Int, args&: ID).second) |
| 575 | continue; |
| 576 | OS << formatv(Fmt: R"( |
| 577 | case {}: |
| 578 | return AttributeSet::get(C, {{ |
| 579 | )" , |
| 580 | Vals&: ID); |
| 581 | auto addAttribute = [&OS](StringRef Attr) { |
| 582 | OS << formatv(Fmt: " Attribute::get(C, Attribute::{}),\n" , Vals&: Attr); |
| 583 | }; |
| 584 | if (!Int.canThrow) |
| 585 | addAttribute("NoUnwind" ); |
| 586 | if (Int.isNoReturn) |
| 587 | addAttribute("NoReturn" ); |
| 588 | if (Int.isNoCallback) |
| 589 | addAttribute("NoCallback" ); |
| 590 | if (Int.isNoSync) |
| 591 | addAttribute("NoSync" ); |
| 592 | if (Int.isNoFree) |
| 593 | addAttribute("NoFree" ); |
| 594 | if (Int.isWillReturn) |
| 595 | addAttribute("WillReturn" ); |
| 596 | if (Int.isCold) |
| 597 | addAttribute("Cold" ); |
| 598 | if (Int.isNoDuplicate) |
| 599 | addAttribute("NoDuplicate" ); |
| 600 | if (Int.isNoMerge) |
| 601 | addAttribute("NoMerge" ); |
| 602 | if (Int.isConvergent) |
| 603 | addAttribute("Convergent" ); |
| 604 | if (Int.isSpeculatable) |
| 605 | addAttribute("Speculatable" ); |
| 606 | if (Int.isStrictFP) |
| 607 | addAttribute("StrictFP" ); |
| 608 | |
| 609 | const MemoryEffects ME = getEffectiveME(Int); |
| 610 | if (ME != MemoryEffects::unknown()) { |
| 611 | OS << formatv(Fmt: " // {}\n" , Vals: ME); |
| 612 | OS << formatv(Fmt: " Attribute::getWithMemoryEffects(C, " |
| 613 | "MemoryEffects::createFromIntValue({})),\n" , |
| 614 | Vals: ME.toIntValue()); |
| 615 | } |
| 616 | OS << " });" ; |
| 617 | } |
| 618 | OS << R"( |
| 619 | } |
| 620 | } // getIntrinsicFnAttributeSet |
| 621 | |
| 622 | static constexpr uint16_t IntrinsicsToAttributesMap[] = {)" ; |
| 623 | |
| 624 | // Compute the maximum number of attribute arguments and the map. For function |
| 625 | // attributes, we only consider whether the intrinsics has any function |
| 626 | // arguments or not. |
| 627 | std::map<const CodeGenIntrinsic *, unsigned, AttributeComparator> |
| 628 | UniqAttributes; |
| 629 | for (const CodeGenIntrinsic &Int : Ints) { |
| 630 | unsigned ID = UniqAttributes.size(); |
| 631 | UniqAttributes.try_emplace(k: &Int, args&: ID); |
| 632 | } |
| 633 | |
| 634 | // Emit an array of AttributeList. Most intrinsics will have at least one |
| 635 | // entry, for the function itself (index ~1), which is usually nounwind. |
| 636 | for (const CodeGenIntrinsic &Int : Ints) { |
| 637 | uint16_t FnAttrIndex = UniqFnAttributes[&Int]; |
| 638 | OS << formatv(Fmt: "\n {} << 8 | {}, // {}" , Vals&: FnAttrIndex, |
| 639 | Vals&: UniqAttributes[&Int], Vals: Int.Name); |
| 640 | } |
| 641 | |
| 642 | // Assign a 16-bit packed ID for each intrinsic. The lower 8-bits will be its |
| 643 | // "argument attribute ID" (index in UniqAttributes) and upper 8 bits will be |
| 644 | // its "function attribute ID" (index in UniqFnAttributes). |
| 645 | if (UniqAttributes.size() > 256) |
| 646 | PrintFatalError(Msg: "Too many unique argument attributes for table!" ); |
| 647 | if (UniqFnAttributes.size() > 256) |
| 648 | PrintFatalError(Msg: "Too many unique function attributes for table!" ); |
| 649 | |
| 650 | OS << R"( |
| 651 | }; |
| 652 | |
| 653 | AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id, |
| 654 | FunctionType *FT) {)" ; |
| 655 | |
| 656 | OS << formatv(Fmt: R"( |
| 657 | if (id == 0) |
| 658 | return AttributeList(); |
| 659 | |
| 660 | uint16_t PackedID = IntrinsicsToAttributesMap[id - 1]; |
| 661 | uint8_t FnAttrID = PackedID >> 8; |
| 662 | switch(PackedID & 0xFF) {{ |
| 663 | default: llvm_unreachable("Invalid attribute number"); |
| 664 | )" ); |
| 665 | |
| 666 | for (const auto [IntPtr, UniqueID] : UniqAttributes) { |
| 667 | OS << formatv(Fmt: " case {}:\n" , Vals: UniqueID); |
| 668 | const CodeGenIntrinsic &Int = *IntPtr; |
| 669 | |
| 670 | // Keep track of the number of attributes we're writing out. |
| 671 | unsigned NumAttrs = |
| 672 | llvm::count_if(Range: Int.ArgumentAttributes, |
| 673 | P: [](const auto &Attrs) { return !Attrs.empty(); }); |
| 674 | NumAttrs += hasFnAttributes(Int); |
| 675 | if (NumAttrs == 0) { |
| 676 | OS << " return AttributeList();\n" ; |
| 677 | continue; |
| 678 | } |
| 679 | |
| 680 | OS << " return AttributeList::get(C, {\n" ; |
| 681 | ListSeparator LS(",\n" ); |
| 682 | for (const auto &[AttrIdx, Attrs] : enumerate(First: Int.ArgumentAttributes)) { |
| 683 | if (Attrs.empty()) |
| 684 | continue; |
| 685 | |
| 686 | unsigned ArgAttrID = UniqArgAttributes.find(x: Attrs)->second; |
| 687 | OS << LS |
| 688 | << formatv(Fmt: " {{{}, getIntrinsicArgAttributeSet(C, {}, " |
| 689 | "FT->getContainedType({}))}" , |
| 690 | Vals&: AttrIdx, Vals&: ArgAttrID, Vals&: AttrIdx); |
| 691 | } |
| 692 | |
| 693 | if (hasFnAttributes(Int)) { |
| 694 | OS << LS |
| 695 | << " {AttributeList::FunctionIndex, " |
| 696 | "getIntrinsicFnAttributeSet(C, FnAttrID)}" ; |
| 697 | } |
| 698 | OS << "\n });\n" ; |
| 699 | } |
| 700 | |
| 701 | OS << R"( } |
| 702 | } |
| 703 | #endif // GET_INTRINSIC_ATTRIBUTES |
| 704 | |
| 705 | )" ; |
| 706 | } |
| 707 | |
| 708 | void IntrinsicEmitter::EmitIntrinsicToBuiltinMap( |
| 709 | const CodeGenIntrinsicTable &Ints, bool IsClang, raw_ostream &OS) { |
| 710 | StringRef CompilerName = IsClang ? "Clang" : "MS" ; |
| 711 | StringRef UpperCompilerName = IsClang ? "CLANG" : "MS" ; |
| 712 | |
| 713 | // map<TargetPrefix, pair<map<BuiltinName, EnumName>, CommonPrefix>. |
| 714 | // Note that we iterate over both the maps in the code below and both |
| 715 | // iterations need to iterate in sorted key order. For the inner map, entries |
| 716 | // need to be emitted in the sorted order of `BuiltinName` with `CommonPrefix` |
| 717 | // rempved, because we use std::lower_bound to search these entries. For the |
| 718 | // outer map as well, entries need to be emitted in sorter order of |
| 719 | // `TargetPrefix` as we use std::lower_bound to search these entries. |
| 720 | using BIMEntryTy = |
| 721 | std::pair<std::map<StringRef, StringRef>, std::optional<StringRef>>; |
| 722 | std::map<StringRef, BIMEntryTy> BuiltinMap; |
| 723 | |
| 724 | for (const CodeGenIntrinsic &Int : Ints) { |
| 725 | StringRef BuiltinName = IsClang ? Int.ClangBuiltinName : Int.MSBuiltinName; |
| 726 | if (BuiltinName.empty()) |
| 727 | continue; |
| 728 | // Get the map for this target prefix. |
| 729 | auto &[Map, CommonPrefix] = BuiltinMap[Int.TargetPrefix]; |
| 730 | |
| 731 | if (!Map.try_emplace(k: BuiltinName, args: Int.EnumName).second) |
| 732 | PrintFatalError(ErrorLoc: Int.TheDef->getLoc(), |
| 733 | Msg: "Intrinsic '" + Int.TheDef->getName() + "': duplicate " + |
| 734 | CompilerName + " builtin name!" ); |
| 735 | |
| 736 | // Update common prefix. |
| 737 | if (!CommonPrefix) { |
| 738 | // For the first builtin for this target, initialize the common prefix. |
| 739 | CommonPrefix = BuiltinName; |
| 740 | continue; |
| 741 | } |
| 742 | |
| 743 | // Update the common prefix. Note that this assumes that `take_front` will |
| 744 | // never set the `Data` pointer in CommonPrefix to nullptr. |
| 745 | const char *Mismatch = mismatch(Range1&: *CommonPrefix, Range2&: BuiltinName).first; |
| 746 | *CommonPrefix = CommonPrefix->take_front(N: Mismatch - CommonPrefix->begin()); |
| 747 | } |
| 748 | |
| 749 | // Populate the string table with the names of all the builtins after |
| 750 | // removing this common prefix. |
| 751 | StringToOffsetTable Table; |
| 752 | for (const auto &[TargetPrefix, Entry] : BuiltinMap) { |
| 753 | auto &[Map, CommonPrefix] = Entry; |
| 754 | for (auto &[BuiltinName, EnumName] : Map) { |
| 755 | StringRef Suffix = BuiltinName.substr(Start: CommonPrefix->size()); |
| 756 | Table.GetOrAddStringOffset(Str: Suffix); |
| 757 | } |
| 758 | } |
| 759 | |
| 760 | OS << formatv(Fmt: R"( |
| 761 | // Get the LLVM intrinsic that corresponds to a builtin. This is used by the |
| 762 | // C front-end. The builtin name is passed in as BuiltinName, and a target |
| 763 | // prefix (e.g. 'ppc') is passed in as TargetPrefix. |
| 764 | #ifdef GET_LLVM_INTRINSIC_FOR_{}_BUILTIN |
| 765 | Intrinsic::ID |
| 766 | Intrinsic::getIntrinsicFor{}Builtin(StringRef TargetPrefix, |
| 767 | StringRef BuiltinName) {{ |
| 768 | using namespace Intrinsic; |
| 769 | )" , |
| 770 | Vals&: UpperCompilerName, Vals&: CompilerName); |
| 771 | |
| 772 | if (BuiltinMap.empty()) { |
| 773 | OS << formatv(Fmt: R"( |
| 774 | return not_intrinsic; |
| 775 | } |
| 776 | #endif // GET_LLVM_INTRINSIC_FOR_{}_BUILTIN |
| 777 | )" , |
| 778 | Vals&: UpperCompilerName); |
| 779 | return; |
| 780 | } |
| 781 | |
| 782 | if (!Table.empty()) { |
| 783 | Table.EmitStringTableDef(OS, Name: "BuiltinNames" ); |
| 784 | |
| 785 | OS << R"( |
| 786 | struct BuiltinEntry { |
| 787 | ID IntrinsicID; |
| 788 | unsigned StrTabOffset; |
| 789 | const char *getName() const { return BuiltinNames[StrTabOffset].data(); } |
| 790 | bool operator<(StringRef RHS) const { |
| 791 | return strncmp(getName(), RHS.data(), RHS.size()) < 0; |
| 792 | } |
| 793 | }; |
| 794 | |
| 795 | )" ; |
| 796 | } |
| 797 | |
| 798 | // Emit a per target table of bultin names. |
| 799 | bool HasTargetIndependentBuiltins = false; |
| 800 | StringRef TargetIndepndentCommonPrefix; |
| 801 | for (const auto &[TargetPrefix, Entry] : BuiltinMap) { |
| 802 | const auto &[Map, CommonPrefix] = Entry; |
| 803 | if (!TargetPrefix.empty()) { |
| 804 | OS << formatv(Fmt: " // Builtins for {0}.\n" , Vals: TargetPrefix); |
| 805 | } else { |
| 806 | OS << " // Target independent builtins.\n" ; |
| 807 | HasTargetIndependentBuiltins = true; |
| 808 | TargetIndepndentCommonPrefix = *CommonPrefix; |
| 809 | } |
| 810 | |
| 811 | // Emit the builtin table for this target prefix. |
| 812 | OS << formatv(Fmt: " static constexpr BuiltinEntry {}Names[] = {{\n" , |
| 813 | Vals: TargetPrefix); |
| 814 | for (const auto &[BuiltinName, EnumName] : Map) { |
| 815 | StringRef Suffix = BuiltinName.substr(Start: CommonPrefix->size()); |
| 816 | OS << formatv(Fmt: " {{{}, {}}, // {}\n" , Vals: EnumName, |
| 817 | Vals: *Table.GetStringOffset(Str: Suffix), Vals: BuiltinName); |
| 818 | } |
| 819 | OS << formatv(Fmt: " }; // {}Names\n\n" , Vals: TargetPrefix); |
| 820 | } |
| 821 | |
| 822 | // After emitting the builtin tables for all targets, emit a lookup table for |
| 823 | // all targets. We will use binary search, similar to the table for builtin |
| 824 | // names to lookup into this table. |
| 825 | OS << R"( |
| 826 | struct TargetEntry { |
| 827 | StringLiteral TargetPrefix; |
| 828 | ArrayRef<BuiltinEntry> Names; |
| 829 | StringLiteral CommonPrefix; |
| 830 | bool operator<(StringRef RHS) const { |
| 831 | return TargetPrefix < RHS; |
| 832 | }; |
| 833 | }; |
| 834 | static constexpr TargetEntry TargetTable[] = { |
| 835 | )" ; |
| 836 | |
| 837 | for (const auto &[TargetPrefix, Entry] : BuiltinMap) { |
| 838 | const auto &[Map, CommonPrefix] = Entry; |
| 839 | if (TargetPrefix.empty()) |
| 840 | continue; |
| 841 | OS << formatv(Fmt: R"( {{"{0}", {0}Names, "{1}"},)" , Vals: TargetPrefix, |
| 842 | Vals: CommonPrefix) |
| 843 | << "\n" ; |
| 844 | } |
| 845 | OS << " };\n" ; |
| 846 | |
| 847 | // Now for the actual lookup, first check the target independent table if |
| 848 | // we emitted one. |
| 849 | if (HasTargetIndependentBuiltins) { |
| 850 | OS << formatv(Fmt: R"( |
| 851 | // Check if it's a target independent builtin. |
| 852 | // Copy the builtin name so we can use it in consume_front without clobbering |
| 853 | // if for the lookup in the target specific table. |
| 854 | StringRef Suffix = BuiltinName; |
| 855 | if (Suffix.consume_front("{}")) {{ |
| 856 | auto II = lower_bound(Names, Suffix); |
| 857 | if (II != std::end(Names) && II->getName() == Suffix) |
| 858 | return II->IntrinsicID; |
| 859 | } |
| 860 | )" , |
| 861 | Vals&: TargetIndepndentCommonPrefix); |
| 862 | } |
| 863 | |
| 864 | // If a target independent builtin was not found, lookup the target specific. |
| 865 | OS << formatv(Fmt: R"( |
| 866 | auto TI = lower_bound(TargetTable, TargetPrefix); |
| 867 | if (TI == std::end(TargetTable) || TI->TargetPrefix != TargetPrefix) |
| 868 | return not_intrinsic; |
| 869 | // This is the last use of BuiltinName, so no need to copy before using it in |
| 870 | // consume_front. |
| 871 | if (!BuiltinName.consume_front(TI->CommonPrefix)) |
| 872 | return not_intrinsic; |
| 873 | auto II = lower_bound(TI->Names, BuiltinName); |
| 874 | if (II == std::end(TI->Names) || II->getName() != BuiltinName) |
| 875 | return not_intrinsic; |
| 876 | return II->IntrinsicID; |
| 877 | } |
| 878 | #endif // GET_LLVM_INTRINSIC_FOR_{}_BUILTIN |
| 879 | |
| 880 | )" , |
| 881 | Vals&: UpperCompilerName); |
| 882 | } |
| 883 | |
| 884 | static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/true>> |
| 885 | X("gen-intrinsic-enums" , "Generate intrinsic enums" ); |
| 886 | |
| 887 | static TableGen::Emitter::OptClass<IntrinsicEmitterOpt</*Enums=*/false>> |
| 888 | Y("gen-intrinsic-impl" , "Generate intrinsic implementation code" ); |
| 889 | |