1 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 2 // -*- mode: C++ -*- 3 // 4 // Copyright 2020-2024 Google LLC 5 // 6 // Licensed under the Apache License v2.0 with LLVM Exceptions (the 7 // "License"); you may not use this file except in compliance with the 8 // License. You may obtain a copy of the License at 9 // 10 // https://llvm.org/LICENSE.txt 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 // 18 // Author: Maria Teguiani 19 // Author: Giuliano Procida 20 // Author: Ignes Simeonova 21 22 #ifndef STG_GRAPH_H_ 23 #define STG_GRAPH_H_ 24 25 #include <cstddef> 26 #include <cstdint> 27 #include <exception> 28 #include <functional> 29 #include <map> 30 #include <optional> 31 #include <ostream> 32 #include <sstream> 33 #include <string> 34 #include <type_traits> 35 #include <utility> 36 #include <vector> 37 38 #include "error.h" 39 40 namespace stg { 41 42 // A wrapped (for type safety) array index. 43 struct Id { 44 // defined in graph.cc as maximum value for index type 45 static const Id kInvalid; IdId46 explicit Id(size_t ix) : ix_(ix) {} 47 auto operator<=>(const Id&) const = default; 48 size_t ix_; 49 }; 50 51 std::ostream& operator<<(std::ostream& os, Id id); 52 53 using Pair = std::pair<Id, Id>; 54 55 } // namespace stg 56 57 namespace std { 58 59 template <> 60 struct hash<stg::Id> { 61 size_t operator()(const stg::Id& id) const { 62 return hash<decltype(id.ix_)>()(id.ix_); 63 } 64 }; 65 66 template <> 67 struct hash<stg::Pair> { 68 size_t operator()(const stg::Pair& comparison) const { 69 const hash<stg::Id> h; 70 auto h1 = h(comparison.first); 71 auto h2 = h(comparison.second); 72 // assumes 64-bit size_t, would be better if std::hash_combine existed 73 return h1 ^ (h2 + 0x9e3779b97f4a7c15 + (h1 << 12) + (h1 >> 4)); 74 } 75 }; 76 77 } // namespace std 78 79 namespace stg { 80 81 struct Special { 82 enum class Kind { 83 VOID, 84 VARIADIC, 85 NULLPTR, 86 }; 87 explicit Special(Kind kind) 88 : kind(kind) {} 89 90 Kind kind; 91 }; 92 93 struct PointerReference { 94 enum class Kind { 95 POINTER, 96 LVALUE_REFERENCE, 97 RVALUE_REFERENCE, 98 }; 99 PointerReference(Kind kind, Id pointee_type_id) 100 : kind(kind), pointee_type_id(pointee_type_id) {} 101 102 Kind kind; 103 Id pointee_type_id; 104 }; 105 106 struct PointerToMember { 107 PointerToMember(Id containing_type_id, Id pointee_type_id) 108 : containing_type_id(containing_type_id), pointee_type_id(pointee_type_id) 109 {} 110 111 Id containing_type_id; 112 Id pointee_type_id; 113 }; 114 115 struct Typedef { 116 Typedef(const std::string& name, Id referred_type_id) 117 : name(name), referred_type_id(referred_type_id) {} 118 119 std::string name; 120 Id referred_type_id; 121 }; 122 123 enum class Qualifier { CONST, VOLATILE, RESTRICT, ATOMIC }; 124 125 std::ostream& operator<<(std::ostream& os, Qualifier qualifier); 126 127 struct Qualified { 128 Qualified(Qualifier qualifier, Id qualified_type_id) 129 : qualifier(qualifier), qualified_type_id(qualified_type_id) {} 130 131 Qualifier qualifier; 132 Id qualified_type_id; 133 }; 134 135 struct Primitive { 136 enum class Encoding { 137 BOOLEAN, 138 SIGNED_INTEGER, 139 UNSIGNED_INTEGER, 140 SIGNED_CHARACTER, 141 UNSIGNED_CHARACTER, 142 REAL_NUMBER, 143 COMPLEX_NUMBER, 144 UTF, 145 }; 146 Primitive(const std::string& name, std::optional<Encoding> encoding, 147 uint32_t bytesize) 148 : name(name), encoding(encoding), bytesize(bytesize) {} 149 150 std::string name; 151 std::optional<Encoding> encoding; 152 uint32_t bytesize; 153 }; 154 155 struct Array { 156 Array(uint64_t number_of_elements, Id element_type_id) 157 : number_of_elements(number_of_elements), 158 element_type_id(element_type_id) {} 159 160 uint64_t number_of_elements; 161 Id element_type_id; 162 }; 163 164 struct BaseClass { 165 enum class Inheritance { NON_VIRTUAL, VIRTUAL }; 166 BaseClass(Id type_id, uint64_t offset, Inheritance inheritance) 167 : type_id(type_id), offset(offset), inheritance(inheritance) {} 168 169 Id type_id; 170 uint64_t offset; 171 Inheritance inheritance; 172 }; 173 174 std::ostream& operator<<(std::ostream& os, BaseClass::Inheritance inheritance); 175 176 struct Method { 177 Method(const std::string& mangled_name, const std::string& name, 178 uint64_t vtable_offset, Id type_id) 179 : mangled_name(mangled_name), name(name), 180 vtable_offset(vtable_offset), type_id(type_id) {} 181 182 std::string mangled_name; 183 std::string name; 184 uint64_t vtable_offset; 185 Id type_id; 186 }; 187 188 struct Member { 189 Member(const std::string& name, Id type_id, uint64_t offset, uint64_t bitsize) 190 : name(name), type_id(type_id), offset(offset), bitsize(bitsize) {} 191 192 std::string name; 193 Id type_id; 194 uint64_t offset; 195 uint64_t bitsize; 196 }; 197 198 struct VariantMember { 199 VariantMember(const std::string& name, 200 std::optional<int64_t> discriminant_value, Id type_id) 201 : name(name), discriminant_value(discriminant_value), type_id(type_id) {} 202 203 std::string name; 204 std::optional<int64_t> discriminant_value; 205 Id type_id; 206 }; 207 208 struct StructUnion { 209 enum class Kind { STRUCT, UNION }; 210 struct Definition { 211 uint64_t bytesize; 212 std::vector<Id> base_classes; 213 std::vector<Id> methods; 214 std::vector<Id> members; 215 }; 216 StructUnion(Kind kind, const std::string& name) : kind(kind), name(name) {} 217 StructUnion(Kind kind, const std::string& name, uint64_t bytesize, 218 const std::vector<Id>& base_classes, 219 const std::vector<Id>& methods, const std::vector<Id>& members) 220 : kind(kind), name(name), 221 definition({bytesize, base_classes, methods, members}) {} 222 223 Kind kind; 224 std::string name; 225 std::optional<Definition> definition; 226 }; 227 228 std::ostream& operator<<(std::ostream& os, StructUnion::Kind kind); 229 std::string& operator+=(std::string& os, StructUnion::Kind kind); 230 231 struct Enumeration { 232 using Enumerators = std::vector<std::pair<std::string, int64_t>>; 233 struct Definition { 234 Id underlying_type_id; 235 Enumerators enumerators; 236 }; 237 explicit Enumeration(const std::string& name) : name(name) {} 238 Enumeration(const std::string& name, Id underlying_type_id, 239 const Enumerators& enumerators) 240 : name(name), definition({underlying_type_id, enumerators}) {} 241 242 std::string name; 243 std::optional<Definition> definition; 244 }; 245 246 struct Variant { 247 Variant(const std::string& name, uint64_t bytesize, 248 std::optional<Id> discriminant, const std::vector<Id>& members) 249 : name(name), 250 bytesize(bytesize), 251 discriminant(discriminant), 252 members(members) {} 253 254 std::string name; 255 uint64_t bytesize; 256 std::optional<Id> discriminant; 257 std::vector<Id> members; 258 }; 259 260 struct Function { 261 Function(Id return_type_id, const std::vector<Id>& parameters) 262 : return_type_id(return_type_id), parameters(parameters) {} 263 264 Id return_type_id; 265 std::vector<Id> parameters; 266 }; 267 268 struct ElfSymbol { 269 enum class SymbolType { NOTYPE, OBJECT, FUNCTION, COMMON, TLS, GNU_IFUNC }; 270 enum class Binding { GLOBAL, LOCAL, WEAK, GNU_UNIQUE }; 271 enum class Visibility { DEFAULT, PROTECTED, HIDDEN, INTERNAL }; 272 struct VersionInfo { 273 auto operator<=>(const VersionInfo&) const = default; 274 bool is_default; 275 std::string name; 276 }; 277 struct CRC { 278 explicit CRC(uint32_t number) : number(number) {} 279 auto operator<=>(const CRC&) const = default; 280 uint32_t number; 281 }; 282 ElfSymbol(const std::string& symbol_name, 283 std::optional<VersionInfo> version_info, 284 bool is_defined, 285 SymbolType symbol_type, 286 Binding binding, 287 Visibility visibility, 288 std::optional<CRC> crc, 289 std::optional<std::string> ns, 290 std::optional<Id> type_id, 291 const std::optional<std::string>& full_name) 292 : symbol_name(symbol_name), 293 version_info(version_info), 294 is_defined(is_defined), 295 symbol_type(symbol_type), 296 binding(binding), 297 visibility(visibility), 298 crc(crc), 299 ns(ns), 300 type_id(type_id), 301 full_name(full_name) {} 302 303 std::string symbol_name; 304 std::optional<VersionInfo> version_info; 305 bool is_defined; 306 SymbolType symbol_type; 307 Binding binding; 308 Visibility visibility; 309 std::optional<CRC> crc; 310 std::optional<std::string> ns; 311 std::optional<Id> type_id; 312 std::optional<std::string> full_name; 313 }; 314 315 std::ostream& operator<<(std::ostream& os, ElfSymbol::SymbolType); 316 std::ostream& operator<<(std::ostream& os, ElfSymbol::Binding); 317 std::ostream& operator<<(std::ostream& os, ElfSymbol::Visibility); 318 319 std::string VersionInfoToString(const ElfSymbol::VersionInfo& version_info); 320 std::string VersionedSymbolName(const ElfSymbol&); 321 322 std::ostream& operator<<(std::ostream& os, ElfSymbol::CRC crc); 323 324 struct Interface { 325 explicit Interface(const std::map<std::string, Id>& symbols) 326 : symbols(symbols) {} 327 Interface(const std::map<std::string, Id>& symbols, 328 const std::map<std::string, Id>& types) 329 : symbols(symbols), types(types) {} 330 331 std::map<std::string, Id> symbols; 332 std::map<std::string, Id> types; 333 }; 334 335 std::ostream& operator<<(std::ostream& os, Primitive::Encoding encoding); 336 337 // Concrete graph type. 338 class Graph { 339 public: 340 Id Limit() const { 341 return Id(indirection_.size()); 342 } 343 344 bool Is(Id id) const { 345 return indirection_[id.ix_].first != Which::ABSENT; 346 } 347 348 Id Allocate() { 349 const auto id = Limit(); 350 indirection_.emplace_back(Which::ABSENT, 0); 351 return id; 352 } 353 354 template <typename Node, typename... Args> 355 void Set(Id id, Args&&... args) { 356 auto& reference = indirection_[id.ix_]; 357 if (reference.first != Which::ABSENT) { 358 Die() << "node value already set: " << id; 359 } 360 if constexpr (std::is_same_v<Node, Special>) { 361 reference = {Which::SPECIAL, special_.size()}; 362 special_.emplace_back(std::forward<Args>(args)...); 363 } else if constexpr (std::is_same_v<Node, PointerReference>) { 364 reference = {Which::POINTER_REFERENCE, pointer_reference_.size()}; 365 pointer_reference_.emplace_back(std::forward<Args>(args)...); 366 } else if constexpr (std::is_same_v<Node, PointerToMember>) { 367 reference = {Which::POINTER_TO_MEMBER, pointer_to_member_.size()}; 368 pointer_to_member_.emplace_back(std::forward<Args>(args)...); 369 } else if constexpr (std::is_same_v<Node, Typedef>) { 370 reference = {Which::TYPEDEF, typedef_.size()}; 371 typedef_.emplace_back(std::forward<Args>(args)...); 372 } else if constexpr (std::is_same_v<Node, Qualified>) { 373 reference = {Which::QUALIFIED, qualified_.size()}; 374 qualified_.emplace_back(std::forward<Args>(args)...); 375 } else if constexpr (std::is_same_v<Node, Primitive>) { 376 reference = {Which::PRIMITIVE, primitive_.size()}; 377 primitive_.emplace_back(std::forward<Args>(args)...); 378 } else if constexpr (std::is_same_v<Node, Array>) { 379 reference = {Which::ARRAY, array_.size()}; 380 array_.emplace_back(std::forward<Args>(args)...); 381 } else if constexpr (std::is_same_v<Node, BaseClass>) { 382 reference = {Which::BASE_CLASS, base_class_.size()}; 383 base_class_.emplace_back(std::forward<Args>(args)...); 384 } else if constexpr (std::is_same_v<Node, Method>) { 385 reference = {Which::METHOD, method_.size()}; 386 method_.emplace_back(std::forward<Args>(args)...); 387 } else if constexpr (std::is_same_v<Node, Member>) { 388 reference = {Which::MEMBER, member_.size()}; 389 member_.emplace_back(std::forward<Args>(args)...); 390 } else if constexpr (std::is_same_v<Node, VariantMember>) { 391 reference = {Which::VARIANT_MEMBER, variant_member_.size()}; 392 variant_member_.emplace_back(std::forward<Args>(args)...); 393 } else if constexpr (std::is_same_v<Node, StructUnion>) { 394 reference = {Which::STRUCT_UNION, struct_union_.size()}; 395 struct_union_.emplace_back(std::forward<Args>(args)...); 396 } else if constexpr (std::is_same_v<Node, Enumeration>) { 397 reference = {Which::ENUMERATION, enumeration_.size()}; 398 enumeration_.emplace_back(std::forward<Args>(args)...); 399 } else if constexpr (std::is_same_v<Node, Variant>) { 400 reference = {Which::VARIANT, variant_.size()}; 401 variant_.emplace_back(std::forward<Args>(args)...); 402 } else if constexpr (std::is_same_v<Node, Function>) { 403 reference = {Which::FUNCTION, function_.size()}; 404 function_.emplace_back(std::forward<Args>(args)...); 405 } else if constexpr (std::is_same_v<Node, ElfSymbol>) { 406 reference = {Which::ELF_SYMBOL, elf_symbol_.size()}; 407 elf_symbol_.emplace_back(std::forward<Args>(args)...); 408 } else if constexpr (std::is_same_v<Node, Interface>) { 409 reference = {Which::INTERFACE, interface_.size()}; 410 interface_.emplace_back(std::forward<Args>(args)...); 411 } else { 412 // unfortunately we cannot static_assert(false, "missing case") 413 static_assert(std::is_same<Node, Node*>::value, "missing case"); 414 } 415 } 416 417 template <typename Node, typename... Args> 418 Id Add(Args&&... args) { 419 auto id = Allocate(); 420 Set<Node>(id, std::forward<Args>(args)...); 421 return id; 422 } 423 424 void Deallocate(Id) { 425 // don't actually do anything, not supported 426 } 427 428 void Unset(Id id) { 429 auto& reference = indirection_[id.ix_]; 430 if (reference.first == Which::ABSENT) { 431 Die() << "node value already unset: " << id; 432 } 433 reference = {Which::ABSENT, 0}; 434 } 435 436 void Remove(Id id) { 437 Unset(id); 438 Deallocate(id); 439 } 440 441 template <typename FunctionObject, typename... Args> 442 decltype(auto) Apply(FunctionObject&& function, Id id, Args&&... args) const; 443 444 template <typename FunctionObject, typename... Args> 445 decltype(auto) Apply2( 446 FunctionObject&& function, Id id1, Id id2, Args&&... args) const; 447 448 template <typename FunctionObject, typename... Args> 449 decltype(auto) Apply(FunctionObject&& function, Id id, Args&&... args); 450 451 template <typename FunctionObject> 452 void ForEach(Id start, Id limit, FunctionObject&& function) const { 453 for (size_t ix = start.ix_; ix < limit.ix_; ++ix) { 454 const Id id(ix); 455 if (Is(id)) { 456 function(id); 457 } 458 } 459 } 460 461 private: 462 enum class Which { 463 ABSENT, 464 SPECIAL, 465 POINTER_REFERENCE, 466 POINTER_TO_MEMBER, 467 TYPEDEF, 468 QUALIFIED, 469 PRIMITIVE, 470 ARRAY, 471 BASE_CLASS, 472 METHOD, 473 MEMBER, 474 VARIANT_MEMBER, 475 STRUCT_UNION, 476 ENUMERATION, 477 VARIANT, 478 FUNCTION, 479 ELF_SYMBOL, 480 INTERFACE, 481 }; 482 483 std::vector<std::pair<Which, size_t>> indirection_; 484 485 std::vector<Special> special_; 486 std::vector<PointerReference> pointer_reference_; 487 std::vector<PointerToMember> pointer_to_member_; 488 std::vector<Typedef> typedef_; 489 std::vector<Qualified> qualified_; 490 std::vector<Primitive> primitive_; 491 std::vector<Array> array_; 492 std::vector<BaseClass> base_class_; 493 std::vector<Method> method_; 494 std::vector<Member> member_; 495 std::vector<VariantMember> variant_member_; 496 std::vector<StructUnion> struct_union_; 497 std::vector<Enumeration> enumeration_; 498 std::vector<Variant> variant_; 499 std::vector<Function> function_; 500 std::vector<ElfSymbol> elf_symbol_; 501 std::vector<Interface> interface_; 502 }; 503 504 template <typename FunctionObject, typename... Args> 505 decltype(auto) Graph::Apply( 506 FunctionObject&& function, Id id, Args&&... args) const { 507 const auto& [which, ix] = indirection_[id.ix_]; 508 switch (which) { 509 case Which::ABSENT: 510 Die() << "undefined node: " << id; 511 case Which::SPECIAL: 512 return function(special_[ix], std::forward<Args>(args)...); 513 case Which::POINTER_REFERENCE: 514 return function(pointer_reference_[ix], std::forward<Args>(args)...); 515 case Which::POINTER_TO_MEMBER: 516 return function(pointer_to_member_[ix], std::forward<Args>(args)...); 517 case Which::TYPEDEF: 518 return function(typedef_[ix], std::forward<Args>(args)...); 519 case Which::QUALIFIED: 520 return function(qualified_[ix], std::forward<Args>(args)...); 521 case Which::PRIMITIVE: 522 return function(primitive_[ix], std::forward<Args>(args)...); 523 case Which::ARRAY: 524 return function(array_[ix], std::forward<Args>(args)...); 525 case Which::BASE_CLASS: 526 return function(base_class_[ix], std::forward<Args>(args)...); 527 case Which::METHOD: 528 return function(method_[ix], std::forward<Args>(args)...); 529 case Which::MEMBER: 530 return function(member_[ix], std::forward<Args>(args)...); 531 case Which::VARIANT_MEMBER: 532 return function(variant_member_[ix], std::forward<Args>(args)...); 533 case Which::STRUCT_UNION: 534 return function(struct_union_[ix], std::forward<Args>(args)...); 535 case Which::ENUMERATION: 536 return function(enumeration_[ix], std::forward<Args>(args)...); 537 case Which::VARIANT: 538 return function(variant_[ix], std::forward<Args>(args)...); 539 case Which::FUNCTION: 540 return function(function_[ix], std::forward<Args>(args)...); 541 case Which::ELF_SYMBOL: 542 return function(elf_symbol_[ix], std::forward<Args>(args)...); 543 case Which::INTERFACE: 544 return function(interface_[ix], std::forward<Args>(args)...); 545 } 546 } 547 548 template <typename FunctionObject, typename... Args> 549 decltype(auto) Graph::Apply2( 550 FunctionObject&& function, Id id1, Id id2, Args&&... args) const { 551 const auto& [which1, ix1] = indirection_[id1.ix_]; 552 const auto& [which2, ix2] = indirection_[id2.ix_]; 553 if (which1 != which2) { 554 return function.Mismatch(std::forward<Args>(args)...); 555 } 556 switch (which1) { 557 case Which::ABSENT: 558 Die() << "undefined nodes: " << id1 << ", " << id2; 559 case Which::SPECIAL: 560 return function(special_[ix1], special_[ix2], 561 std::forward<Args>(args)...); 562 case Which::POINTER_REFERENCE: 563 return function(pointer_reference_[ix1], pointer_reference_[ix2], 564 std::forward<Args>(args)...); 565 case Which::POINTER_TO_MEMBER: 566 return function(pointer_to_member_[ix1], pointer_to_member_[ix2], 567 std::forward<Args>(args)...); 568 case Which::TYPEDEF: 569 return function(typedef_[ix1], typedef_[ix2], 570 std::forward<Args>(args)...); 571 case Which::QUALIFIED: 572 return function(qualified_[ix1], qualified_[ix2], 573 std::forward<Args>(args)...); 574 case Which::PRIMITIVE: 575 return function(primitive_[ix1], primitive_[ix2], 576 std::forward<Args>(args)...); 577 case Which::ARRAY: 578 return function(array_[ix1], array_[ix2], 579 std::forward<Args>(args)...); 580 case Which::BASE_CLASS: 581 return function(base_class_[ix1], base_class_[ix2], 582 std::forward<Args>(args)...); 583 case Which::METHOD: 584 return function(method_[ix1], method_[ix2], 585 std::forward<Args>(args)...); 586 case Which::MEMBER: 587 return function(member_[ix1], member_[ix2], 588 std::forward<Args>(args)...); 589 case Which::VARIANT_MEMBER: 590 return function(variant_member_[ix1], variant_member_[ix2], 591 std::forward<Args>(args)...); 592 case Which::STRUCT_UNION: 593 return function(struct_union_[ix1], struct_union_[ix2], 594 std::forward<Args>(args)...); 595 case Which::ENUMERATION: 596 return function(enumeration_[ix1], enumeration_[ix2], 597 std::forward<Args>(args)...); 598 case Which::VARIANT: 599 return function(variant_[ix1], variant_[ix2], 600 std::forward<Args>(args)...); 601 case Which::FUNCTION: 602 return function(function_[ix1], function_[ix2], 603 std::forward<Args>(args)...); 604 case Which::ELF_SYMBOL: 605 return function(elf_symbol_[ix1], elf_symbol_[ix2], 606 std::forward<Args>(args)...); 607 case Which::INTERFACE: 608 return function(interface_[ix1], interface_[ix2], 609 std::forward<Args>(args)...); 610 } 611 } 612 613 template <typename FunctionObject, typename... Args> 614 struct ConstAdapter { 615 explicit ConstAdapter(FunctionObject& function) : function(function) {} 616 template <typename Node> 617 decltype(auto) operator()(const Node& node, Args&&... args) { 618 return function(const_cast<Node&>(node), std::forward<Args>(args)...); 619 } 620 FunctionObject& function; 621 }; 622 623 template <typename FunctionObject, typename... Args> 624 decltype(auto) Graph::Apply(FunctionObject&& function, Id id, Args&&... args) { 625 ConstAdapter<FunctionObject, Args&&...> adapter(function); 626 return static_cast<const Graph&>(*this).Apply( 627 adapter, id, std::forward<Args>(args)...); 628 } 629 630 struct InterfaceKey { 631 explicit InterfaceKey(const Graph& graph) : graph(graph) {} 632 633 std::string operator()(Id id) const { 634 return graph.Apply(*this, id); 635 } 636 637 std::string operator()(const stg::Typedef& x) const { 638 return x.name; 639 } 640 641 std::string operator()(const stg::StructUnion& x) const { 642 if (x.name.empty()) { 643 Die() << "anonymous struct/union interface type"; 644 } 645 std::ostringstream os; 646 os << x.kind << ' ' << x.name; 647 return os.str(); 648 } 649 650 std::string operator()(const stg::Enumeration& x) const { 651 if (x.name.empty()) { 652 Die() << "anonymous enum interface type"; 653 } 654 return "enum " + x.name; 655 } 656 657 std::string operator()(const stg::Variant& x) const { 658 if (x.name.empty()) { 659 Die() << "anonymous variant interface type"; 660 } 661 return "variant " + x.name; 662 } 663 664 std::string operator()(const stg::ElfSymbol& x) const { 665 return VersionedSymbolName(x); 666 } 667 668 template <typename Node> 669 std::string operator()(const Node&) const { 670 Die() << "unexpected interface type"; 671 } 672 673 const Graph& graph; 674 }; 675 676 // Roughly equivalent to std::set<Id> but with constant time operations and 677 // key set limited to allocated Ids. 678 class DenseIdSet { 679 public: 680 explicit DenseIdSet(Id start) : offset_(start.ix_) {} 681 void Reserve(Id limit) { 682 ids_.reserve(limit.ix_ - offset_); 683 } 684 bool Insert(Id id) { 685 const auto ix = id.ix_; 686 if (ix < offset_) { 687 Die() << "DenseIdSet: out of range access to " << id; 688 } 689 const auto offset_ix = ix - offset_; 690 if (offset_ix >= ids_.size()) { 691 ids_.resize(offset_ix + 1, false); 692 } 693 if (ids_[offset_ix]) { 694 return false; 695 } 696 ids_[offset_ix] = true; 697 return true; 698 } 699 700 private: 701 size_t offset_; 702 std::vector<bool> ids_; 703 }; 704 705 // Roughly equivalent to std::map<Id, Id>, defaulted to the identity mapping, 706 // but with constant time operations and key set limited to allocated Ids. 707 class DenseIdMapping { 708 public: 709 explicit DenseIdMapping(Id start) : offset_(start.ix_) {} 710 void Reserve(Id limit) { 711 ids_.reserve(limit.ix_ - offset_); 712 } 713 Id& operator[](Id id) { 714 const auto ix = id.ix_; 715 if (ix < offset_) { 716 Die() << "DenseIdMapping: out of range access to " << id; 717 } 718 Populate(ix + 1); 719 return ids_[ix - offset_]; 720 } 721 722 private: 723 void Populate(size_t size) { 724 for (size_t ix = offset_ + ids_.size(); ix < size; ++ix) { 725 ids_.emplace_back(ix); 726 } 727 } 728 729 size_t offset_; 730 std::vector<Id> ids_; 731 }; 732 733 template <typename ExternalId> 734 class Maker { 735 public: 736 explicit Maker(Graph& graph) : graph_(graph) {} 737 738 ~Maker() noexcept(false) { 739 if (std::uncaught_exceptions() == 0) { 740 if (undefined_ > 0) { 741 Die die; 742 die << "undefined nodes:"; 743 for (const auto& [external_id, id] : map_) { 744 if (!graph_.Is(id)) { 745 die << ' ' << external_id; 746 } 747 } 748 } 749 } 750 } 751 752 Id Get(const ExternalId& external_id) { 753 auto [it, inserted] = map_.emplace(external_id, 0); 754 if (inserted) { 755 it->second = graph_.Allocate(); 756 ++undefined_; 757 } 758 return it->second; 759 } 760 761 template <typename Node, typename... Args> 762 Id Set(const ExternalId& external_id, Args&&... args) { 763 return Set<Node>(DieDuplicate, external_id, std::forward<Args>(args)...); 764 } 765 766 template <typename Node, typename... Args> 767 Id MaybeSet(const ExternalId& external_id, Args&&... args) { 768 return Set<Node>(WarnDuplicate, external_id, std::forward<Args>(args)...); 769 } 770 771 template <typename Node, typename... Args> 772 Id Add(Args&&... args) { 773 return graph_.Add<Node>(std::forward<Args>(args)...); 774 } 775 776 private: 777 Graph& graph_; 778 size_t undefined_ = 0; 779 std::unordered_map<ExternalId, Id> map_; 780 781 template <typename Node, typename... Args> 782 Id Set(void(& fail)(const ExternalId&), const ExternalId& external_id, 783 Args&&... args) { 784 const Id id = Get(external_id); 785 if (graph_.Is(id)) { 786 fail(external_id); 787 } else { 788 graph_.Set<Node>(id, std::forward<Args>(args)...); 789 --undefined_; 790 } 791 return id; 792 } 793 794 // These helpers should probably not be inlined. 795 [[noreturn]] static void DieDuplicate(const ExternalId& external_id) { 796 Die() << "duplicate definition of node: " << external_id; 797 } 798 static void WarnDuplicate(const ExternalId& external_id) { 799 Warn() << "ignoring duplicate definition of node: " << external_id; 800 } 801 }; 802 803 } // namespace stg 804 805 #endif // STG_GRAPH_H_ 806