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