Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 1 | //===- SearchableTableEmitter.cpp - Generate efficiently searchable tables -==// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This tablegen backend emits a generic array initialized by specified fields, |
| 10 | // together with companion index tables and lookup functions (binary search, |
| 11 | // currently). |
| 12 | // |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 15 | #include "llvm/ADT/DenseMap.h" |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 16 | #include "llvm/ADT/StringExtras.h" |
| 17 | #include "llvm/Support/Format.h" |
| 18 | #include "llvm/Support/MemoryBuffer.h" |
| 19 | #include "llvm/Support/SourceMgr.h" |
| 20 | #include "llvm/TableGen/Error.h" |
| 21 | #include "llvm/TableGen/Record.h" |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 22 | #include "CodeGenIntrinsics.h" |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 23 | #include <algorithm> |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 24 | #include <set> |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 25 | #include <string> |
| 26 | #include <vector> |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 27 | |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 28 | using namespace llvm; |
| 29 | |
| 30 | #define DEBUG_TYPE "searchable-table-emitter" |
| 31 | |
| 32 | namespace { |
| 33 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 34 | struct GenericTable; |
| 35 | |
| 36 | int getAsInt(Init *B) { |
| 37 | return cast<IntInit>(B->convertInitializerTo(IntRecTy::get()))->getValue(); |
| 38 | } |
| 39 | int getInt(Record *R, StringRef Field) { |
| 40 | return getAsInt(R->getValueInit(Field)); |
| 41 | } |
| 42 | |
| 43 | struct GenericEnum { |
| 44 | using Entry = std::pair<StringRef, int64_t>; |
| 45 | |
| 46 | std::string Name; |
Simon Pilgrim | 612810e | 2019-11-09 17:02:51 +0000 | [diff] [blame] | 47 | Record *Class = nullptr; |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 48 | std::string PreprocessorGuard; |
| 49 | std::vector<std::unique_ptr<Entry>> Entries; |
| 50 | DenseMap<Record *, Entry *> EntryMap; |
| 51 | }; |
| 52 | |
| 53 | struct GenericField { |
| 54 | std::string Name; |
| 55 | RecTy *RecType = nullptr; |
| 56 | bool IsIntrinsic = false; |
| 57 | bool IsInstruction = false; |
| 58 | GenericEnum *Enum = nullptr; |
| 59 | |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 60 | GenericField(StringRef Name) : Name(std::string(Name)) {} |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 61 | }; |
| 62 | |
| 63 | struct SearchIndex { |
| 64 | std::string Name; |
| 65 | SmallVector<GenericField, 1> Fields; |
Simon Pilgrim | 612810e | 2019-11-09 17:02:51 +0000 | [diff] [blame] | 66 | bool EarlyOut = false; |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 67 | }; |
| 68 | |
| 69 | struct GenericTable { |
| 70 | std::string Name; |
| 71 | std::string PreprocessorGuard; |
| 72 | std::string CppTypeName; |
| 73 | SmallVector<GenericField, 2> Fields; |
| 74 | std::vector<Record *> Entries; |
| 75 | |
| 76 | std::unique_ptr<SearchIndex> PrimaryKey; |
| 77 | SmallVector<std::unique_ptr<SearchIndex>, 2> Indices; |
| 78 | |
| 79 | const GenericField *getFieldByName(StringRef Name) const { |
| 80 | for (const auto &Field : Fields) { |
| 81 | if (Name == Field.Name) |
| 82 | return &Field; |
| 83 | } |
| 84 | return nullptr; |
| 85 | } |
| 86 | }; |
| 87 | |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 88 | class SearchableTableEmitter { |
| 89 | RecordKeeper &Records; |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 90 | DenseMap<Init *, std::unique_ptr<CodeGenIntrinsic>> Intrinsics; |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 91 | std::vector<std::unique_ptr<GenericEnum>> Enums; |
| 92 | DenseMap<Record *, GenericEnum *> EnumMap; |
| 93 | std::set<std::string> PreprocessorGuards; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 94 | |
| 95 | public: |
| 96 | SearchableTableEmitter(RecordKeeper &R) : Records(R) {} |
| 97 | |
| 98 | void run(raw_ostream &OS); |
| 99 | |
| 100 | private: |
| 101 | typedef std::pair<Init *, int> SearchTableEntry; |
| 102 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 103 | enum TypeContext { |
| 104 | TypeInStaticStruct, |
| 105 | TypeInTempStruct, |
| 106 | TypeInArgument, |
| 107 | }; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 108 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 109 | std::string primaryRepresentation(const GenericField &Field, Init *I) { |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 110 | if (StringInit *SI = dyn_cast<StringInit>(I)) |
| 111 | return SI->getAsString(); |
| 112 | else if (BitsInit *BI = dyn_cast<BitsInit>(I)) |
| 113 | return "0x" + utohexstr(getAsInt(BI)); |
| 114 | else if (BitInit *BI = dyn_cast<BitInit>(I)) |
| 115 | return BI->getValue() ? "true" : "false"; |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 116 | else if (CodeInit *CI = dyn_cast<CodeInit>(I)) |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 117 | return std::string(CI->getValue()); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 118 | else if (Field.IsIntrinsic) |
| 119 | return "Intrinsic::" + getIntrinsic(I).EnumName; |
| 120 | else if (Field.IsInstruction) |
| 121 | return I->getAsString(); |
| 122 | else if (Field.Enum) |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 123 | return std::string( |
| 124 | Field.Enum->EntryMap[cast<DefInit>(I)->getDef()]->first); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 125 | PrintFatalError(Twine("invalid field type for field '") + Field.Name + |
| 126 | "', expected: string, bits, bit or code"); |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 127 | } |
| 128 | |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 129 | bool isIntrinsic(Init *I) { |
| 130 | if (DefInit *DI = dyn_cast<DefInit>(I)) |
| 131 | return DI->getDef()->isSubClassOf("Intrinsic"); |
| 132 | return false; |
| 133 | } |
| 134 | |
| 135 | CodeGenIntrinsic &getIntrinsic(Init *I) { |
| 136 | std::unique_ptr<CodeGenIntrinsic> &Intr = Intrinsics[I]; |
| 137 | if (!Intr) |
Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 138 | Intr = std::make_unique<CodeGenIntrinsic>(cast<DefInit>(I)->getDef()); |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 139 | return *Intr; |
| 140 | } |
| 141 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 142 | bool compareBy(Record *LHS, Record *RHS, const SearchIndex &Index); |
| 143 | |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 144 | bool isIntegral(Init *I) { |
Stanislav Mekhanoshin | ec42fc1 | 2019-05-13 21:59:03 +0000 | [diff] [blame] | 145 | return isa<BitsInit>(I) || isa<CodeInit>(I) || isIntrinsic(I); |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 146 | } |
| 147 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 148 | std::string searchableFieldType(const GenericField &Field, TypeContext Ctx) { |
| 149 | if (isa<StringRecTy>(Field.RecType)) { |
| 150 | if (Ctx == TypeInStaticStruct) |
| 151 | return "const char *"; |
| 152 | if (Ctx == TypeInTempStruct) |
| 153 | return "std::string"; |
| 154 | return "StringRef"; |
| 155 | } else if (BitsRecTy *BI = dyn_cast<BitsRecTy>(Field.RecType)) { |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 156 | unsigned NumBits = BI->getNumBits(); |
| 157 | if (NumBits <= 8) |
Nicolai Haehnle | 6feb62a | 2018-10-31 17:46:21 +0000 | [diff] [blame] | 158 | return "uint8_t"; |
| 159 | if (NumBits <= 16) |
| 160 | return "uint16_t"; |
| 161 | if (NumBits <= 32) |
| 162 | return "uint32_t"; |
| 163 | if (NumBits <= 64) |
| 164 | return "uint64_t"; |
| 165 | PrintFatalError(Twine("bitfield '") + Field.Name + |
| 166 | "' too large to search"); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 167 | } else if (Field.Enum || Field.IsIntrinsic || Field.IsInstruction) |
Nicolai Haehnle | 398c0b6 | 2018-04-01 17:08:58 +0000 | [diff] [blame] | 168 | return "unsigned"; |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 169 | PrintFatalError(Twine("Field '") + Field.Name + "' has unknown type '" + |
| 170 | Field.RecType->getAsString() + "' to search by"); |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 171 | } |
| 172 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 173 | void emitGenericTable(const GenericTable &Table, raw_ostream &OS); |
| 174 | void emitGenericEnum(const GenericEnum &Enum, raw_ostream &OS); |
| 175 | void emitLookupDeclaration(const GenericTable &Table, |
| 176 | const SearchIndex &Index, raw_ostream &OS); |
| 177 | void emitLookupFunction(const GenericTable &Table, const SearchIndex &Index, |
| 178 | bool IsPrimary, raw_ostream &OS); |
| 179 | void emitIfdef(StringRef Guard, raw_ostream &OS); |
| 180 | |
| 181 | bool parseFieldType(GenericField &Field, Init *II); |
| 182 | std::unique_ptr<SearchIndex> |
| 183 | parseSearchIndex(GenericTable &Table, StringRef Name, |
| 184 | const std::vector<StringRef> &Key, bool EarlyOut); |
| 185 | void collectEnumEntries(GenericEnum &Enum, StringRef NameField, |
| 186 | StringRef ValueField, |
| 187 | const std::vector<Record *> &Items); |
| 188 | void collectTableEntries(GenericTable &Table, |
| 189 | const std::vector<Record *> &Items); |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 190 | }; |
| 191 | |
| 192 | } // End anonymous namespace. |
| 193 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 194 | // For search indices that consists of a single field whose numeric value is |
| 195 | // known, return that numeric value. |
| 196 | static int64_t getNumericKey(const SearchIndex &Index, Record *Rec) { |
| 197 | assert(Index.Fields.size() == 1); |
| 198 | |
| 199 | if (Index.Fields[0].Enum) { |
| 200 | Record *EnumEntry = Rec->getValueAsDef(Index.Fields[0].Name); |
| 201 | return Index.Fields[0].Enum->EntryMap[EnumEntry]->second; |
| 202 | } |
| 203 | |
| 204 | return getInt(Rec, Index.Fields[0].Name); |
| 205 | } |
| 206 | |
| 207 | /// Less-than style comparison between \p LHS and \p RHS according to the |
| 208 | /// key of \p Index. |
| 209 | bool SearchableTableEmitter::compareBy(Record *LHS, Record *RHS, |
| 210 | const SearchIndex &Index) { |
| 211 | for (const auto &Field : Index.Fields) { |
| 212 | Init *LHSI = LHS->getValueInit(Field.Name); |
| 213 | Init *RHSI = RHS->getValueInit(Field.Name); |
| 214 | |
| 215 | if (isa<BitsRecTy>(Field.RecType) || isa<IntRecTy>(Field.RecType)) { |
| 216 | int64_t LHSi = getAsInt(LHSI); |
| 217 | int64_t RHSi = getAsInt(RHSI); |
| 218 | if (LHSi < RHSi) |
| 219 | return true; |
| 220 | if (LHSi > RHSi) |
| 221 | return false; |
| 222 | } else if (Field.IsIntrinsic) { |
| 223 | CodeGenIntrinsic &LHSi = getIntrinsic(LHSI); |
| 224 | CodeGenIntrinsic &RHSi = getIntrinsic(RHSI); |
| 225 | if (std::tie(LHSi.TargetPrefix, LHSi.Name) < |
| 226 | std::tie(RHSi.TargetPrefix, RHSi.Name)) |
| 227 | return true; |
| 228 | if (std::tie(LHSi.TargetPrefix, LHSi.Name) > |
| 229 | std::tie(RHSi.TargetPrefix, RHSi.Name)) |
| 230 | return false; |
| 231 | } else if (Field.IsInstruction) { |
| 232 | // This does not correctly compare the predefined instructions! |
| 233 | Record *LHSr = cast<DefInit>(LHSI)->getDef(); |
| 234 | Record *RHSr = cast<DefInit>(RHSI)->getDef(); |
| 235 | |
| 236 | bool LHSpseudo = LHSr->getValueAsBit("isPseudo"); |
| 237 | bool RHSpseudo = RHSr->getValueAsBit("isPseudo"); |
| 238 | if (LHSpseudo && !RHSpseudo) |
| 239 | return true; |
| 240 | if (!LHSpseudo && RHSpseudo) |
| 241 | return false; |
| 242 | |
| 243 | int comp = LHSr->getName().compare(RHSr->getName()); |
| 244 | if (comp < 0) |
| 245 | return true; |
| 246 | if (comp > 0) |
| 247 | return false; |
| 248 | } else if (Field.Enum) { |
| 249 | auto LHSr = cast<DefInit>(LHSI)->getDef(); |
| 250 | auto RHSr = cast<DefInit>(RHSI)->getDef(); |
| 251 | int64_t LHSv = Field.Enum->EntryMap[LHSr]->second; |
| 252 | int64_t RHSv = Field.Enum->EntryMap[RHSr]->second; |
| 253 | if (LHSv < RHSv) |
| 254 | return true; |
| 255 | if (LHSv > RHSv) |
| 256 | return false; |
| 257 | } else { |
| 258 | std::string LHSs = primaryRepresentation(Field, LHSI); |
| 259 | std::string RHSs = primaryRepresentation(Field, RHSI); |
| 260 | |
| 261 | if (isa<StringRecTy>(Field.RecType)) { |
| 262 | LHSs = StringRef(LHSs).upper(); |
| 263 | RHSs = StringRef(RHSs).upper(); |
| 264 | } |
| 265 | |
| 266 | int comp = LHSs.compare(RHSs); |
| 267 | if (comp < 0) |
| 268 | return true; |
| 269 | if (comp > 0) |
| 270 | return false; |
| 271 | } |
| 272 | } |
| 273 | return false; |
| 274 | } |
| 275 | |
| 276 | void SearchableTableEmitter::emitIfdef(StringRef Guard, raw_ostream &OS) { |
| 277 | OS << "#ifdef " << Guard << "\n"; |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 278 | PreprocessorGuards.insert(std::string(Guard)); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 279 | } |
| 280 | |
| 281 | /// Emit a generic enum. |
| 282 | void SearchableTableEmitter::emitGenericEnum(const GenericEnum &Enum, |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 283 | raw_ostream &OS) { |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 284 | emitIfdef((Twine("GET_") + Enum.PreprocessorGuard + "_DECL").str(), OS); |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 285 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 286 | OS << "enum " << Enum.Name << " {\n"; |
| 287 | for (const auto &Entry : Enum.Entries) |
| 288 | OS << " " << Entry->first << " = " << Entry->second << ",\n"; |
| 289 | OS << "};\n"; |
| 290 | |
| 291 | OS << "#endif\n\n"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 292 | } |
| 293 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 294 | void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table, |
| 295 | const SearchIndex &Index, |
| 296 | bool IsPrimary, |
| 297 | raw_ostream &OS) { |
| 298 | OS << "\n"; |
| 299 | emitLookupDeclaration(Table, Index, OS); |
| 300 | OS << " {\n"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 301 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 302 | std::vector<Record *> IndexRowsStorage; |
| 303 | ArrayRef<Record *> IndexRows; |
| 304 | StringRef IndexTypeName; |
| 305 | StringRef IndexName; |
| 306 | |
| 307 | if (IsPrimary) { |
| 308 | IndexTypeName = Table.CppTypeName; |
| 309 | IndexName = Table.Name; |
| 310 | IndexRows = Table.Entries; |
| 311 | } else { |
| 312 | OS << " struct IndexType {\n"; |
| 313 | for (const auto &Field : Index.Fields) { |
| 314 | OS << " " << searchableFieldType(Field, TypeInStaticStruct) << " " |
| 315 | << Field.Name << ";\n"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 316 | } |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 317 | OS << " unsigned _index;\n"; |
| 318 | OS << " };\n"; |
| 319 | |
| 320 | OS << " static const struct IndexType Index[] = {\n"; |
| 321 | |
| 322 | std::vector<std::pair<Record *, unsigned>> Entries; |
| 323 | Entries.reserve(Table.Entries.size()); |
| 324 | for (unsigned i = 0; i < Table.Entries.size(); ++i) |
| 325 | Entries.emplace_back(Table.Entries[i], i); |
| 326 | |
| 327 | std::stable_sort(Entries.begin(), Entries.end(), |
| 328 | [&](const std::pair<Record *, unsigned> &LHS, |
| 329 | const std::pair<Record *, unsigned> &RHS) { |
| 330 | return compareBy(LHS.first, RHS.first, Index); |
| 331 | }); |
| 332 | |
| 333 | IndexRowsStorage.reserve(Entries.size()); |
| 334 | for (const auto &Entry : Entries) { |
| 335 | IndexRowsStorage.push_back(Entry.first); |
| 336 | |
| 337 | OS << " { "; |
| 338 | bool NeedComma = false; |
| 339 | for (const auto &Field : Index.Fields) { |
| 340 | if (NeedComma) |
| 341 | OS << ", "; |
| 342 | NeedComma = true; |
| 343 | |
| 344 | std::string Repr = |
| 345 | primaryRepresentation(Field, Entry.first->getValueInit(Field.Name)); |
| 346 | if (isa<StringRecTy>(Field.RecType)) |
| 347 | Repr = StringRef(Repr).upper(); |
| 348 | OS << Repr; |
| 349 | } |
| 350 | OS << ", " << Entry.second << " },\n"; |
| 351 | } |
| 352 | |
| 353 | OS << " };\n\n"; |
| 354 | |
| 355 | IndexTypeName = "IndexType"; |
| 356 | IndexName = "Index"; |
| 357 | IndexRows = IndexRowsStorage; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 358 | } |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 359 | |
| 360 | bool IsContiguous = false; |
| 361 | |
| 362 | if (Index.Fields.size() == 1 && |
| 363 | (Index.Fields[0].Enum || isa<BitsRecTy>(Index.Fields[0].RecType))) { |
| 364 | IsContiguous = true; |
| 365 | for (unsigned i = 0; i < IndexRows.size(); ++i) { |
| 366 | if (getNumericKey(Index, IndexRows[i]) != i) { |
| 367 | IsContiguous = false; |
| 368 | break; |
| 369 | } |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | if (IsContiguous) { |
| 374 | OS << " auto Table = makeArrayRef(" << IndexName << ");\n"; |
| 375 | OS << " size_t Idx = " << Index.Fields[0].Name << ";\n"; |
| 376 | OS << " return Idx >= Table.size() ? nullptr : "; |
| 377 | if (IsPrimary) |
| 378 | OS << "&Table[Idx]"; |
| 379 | else |
| 380 | OS << "&" << Table.Name << "[Table[Idx]._index]"; |
| 381 | OS << ";\n"; |
| 382 | OS << "}\n"; |
| 383 | return; |
| 384 | } |
| 385 | |
| 386 | if (Index.EarlyOut) { |
| 387 | const GenericField &Field = Index.Fields[0]; |
| 388 | std::string FirstRepr = |
| 389 | primaryRepresentation(Field, IndexRows[0]->getValueInit(Field.Name)); |
| 390 | std::string LastRepr = primaryRepresentation( |
| 391 | Field, IndexRows.back()->getValueInit(Field.Name)); |
| 392 | OS << " if ((" << Field.Name << " < " << FirstRepr << ") ||\n"; |
| 393 | OS << " (" << Field.Name << " > " << LastRepr << "))\n"; |
| 394 | OS << " return nullptr;\n\n"; |
| 395 | } |
| 396 | |
| 397 | OS << " struct KeyType {\n"; |
| 398 | for (const auto &Field : Index.Fields) { |
| 399 | OS << " " << searchableFieldType(Field, TypeInTempStruct) << " " |
| 400 | << Field.Name << ";\n"; |
| 401 | } |
| 402 | OS << " };\n"; |
| 403 | OS << " KeyType Key = { "; |
| 404 | bool NeedComma = false; |
| 405 | for (const auto &Field : Index.Fields) { |
| 406 | if (NeedComma) |
| 407 | OS << ", "; |
| 408 | NeedComma = true; |
| 409 | |
| 410 | OS << Field.Name; |
| 411 | if (isa<StringRecTy>(Field.RecType)) { |
| 412 | OS << ".upper()"; |
| 413 | if (IsPrimary) |
| 414 | PrintFatalError(Twine("Use a secondary index for case-insensitive " |
| 415 | "comparison of field '") + |
| 416 | Field.Name + "' in table '" + Table.Name + "'"); |
| 417 | } |
| 418 | } |
| 419 | OS << " };\n"; |
| 420 | |
| 421 | OS << " auto Table = makeArrayRef(" << IndexName << ");\n"; |
| 422 | OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,\n"; |
| 423 | OS << " [](const " << IndexTypeName << " &LHS, const KeyType &RHS) {\n"; |
| 424 | |
| 425 | for (const auto &Field : Index.Fields) { |
| 426 | if (isa<StringRecTy>(Field.RecType)) { |
| 427 | OS << " int Cmp" << Field.Name << " = StringRef(LHS." << Field.Name |
| 428 | << ").compare(RHS." << Field.Name << ");\n"; |
| 429 | OS << " if (Cmp" << Field.Name << " < 0) return true;\n"; |
| 430 | OS << " if (Cmp" << Field.Name << " > 0) return false;\n"; |
Nicolai Haehnle | ba9eee5 | 2018-08-23 08:02:02 +0000 | [diff] [blame] | 431 | } else if (Field.Enum) { |
| 432 | // Explicitly cast to unsigned, because the signedness of enums is |
| 433 | // compiler-dependent. |
| 434 | OS << " if ((unsigned)LHS." << Field.Name << " < (unsigned)RHS." |
| 435 | << Field.Name << ")\n"; |
| 436 | OS << " return true;\n"; |
| 437 | OS << " if ((unsigned)LHS." << Field.Name << " > (unsigned)RHS." |
| 438 | << Field.Name << ")\n"; |
| 439 | OS << " return false;\n"; |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 440 | } else { |
| 441 | OS << " if (LHS." << Field.Name << " < RHS." << Field.Name << ")\n"; |
| 442 | OS << " return true;\n"; |
| 443 | OS << " if (LHS." << Field.Name << " > RHS." << Field.Name << ")\n"; |
| 444 | OS << " return false;\n"; |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | OS << " return false;\n"; |
| 449 | OS << " });\n\n"; |
| 450 | |
| 451 | OS << " if (Idx == Table.end()"; |
| 452 | |
| 453 | for (const auto &Field : Index.Fields) |
| 454 | OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name; |
| 455 | OS << ")\n return nullptr;\n"; |
| 456 | |
| 457 | if (IsPrimary) |
| 458 | OS << " return &*Idx;\n"; |
| 459 | else |
| 460 | OS << " return &" << Table.Name << "[Idx->_index];\n"; |
| 461 | |
| 462 | OS << "}\n"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 463 | } |
| 464 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 465 | void SearchableTableEmitter::emitLookupDeclaration(const GenericTable &Table, |
| 466 | const SearchIndex &Index, |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 467 | raw_ostream &OS) { |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 468 | OS << "const " << Table.CppTypeName << " *" << Index.Name << "("; |
| 469 | |
| 470 | bool NeedComma = false; |
| 471 | for (const auto &Field : Index.Fields) { |
| 472 | if (NeedComma) |
| 473 | OS << ", "; |
| 474 | NeedComma = true; |
| 475 | |
| 476 | OS << searchableFieldType(Field, TypeInArgument) << " " << Field.Name; |
| 477 | } |
| 478 | OS << ")"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 479 | } |
| 480 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 481 | void SearchableTableEmitter::emitGenericTable(const GenericTable &Table, |
| 482 | raw_ostream &OS) { |
| 483 | emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_DECL").str(), OS); |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 484 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 485 | // Emit the declarations for the functions that will perform lookup. |
| 486 | if (Table.PrimaryKey) { |
| 487 | emitLookupDeclaration(Table, *Table.PrimaryKey, OS); |
| 488 | OS << ";\n"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 489 | } |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 490 | for (const auto &Index : Table.Indices) { |
| 491 | emitLookupDeclaration(Table, *Index, OS); |
| 492 | OS << ";\n"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 493 | } |
| 494 | |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 495 | OS << "#endif\n\n"; |
| 496 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 497 | emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_IMPL").str(), OS); |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 498 | |
| 499 | // The primary data table contains all the fields defined for this map. |
Benjamin Kramer | 16b3229 | 2019-08-24 15:02:44 +0000 | [diff] [blame] | 500 | OS << "constexpr " << Table.CppTypeName << " " << Table.Name << "[] = {\n"; |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 501 | for (unsigned i = 0; i < Table.Entries.size(); ++i) { |
| 502 | Record *Entry = Table.Entries[i]; |
| 503 | OS << " { "; |
| 504 | |
| 505 | bool NeedComma = false; |
| 506 | for (const auto &Field : Table.Fields) { |
| 507 | if (NeedComma) |
| 508 | OS << ", "; |
| 509 | NeedComma = true; |
| 510 | |
| 511 | OS << primaryRepresentation(Field, Entry->getValueInit(Field.Name)); |
| 512 | } |
| 513 | |
| 514 | OS << " }, // " << i << "\n"; |
| 515 | } |
| 516 | OS << " };\n"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 517 | |
| 518 | // Indexes are sorted "{ Thing, PrimaryIdx }" arrays, so that a binary |
| 519 | // search can be performed by "Thing". |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 520 | if (Table.PrimaryKey) |
| 521 | emitLookupFunction(Table, *Table.PrimaryKey, true, OS); |
| 522 | for (const auto &Index : Table.Indices) |
| 523 | emitLookupFunction(Table, *Index, false, OS); |
| 524 | |
| 525 | OS << "#endif\n\n"; |
| 526 | } |
| 527 | |
| 528 | bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *II) { |
| 529 | if (auto DI = dyn_cast<DefInit>(II)) { |
| 530 | Record *TypeRec = DI->getDef(); |
| 531 | if (TypeRec->isSubClassOf("GenericEnum")) { |
| 532 | Field.Enum = EnumMap[TypeRec]; |
| 533 | Field.RecType = RecordRecTy::get(Field.Enum->Class); |
| 534 | return true; |
| 535 | } |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 536 | } |
| 537 | |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 538 | return false; |
| 539 | } |
| 540 | |
| 541 | std::unique_ptr<SearchIndex> |
| 542 | SearchableTableEmitter::parseSearchIndex(GenericTable &Table, StringRef Name, |
| 543 | const std::vector<StringRef> &Key, |
| 544 | bool EarlyOut) { |
Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 545 | auto Index = std::make_unique<SearchIndex>(); |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 546 | Index->Name = std::string(Name); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 547 | Index->EarlyOut = EarlyOut; |
| 548 | |
| 549 | for (const auto &FieldName : Key) { |
| 550 | const GenericField *Field = Table.getFieldByName(FieldName); |
| 551 | if (!Field) |
| 552 | PrintFatalError(Twine("Search index '") + Name + |
| 553 | "' refers to non-existing field '" + FieldName + |
| 554 | "' in table '" + Table.Name + "'"); |
| 555 | Index->Fields.push_back(*Field); |
| 556 | } |
| 557 | |
| 558 | if (EarlyOut && isa<StringRecTy>(Index->Fields[0].RecType)) { |
| 559 | PrintFatalError( |
| 560 | "Early-out is not supported for string types (in search index '" + |
| 561 | Twine(Name) + "'"); |
| 562 | } |
| 563 | |
| 564 | return Index; |
| 565 | } |
| 566 | |
| 567 | void SearchableTableEmitter::collectEnumEntries( |
| 568 | GenericEnum &Enum, StringRef NameField, StringRef ValueField, |
| 569 | const std::vector<Record *> &Items) { |
| 570 | for (auto EntryRec : Items) { |
| 571 | StringRef Name; |
| 572 | if (NameField.empty()) |
| 573 | Name = EntryRec->getName(); |
| 574 | else |
| 575 | Name = EntryRec->getValueAsString(NameField); |
| 576 | |
| 577 | int64_t Value = 0; |
| 578 | if (!ValueField.empty()) |
| 579 | Value = getInt(EntryRec, ValueField); |
| 580 | |
Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 581 | Enum.Entries.push_back(std::make_unique<GenericEnum::Entry>(Name, Value)); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 582 | Enum.EntryMap.insert(std::make_pair(EntryRec, Enum.Entries.back().get())); |
| 583 | } |
| 584 | |
| 585 | if (ValueField.empty()) { |
| 586 | std::stable_sort(Enum.Entries.begin(), Enum.Entries.end(), |
| 587 | [](const std::unique_ptr<GenericEnum::Entry> &LHS, |
| 588 | const std::unique_ptr<GenericEnum::Entry> &RHS) { |
| 589 | return LHS->first < RHS->first; |
| 590 | }); |
| 591 | |
| 592 | for (size_t i = 0; i < Enum.Entries.size(); ++i) |
| 593 | Enum.Entries[i]->second = i; |
| 594 | } |
| 595 | } |
| 596 | |
| 597 | void SearchableTableEmitter::collectTableEntries( |
| 598 | GenericTable &Table, const std::vector<Record *> &Items) { |
| 599 | for (auto EntryRec : Items) { |
| 600 | for (auto &Field : Table.Fields) { |
| 601 | auto TI = dyn_cast<TypedInit>(EntryRec->getValueInit(Field.Name)); |
Jay Foad | a912275 | 2020-02-07 11:13:51 +0000 | [diff] [blame] | 602 | if (!TI || !TI->isComplete()) { |
Daniel Sanders | dff673b | 2019-02-12 17:36:57 +0000 | [diff] [blame] | 603 | PrintFatalError(EntryRec->getLoc(), |
| 604 | Twine("Record '") + EntryRec->getName() + |
| 605 | "' in table '" + Table.Name + |
| 606 | "' is missing field '" + Field.Name + "'"); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 607 | } |
| 608 | if (!Field.RecType) { |
| 609 | Field.RecType = TI->getType(); |
| 610 | } else { |
| 611 | RecTy *Ty = resolveTypes(Field.RecType, TI->getType()); |
| 612 | if (!Ty) |
| 613 | PrintFatalError(Twine("Field '") + Field.Name + "' of table '" + |
| 614 | Table.Name + "' has incompatible type: " + |
Simon Pilgrim | 41232d2 | 2019-04-29 17:41:27 +0000 | [diff] [blame] | 615 | Field.RecType->getAsString() + " vs. " + |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 616 | TI->getType()->getAsString()); |
| 617 | Field.RecType = Ty; |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | Table.Entries.push_back(EntryRec); |
| 622 | } |
| 623 | |
| 624 | Record *IntrinsicClass = Records.getClass("Intrinsic"); |
| 625 | Record *InstructionClass = Records.getClass("Instruction"); |
| 626 | for (auto &Field : Table.Fields) { |
| 627 | if (auto RecordTy = dyn_cast<RecordRecTy>(Field.RecType)) { |
| 628 | if (IntrinsicClass && RecordTy->isSubClassOf(IntrinsicClass)) |
| 629 | Field.IsIntrinsic = true; |
| 630 | else if (InstructionClass && RecordTy->isSubClassOf(InstructionClass)) |
| 631 | Field.IsInstruction = true; |
| 632 | } |
| 633 | } |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 634 | } |
| 635 | |
| 636 | void SearchableTableEmitter::run(raw_ostream &OS) { |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 637 | // Emit tables in a deterministic order to avoid needless rebuilds. |
| 638 | SmallVector<std::unique_ptr<GenericTable>, 4> Tables; |
| 639 | DenseMap<Record *, GenericTable *> TableMap; |
| 640 | |
| 641 | // Collect all definitions first. |
| 642 | for (auto EnumRec : Records.getAllDerivedDefinitions("GenericEnum")) { |
| 643 | StringRef NameField; |
| 644 | if (!EnumRec->isValueUnset("NameField")) |
| 645 | NameField = EnumRec->getValueAsString("NameField"); |
| 646 | |
| 647 | StringRef ValueField; |
| 648 | if (!EnumRec->isValueUnset("ValueField")) |
| 649 | ValueField = EnumRec->getValueAsString("ValueField"); |
| 650 | |
Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 651 | auto Enum = std::make_unique<GenericEnum>(); |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 652 | Enum->Name = std::string(EnumRec->getName()); |
| 653 | Enum->PreprocessorGuard = std::string(EnumRec->getName()); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 654 | |
| 655 | StringRef FilterClass = EnumRec->getValueAsString("FilterClass"); |
| 656 | Enum->Class = Records.getClass(FilterClass); |
| 657 | if (!Enum->Class) |
Daniel Sanders | dff673b | 2019-02-12 17:36:57 +0000 | [diff] [blame] | 658 | PrintFatalError(EnumRec->getLoc(), Twine("Enum FilterClass '") + |
| 659 | FilterClass + "' does not exist"); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 660 | |
| 661 | collectEnumEntries(*Enum, NameField, ValueField, |
| 662 | Records.getAllDerivedDefinitions(FilterClass)); |
| 663 | EnumMap.insert(std::make_pair(EnumRec, Enum.get())); |
| 664 | Enums.emplace_back(std::move(Enum)); |
| 665 | } |
| 666 | |
| 667 | for (auto TableRec : Records.getAllDerivedDefinitions("GenericTable")) { |
Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 668 | auto Table = std::make_unique<GenericTable>(); |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 669 | Table->Name = std::string(TableRec->getName()); |
| 670 | Table->PreprocessorGuard = std::string(TableRec->getName()); |
| 671 | Table->CppTypeName = std::string(TableRec->getValueAsString("CppTypeName")); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 672 | |
| 673 | std::vector<StringRef> Fields = TableRec->getValueAsListOfStrings("Fields"); |
| 674 | for (const auto &FieldName : Fields) { |
| 675 | Table->Fields.emplace_back(FieldName); |
| 676 | |
| 677 | if (auto TypeOfVal = TableRec->getValue(("TypeOf_" + FieldName).str())) { |
| 678 | if (!parseFieldType(Table->Fields.back(), TypeOfVal->getValue())) { |
Daniel Sanders | dff673b | 2019-02-12 17:36:57 +0000 | [diff] [blame] | 679 | PrintFatalError(TableRec->getLoc(), |
| 680 | Twine("Table '") + Table->Name + |
| 681 | "' has bad 'TypeOf_" + FieldName + |
| 682 | "': " + TypeOfVal->getValue()->getAsString()); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 683 | } |
| 684 | } |
| 685 | } |
| 686 | |
| 687 | collectTableEntries(*Table, Records.getAllDerivedDefinitions( |
| 688 | TableRec->getValueAsString("FilterClass"))); |
| 689 | |
| 690 | if (!TableRec->isValueUnset("PrimaryKey")) { |
| 691 | Table->PrimaryKey = |
| 692 | parseSearchIndex(*Table, TableRec->getValueAsString("PrimaryKeyName"), |
| 693 | TableRec->getValueAsListOfStrings("PrimaryKey"), |
| 694 | TableRec->getValueAsBit("PrimaryKeyEarlyOut")); |
| 695 | |
| 696 | std::stable_sort(Table->Entries.begin(), Table->Entries.end(), |
| 697 | [&](Record *LHS, Record *RHS) { |
| 698 | return compareBy(LHS, RHS, *Table->PrimaryKey); |
| 699 | }); |
| 700 | } |
| 701 | |
| 702 | TableMap.insert(std::make_pair(TableRec, Table.get())); |
| 703 | Tables.emplace_back(std::move(Table)); |
| 704 | } |
| 705 | |
| 706 | for (Record *IndexRec : Records.getAllDerivedDefinitions("SearchIndex")) { |
| 707 | Record *TableRec = IndexRec->getValueAsDef("Table"); |
| 708 | auto It = TableMap.find(TableRec); |
| 709 | if (It == TableMap.end()) |
Daniel Sanders | dff673b | 2019-02-12 17:36:57 +0000 | [diff] [blame] | 710 | PrintFatalError(IndexRec->getLoc(), |
| 711 | Twine("SearchIndex '") + IndexRec->getName() + |
| 712 | "' refers to non-existing table '" + |
| 713 | TableRec->getName()); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 714 | |
| 715 | GenericTable &Table = *It->second; |
| 716 | Table.Indices.push_back(parseSearchIndex( |
| 717 | Table, IndexRec->getName(), IndexRec->getValueAsListOfStrings("Key"), |
| 718 | IndexRec->getValueAsBit("EarlyOut"))); |
| 719 | } |
| 720 | |
| 721 | // Translate legacy tables. |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 722 | Record *SearchableTable = Records.getClass("SearchableTable"); |
| 723 | for (auto &NameRec : Records.getClasses()) { |
| 724 | Record *Class = NameRec.second.get(); |
| 725 | if (Class->getSuperClasses().size() != 1 || |
| 726 | !Class->isSubClassOf(SearchableTable)) |
| 727 | continue; |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 728 | |
| 729 | StringRef TableName = Class->getName(); |
| 730 | std::vector<Record *> Items = Records.getAllDerivedDefinitions(TableName); |
| 731 | if (!Class->isValueUnset("EnumNameField")) { |
| 732 | StringRef NameField = Class->getValueAsString("EnumNameField"); |
| 733 | StringRef ValueField; |
| 734 | if (!Class->isValueUnset("EnumValueField")) |
| 735 | ValueField = Class->getValueAsString("EnumValueField"); |
| 736 | |
Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 737 | auto Enum = std::make_unique<GenericEnum>(); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 738 | Enum->Name = (Twine(Class->getName()) + "Values").str(); |
| 739 | Enum->PreprocessorGuard = Class->getName().upper(); |
| 740 | Enum->Class = Class; |
| 741 | |
| 742 | collectEnumEntries(*Enum, NameField, ValueField, Items); |
| 743 | |
| 744 | Enums.emplace_back(std::move(Enum)); |
| 745 | } |
| 746 | |
Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 747 | auto Table = std::make_unique<GenericTable>(); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 748 | Table->Name = (Twine(Class->getName()) + "sList").str(); |
| 749 | Table->PreprocessorGuard = Class->getName().upper(); |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 750 | Table->CppTypeName = std::string(Class->getName()); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 751 | |
| 752 | for (const RecordVal &Field : Class->getValues()) { |
Benjamin Kramer | adcd026 | 2020-01-28 20:23:46 +0100 | [diff] [blame] | 753 | std::string FieldName = std::string(Field.getName()); |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 754 | |
| 755 | // Skip uninteresting fields: either special to us, or injected |
| 756 | // template parameters (if they contain a ':'). |
| 757 | if (FieldName.find(':') != std::string::npos || |
| 758 | FieldName == "SearchableFields" || FieldName == "EnumNameField" || |
| 759 | FieldName == "EnumValueField") |
| 760 | continue; |
| 761 | |
| 762 | Table->Fields.emplace_back(FieldName); |
| 763 | } |
| 764 | |
| 765 | collectTableEntries(*Table, Items); |
| 766 | |
| 767 | for (const auto &Field : |
| 768 | Class->getValueAsListOfStrings("SearchableFields")) { |
| 769 | std::string Name = |
| 770 | (Twine("lookup") + Table->CppTypeName + "By" + Field).str(); |
| 771 | Table->Indices.push_back(parseSearchIndex(*Table, Name, {Field}, false)); |
| 772 | } |
| 773 | |
| 774 | Tables.emplace_back(std::move(Table)); |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 775 | } |
Nicolai Haehnle | 0ea4d06 | 2018-06-21 13:36:22 +0000 | [diff] [blame] | 776 | |
| 777 | // Emit everything. |
| 778 | for (const auto &Enum : Enums) |
| 779 | emitGenericEnum(*Enum, OS); |
| 780 | |
| 781 | for (const auto &Table : Tables) |
| 782 | emitGenericTable(*Table, OS); |
| 783 | |
| 784 | // Put all #undefs last, to allow multiple sections guarded by the same |
| 785 | // define. |
| 786 | for (const auto &Guard : PreprocessorGuards) |
| 787 | OS << "#undef " << Guard << "\n"; |
Tim Northover | e6ae676 | 2016-07-05 21:23:04 +0000 | [diff] [blame] | 788 | } |
| 789 | |
| 790 | namespace llvm { |
| 791 | |
| 792 | void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS) { |
| 793 | SearchableTableEmitter(RK).run(OS); |
| 794 | } |
| 795 | |
| 796 | } // End llvm namespace. |