1 //===--- CacheTokens.cpp - Caching of lexer tokens for PTH support --------===// 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 provides a possible implementation of PTH support for Clang that is 11 // based on caching lexed tokens and identifiers. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "clang/Frontend/Utils.h" 16 #include "clang/Basic/Diagnostic.h" 17 #include "clang/Basic/FileManager.h" 18 #include "clang/Basic/FileSystemStatCache.h" 19 #include "clang/Basic/IdentifierTable.h" 20 #include "clang/Basic/SourceManager.h" 21 #include "clang/Lex/Lexer.h" 22 #include "clang/Lex/Preprocessor.h" 23 #include "llvm/ADT/StringExtras.h" 24 #include "llvm/ADT/StringMap.h" 25 #include "llvm/Support/EndianStream.h" 26 #include "llvm/Support/FileSystem.h" 27 #include "llvm/Support/MemoryBuffer.h" 28 #include "llvm/Support/OnDiskHashTable.h" 29 #include "llvm/Support/Path.h" 30 #include "llvm/Support/raw_ostream.h" 31 32 // FIXME: put this somewhere else? 33 #ifndef S_ISDIR 34 #define S_ISDIR(x) (((x)&_S_IFDIR)!=0) 35 #endif 36 37 using namespace clang; 38 39 //===----------------------------------------------------------------------===// 40 // PTH-specific stuff. 41 //===----------------------------------------------------------------------===// 42 43 typedef uint32_t Offset; 44 45 namespace { 46 class PTHEntry { 47 Offset TokenData, PPCondData; 48 49 public: 50 PTHEntry() {} 51 52 PTHEntry(Offset td, Offset ppcd) 53 : TokenData(td), PPCondData(ppcd) {} 54 55 Offset getTokenOffset() const { return TokenData; } 56 Offset getPPCondTableOffset() const { return PPCondData; } 57 }; 58 59 60 class PTHEntryKeyVariant { 61 union { const FileEntry* FE; const char* Path; }; 62 enum { IsFE = 0x1, IsDE = 0x2, IsNoExist = 0x0 } Kind; 63 FileData *Data; 64 65 public: 66 PTHEntryKeyVariant(const FileEntry *fe) : FE(fe), Kind(IsFE), Data(nullptr) {} 67 68 PTHEntryKeyVariant(FileData *Data, const char *path) 69 : Path(path), Kind(IsDE), Data(new FileData(*Data)) {} 70 71 explicit PTHEntryKeyVariant(const char *path) 72 : Path(path), Kind(IsNoExist), Data(nullptr) {} 73 74 bool isFile() const { return Kind == IsFE; } 75 76 StringRef getString() const { 77 return Kind == IsFE ? FE->getName() : Path; 78 } 79 80 unsigned getKind() const { return (unsigned) Kind; } 81 82 void EmitData(raw_ostream& Out) { 83 using namespace llvm::support; 84 endian::Writer<little> LE(Out); 85 switch (Kind) { 86 case IsFE: { 87 // Emit stat information. 88 llvm::sys::fs::UniqueID UID = FE->getUniqueID(); 89 LE.write<uint64_t>(UID.getFile()); 90 LE.write<uint64_t>(UID.getDevice()); 91 LE.write<uint64_t>(FE->getModificationTime()); 92 LE.write<uint64_t>(FE->getSize()); 93 } break; 94 case IsDE: 95 // Emit stat information. 96 LE.write<uint64_t>(Data->UniqueID.getFile()); 97 LE.write<uint64_t>(Data->UniqueID.getDevice()); 98 LE.write<uint64_t>(Data->ModTime); 99 LE.write<uint64_t>(Data->Size); 100 delete Data; 101 break; 102 default: 103 break; 104 } 105 } 106 107 unsigned getRepresentationLength() const { 108 return Kind == IsNoExist ? 0 : 4 + 4 + 2 + 8 + 8; 109 } 110 }; 111 112 class FileEntryPTHEntryInfo { 113 public: 114 typedef PTHEntryKeyVariant key_type; 115 typedef key_type key_type_ref; 116 117 typedef PTHEntry data_type; 118 typedef const PTHEntry& data_type_ref; 119 120 typedef unsigned hash_value_type; 121 typedef unsigned offset_type; 122 123 static hash_value_type ComputeHash(PTHEntryKeyVariant V) { 124 return llvm::HashString(V.getString()); 125 } 126 127 static std::pair<unsigned,unsigned> 128 EmitKeyDataLength(raw_ostream& Out, PTHEntryKeyVariant V, 129 const PTHEntry& E) { 130 using namespace llvm::support; 131 endian::Writer<little> LE(Out); 132 133 unsigned n = V.getString().size() + 1 + 1; 134 LE.write<uint16_t>(n); 135 136 unsigned m = V.getRepresentationLength() + (V.isFile() ? 4 + 4 : 0); 137 LE.write<uint8_t>(m); 138 139 return std::make_pair(n, m); 140 } 141 142 static void EmitKey(raw_ostream& Out, PTHEntryKeyVariant V, unsigned n){ 143 using namespace llvm::support; 144 // Emit the entry kind. 145 endian::Writer<little>(Out).write<uint8_t>((unsigned)V.getKind()); 146 // Emit the string. 147 Out.write(V.getString().data(), n - 1); 148 } 149 150 static void EmitData(raw_ostream& Out, PTHEntryKeyVariant V, 151 const PTHEntry& E, unsigned) { 152 using namespace llvm::support; 153 endian::Writer<little> LE(Out); 154 155 // For file entries emit the offsets into the PTH file for token data 156 // and the preprocessor blocks table. 157 if (V.isFile()) { 158 LE.write<uint32_t>(E.getTokenOffset()); 159 LE.write<uint32_t>(E.getPPCondTableOffset()); 160 } 161 162 // Emit any other data associated with the key (i.e., stat information). 163 V.EmitData(Out); 164 } 165 }; 166 167 class OffsetOpt { 168 bool valid; 169 Offset off; 170 public: 171 OffsetOpt() : valid(false) {} 172 bool hasOffset() const { return valid; } 173 Offset getOffset() const { assert(valid); return off; } 174 void setOffset(Offset o) { off = o; valid = true; } 175 }; 176 } // end anonymous namespace 177 178 typedef llvm::OnDiskChainedHashTableGenerator<FileEntryPTHEntryInfo> PTHMap; 179 180 namespace { 181 class PTHWriter { 182 typedef llvm::DenseMap<const IdentifierInfo*,uint32_t> IDMap; 183 typedef llvm::StringMap<OffsetOpt, llvm::BumpPtrAllocator> CachedStrsTy; 184 185 IDMap IM; 186 llvm::raw_fd_ostream& Out; 187 Preprocessor& PP; 188 uint32_t idcount; 189 PTHMap PM; 190 CachedStrsTy CachedStrs; 191 Offset CurStrOffset; 192 std::vector<llvm::StringMapEntry<OffsetOpt>*> StrEntries; 193 194 //// Get the persistent id for the given IdentifierInfo*. 195 uint32_t ResolveID(const IdentifierInfo* II); 196 197 /// Emit a token to the PTH file. 198 void EmitToken(const Token& T); 199 200 void Emit8(uint32_t V) { 201 using namespace llvm::support; 202 endian::Writer<little>(Out).write<uint8_t>(V); 203 } 204 205 void Emit16(uint32_t V) { 206 using namespace llvm::support; 207 endian::Writer<little>(Out).write<uint16_t>(V); 208 } 209 210 void Emit32(uint32_t V) { 211 using namespace llvm::support; 212 endian::Writer<little>(Out).write<uint32_t>(V); 213 } 214 215 void EmitBuf(const char *Ptr, unsigned NumBytes) { 216 Out.write(Ptr, NumBytes); 217 } 218 219 void EmitString(StringRef V) { 220 using namespace llvm::support; 221 endian::Writer<little>(Out).write<uint16_t>(V.size()); 222 EmitBuf(V.data(), V.size()); 223 } 224 225 /// EmitIdentifierTable - Emits two tables to the PTH file. The first is 226 /// a hashtable mapping from identifier strings to persistent IDs. 227 /// The second is a straight table mapping from persistent IDs to string data 228 /// (the keys of the first table). 229 std::pair<Offset, Offset> EmitIdentifierTable(); 230 231 /// EmitFileTable - Emit a table mapping from file name strings to PTH 232 /// token data. 233 Offset EmitFileTable() { return PM.Emit(Out); } 234 235 PTHEntry LexTokens(Lexer& L); 236 Offset EmitCachedSpellings(); 237 238 public: 239 PTHWriter(llvm::raw_fd_ostream& out, Preprocessor& pp) 240 : Out(out), PP(pp), idcount(0), CurStrOffset(0) {} 241 242 PTHMap &getPM() { return PM; } 243 void GeneratePTH(const std::string &MainFile); 244 }; 245 } // end anonymous namespace 246 247 uint32_t PTHWriter::ResolveID(const IdentifierInfo* II) { 248 // Null IdentifierInfo's map to the persistent ID 0. 249 if (!II) 250 return 0; 251 252 IDMap::iterator I = IM.find(II); 253 if (I != IM.end()) 254 return I->second; // We've already added 1. 255 256 IM[II] = ++idcount; // Pre-increment since '0' is reserved for NULL. 257 return idcount; 258 } 259 260 void PTHWriter::EmitToken(const Token& T) { 261 // Emit the token kind, flags, and length. 262 Emit32(((uint32_t) T.getKind()) | ((((uint32_t) T.getFlags())) << 8)| 263 (((uint32_t) T.getLength()) << 16)); 264 265 if (!T.isLiteral()) { 266 Emit32(ResolveID(T.getIdentifierInfo())); 267 } else { 268 // We cache *un-cleaned* spellings. This gives us 100% fidelity with the 269 // source code. 270 StringRef s(T.getLiteralData(), T.getLength()); 271 272 // Get the string entry. 273 llvm::StringMapEntry<OffsetOpt> *E = &CachedStrs.GetOrCreateValue(s); 274 275 // If this is a new string entry, bump the PTH offset. 276 if (!E->getValue().hasOffset()) { 277 E->getValue().setOffset(CurStrOffset); 278 StrEntries.push_back(E); 279 CurStrOffset += s.size() + 1; 280 } 281 282 // Emit the relative offset into the PTH file for the spelling string. 283 Emit32(E->getValue().getOffset()); 284 } 285 286 // Emit the offset into the original source file of this token so that we 287 // can reconstruct its SourceLocation. 288 Emit32(PP.getSourceManager().getFileOffset(T.getLocation())); 289 } 290 291 PTHEntry PTHWriter::LexTokens(Lexer& L) { 292 // Pad 0's so that we emit tokens to a 4-byte alignment. 293 // This speed up reading them back in. 294 using namespace llvm::support; 295 endian::Writer<little> LE(Out); 296 uint32_t TokenOff = Out.tell(); 297 for (uint64_t N = llvm::OffsetToAlignment(TokenOff, 4); N; --N, ++TokenOff) 298 LE.write<uint8_t>(0); 299 300 // Keep track of matching '#if' ... '#endif'. 301 typedef std::vector<std::pair<Offset, unsigned> > PPCondTable; 302 PPCondTable PPCond; 303 std::vector<unsigned> PPStartCond; 304 bool ParsingPreprocessorDirective = false; 305 Token Tok; 306 307 do { 308 L.LexFromRawLexer(Tok); 309 NextToken: 310 311 if ((Tok.isAtStartOfLine() || Tok.is(tok::eof)) && 312 ParsingPreprocessorDirective) { 313 // Insert an eod token into the token cache. It has the same 314 // position as the next token that is not on the same line as the 315 // preprocessor directive. Observe that we continue processing 316 // 'Tok' when we exit this branch. 317 Token Tmp = Tok; 318 Tmp.setKind(tok::eod); 319 Tmp.clearFlag(Token::StartOfLine); 320 Tmp.setIdentifierInfo(nullptr); 321 EmitToken(Tmp); 322 ParsingPreprocessorDirective = false; 323 } 324 325 if (Tok.is(tok::raw_identifier)) { 326 PP.LookUpIdentifierInfo(Tok); 327 EmitToken(Tok); 328 continue; 329 } 330 331 if (Tok.is(tok::hash) && Tok.isAtStartOfLine()) { 332 // Special processing for #include. Store the '#' token and lex 333 // the next token. 334 assert(!ParsingPreprocessorDirective); 335 Offset HashOff = (Offset) Out.tell(); 336 337 // Get the next token. 338 Token NextTok; 339 L.LexFromRawLexer(NextTok); 340 341 // If we see the start of line, then we had a null directive "#". In 342 // this case, discard both tokens. 343 if (NextTok.isAtStartOfLine()) 344 goto NextToken; 345 346 // The token is the start of a directive. Emit it. 347 EmitToken(Tok); 348 Tok = NextTok; 349 350 // Did we see 'include'/'import'/'include_next'? 351 if (Tok.isNot(tok::raw_identifier)) { 352 EmitToken(Tok); 353 continue; 354 } 355 356 IdentifierInfo* II = PP.LookUpIdentifierInfo(Tok); 357 tok::PPKeywordKind K = II->getPPKeywordID(); 358 359 ParsingPreprocessorDirective = true; 360 361 switch (K) { 362 case tok::pp_not_keyword: 363 // Invalid directives "#foo" can occur in #if 0 blocks etc, just pass 364 // them through. 365 default: 366 break; 367 368 case tok::pp_include: 369 case tok::pp_import: 370 case tok::pp_include_next: { 371 // Save the 'include' token. 372 EmitToken(Tok); 373 // Lex the next token as an include string. 374 L.setParsingPreprocessorDirective(true); 375 L.LexIncludeFilename(Tok); 376 L.setParsingPreprocessorDirective(false); 377 assert(!Tok.isAtStartOfLine()); 378 if (Tok.is(tok::raw_identifier)) 379 PP.LookUpIdentifierInfo(Tok); 380 381 break; 382 } 383 case tok::pp_if: 384 case tok::pp_ifdef: 385 case tok::pp_ifndef: { 386 // Add an entry for '#if' and friends. We initially set the target 387 // index to 0. This will get backpatched when we hit #endif. 388 PPStartCond.push_back(PPCond.size()); 389 PPCond.push_back(std::make_pair(HashOff, 0U)); 390 break; 391 } 392 case tok::pp_endif: { 393 // Add an entry for '#endif'. We set the target table index to itself. 394 // This will later be set to zero when emitting to the PTH file. We 395 // use 0 for uninitialized indices because that is easier to debug. 396 unsigned index = PPCond.size(); 397 // Backpatch the opening '#if' entry. 398 assert(!PPStartCond.empty()); 399 assert(PPCond.size() > PPStartCond.back()); 400 assert(PPCond[PPStartCond.back()].second == 0); 401 PPCond[PPStartCond.back()].second = index; 402 PPStartCond.pop_back(); 403 // Add the new entry to PPCond. 404 PPCond.push_back(std::make_pair(HashOff, index)); 405 EmitToken(Tok); 406 407 // Some files have gibberish on the same line as '#endif'. 408 // Discard these tokens. 409 do 410 L.LexFromRawLexer(Tok); 411 while (Tok.isNot(tok::eof) && !Tok.isAtStartOfLine()); 412 // We have the next token in hand. 413 // Don't immediately lex the next one. 414 goto NextToken; 415 } 416 case tok::pp_elif: 417 case tok::pp_else: { 418 // Add an entry for #elif or #else. 419 // This serves as both a closing and opening of a conditional block. 420 // This means that its entry will get backpatched later. 421 unsigned index = PPCond.size(); 422 // Backpatch the previous '#if' entry. 423 assert(!PPStartCond.empty()); 424 assert(PPCond.size() > PPStartCond.back()); 425 assert(PPCond[PPStartCond.back()].second == 0); 426 PPCond[PPStartCond.back()].second = index; 427 PPStartCond.pop_back(); 428 // Now add '#elif' as a new block opening. 429 PPCond.push_back(std::make_pair(HashOff, 0U)); 430 PPStartCond.push_back(index); 431 break; 432 } 433 } 434 } 435 436 EmitToken(Tok); 437 } 438 while (Tok.isNot(tok::eof)); 439 440 assert(PPStartCond.empty() && "Error: imblanced preprocessor conditionals."); 441 442 // Next write out PPCond. 443 Offset PPCondOff = (Offset) Out.tell(); 444 445 // Write out the size of PPCond so that clients can identifer empty tables. 446 Emit32(PPCond.size()); 447 448 for (unsigned i = 0, e = PPCond.size(); i!=e; ++i) { 449 Emit32(PPCond[i].first - TokenOff); 450 uint32_t x = PPCond[i].second; 451 assert(x != 0 && "PPCond entry not backpatched."); 452 // Emit zero for #endifs. This allows us to do checking when 453 // we read the PTH file back in. 454 Emit32(x == i ? 0 : x); 455 } 456 457 return PTHEntry(TokenOff, PPCondOff); 458 } 459 460 Offset PTHWriter::EmitCachedSpellings() { 461 // Write each cached strings to the PTH file. 462 Offset SpellingsOff = Out.tell(); 463 464 for (std::vector<llvm::StringMapEntry<OffsetOpt>*>::iterator 465 I = StrEntries.begin(), E = StrEntries.end(); I!=E; ++I) 466 EmitBuf((*I)->getKeyData(), (*I)->getKeyLength()+1 /*nul included*/); 467 468 return SpellingsOff; 469 } 470 471 void PTHWriter::GeneratePTH(const std::string &MainFile) { 472 // Generate the prologue. 473 Out << "cfe-pth" << '\0'; 474 Emit32(PTHManager::Version); 475 476 // Leave 4 words for the prologue. 477 Offset PrologueOffset = Out.tell(); 478 for (unsigned i = 0; i < 4; ++i) 479 Emit32(0); 480 481 // Write the name of the MainFile. 482 if (!MainFile.empty()) { 483 EmitString(MainFile); 484 } else { 485 // String with 0 bytes. 486 Emit16(0); 487 } 488 Emit8(0); 489 490 // Iterate over all the files in SourceManager. Create a lexer 491 // for each file and cache the tokens. 492 SourceManager &SM = PP.getSourceManager(); 493 const LangOptions &LOpts = PP.getLangOpts(); 494 495 for (SourceManager::fileinfo_iterator I = SM.fileinfo_begin(), 496 E = SM.fileinfo_end(); I != E; ++I) { 497 const SrcMgr::ContentCache &C = *I->second; 498 const FileEntry *FE = C.OrigEntry; 499 500 // FIXME: Handle files with non-absolute paths. 501 if (llvm::sys::path::is_relative(FE->getName())) 502 continue; 503 504 const llvm::MemoryBuffer *B = C.getBuffer(PP.getDiagnostics(), SM); 505 if (!B) continue; 506 507 FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); 508 const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); 509 Lexer L(FID, FromFile, SM, LOpts); 510 PM.insert(FE, LexTokens(L)); 511 } 512 513 // Write out the identifier table. 514 const std::pair<Offset,Offset> &IdTableOff = EmitIdentifierTable(); 515 516 // Write out the cached strings table. 517 Offset SpellingOff = EmitCachedSpellings(); 518 519 // Write out the file table. 520 Offset FileTableOff = EmitFileTable(); 521 522 // Finally, write the prologue. 523 Out.seek(PrologueOffset); 524 Emit32(IdTableOff.first); 525 Emit32(IdTableOff.second); 526 Emit32(FileTableOff); 527 Emit32(SpellingOff); 528 } 529 530 namespace { 531 /// StatListener - A simple "interpose" object used to monitor stat calls 532 /// invoked by FileManager while processing the original sources used 533 /// as input to PTH generation. StatListener populates the PTHWriter's 534 /// file map with stat information for directories as well as negative stats. 535 /// Stat information for files are populated elsewhere. 536 class StatListener : public FileSystemStatCache { 537 PTHMap &PM; 538 public: 539 StatListener(PTHMap &pm) : PM(pm) {} 540 ~StatListener() {} 541 542 LookupResult getStat(const char *Path, FileData &Data, bool isFile, 543 std::unique_ptr<vfs::File> *F, 544 vfs::FileSystem &FS) override { 545 LookupResult Result = statChained(Path, Data, isFile, F, FS); 546 547 if (Result == CacheMissing) // Failed 'stat'. 548 PM.insert(PTHEntryKeyVariant(Path), PTHEntry()); 549 else if (Data.IsDirectory) { 550 // Only cache directories with absolute paths. 551 if (llvm::sys::path::is_relative(Path)) 552 return Result; 553 554 PM.insert(PTHEntryKeyVariant(&Data, Path), PTHEntry()); 555 } 556 557 return Result; 558 } 559 }; 560 } // end anonymous namespace 561 562 563 void clang::CacheTokens(Preprocessor &PP, llvm::raw_fd_ostream* OS) { 564 // Get the name of the main file. 565 const SourceManager &SrcMgr = PP.getSourceManager(); 566 const FileEntry *MainFile = SrcMgr.getFileEntryForID(SrcMgr.getMainFileID()); 567 SmallString<128> MainFilePath(MainFile->getName()); 568 569 llvm::sys::fs::make_absolute(MainFilePath); 570 571 // Create the PTHWriter. 572 PTHWriter PW(*OS, PP); 573 574 // Install the 'stat' system call listener in the FileManager. 575 StatListener *StatCache = new StatListener(PW.getPM()); 576 PP.getFileManager().addStatCache(StatCache, /*AtBeginning=*/true); 577 578 // Lex through the entire file. This will populate SourceManager with 579 // all of the header information. 580 Token Tok; 581 PP.EnterMainSourceFile(); 582 do { PP.Lex(Tok); } while (Tok.isNot(tok::eof)); 583 584 // Generate the PTH file. 585 PP.getFileManager().removeStatCache(StatCache); 586 PW.GeneratePTH(MainFilePath.str()); 587 } 588 589 //===----------------------------------------------------------------------===// 590 591 namespace { 592 class PTHIdKey { 593 public: 594 const IdentifierInfo* II; 595 uint32_t FileOffset; 596 }; 597 598 class PTHIdentifierTableTrait { 599 public: 600 typedef PTHIdKey* key_type; 601 typedef key_type key_type_ref; 602 603 typedef uint32_t data_type; 604 typedef data_type data_type_ref; 605 606 typedef unsigned hash_value_type; 607 typedef unsigned offset_type; 608 609 static hash_value_type ComputeHash(PTHIdKey* key) { 610 return llvm::HashString(key->II->getName()); 611 } 612 613 static std::pair<unsigned,unsigned> 614 EmitKeyDataLength(raw_ostream& Out, const PTHIdKey* key, uint32_t) { 615 using namespace llvm::support; 616 unsigned n = key->II->getLength() + 1; 617 endian::Writer<little>(Out).write<uint16_t>(n); 618 return std::make_pair(n, sizeof(uint32_t)); 619 } 620 621 static void EmitKey(raw_ostream& Out, PTHIdKey* key, unsigned n) { 622 // Record the location of the key data. This is used when generating 623 // the mapping from persistent IDs to strings. 624 key->FileOffset = Out.tell(); 625 Out.write(key->II->getNameStart(), n); 626 } 627 628 static void EmitData(raw_ostream& Out, PTHIdKey*, uint32_t pID, 629 unsigned) { 630 using namespace llvm::support; 631 endian::Writer<little>(Out).write<uint32_t>(pID); 632 } 633 }; 634 } // end anonymous namespace 635 636 /// EmitIdentifierTable - Emits two tables to the PTH file. The first is 637 /// a hashtable mapping from identifier strings to persistent IDs. The second 638 /// is a straight table mapping from persistent IDs to string data (the 639 /// keys of the first table). 640 /// 641 std::pair<Offset,Offset> PTHWriter::EmitIdentifierTable() { 642 // Build two maps: 643 // (1) an inverse map from persistent IDs -> (IdentifierInfo*,Offset) 644 // (2) a map from (IdentifierInfo*, Offset)* -> persistent IDs 645 646 // Note that we use 'calloc', so all the bytes are 0. 647 PTHIdKey *IIDMap = (PTHIdKey*)calloc(idcount, sizeof(PTHIdKey)); 648 649 // Create the hashtable. 650 llvm::OnDiskChainedHashTableGenerator<PTHIdentifierTableTrait> IIOffMap; 651 652 // Generate mapping from persistent IDs -> IdentifierInfo*. 653 for (IDMap::iterator I = IM.begin(), E = IM.end(); I != E; ++I) { 654 // Decrement by 1 because we are using a vector for the lookup and 655 // 0 is reserved for NULL. 656 assert(I->second > 0); 657 assert(I->second-1 < idcount); 658 unsigned idx = I->second-1; 659 660 // Store the mapping from persistent ID to IdentifierInfo* 661 IIDMap[idx].II = I->first; 662 663 // Store the reverse mapping in a hashtable. 664 IIOffMap.insert(&IIDMap[idx], I->second); 665 } 666 667 // Write out the inverse map first. This causes the PCIDKey entries to 668 // record PTH file offsets for the string data. This is used to write 669 // the second table. 670 Offset StringTableOffset = IIOffMap.Emit(Out); 671 672 // Now emit the table mapping from persistent IDs to PTH file offsets. 673 Offset IDOff = Out.tell(); 674 Emit32(idcount); // Emit the number of identifiers. 675 for (unsigned i = 0 ; i < idcount; ++i) 676 Emit32(IIDMap[i].FileOffset); 677 678 // Finally, release the inverse map. 679 free(IIDMap); 680 681 return std::make_pair(IDOff, StringTableOffset); 682 } 683