summaryrefslogtreecommitdiff
path: root/compiler/cclasses.pas
diff options
context:
space:
mode:
authorflorian <florian@3ad0048d-3df7-0310-abae-a5850022a9f2>2008-08-27 15:16:45 +0000
committerflorian <florian@3ad0048d-3df7-0310-abae-a5850022a9f2>2008-08-27 15:16:45 +0000
commit0f0aea9011a5e0347a60640a7197cdc75e09e382 (patch)
tree0ecb7c57a2a95f4e8e8d2c1b709d3b400b552600 /compiler/cclasses.pas
parent99b31d58de46c66b3f0182df7ac2bcf7d9cd5761 (diff)
downloadfpc-0f0aea9011a5e0347a60640a7197cdc75e09e382.tar.gz
o patch from Sergej Gorelkin to improvement code generation for string literals
* Replaces linear search through assembler list by the hash lookup. This considerably improves performance on large projects (one example is winunits-jedi package, in which tcgstringconstnode.pass_generate_code was top #1 in calltree, consuming about 12% IRefs). * Enables reusing memory locations for widestring constants (and in general, the same approach may be used for any other type of constants). * Saves a sizeof(pointer) bytes per constant, by removing a location which points to the string. This location is necessary for the typed consts which may be modified, but redundant for string literals because the language does not allow to modify string literals in any way. git-svn-id: http://svn.freepascal.org/svn/fpc/trunk@11657 3ad0048d-3df7-0310-abae-a5850022a9f2
Diffstat (limited to 'compiler/cclasses.pas')
-rw-r--r--compiler/cclasses.pas248
1 files changed, 244 insertions, 4 deletions
diff --git a/compiler/cclasses.pas b/compiler/cclasses.pas
index 66ea091c45..51d6cd299b 100644
--- a/compiler/cclasses.pas
+++ b/compiler/cclasses.pas
@@ -459,7 +459,51 @@ type
end;
+{******************************************************************
+ THashSet (keys not limited to ShortString, no indexed access)
+*******************************************************************}
+
+ PPHashSetItem = ^PHashSetItem;
+ PHashSetItem = ^THashSetItem;
+ THashSetItem = record
+ Next: PHashSetItem;
+ Key: Pointer;
+ KeyLength: Integer;
+ HashValue: LongWord;
+ Data: TObject;
+ end;
+
+ THashSet = class(TObject)
+ private
+ FCount: LongWord;
+ FBucketCount: LongWord;
+ FBucket: PPHashSetItem;
+ FOwnsObjects: Boolean;
+ FOwnsKeys: Boolean;
+ function Lookup(Key: Pointer; KeyLen: Integer; var Found: Boolean;
+ CanCreate: Boolean): PHashSetItem;
+ procedure Resize(NewCapacity: LongWord);
+ public
+ constructor Create(InitSize: Integer; OwnKeys, OwnObjects: Boolean);
+ destructor Destroy; override;
+ procedure Clear;
+ { finds an entry by key }
+ function Find(Key: Pointer; KeyLen: Integer): PHashSetItem;
+ { finds an entry, creates one if not exists }
+ function FindOrAdd(Key: Pointer; KeyLen: Integer;
+ var Found: Boolean): PHashSetItem;
+ { finds an entry, creates one if not exists }
+ function FindOrAdd(Key: Pointer; KeyLen: Integer): PHashSetItem;
+ { returns Data by given Key }
+ function Get(Key: Pointer; KeyLen: Integer): TObject;
+ { removes an entry, returns False if entry wasn't there }
+ function Remove(Entry: PHashSetItem): Boolean;
+ property Count: LongWord read FCount;
+ end;
+
+
function FPHash(const s:shortstring):LongWord;
+ function FPHash(P: PChar; Len: Integer): LongWord;
implementation
@@ -1043,7 +1087,7 @@ end;
pmax:=@s[length(s)+1];
while (p<pmax) do
begin
- result:=LongWord((result shl 5) - result) xor LongWord(P^);
+ result:=LongWord(LongInt(result shl 5) - LongInt(result)) xor LongWord(P^);
inc(p);
end;
{$ifdef overflowon}
@@ -1052,6 +1096,26 @@ end;
{$endif}
end;
+ function FPHash(P: PChar; Len: Integer): LongWord;
+ Var
+ pmax : pchar;
+ begin
+{$ifopt Q+}
+{$define overflowon}
+{$Q-}
+{$endif}
+ result:=0;
+ pmax:=p+len;
+ while (p<pmax) do
+ begin
+ result:=LongWord(LongInt(result shl 5) - LongInt(result)) xor LongWord(P^);
+ inc(p);
+ end;
+{$ifdef overflowon}
+{$Q+}
+{$undef overflowon}
+{$endif}
+ end;
procedure TFPHashList.RaiseIndexError(Index : Integer);
begin
@@ -2226,16 +2290,14 @@ end;
function TCmdStrList.Find(const s:TCmdStr):TCmdStrListItem;
var
NewNode : TCmdStrListItem;
- ups : string;
begin
result:=nil;
if s='' then
exit;
- ups:=upper(s);
NewNode:=TCmdStrListItem(FFirst);
while assigned(NewNode) do
begin
- if upper(NewNode.FPStr)=ups then
+ if SysUtils.CompareText(s, NewNode.FPStr)=0 then
begin
result:=NewNode;
exit;
@@ -2521,4 +2583,182 @@ end;
end;
end;
+{****************************************************************************
+ thashset
+****************************************************************************}
+
+ constructor THashSet.Create(InitSize: Integer; OwnKeys, OwnObjects: Boolean);
+ var
+ I: Integer;
+ begin
+ inherited Create;
+ FOwnsObjects := OwnObjects;
+ FOwnsKeys := OwnKeys;
+ I := 64;
+ while I < InitSize do I := I shl 1;
+ FBucketCount := I;
+ FBucket := AllocMem(I * sizeof(PHashSetItem));
+ end;
+
+
+ destructor THashSet.Destroy;
+ begin
+ Clear;
+ FreeMem(FBucket);
+ inherited Destroy;
+ end;
+
+
+ procedure THashSet.Clear;
+ var
+ I: Integer;
+ item, next: PHashSetItem;
+ begin
+ for I := 0 to FBucketCount-1 do
+ begin
+ item := FBucket[I];
+ while Assigned(item) do
+ begin
+ next := item^.Next;
+ if FOwnsObjects then
+ item^.Data.Free;
+ if FOwnsKeys then
+ FreeMem(item^.Key);
+ Dispose(item);
+ item := next;
+ end;
+ end;
+ FillChar(FBucket^, FBucketCount * sizeof(PHashSetItem), 0);
+ end;
+
+
+ function THashSet.Find(Key: Pointer; KeyLen: Integer): PHashSetItem;
+ var
+ Dummy: Boolean;
+ begin
+ Result := Lookup(Key, KeyLen, Dummy, False);
+ end;
+
+
+ function THashSet.FindOrAdd(Key: Pointer; KeyLen: Integer;
+ var Found: Boolean): PHashSetItem;
+ begin
+ Result := Lookup(Key, KeyLen, Found, True);
+ end;
+
+
+ function THashSet.FindOrAdd(Key: Pointer; KeyLen: Integer): PHashSetItem;
+ var
+ Dummy: Boolean;
+ begin
+ Result := Lookup(Key, KeyLen, Dummy, True);
+ end;
+
+
+ function THashSet.Get(Key: Pointer; KeyLen: Integer): TObject;
+ var
+ e: PHashSetItem;
+ Dummy: Boolean;
+ begin
+ e := Lookup(Key, KeyLen, Dummy, False);
+ if Assigned(e) then
+ Result := e^.Data
+ else
+ Result := nil;
+ end;
+
+
+ function THashSet.Lookup(Key: Pointer; KeyLen: Integer;
+ var Found: Boolean; CanCreate: Boolean): PHashSetItem;
+ var
+ Entry: PPHashSetItem;
+ h: LongWord;
+ begin
+ h := FPHash(Key, KeyLen);
+ Entry := @FBucket[h mod FBucketCount];
+ while Assigned(Entry^) and
+ not ((Entry^^.HashValue = h) and (Entry^^.KeyLength = KeyLen) and
+ (CompareByte(Entry^^.Key^, Key^, KeyLen) = 0)) do
+ Entry := @Entry^^.Next;
+ Found := Assigned(Entry^);
+ if Found or (not CanCreate) then
+ begin
+ Result := Entry^;
+ Exit;
+ end;
+ if FCount > FBucketCount then { arbitrary limit, probably too high }
+ begin
+ { rehash and repeat search }
+ Resize(FBucketCount * 2);
+ Result := Lookup(Key, KeyLen, Found, CanCreate);
+ end
+ else
+ begin
+ New(Result);
+ if FOwnsKeys then
+ begin
+ GetMem(Result^.Key, KeyLen);
+ Move(Key^, Result^.Key^, KeyLen);
+ end
+ else
+ Result^.Key := Key;
+ Result^.KeyLength := KeyLen;
+ Result^.HashValue := h;
+ Result^.Data := nil;
+ Result^.Next := nil;
+ Inc(FCount);
+ Entry^ := Result;
+ end;
+ end;
+
+
+ procedure THashSet.Resize(NewCapacity: LongWord);
+ var
+ p, chain: PPHashSetItem;
+ i: Integer;
+ e, n: PHashSetItem;
+ begin
+ p := AllocMem(NewCapacity * sizeof(PHashSetItem));
+ for i := 0 to FBucketCount-1 do
+ begin
+ e := FBucket[i];
+ while Assigned(e) do
+ begin
+ chain := @p[e^.HashValue mod NewCapacity];
+ n := e^.Next;
+ e^.Next := chain^;
+ chain^ := e;
+ e := n;
+ end;
+ end;
+ FBucketCount := NewCapacity;
+ FreeMem(FBucket);
+ FBucket := p;
+ end;
+
+
+ function THashSet.Remove(Entry: PHashSetItem): Boolean;
+ var
+ chain: PPHashSetItem;
+ begin
+ chain := @FBucket[Entry^.HashValue mod FBucketCount];
+ while Assigned(chain^) do
+ begin
+ if chain^ = Entry then
+ begin
+ chain^ := Entry^.Next;
+ if FOwnsObjects then
+ Entry^.Data.Free;
+ if FOwnsKeys then
+ FreeMem(Entry^.Key);
+ Dispose(Entry);
+ Dec(FCount);
+ Result := True;
+ Exit;
+ end;
+ chain := @chain^^.Next;
+ end;
+ Result := False;
+ end;
+
end.