{ $Id$ Copyright (c) 1998-2000 by Daniel Mantione member of the Free Pascal development team Commandline compiler for Free Pascal This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************} unit symstack; interface uses objects,symtable,globtype; const cachesize=64; {This should be a power of 2.} type Tsymtablestack=object(Tobject) srsym:Psym; {Result of the last search.} srsymtable:Psymtable; lastsrsym:Psym; {Last sym found in statement.} lastsrsymtable:Psymtable; constructor init; procedure clearcache; procedure insert(s:Psym;addtocache:boolean); function pop:Psymtable; procedure push(s:Psymtable); procedure search(const s:stringid;notfounderror:boolean); function search_a_symtable(const symbol:stringid;symtabletype:pointer):Psym; function top:Psymtable; procedure topfree; destructor done;virtual; private cache:array[1..cachesize] of Psym; cachetables:array[1..cachesize] of Psymtable; symtablestack:Tcollection; {For speed reasons this is not a pointer. A Tcollection is not the perfect data structure for a stack; it could be a good idea to write an abstract stack object.} procedure decache(s:Psymtable); end; {$IFDEF STATISTICS} var hits,misses:longint; {$ENDIF STATISTICS} implementation uses cobjects,symtablt,verbose,symbols,defs; var oldexit:pointer; constructor Tsymtablestack.init; begin symtablestack.init(16,8); clearcache; end; procedure Tsymtablestack.clearcache; begin fillchar(cache,sizeof(cache),0); fillchar(cachetables,sizeof(cache),0); end; procedure Tsymtablestack.decache(s:Psymtable); var p,endp:^Psymtable; q:^Psym; begin {Must be fast, otherwise the speed advantage is lost! Therefore, the cache should not be too large...} p:=@cachetables; endp:=pointer(longint(@cachetables)+cachesize*sizeof(pointer)); q:=@cache; repeat if p^=s then begin p^:=nil; q^:=nil; end; inc(p); inc(q); until p=endp; end; procedure Tsymtablestack.search(const s:stringid;notfounderror:boolean); var speedvalue,entry:longint; i:word; begin speedvalue:=getspeedvalue(s); lastsrsym:=nil; {Check the cache.} entry:=(speedvalue and cachesize-1)+1; if (cache[entry]<>nil) and (cache[entry]^.speedvalue=speedvalue) and (cache[entry]^.name=s) then begin {Cache hit!} srsym:=cache[entry]; srsymtable:=cachetables[entry]; {$IFDEF STATISTICS} inc(hits); {$ENDIF STATISTICS} end else begin {Cache miss. :( } {$IFDEF STATISTICS} inc(misses); {$ENDIF STATISTICS} for i:=symtablestack.count-1 downto 0 do begin srsymtable:=Psymtable(symtablestack.at(i)); srsym:=srsymtable^.speedsearch(s,speedvalue); if srsym<>nil then begin {Found! Place it in the cache.} cache[entry]:=srsym; cachetables[entry]:=srsymtable; exit; end end; {Not found...} srsym:=nil; if notfounderror then begin message1(sym_e_id_not_found,s); srsym:=generrorsym; end; end; end; function Tsymtablestack.pop:Psymtable; var r:Psymtable; begin r:=symtablestack.at(symtablestack.count); decache(r); pop:=r; symtablestack.atdelete(symtablestack.count); end; procedure Tsymtablestack.push(s:Psymtable); begin symtablestack.insert(s); end; procedure Tsymtablestack.insert(s:Psym;addtocache:boolean); var pretop,sttop:Psymtable; hsym:Psym; entry:longint; begin sttop:=Psymtable(symtablestack.at(symtablestack.count)); pretop:=Psymtable(symtablestack.at(symtablestack.count-1)); if typeof(sttop^)=typeof(Timplsymtable) then begin {There must also be an interface symtable...} if pretop^.speedsearch(s^.name,s^.speedvalue)<>nil then duplicatesym(s); end; {Check for duplicate field id in inherited classes.} if sttop^.is_object(typeof(Tobjectsymtable)) and (Pobjectsymtable(sttop)^.defowner<>nil) then begin {Even though the private symtable is disposed and set to nil after the unit has been compiled, we will still have to check for a private sym, because of interdependend units.} hsym:=Pobjectdef(Pobjectsymtable(sttop)^.defowner)^. speedsearch(s^.name,s^.speedvalue); if (hsym<>nil) and (hsym^.is_object(typeof(Tprocsym)) and (sp_private in Pprocsym(hsym)^.objprop)) and (hsym^.is_object(typeof(Tvarsym)) and (sp_private in Pvarsym(hsym)^.objprop)) then duplicatesym(hsym); end; entry:=(s^.speedvalue and cachesize-1)+1; if s^.is_object(typeof(Tenumsym)) and sttop^.is_object(Tabstractrecordsymtable)) then begin if pretop^.insert(s) and addtocache then begin cache[entry]:=s; cachetables[entry]:=pretop; end; end else begin if sttop^.insert(s) and addtocache then begin cache[entry]:=s; cachetables[entry]:=top; end; end; end; function Tsymtablestack.top:Psymtable; begin top:=symtablestack.at(symtablestack.count); end; function Tsymtablestack.search_a_symtable(const symbol:stringid;symtabletype:pointer):Psym; {Search for a symbol in a specified symbol table. Returns nil if the symtable is not found, and also if the symbol cannot be found in the desired symtable.} var hsymtab:Psymtable; res:Psym; i:word; begin res:=nil; for i:=symtablestack.count-1 downto 0 do if typeof((Psymtable(symtablestack.at(i))^))=symtabletype then begin {We found the desired symtable. Now check if the symbol we search for is defined in it } res:=hsymtab^.search(symbol); break; end; search_a_symtable:=res; end; procedure Tsymtablestack.topfree; begin decache(symtablestack.at(symtablestack.count)); symtablestack.atfree(symtablestack.count); end; destructor Tsymtablestack.done; begin symtablestack.done; end; {$IFDEF STATISTICS} procedure exitprocedure;{$IFDEF TP}far;{$ENDIF} begin writeln('Symbol cache statistics:'); writeln('------------------------'); writeln; writeln('Hits: ',hits); writeln('Misses: ',misses); writeln; writeln('Hit percentage: ',(hits*100) div (hits+misses),'%'); exitproc:=oldexit; end; begin hits:=0; misses:=0; oldexit:=exitproc; exitproc:=@exitprocedure; {$ENDIF STATISTICS} end.