From: Nikita Popov Date: Tue, 11 Oct 2022 11:56:06 +0000 (+0200) Subject: [Attributes] Make intrinsic attribute generation more flexible (NFC) X-Git-Tag: upstream/17.0.6~30874 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2a26a445b342b0fa6090e8e99bf1fd0f18f6f7df;p=platform%2Fupstream%2Fllvm.git [Attributes] Make intrinsic attribute generation more flexible (NFC) Currently attributes for intrinsics are emitted using the ArrayRef based constructor for AttributeLists. This works out fine for simple enum attributes, but doesn't really generalize to attributes that accept values. We're already doing something awkward for alignment attributes, and I'd like to have a cleaner solution to this with https://discourse.llvm.org/t/rfc-unify-memory-effect-attributes/65579 in mind. The new generation approach is to instead directly construct Attributes, giving us access to the full generality of that interface. To avoid significantly increasing the size of the generated code, we now also deduplicate the attribute sets. The code generated per unique AttributeList looks like this: case 204: { AS[0] = {1, getIntrinsicArgAttributeSet(C, 5)}; AS[1] = {AttributeList::FunctionIndex, getIntrinsicFnAttributeSet(C, 10)}; NumAttrs = 2; break; } and then the helper functions contain something like case 5: return AttributeSet::get(C, { Attribute::get(C, Attribute::NoCapture), }); and case 10: return AttributeSet::get(C, { Attribute::get(C, Attribute::NoUnwind), Attribute::get(C, Attribute::ArgMemOnly), }); A casualty of this change is the intrin-properties.td test, as I don't think that FileCheck allows matching this kind of output. Differential Revision: https://reviews.llvm.org/D135679 --- diff --git a/llvm/test/TableGen/intrin-properties.td b/llvm/test/TableGen/intrin-properties.td deleted file mode 100644 index 2e2e32d..0000000 --- a/llvm/test/TableGen/intrin-properties.td +++ /dev/null @@ -1,24 +0,0 @@ -// RUN: sed -e 's//ArgMemOnly/;s//ArgMemOnly/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//Cold/;s//Cold/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//Convergent/;s//Convergent/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//InaccessibleMemOnly/;s//InaccessibleMemOnly/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//InaccessibleMemOrArgMemOnly/;s//InaccessibleMemOrArgMemOnly/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//NoDuplicate/;s//NoDuplicate/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//NoFree/;s//NoFree/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//NoMem/;s//ReadNone/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//NoMerge/;s//NoMerge/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//NoReturn/;s//NoReturn/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//NoSync/;s//NoSync/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//ReadMem/;s//ReadOnly/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//Speculatable/;s//Speculatable/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//WillReturn/;s//WillReturn/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t -// RUN: sed -e 's//WriteMem/;s//WriteOnly/' < %s > %t; llvm-tblgen -gen-intrinsic-impl -I %p/../../include %t | FileCheck %t - -include "llvm/IR/Intrinsics.td" - -// CHECK: [[I:[0-9]+]], // llvm.tgtest.Intr -// CHECK: case [[I]]: -// CHECK-NEXT: Atts[] = {{.*}}Attribute:: -def int_tgtest_Intr: - Intrinsic<[llvm_i32_ty], [llvm_ptr_ty], [Intr]>; - diff --git a/llvm/test/TableGen/intrin-side-effects.td b/llvm/test/TableGen/intrin-side-effects.td index 694fd6cd..35b7dc9 100644 --- a/llvm/test/TableGen/intrin-side-effects.td +++ b/llvm/test/TableGen/intrin-side-effects.td @@ -40,6 +40,12 @@ class Intrinsic ret_types, // ... this intrinsic. def int_random_gen : Intrinsic<[llvm_i32_ty], [], [IntrNoMem, IntrHasSideEffects]>; +// CHECK: static AttributeSet getIntrinsicFnAttributeSet( +// CHECK: case 0: +// CHECK-NEXT: return AttributeSet::get(C, { +// CHECK-NEXT: Attribute::get(C, Attribute::NoUnwind), +// CHECK-NEXT: }); + // CHECK: 1, // llvm.random.gen // CHECK: case 1: -// CHECK-NEXT: Atts[] = {Attribute::NoUnwind} +// CHECK-NEXT: AS[0] = {AttributeList::FunctionIndex, getIntrinsicFnAttributeSet(C, 0)}; diff --git a/llvm/utils/TableGen/CodeGenIntrinsics.h b/llvm/utils/TableGen/CodeGenIntrinsics.h index 41e2365..5258b3a 100644 --- a/llvm/utils/TableGen/CodeGenIntrinsics.h +++ b/llvm/utils/TableGen/CodeGenIntrinsics.h @@ -219,6 +219,8 @@ public: bool empty() const { return Intrinsics.empty(); } size_t size() const { return Intrinsics.size(); } + auto begin() const { return Intrinsics.begin(); } + auto end() const { return Intrinsics.end(); } CodeGenIntrinsic &operator[](size_t Pos) { return Intrinsics[Pos]; } const CodeGenIntrinsic &operator[](size_t Pos) const { return Intrinsics[Pos]; diff --git a/llvm/utils/TableGen/IntrinsicEmitter.cpp b/llvm/utils/TableGen/IntrinsicEmitter.cpp index a568560..ec3023f 100644 --- a/llvm/utils/TableGen/IntrinsicEmitter.cpp +++ b/llvm/utils/TableGen/IntrinsicEmitter.cpp @@ -14,6 +14,7 @@ #include "CodeGenTarget.h" #include "SequenceToOffsetTable.h" #include "TableGenBackends.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/TableGen/Error.h" @@ -601,49 +602,64 @@ void IntrinsicEmitter::EmitGenerator(const CodeGenIntrinsicTable &Ints, } namespace { -struct AttributeComparator { - bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const { - // Sort throwing intrinsics after non-throwing intrinsics. - if (L->canThrow != R->canThrow) - return R->canThrow; +Optional compareFnAttributes(const CodeGenIntrinsic *L, + const CodeGenIntrinsic *R) { + // Sort throwing intrinsics after non-throwing intrinsics. + if (L->canThrow != R->canThrow) + return R->canThrow; + + if (L->isNoDuplicate != R->isNoDuplicate) + return R->isNoDuplicate; + + if (L->isNoMerge != R->isNoMerge) + return R->isNoMerge; - if (L->isNoDuplicate != R->isNoDuplicate) - return R->isNoDuplicate; + if (L->isNoReturn != R->isNoReturn) + return R->isNoReturn; - if (L->isNoMerge != R->isNoMerge) - return R->isNoMerge; + if (L->isNoCallback != R->isNoCallback) + return R->isNoCallback; - if (L->isNoReturn != R->isNoReturn) - return R->isNoReturn; + if (L->isNoSync != R->isNoSync) + return R->isNoSync; - if (L->isNoCallback != R->isNoCallback) - return R->isNoCallback; + if (L->isNoFree != R->isNoFree) + return R->isNoFree; - if (L->isNoSync != R->isNoSync) - return R->isNoSync; + if (L->isWillReturn != R->isWillReturn) + return R->isWillReturn; - if (L->isNoFree != R->isNoFree) - return R->isNoFree; + if (L->isCold != R->isCold) + return R->isCold; - if (L->isWillReturn != R->isWillReturn) - return R->isWillReturn; + if (L->isConvergent != R->isConvergent) + return R->isConvergent; - if (L->isCold != R->isCold) - return R->isCold; + if (L->isSpeculatable != R->isSpeculatable) + return R->isSpeculatable; - if (L->isConvergent != R->isConvergent) - return R->isConvergent; + if (L->hasSideEffects != R->hasSideEffects) + return R->hasSideEffects; - if (L->isSpeculatable != R->isSpeculatable) - return R->isSpeculatable; + // Try to order by readonly/readnone attribute. + CodeGenIntrinsic::ModRefBehavior LK = L->ModRef; + CodeGenIntrinsic::ModRefBehavior RK = R->ModRef; + if (LK != RK) return (LK > RK); - if (L->hasSideEffects != R->hasSideEffects) - return R->hasSideEffects; + return None; +} + +struct FnAttributeComparator { + bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const { + return compareFnAttributes(L, R).value_or(false); + } +}; + +struct AttributeComparator { + bool operator()(const CodeGenIntrinsic *L, const CodeGenIntrinsic *R) const { + if (Optional Res = compareFnAttributes(L, R)) + return *Res; - // Try to order by readonly/readnone attribute. - CodeGenIntrinsic::ModRefBehavior LK = L->ModRef; - CodeGenIntrinsic::ModRefBehavior RK = R->ModRef; - if (LK != RK) return (LK > RK); // Order by argument attributes. // This is reliable because each side is already sorted internally. return (L->ArgumentAttributes < R->ArgumentAttributes); @@ -656,6 +672,161 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, raw_ostream &OS) { OS << "// Add parameter attributes that are not common to all intrinsics.\n"; OS << "#ifdef GET_INTRINSIC_ATTRIBUTES\n"; + + // Compute unique argument attribute sets. + std::map, unsigned> + UniqArgAttributes; + OS << "static AttributeSet getIntrinsicArgAttributeSet(" + << "LLVMContext &C, unsigned ID) {\n" + << " switch (ID) {\n" + << " default: llvm_unreachable(\"Invalid attribute set number\");\n"; + 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 << " case " << ID << ":\n"; + OS << " return AttributeSet::get(C, {\n"; + for (const CodeGenIntrinsic::ArgAttribute &Attr : Attrs) { + switch (Attr.Kind) { + case CodeGenIntrinsic::NoCapture: + OS << " Attribute::get(C, Attribute::NoCapture),\n"; + break; + case CodeGenIntrinsic::NoAlias: + OS << " Attribute::get(C, Attribute::NoAlias),\n"; + break; + case CodeGenIntrinsic::NoUndef: + OS << " Attribute::get(C, Attribute::NoUndef),\n"; + break; + case CodeGenIntrinsic::NonNull: + OS << " Attribute::get(C, Attribute::NonNull),\n"; + break; + case CodeGenIntrinsic::Returned: + OS << " Attribute::get(C, Attribute::Returned),\n"; + break; + case CodeGenIntrinsic::ReadOnly: + OS << " Attribute::get(C, Attribute::ReadOnly),\n"; + break; + case CodeGenIntrinsic::WriteOnly: + OS << " Attribute::get(C, Attribute::WriteOnly),\n"; + break; + case CodeGenIntrinsic::ReadNone: + OS << " Attribute::get(C, Attribute::ReadNone),\n"; + break; + case CodeGenIntrinsic::ImmArg: + OS << " Attribute::get(C, Attribute::ImmArg),\n"; + break; + case CodeGenIntrinsic::Alignment: + OS << " Attribute::get(C, Attribute::Alignment, " + << Attr.Value << "),\n"; + break; + } + } + OS << " });\n"; + } + } + OS << " }\n"; + OS << "}\n\n"; + + // Compute unique function attribute sets. + std::map + UniqFnAttributes; + OS << "static AttributeSet getIntrinsicFnAttributeSet(" + << "LLVMContext &C, unsigned ID) {\n" + << " switch (ID) {\n" + << " default: llvm_unreachable(\"Invalid attribute set number\");\n"; + for (const CodeGenIntrinsic &Intrinsic : Ints) { + unsigned ID = UniqFnAttributes.size(); + if (!UniqFnAttributes.try_emplace(&Intrinsic, ID).second) + continue; + + OS << " case " << ID << ":\n" + << " return AttributeSet::get(C, {\n"; + if (!Intrinsic.canThrow) + OS << " Attribute::get(C, Attribute::NoUnwind),\n"; + if (Intrinsic.isNoReturn) + OS << " Attribute::get(C, Attribute::NoReturn),\n"; + if (Intrinsic.isNoCallback) + OS << " Attribute::get(C, Attribute::NoCallback),\n"; + if (Intrinsic.isNoSync) + OS << " Attribute::get(C, Attribute::NoSync),\n"; + if (Intrinsic.isNoFree) + OS << " Attribute::get(C, Attribute::NoFree),\n"; + if (Intrinsic.isWillReturn) + OS << " Attribute::get(C, Attribute::WillReturn),\n"; + if (Intrinsic.isCold) + OS << " Attribute::get(C, Attribute::Cold),\n"; + if (Intrinsic.isNoDuplicate) + OS << " Attribute::get(C, Attribute::NoDuplicate),\n"; + if (Intrinsic.isNoMerge) + OS << " Attribute::get(C, Attribute::NoMerge),\n"; + if (Intrinsic.isConvergent) + OS << " Attribute::get(C, Attribute::Convergent),\n"; + if (Intrinsic.isSpeculatable) + OS << " Attribute::get(C, Attribute::Speculatable),\n"; + + switch (Intrinsic.ModRef) { + case CodeGenIntrinsic::NoMem: + if (Intrinsic.hasSideEffects) + break; + OS << " Attribute::get(C, Attribute::ReadNone),\n"; + break; + case CodeGenIntrinsic::ReadArgMem: + OS << " Attribute::get(C, Attribute::ReadOnly),\n"; + OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + break; + case CodeGenIntrinsic::ReadMem: + OS << " Attribute::get(C, Attribute::ReadOnly),\n"; + break; + case CodeGenIntrinsic::ReadInaccessibleMem: + OS << " Attribute::get(C, Attribute::ReadOnly),\n"; + OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + break; + case CodeGenIntrinsic::ReadInaccessibleMemOrArgMem: + OS << " Attribute::get(C, Attribute::ReadOnly),\n"; + OS << " Attribute::get(C, " + << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + break; + case CodeGenIntrinsic::WriteArgMem: + OS << " Attribute::get(C, Attribute::WriteOnly),\n"; + OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + break; + case CodeGenIntrinsic::WriteMem: + OS << " Attribute::get(C, Attribute::WriteOnly),\n"; + break; + case CodeGenIntrinsic::WriteInaccessibleMem: + OS << " Attribute::get(C, Attribute::WriteOnly),\n"; + OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + break; + case CodeGenIntrinsic::WriteInaccessibleMemOrArgMem: + OS << " Attribute::get(C, Attribute::WriteOnly),\n"; + OS << " Attribute::get(C, " + << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + break; + case CodeGenIntrinsic::ReadWriteArgMem: + OS << " Attribute::get(C, Attribute::ArgMemOnly),\n"; + break; + case CodeGenIntrinsic::ReadWriteInaccessibleMem: + OS << " Attribute::get(C, Attribute::InaccessibleMemOnly),\n"; + break; + case CodeGenIntrinsic::ReadWriteInaccessibleMemOrArgMem: + OS << " Attribute::get(C, " + << "Attribute::InaccessibleMemOrArgMemOnly),\n"; + break; + case CodeGenIntrinsic::ReadWriteMem: + break; + } + OS << " });\n"; + } + OS << " }\n"; + OS << "}\n\n"; OS << "AttributeList Intrinsic::getAttributes(LLVMContext &C, ID id) {\n"; // Compute the maximum number of attribute arguments and the map @@ -686,7 +857,7 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, } OS << " };\n\n"; - OS << " AttributeList AS[" << maxArgAttrs + 1 << "];\n"; + OS << " std::pair AS[" << maxArgAttrs + 1 << "];\n"; OS << " unsigned NumAttrs = 0;\n"; OS << " if (id != 0) {\n"; OS << " switch(IntrinsicsToAttributesMap[id - 1]) {\n"; @@ -704,69 +875,9 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, if (Attrs.empty()) continue; - // The argument attributes are alreadys sorted by argument kind. - assert(is_sorted(Attrs) && - "Argument attributes are not sorted"); - - OS << " const Attribute::AttrKind AttrParam" << AttrIdx << "[] = {"; - ListSeparator LS(","); - bool AllValuesAreZero = true; - SmallVector Values; - for (const CodeGenIntrinsic::ArgAttribute &Attr : Attrs) { - switch (Attr.Kind) { - case CodeGenIntrinsic::NoCapture: - OS << LS << "Attribute::NoCapture"; - break; - case CodeGenIntrinsic::NoAlias: - OS << LS << "Attribute::NoAlias"; - break; - case CodeGenIntrinsic::NoUndef: - OS << LS << "Attribute::NoUndef"; - break; - case CodeGenIntrinsic::NonNull: - OS << LS << "Attribute::NonNull"; - break; - case CodeGenIntrinsic::Returned: - OS << LS << "Attribute::Returned"; - break; - case CodeGenIntrinsic::ReadOnly: - OS << LS << "Attribute::ReadOnly"; - break; - case CodeGenIntrinsic::WriteOnly: - OS << LS << "Attribute::WriteOnly"; - break; - case CodeGenIntrinsic::ReadNone: - OS << LS << "Attribute::ReadNone"; - break; - case CodeGenIntrinsic::ImmArg: - OS << LS << "Attribute::ImmArg"; - break; - case CodeGenIntrinsic::Alignment: - OS << LS << "Attribute::Alignment"; - break; - } - - Values.push_back(Attr.Value); - AllValuesAreZero &= (Attr.Value == 0); - } - - OS << "};\n"; - - // Generate attribute value array if not all attribute values are zero. - if (!AllValuesAreZero) { - OS << " const uint64_t AttrValParam" << AttrIdx << "[]= {"; - ListSeparator LSV(","); - for (const auto V : Values) - OS << LSV << V; - OS << "};\n"; - } - // AttributeList::ReturnIndex = 0, AttrParam0 corresponds to return - // value. - OS << " AS[" << numAttrs++ << "] = AttributeList::get(C, " - << AttrIdx << ", AttrParam" << AttrIdx; - if (!AllValuesAreZero) - OS << ", AttrValParam" << AttrIdx; - OS << ");\n"; + unsigned ID = UniqArgAttributes.find(Attrs)->second; + OS << " AS[" << numAttrs++ << "] = {" << AttrIdx + << ", getIntrinsicArgAttributeSet(C, " << ID << ")};\n"; } if (!Intrinsic.canThrow || @@ -776,103 +887,18 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints, Intrinsic.isNoFree || Intrinsic.isWillReturn || Intrinsic.isCold || Intrinsic.isNoDuplicate || Intrinsic.isNoMerge || Intrinsic.isConvergent || Intrinsic.isSpeculatable) { - OS << " const Attribute::AttrKind Atts[] = {"; - ListSeparator LS(","); - if (!Intrinsic.canThrow) - OS << LS << "Attribute::NoUnwind"; - if (Intrinsic.isNoReturn) - OS << LS << "Attribute::NoReturn"; - if (Intrinsic.isNoCallback) - OS << LS << "Attribute::NoCallback"; - if (Intrinsic.isNoSync) - OS << LS << "Attribute::NoSync"; - if (Intrinsic.isNoFree) - OS << LS << "Attribute::NoFree"; - if (Intrinsic.isWillReturn) - OS << LS << "Attribute::WillReturn"; - if (Intrinsic.isCold) - OS << LS << "Attribute::Cold"; - if (Intrinsic.isNoDuplicate) - OS << LS << "Attribute::NoDuplicate"; - if (Intrinsic.isNoMerge) - OS << LS << "Attribute::NoMerge"; - if (Intrinsic.isConvergent) - OS << LS << "Attribute::Convergent"; - if (Intrinsic.isSpeculatable) - OS << LS << "Attribute::Speculatable"; - - switch (Intrinsic.ModRef) { - case CodeGenIntrinsic::NoMem: - if (Intrinsic.hasSideEffects) - break; - OS << LS; - OS << "Attribute::ReadNone"; - break; - case CodeGenIntrinsic::ReadArgMem: - OS << LS; - OS << "Attribute::ReadOnly,"; - OS << "Attribute::ArgMemOnly"; - break; - case CodeGenIntrinsic::ReadMem: - OS << LS; - OS << "Attribute::ReadOnly"; - break; - case CodeGenIntrinsic::ReadInaccessibleMem: - OS << LS; - OS << "Attribute::ReadOnly,"; - OS << "Attribute::InaccessibleMemOnly"; - break; - case CodeGenIntrinsic::ReadInaccessibleMemOrArgMem: - OS << LS; - OS << "Attribute::ReadOnly,"; - OS << "Attribute::InaccessibleMemOrArgMemOnly"; - break; - case CodeGenIntrinsic::WriteArgMem: - OS << LS; - OS << "Attribute::WriteOnly,"; - OS << "Attribute::ArgMemOnly"; - break; - case CodeGenIntrinsic::WriteMem: - OS << LS; - OS << "Attribute::WriteOnly"; - break; - case CodeGenIntrinsic::WriteInaccessibleMem: - OS << LS; - OS << "Attribute::WriteOnly,"; - OS << "Attribute::InaccessibleMemOnly"; - break; - case CodeGenIntrinsic::WriteInaccessibleMemOrArgMem: - OS << LS; - OS << "Attribute::WriteOnly,"; - OS << "Attribute::InaccessibleMemOrArgMemOnly"; - break; - case CodeGenIntrinsic::ReadWriteArgMem: - OS << LS; - OS << "Attribute::ArgMemOnly"; - break; - case CodeGenIntrinsic::ReadWriteInaccessibleMem: - OS << LS; - OS << "Attribute::InaccessibleMemOnly"; - break; - case CodeGenIntrinsic::ReadWriteInaccessibleMemOrArgMem: - OS << LS; - OS << "Attribute::InaccessibleMemOrArgMemOnly"; - break; - case CodeGenIntrinsic::ReadWriteMem: - break; - } - OS << "};\n"; - OS << " AS[" << numAttrs++ << "] = AttributeList::get(C, " - << "AttributeList::FunctionIndex, Atts);\n"; + unsigned ID = UniqFnAttributes.find(&Intrinsic)->second; + OS << " AS[" << numAttrs++ << "] = {AttributeList::FunctionIndex, " + << "getIntrinsicFnAttributeSet(C, " << ID << ")};\n"; } if (numAttrs) { OS << " NumAttrs = " << numAttrs << ";\n"; OS << " break;\n"; - OS << " }\n"; + OS << " }\n"; } else { OS << " return AttributeList();\n"; - OS << " }\n"; + OS << " }\n"; } }