2006-06-18 05:43:12 +00:00
|
|
|
//===--- Preprocess.cpp - C Language Family Preprocessor Implementation ---===//
|
|
|
|
|
//
|
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
|
//
|
|
|
|
|
// This file was developed by Chris Lattner and is distributed under
|
|
|
|
|
// the University of Illinois Open Source License. See LICENSE.TXT for details.
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
//
|
2006-07-04 17:53:21 +00:00
|
|
|
// This file implements the IdentifierInfo, IdentifierVisitor, and
|
2006-07-03 04:28:52 +00:00
|
|
|
// IdentifierTable interfaces.
|
2006-06-18 05:43:12 +00:00
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
#include "clang/Lex/IdentifierTable.h"
|
|
|
|
|
#include "clang/Lex/MacroInfo.h"
|
2006-10-18 06:07:05 +00:00
|
|
|
#include "clang/Basic/LangOptions.h"
|
2006-06-18 05:43:12 +00:00
|
|
|
#include <iostream>
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
using namespace clang;
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2006-07-04 17:53:21 +00:00
|
|
|
// IdentifierInfo Implementation
|
2006-06-18 05:43:12 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2006-07-04 17:53:21 +00:00
|
|
|
void IdentifierInfo::Destroy() {
|
2006-06-18 05:43:12 +00:00
|
|
|
delete Macro;
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-03 04:28:52 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// IdentifierVisitor Implementation
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
IdentifierVisitor::~IdentifierVisitor() {
|
|
|
|
|
}
|
2006-06-18 05:43:12 +00:00
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Memory Allocation Support
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
/// The identifier table has a very simple memory allocation pattern: it just
|
|
|
|
|
/// keeps allocating identifiers, then never frees them unless it frees them
|
|
|
|
|
/// all. As such, we use a simple bump-pointer memory allocator to make
|
|
|
|
|
/// allocation speedy. Shark showed that malloc was 27% of the time spent in
|
|
|
|
|
/// IdentifierTable::getIdentifier with malloc, and takes a 4.3% time with this.
|
|
|
|
|
#define USE_ALLOCATOR 1
|
|
|
|
|
#if USE_ALLOCATOR
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
class MemRegion {
|
|
|
|
|
unsigned RegionSize;
|
|
|
|
|
MemRegion *Next;
|
|
|
|
|
char *NextPtr;
|
|
|
|
|
public:
|
|
|
|
|
void Init(unsigned size, MemRegion *next) {
|
|
|
|
|
RegionSize = size;
|
|
|
|
|
Next = next;
|
|
|
|
|
NextPtr = (char*)(this+1);
|
|
|
|
|
|
|
|
|
|
// FIXME: uses GCC extension.
|
2006-07-04 17:53:21 +00:00
|
|
|
unsigned Alignment = __alignof__(IdentifierInfo);
|
2006-06-18 05:43:12 +00:00
|
|
|
NextPtr = (char*)((intptr_t)(NextPtr+Alignment-1) &
|
|
|
|
|
~(intptr_t)(Alignment-1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const MemRegion *getNext() const { return Next; }
|
|
|
|
|
unsigned getNumBytesAllocated() const {
|
|
|
|
|
return NextPtr-(const char*)this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Allocate - Allocate and return at least the specified number of bytes.
|
|
|
|
|
///
|
|
|
|
|
void *Allocate(unsigned AllocSize, MemRegion **RegPtr) {
|
|
|
|
|
// FIXME: uses GCC extension.
|
2006-07-04 17:53:21 +00:00
|
|
|
unsigned Alignment = __alignof__(IdentifierInfo);
|
2006-06-18 05:43:12 +00:00
|
|
|
// Round size up to an even multiple of the alignment.
|
|
|
|
|
AllocSize = (AllocSize+Alignment-1) & ~(Alignment-1);
|
|
|
|
|
|
|
|
|
|
// If there is space in this region for the identifier, return it.
|
|
|
|
|
if (unsigned(NextPtr+AllocSize-(char*)this) <= RegionSize) {
|
|
|
|
|
void *Result = NextPtr;
|
|
|
|
|
NextPtr += AllocSize;
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, we have to allocate a new chunk. Create one twice as big as
|
|
|
|
|
// this one.
|
|
|
|
|
MemRegion *NewRegion = (MemRegion *)malloc(RegionSize*2);
|
|
|
|
|
NewRegion->Init(RegionSize*2, this);
|
|
|
|
|
|
|
|
|
|
// Update the current "first region" pointer to point to the new region.
|
|
|
|
|
*RegPtr = NewRegion;
|
|
|
|
|
|
|
|
|
|
// Try allocating from it now.
|
|
|
|
|
return NewRegion->Allocate(AllocSize, RegPtr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Deallocate - Release all memory for this region to the system.
|
|
|
|
|
///
|
|
|
|
|
void Deallocate() {
|
|
|
|
|
MemRegion *next = Next;
|
|
|
|
|
free(this);
|
|
|
|
|
if (next)
|
|
|
|
|
next->Deallocate();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// IdentifierTable Implementation
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
/// IdentifierBucket - The hash table consists of an array of these. If Info is
|
|
|
|
|
/// non-null, this is an extant entry, otherwise, it is a hole.
|
2006-06-18 05:43:12 +00:00
|
|
|
struct IdentifierBucket {
|
2006-10-27 03:59:10 +00:00
|
|
|
/// FullHashValue - This remembers the full hash value of the identifier for
|
|
|
|
|
/// easy scanning.
|
|
|
|
|
unsigned FullHashValue;
|
2006-06-18 05:43:12 +00:00
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
/// Info - This is a pointer to the actual identifier info object.
|
|
|
|
|
IdentifierInfo *Info;
|
2006-06-18 05:43:12 +00:00
|
|
|
};
|
|
|
|
|
|
2006-10-18 06:07:05 +00:00
|
|
|
IdentifierTable::IdentifierTable(const LangOptions &LangOpts) {
|
2006-10-27 03:59:10 +00:00
|
|
|
HashTableSize = 8192; // Start with space for 8K identifiers.
|
|
|
|
|
IdentifierBucket *TableArray = new IdentifierBucket[HashTableSize]();
|
|
|
|
|
memset(TableArray, 0, HashTableSize*sizeof(IdentifierBucket));
|
|
|
|
|
|
2006-06-18 05:43:12 +00:00
|
|
|
TheTable = TableArray;
|
|
|
|
|
NumIdentifiers = 0;
|
|
|
|
|
#if USE_ALLOCATOR
|
|
|
|
|
TheMemory = malloc(8*4096);
|
|
|
|
|
((MemRegion*)TheMemory)->Init(8*4096, 0);
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
// Populate the identifier table with info about keywords for the current
|
|
|
|
|
// language.
|
2006-10-18 06:07:05 +00:00
|
|
|
AddKeywords(LangOpts);
|
2006-06-18 05:43:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IdentifierTable::~IdentifierTable() {
|
2006-10-27 03:59:10 +00:00
|
|
|
IdentifierBucket *TableArray = (IdentifierBucket*)TheTable;
|
|
|
|
|
for (unsigned i = 0, e = HashTableSize; i != e; ++i) {
|
|
|
|
|
if (IdentifierInfo *Id = TableArray[i].Info) {
|
2006-06-18 05:43:12 +00:00
|
|
|
// Free memory referenced by the identifier (e.g. macro info).
|
2006-10-27 03:59:10 +00:00
|
|
|
Id->Destroy();
|
2006-06-18 05:43:12 +00:00
|
|
|
|
|
|
|
|
#if !USE_ALLOCATOR
|
2006-10-27 03:59:10 +00:00
|
|
|
// Free the memory for the identifier itself.
|
2006-06-18 05:43:12 +00:00
|
|
|
free(Id);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#if USE_ALLOCATOR
|
|
|
|
|
((MemRegion*)TheMemory)->Deallocate();
|
|
|
|
|
#endif
|
|
|
|
|
delete [] TableArray;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// HashString - Compute a hash code for the specified string.
|
|
|
|
|
///
|
|
|
|
|
static unsigned HashString(const char *Start, const char *End) {
|
|
|
|
|
unsigned int Result = 0;
|
|
|
|
|
// Perl hash function.
|
|
|
|
|
while (Start != End)
|
|
|
|
|
Result = Result * 33 + *Start++;
|
|
|
|
|
Result = Result + (Result >> 5);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-04 17:53:21 +00:00
|
|
|
IdentifierInfo &IdentifierTable::get(const char *NameStart,
|
2006-07-04 18:53:52 +00:00
|
|
|
const char *NameEnd) {
|
2006-10-27 03:59:10 +00:00
|
|
|
IdentifierBucket *TableArray = (IdentifierBucket*)TheTable;
|
2006-06-18 05:43:12 +00:00
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
unsigned HTSize = HashTableSize;
|
|
|
|
|
unsigned FullHashValue = HashString(NameStart, NameEnd);
|
|
|
|
|
unsigned BucketNo = FullHashValue & (HTSize-1);
|
2006-06-18 05:43:12 +00:00
|
|
|
unsigned Length = NameEnd-NameStart;
|
|
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
unsigned ProbeAmt = 1;
|
|
|
|
|
while (1) {
|
|
|
|
|
IdentifierBucket &Bucket = TableArray[BucketNo];
|
|
|
|
|
IdentifierInfo *BucketII = Bucket.Info;
|
|
|
|
|
// If we found an empty bucket, this identifier isn't in the table yet.
|
|
|
|
|
if (BucketII == 0) break;
|
|
|
|
|
|
|
|
|
|
// If the full hash value matches, check deeply for a match. The common
|
|
|
|
|
// case here is that we are only looking at the buckets (for identifier info
|
|
|
|
|
// being non-null and for the full hash value) not at the identifiers. This
|
|
|
|
|
// is important for cache locality.
|
|
|
|
|
if (Bucket.FullHashValue == FullHashValue &&
|
|
|
|
|
memcmp(BucketII->getName(), NameStart, Length) == 0)
|
|
|
|
|
// We found a match!
|
|
|
|
|
return *BucketII;
|
|
|
|
|
|
|
|
|
|
// Okay, we didn't find the identifier. Probe to the next bucket.
|
|
|
|
|
BucketNo = (BucketNo+ProbeAmt) & (HashTableSize-1);
|
|
|
|
|
|
|
|
|
|
// Use quadratic probing, it has fewer clumping artifacts than linear
|
|
|
|
|
// probing and has good cache behavior in the common case.
|
|
|
|
|
++ProbeAmt;
|
2006-06-18 05:43:12 +00:00
|
|
|
}
|
2006-10-27 03:59:10 +00:00
|
|
|
|
|
|
|
|
// Okay, the identifier doesn't already exist, and BucketNo is the bucket to
|
|
|
|
|
// fill in. Allocate a new identifier with space for the null-terminated
|
|
|
|
|
// string at the end.
|
|
|
|
|
unsigned AllocSize = sizeof(IdentifierInfo)+Length+1;
|
2006-06-18 05:43:12 +00:00
|
|
|
#if USE_ALLOCATOR
|
2006-10-27 03:59:10 +00:00
|
|
|
IdentifierInfo *Identifier = (IdentifierInfo*)
|
2006-06-18 05:43:12 +00:00
|
|
|
((MemRegion*)TheMemory)->Allocate(AllocSize, (MemRegion**)&TheMemory);
|
|
|
|
|
#else
|
2006-10-27 03:59:10 +00:00
|
|
|
IdentifierInfo *Identifier = (IdentifierInfo*)malloc(AllocSize);
|
2006-06-18 05:43:12 +00:00
|
|
|
#endif
|
2006-10-27 03:59:10 +00:00
|
|
|
Identifier->Macro = 0;
|
|
|
|
|
Identifier->TokenID = tok::identifier;
|
|
|
|
|
Identifier->PPID = tok::pp_not_keyword;
|
|
|
|
|
Identifier->ObjCID = tok::objc_not_keyword;
|
|
|
|
|
Identifier->IsExtension = false;
|
|
|
|
|
Identifier->IsPoisoned = false;
|
|
|
|
|
Identifier->IsOtherTargetMacro = false;
|
|
|
|
|
Identifier->FETokenInfo = 0;
|
|
|
|
|
++NumIdentifiers;
|
2006-06-18 05:43:12 +00:00
|
|
|
|
|
|
|
|
// Copy the string information.
|
|
|
|
|
char *StrBuffer = (char*)(Identifier+1);
|
|
|
|
|
memcpy(StrBuffer, NameStart, Length);
|
|
|
|
|
StrBuffer[Length] = 0; // Null terminate string.
|
|
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
// Fill in the bucket for the hash table.
|
|
|
|
|
TableArray[BucketNo].Info = Identifier;
|
|
|
|
|
TableArray[BucketNo].FullHashValue = FullHashValue;
|
|
|
|
|
|
|
|
|
|
// If the hash table is now more than 3/4 full, rehash into a larger table.
|
|
|
|
|
if (NumIdentifiers > HashTableSize*3/4)
|
|
|
|
|
RehashTable();
|
|
|
|
|
|
|
|
|
|
return *Identifier;
|
2006-06-18 05:43:12 +00:00
|
|
|
}
|
|
|
|
|
|
2006-07-04 17:53:21 +00:00
|
|
|
IdentifierInfo &IdentifierTable::get(const std::string &Name) {
|
2006-06-18 05:43:12 +00:00
|
|
|
// Don't use c_str() here: no need to be null terminated.
|
|
|
|
|
const char *NameBytes = &Name[0];
|
|
|
|
|
unsigned Size = Name.size();
|
|
|
|
|
return get(NameBytes, NameBytes+Size);
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
void IdentifierTable::RehashTable() {
|
|
|
|
|
unsigned NewSize = HashTableSize*2;
|
|
|
|
|
IdentifierBucket *NewTableArray = new IdentifierBucket[NewSize]();
|
|
|
|
|
memset(NewTableArray, 0, NewSize*sizeof(IdentifierBucket));
|
|
|
|
|
|
|
|
|
|
// Rehash all the identifier into their new buckets. Luckily we already have
|
|
|
|
|
// the hash values available :).
|
|
|
|
|
IdentifierBucket *CurTable = (IdentifierBucket *)TheTable;
|
|
|
|
|
for (IdentifierBucket *IB = CurTable, *E = CurTable+HashTableSize;
|
|
|
|
|
IB != E; ++IB) {
|
|
|
|
|
if (IB->Info) {
|
|
|
|
|
// Fast case, bucket available.
|
|
|
|
|
unsigned FullHash = IB->FullHashValue;
|
|
|
|
|
unsigned NewBucket = FullHash & (NewSize-1);
|
|
|
|
|
if (NewTableArray[NewBucket].Info == 0) {
|
|
|
|
|
NewTableArray[FullHash & (NewSize-1)].Info = IB->Info;
|
|
|
|
|
NewTableArray[FullHash & (NewSize-1)].FullHashValue = FullHash;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned ProbeSize = 1;
|
|
|
|
|
do {
|
|
|
|
|
NewBucket = (NewBucket + ProbeSize++) & (NewSize-1);
|
|
|
|
|
} while (NewTableArray[NewBucket].Info);
|
|
|
|
|
|
|
|
|
|
// Finally found a slot. Fill it in.
|
2006-10-27 04:54:47 +00:00
|
|
|
NewTableArray[NewBucket].Info = IB->Info;
|
|
|
|
|
NewTableArray[NewBucket].FullHashValue = FullHash;
|
2006-10-27 03:59:10 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete[] CurTable;
|
|
|
|
|
|
|
|
|
|
TheTable = NewTableArray;
|
|
|
|
|
HashTableSize = NewSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-07-03 04:28:52 +00:00
|
|
|
/// VisitIdentifiers - This method walks through all of the identifiers,
|
|
|
|
|
/// invoking IV->VisitIdentifier for each of them.
|
|
|
|
|
void IdentifierTable::VisitIdentifiers(const IdentifierVisitor &IV) {
|
2006-10-27 03:59:10 +00:00
|
|
|
IdentifierBucket *TableArray = (IdentifierBucket*)TheTable;
|
|
|
|
|
for (unsigned i = 0, e = HashTableSize; i != e; ++i) {
|
|
|
|
|
if (IdentifierInfo *Id = TableArray[i].Info)
|
|
|
|
|
IV.VisitIdentifier(*Id);
|
2006-07-03 04:28:52 +00:00
|
|
|
}
|
|
|
|
|
}
|
2006-06-18 05:43:12 +00:00
|
|
|
|
2006-10-18 06:07:05 +00:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Language Keyword Implementation
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
/// AddKeyword - This method is used to associate a token ID with specific
|
|
|
|
|
/// identifiers because they are language keywords. This causes the lexer to
|
|
|
|
|
/// automatically map matching identifiers to specialized token codes.
|
|
|
|
|
///
|
|
|
|
|
/// The C90/C99/CPP flags are set to 0 if the token should be enabled in the
|
|
|
|
|
/// specified langauge, set to 1 if it is an extension in the specified
|
|
|
|
|
/// language, and set to 2 if disabled in the specified language.
|
|
|
|
|
static void AddKeyword(const std::string &Keyword, tok::TokenKind TokenCode,
|
2006-10-20 06:13:26 +00:00
|
|
|
int C90, int C99, int CXX,
|
2006-10-18 06:07:05 +00:00
|
|
|
const LangOptions &LangOpts, IdentifierTable &Table) {
|
2006-10-20 06:13:26 +00:00
|
|
|
int Flags = LangOpts.CPlusPlus ? CXX : (LangOpts.C99 ? C99 : C90);
|
2006-10-18 06:07:05 +00:00
|
|
|
|
|
|
|
|
// Don't add this keyword if disabled in this language or if an extension
|
|
|
|
|
// and extensions are disabled.
|
|
|
|
|
if (Flags + LangOpts.NoExtensions >= 2) return;
|
|
|
|
|
|
|
|
|
|
const char *Str = &Keyword[0];
|
|
|
|
|
IdentifierInfo &Info = Table.get(Str, Str+Keyword.size());
|
|
|
|
|
Info.setTokenID(TokenCode);
|
|
|
|
|
Info.setIsExtensionToken(Flags == 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// AddPPKeyword - Register a preprocessor keyword like "define" "undef" or
|
|
|
|
|
/// "elif".
|
|
|
|
|
static void AddPPKeyword(tok::PPKeywordKind PPID,
|
|
|
|
|
const char *Name, unsigned NameLen,
|
|
|
|
|
IdentifierTable &Table) {
|
|
|
|
|
Table.get(Name, Name+NameLen).setPPKeywordID(PPID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// AddObjCKeyword - Register an Objective-C @keyword like "class" "selector" or
|
|
|
|
|
/// "property".
|
|
|
|
|
static void AddObjCKeyword(tok::ObjCKeywordKind ObjCID,
|
|
|
|
|
const char *Name, unsigned NameLen,
|
|
|
|
|
IdentifierTable &Table) {
|
|
|
|
|
Table.get(Name, Name+NameLen).setObjCKeywordID(ObjCID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// AddKeywords - Add all keywords to the symbol table.
|
|
|
|
|
///
|
|
|
|
|
void IdentifierTable::AddKeywords(const LangOptions &LangOpts) {
|
|
|
|
|
enum {
|
|
|
|
|
C90Shift = 0,
|
|
|
|
|
EXTC90 = 1 << C90Shift,
|
|
|
|
|
NOTC90 = 2 << C90Shift,
|
|
|
|
|
C99Shift = 2,
|
|
|
|
|
EXTC99 = 1 << C99Shift,
|
|
|
|
|
NOTC99 = 2 << C99Shift,
|
|
|
|
|
CPPShift = 4,
|
|
|
|
|
EXTCPP = 1 << CPPShift,
|
|
|
|
|
NOTCPP = 2 << CPPShift,
|
|
|
|
|
Mask = 3
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Add keywords and tokens for the current language.
|
|
|
|
|
#define KEYWORD(NAME, FLAGS) \
|
|
|
|
|
AddKeyword(#NAME, tok::kw_ ## NAME, \
|
|
|
|
|
((FLAGS) >> C90Shift) & Mask, \
|
|
|
|
|
((FLAGS) >> C99Shift) & Mask, \
|
|
|
|
|
((FLAGS) >> CPPShift) & Mask, LangOpts, *this);
|
|
|
|
|
#define ALIAS(NAME, TOK) \
|
|
|
|
|
AddKeyword(NAME, tok::kw_ ## TOK, 0, 0, 0, LangOpts, *this);
|
|
|
|
|
#define PPKEYWORD(NAME) \
|
|
|
|
|
AddPPKeyword(tok::pp_##NAME, #NAME, strlen(#NAME), *this);
|
|
|
|
|
#define OBJC1_AT_KEYWORD(NAME) \
|
|
|
|
|
if (LangOpts.ObjC1) \
|
|
|
|
|
AddObjCKeyword(tok::objc_##NAME, #NAME, strlen(#NAME), *this);
|
|
|
|
|
#define OBJC2_AT_KEYWORD(NAME) \
|
|
|
|
|
if (LangOpts.ObjC2) \
|
|
|
|
|
AddObjCKeyword(tok::objc_##NAME, #NAME, strlen(#NAME), *this);
|
|
|
|
|
#include "clang/Basic/TokenKinds.def"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Stats Implementation
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2006-06-18 05:43:12 +00:00
|
|
|
/// PrintStats - Print statistics about how well the identifier table is doing
|
|
|
|
|
/// at hashing identifiers.
|
|
|
|
|
void IdentifierTable::PrintStats() const {
|
|
|
|
|
unsigned NumEmptyBuckets = 0;
|
|
|
|
|
unsigned AverageIdentifierSize = 0;
|
|
|
|
|
unsigned MaxIdentifierLength = 0;
|
2006-10-27 03:59:10 +00:00
|
|
|
unsigned NumProbed = 0;
|
2006-06-18 05:43:12 +00:00
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
IdentifierBucket *TableArray = (IdentifierBucket*)TheTable;
|
|
|
|
|
for (unsigned i = 0, e = HashTableSize; i != e; ++i) {
|
|
|
|
|
if (TableArray[i].Info == 0) {
|
2006-06-18 05:43:12 +00:00
|
|
|
++NumEmptyBuckets;
|
2006-10-27 03:59:10 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
IdentifierInfo *Id = TableArray[i].Info;
|
2006-10-27 05:06:38 +00:00
|
|
|
unsigned IdLen = strlen(Id->getName());
|
|
|
|
|
AverageIdentifierSize += IdLen;
|
|
|
|
|
if (MaxIdentifierLength < IdLen)
|
|
|
|
|
MaxIdentifierLength = IdLen;
|
2006-10-27 03:59:10 +00:00
|
|
|
|
|
|
|
|
// Count the number of times something was probed.
|
|
|
|
|
if ((TableArray[i].FullHashValue & (e-1)) != i)
|
|
|
|
|
++NumProbed;
|
2006-06-18 05:43:12 +00:00
|
|
|
|
2006-10-27 03:59:10 +00:00
|
|
|
// TODO: Figure out maximum times an identifier had to probe for -stats.
|
2006-06-18 05:43:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cerr << "\n*** Identifier Table Stats:\n";
|
|
|
|
|
std::cerr << "# Identifiers: " << NumIdentifiers << "\n";
|
|
|
|
|
std::cerr << "# Empty Buckets: " << NumEmptyBuckets << "\n";
|
|
|
|
|
std::cerr << "Hash density (#identifiers per bucket): "
|
2006-10-27 03:59:10 +00:00
|
|
|
<< NumIdentifiers/(double)HashTableSize << "\n";
|
|
|
|
|
std::cerr << "Num probed identifiers: " << NumProbed << " ("
|
|
|
|
|
<< NumProbed*100.0/NumIdentifiers << "%)\n";
|
2006-06-18 05:43:12 +00:00
|
|
|
std::cerr << "Ave identifier length: "
|
|
|
|
|
<< (AverageIdentifierSize/(double)NumIdentifiers) << "\n";
|
|
|
|
|
std::cerr << "Max identifier length: " << MaxIdentifierLength << "\n";
|
|
|
|
|
|
|
|
|
|
// Compute statistics about the memory allocated for identifiers.
|
|
|
|
|
#if USE_ALLOCATOR
|
|
|
|
|
unsigned BytesUsed = 0;
|
|
|
|
|
unsigned NumRegions = 0;
|
|
|
|
|
const MemRegion *R = (MemRegion*)TheMemory;
|
|
|
|
|
for (; R; R = R->getNext(), ++NumRegions) {
|
|
|
|
|
BytesUsed += R->getNumBytesAllocated();
|
|
|
|
|
}
|
|
|
|
|
std::cerr << "\nNumber of memory regions: " << NumRegions << "\n";
|
|
|
|
|
std::cerr << "Bytes allocated for identifiers: " << BytesUsed << "\n";
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|