summaryrefslogtreecommitdiff
path: root/closures/compiler/tgobj.pas
diff options
context:
space:
mode:
Diffstat (limited to 'closures/compiler/tgobj.pas')
-rw-r--r--closures/compiler/tgobj.pas663
1 files changed, 663 insertions, 0 deletions
diff --git a/closures/compiler/tgobj.pas b/closures/compiler/tgobj.pas
new file mode 100644
index 0000000000..761d4f20c1
--- /dev/null
+++ b/closures/compiler/tgobj.pas
@@ -0,0 +1,663 @@
+{
+ Copyright (c) 1998-2002 by Florian Klaempfl
+
+ This unit implements the base object for temp. generator
+
+ 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.
+
+ ****************************************************************************
+}
+{#@abstract(Temporary reference allocator unit)
+ Temporary reference allocator unit. This unit contains
+ all which is related to allocating temporary memory
+ space on the stack, as required, by the code generator.
+}
+
+unit tgobj;
+
+{$i fpcdefs.inc}
+
+ interface
+
+ uses
+ cclasses,
+ globals,globtype,
+ symtype,
+ cpubase,cpuinfo,cgbase,cgutils,
+ aasmbase,aasmtai,aasmdata;
+
+ type
+ ptemprecord = ^ttemprecord;
+ ttemprecord = record
+ temptype : ttemptype;
+ pos : longint;
+ size : longint;
+ def : tdef;
+ next : ptemprecord;
+ nextfree : ptemprecord; { for faster freeblock checking }
+{$ifdef EXTDEBUG}
+ posinfo,
+ releaseposinfo : tfileposinfo;
+{$endif}
+ end;
+
+
+ {# Generates temporary variables }
+ ttgobj = class
+ private
+ { contains all free temps using nextfree links }
+ tempfreelist : ptemprecord;
+ function alloctemp(list: TAsmList; size,alignment : longint; temptype : ttemptype; def:tdef) : longint;
+ procedure freetemp(list: TAsmList; pos:longint;temptypes:ttemptypeset);
+ public
+ { contains all temps }
+ templist : ptemprecord;
+ { Offsets of the first/last temp }
+ firsttemp,
+ lasttemp : longint;
+ direction : shortint;
+ constructor create;
+ {# Clear and free the complete linked list of temporary memory
+ locations. The list is set to nil.}
+ procedure resettempgen;
+ {# Sets the first offset from the frame pointer or stack pointer where
+ the temporary references will be allocated. It is to note that this
+ value should always be negative.
+
+ @param(l start offset where temps will start in stack)
+ }
+ procedure setfirsttemp(l : longint);
+
+ procedure gettemp(list: TAsmList; size, alignment : longint;temptype:ttemptype;out ref : treference);
+ procedure gettemptyped(list: TAsmList; def:tdef;temptype:ttemptype;out ref : treference);
+ procedure ungettemp(list: TAsmList; const ref : treference);
+
+ function sizeoftemp(list: TAsmList; const ref: treference): longint;
+ function changetemptype(list: TAsmList; const ref:treference;temptype:ttemptype):boolean;
+ function gettypeoftemp(const ref:treference): ttemptype;
+
+ {# Returns TRUE if the reference ref is allocated in temporary volatile memory space,
+ otherwise returns FALSE.
+
+ @param(ref reference to verify)
+ }
+ function istemp(const ref : treference) : boolean;
+ {# Frees a reference @var(ref) which was allocated in the volatile temporary memory space.
+ The freed space can later be reallocated and reused. If this reference
+ is not in the temporary memory, it is simply not freed.
+ }
+ procedure ungetiftemp(list: TAsmList; const ref : treference);
+
+ { Allocate space for a local }
+ procedure getlocal(list: TAsmList; size : longint;def:tdef;var ref : treference);
+ procedure getlocal(list: TAsmList; size : longint; alignment : shortint; def:tdef;var ref : treference);
+ procedure UnGetLocal(list: TAsmList; const ref : treference);
+ end;
+
+ var
+ tg: ttgobj;
+
+ procedure location_freetemp(list:TAsmList; const l : tlocation);
+
+
+implementation
+
+ uses
+ cutils,
+ systems,verbose,
+ procinfo,
+ symconst
+ ;
+
+
+ const
+ FreeTempTypes = [tt_free,tt_freenoreuse];
+
+{$ifdef EXTDEBUG}
+ TempTypeStr : array[ttemptype] of string[18] = (
+ '<none>',
+ 'free','normal','persistant',
+ 'noreuse','freenoreuse'
+ );
+{$endif EXTDEBUG}
+
+ Used2Free : array[ttemptype] of ttemptype = (
+ tt_none,
+ tt_none,tt_free,tt_free,
+ tt_freenoreuse,tt_none
+ );
+
+
+{*****************************************************************************
+ Helpers
+*****************************************************************************}
+
+ procedure location_freetemp(list:TAsmList; const l : tlocation);
+ begin
+ if (l.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
+ tg.ungetiftemp(list,l.reference);
+ end;
+
+
+{*****************************************************************************
+ TTGOBJ
+*****************************************************************************}
+
+ constructor ttgobj.create;
+
+ begin
+ tempfreelist:=nil;
+ templist:=nil;
+ { we could create a new child class for this but I don't if it is worth the effort (FK) }
+{$if defined(powerpc) or defined(powerpc64) or defined(avr)}
+ direction:=1;
+{$else}
+ direction:=-1;
+{$endif}
+ end;
+
+
+ procedure ttgobj.resettempgen;
+ var
+ hp : ptemprecord;
+ begin
+ { Clear the old templist }
+ while assigned(templist) do
+ begin
+{$ifdef EXTDEBUG}
+ if not(templist^.temptype in FreeTempTypes) then
+ begin
+ Comment(V_Warning,'tgobj: (ResetTempgen) temp at pos '+tostr(templist^.pos)+
+ ' with size '+tostr(templist^.size)+' and type '+TempTypeStr[templist^.temptype]+
+ ' from pos '+tostr(templist^.posinfo.line)+':'+tostr(templist^.posinfo.column)+
+ ' not freed at the end of the procedure');
+ end;
+{$endif EXTDEBUG}
+ hp:=templist;
+ templist:=hp^.next;
+ dispose(hp);
+ end;
+ templist:=nil;
+ tempfreelist:=nil;
+ firsttemp:=0;
+ lasttemp:=0;
+ end;
+
+
+ procedure ttgobj.setfirsttemp(l : longint);
+ begin
+ { this is a negative value normally }
+ if l*direction>=0 then
+ begin
+ if odd(l) then
+ inc(l,direction);
+ end
+ else
+ internalerror(200204221);
+ firsttemp:=l;
+ lasttemp:=l;
+ end;
+
+
+ function ttgobj.AllocTemp(list: TAsmList; size,alignment : longint; temptype : ttemptype;def : tdef) : longint;
+ var
+ tl,htl,
+ bestslot,bestprev,
+ hprev,hp : ptemprecord;
+ freetype : ttemptype;
+ bestatend,
+ fitatbegin,
+ fitatend : boolean;
+ begin
+ AllocTemp:=0;
+ bestprev:=nil;
+ bestslot:=nil;
+ tl:=nil;
+ bestatend:=false;
+
+ if size=0 then
+ begin
+{$ifdef EXTDEBUG}
+ Comment(V_Warning,'tgobj: (AllocTemp) temp of size 0 requested, allocating 4 bytes');
+{$endif}
+ size:=4;
+ end;
+
+ freetype:=Used2Free[temptype];
+ if freetype=tt_none then
+ internalerror(200208201);
+ size:=align(size,alignment);
+ { First check the tmpfreelist, but not when
+ we don't want to reuse an already allocated block }
+ if assigned(tempfreelist) and
+ (temptype<>tt_noreuse) then
+ begin
+ hprev:=nil;
+ hp:=tempfreelist;
+ while assigned(hp) do
+ begin
+{$ifdef EXTDEBUG}
+ if not(hp^.temptype in FreeTempTypes) then
+ Comment(V_Warning,'tgobj: (AllocTemp) temp at pos '+tostr(hp^.pos)+ ' in freelist is not set to tt_free !');
+{$endif}
+ { Check only slots that are
+ - free
+ - share the same type
+ - contain enough space
+ - has a correct alignment }
+ if (hp^.temptype=freetype) and
+ (hp^.def=def) and
+ (hp^.size>=size) and
+ ((hp^.pos=align(hp^.pos,alignment)) or
+ (hp^.pos+hp^.size-size = align(hp^.pos+hp^.size-size,alignment))) then
+ begin
+ { Slot is the same size then leave immediatly }
+ if (hp^.size=size) then
+ begin
+ bestprev:=hprev;
+ bestslot:=hp;
+ break;
+ end
+ else
+ begin
+ { we can fit a smaller block either at the begin or at }
+ { the end of a block. For direction=-1 we prefer the }
+ { end, for direction=1 we prefer the begin (i.e., }
+ { always closest to the source). We also try to use }
+ { the block with the worst possible alignment that }
+ { still suffices. And we pick the block which will }
+ { have the best alignmenment after this new block is }
+ { substracted from it. }
+ fitatend:=(hp^.pos+hp^.size-size)=align(hp^.pos+hp^.size-size,alignment);
+ fitatbegin:=hp^.pos=align(hp^.pos,alignment);
+ if assigned(bestslot) then
+ begin
+ fitatend:=fitatend and
+ ((not bestatend and
+ (direction=-1)) or
+ (bestatend and
+ isbetteralignedthan(abs(bestslot^.pos+hp^.size-size),abs(hp^.pos+hp^.size-size),current_settings.alignment.localalignmax)));
+ fitatbegin:=fitatbegin and
+ (not bestatend or
+ (direction=1)) and
+ isbetteralignedthan(abs(hp^.pos+size),abs(bestslot^.pos+size),current_settings.alignment.localalignmax);
+ end;
+ if fitatend and
+ fitatbegin then
+ if isbetteralignedthan(abs(hp^.pos+hp^.size-size),abs(hp^.pos+size),current_settings.alignment.localalignmax) then
+ fitatbegin:=false
+ else if isbetteralignedthan(abs(hp^.pos+size),abs(hp^.pos+hp^.size-size),current_settings.alignment.localalignmax) then
+ fitatend:=false
+ else if (direction=1) then
+ fitatend:=false
+ else
+ fitatbegin:=false;
+ if fitatend or
+ fitatbegin then
+ begin
+ bestprev:=hprev;
+ bestslot:=hp;
+ bestatend:=fitatend;
+ end;
+ end;
+ end;
+ hprev:=hp;
+ hp:=hp^.nextfree;
+ end;
+ end;
+ { Reuse an old temp ? }
+ if assigned(bestslot) then
+ begin
+ if bestslot^.size=size then
+ begin
+ tl:=bestslot;
+ { Remove from the tempfreelist }
+ if assigned(bestprev) then
+ bestprev^.nextfree:=tl^.nextfree
+ else
+ tempfreelist:=tl^.nextfree;
+ end
+ else
+ begin
+ { Duplicate bestlost and the block in the list }
+ new(tl);
+ move(bestslot^,tl^,sizeof(ttemprecord));
+ tl^.next:=bestslot^.next;
+ bestslot^.next:=tl;
+ { Now we split the block in 2 parts. Depending on the direction
+ we need to resize the newly inserted block or the old reused block.
+ For direction=1 we can use tl for the new block. For direction=-1 we
+ will be reusing bestslot and resize the new block, that means we need
+ to swap the pointers }
+ if (direction=-1) xor
+ bestatend then
+ begin
+ htl:=tl;
+ tl:=bestslot;
+ bestslot:=htl;
+ { Update the tempfreelist to point to the new block }
+ if assigned(bestprev) then
+ bestprev^.nextfree:=bestslot
+ else
+ tempfreelist:=bestslot;
+ end;
+
+ if not bestatend then
+ inc(bestslot^.pos,size)
+ else
+ inc(tl^.pos,tl^.size-size);
+
+ { Create new block and resize the old block }
+ tl^.size:=size;
+ tl^.nextfree:=nil;
+ { Resize the old block }
+ dec(bestslot^.size,size);
+ end;
+ tl^.temptype:=temptype;
+ tl^.def:=def;
+ tl^.nextfree:=nil;
+ end
+ else
+ begin
+ { now we can create the templist entry }
+ new(tl);
+ tl^.temptype:=temptype;
+ tl^.def:=def;
+
+ { Extend the temp }
+ if direction=-1 then
+ begin
+ lasttemp:=(-align(-lasttemp,alignment))-size;
+ tl^.pos:=lasttemp;
+ end
+ else
+ begin
+ tl^.pos:=align(lasttemp,alignment);
+ lasttemp:=tl^.pos+size;
+ end;
+
+ tl^.size:=size;
+ tl^.next:=templist;
+ tl^.nextfree:=nil;
+ templist:=tl;
+ end;
+{$ifdef EXTDEBUG}
+ tl^.posinfo:=current_filepos;
+ if assigned(tl^.def) then
+ list.concat(tai_tempalloc.allocinfo(tl^.pos,tl^.size,'allocated with type '+TempTypeStr[tl^.temptype]+' for def '+tl^.def.typename))
+ else
+ list.concat(tai_tempalloc.allocinfo(tl^.pos,tl^.size,'allocated with type '+TempTypeStr[tl^.temptype]));
+{$else}
+ list.concat(tai_tempalloc.alloc(tl^.pos,tl^.size));
+{$endif}
+ AllocTemp:=tl^.pos;
+ end;
+
+
+ procedure ttgobj.FreeTemp(list: TAsmList; pos:longint;temptypes:ttemptypeset);
+ var
+ hp,hnext,hprev,hprevfree : ptemprecord;
+ begin
+ hp:=templist;
+ hprev:=nil;
+ hprevfree:=nil;
+ while assigned(hp) do
+ begin
+ if (hp^.pos=pos) then
+ begin
+ { check if already freed }
+ if hp^.temptype in FreeTempTypes then
+ begin
+{$ifdef EXTDEBUG}
+ Comment(V_Warning,'tgobj: (FreeTemp) temp at pos '+tostr(pos)+ ' is already free !');
+ list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp is already freed'));
+{$endif}
+ exit;
+ end;
+ { check type that are allowed to be released }
+ if not(hp^.temptype in temptypes) then
+ begin
+{$ifdef EXTDEBUG}
+ Comment(V_Debug,'tgobj: (Freetemp) temp at pos '+tostr(pos)+ ' has different type ('+TempTypeStr[hp^.temptype]+'), not releasing');
+ list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp has wrong type ('+TempTypeStr[hp^.temptype]+') not releasing'));
+{$endif}
+ exit;
+ end;
+ list.concat(tai_tempalloc.dealloc(hp^.pos,hp^.size));
+ { set this block to free }
+ hp^.temptype:=Used2Free[hp^.temptype];
+ { Update tempfreelist }
+ if assigned(hprevfree) then
+ begin
+ { Concat blocks when the previous block is free and
+ there is no block assigned for a tdef }
+ if assigned(hprev) and
+ (hp^.temptype=tt_free) and
+ not assigned(hp^.def) and
+ (hprev^.temptype=tt_free) and
+ not assigned(hprev^.def) then
+ begin
+ inc(hprev^.size,hp^.size);
+ if direction=1 then
+ hprev^.pos:=hp^.pos;
+ hprev^.next:=hp^.next;
+ dispose(hp);
+ hp:=hprev;
+ end
+ else
+ hprevfree^.nextfree:=hp;
+ end
+ else
+ begin
+ hp^.nextfree:=tempfreelist;
+ tempfreelist:=hp;
+ end;
+ { Concat blocks when the next block is free and
+ there is no block assigned for a tdef }
+ hnext:=hp^.next;
+ if assigned(hnext) and
+ (hp^.temptype=tt_free) and
+ not assigned(hp^.def) and
+ (hnext^.temptype=tt_free) and
+ not assigned(hnext^.def) then
+ begin
+ inc(hp^.size,hnext^.size);
+ if direction=1 then
+ hp^.pos:=hnext^.pos;
+ hp^.nextfree:=hnext^.nextfree;
+ hp^.next:=hnext^.next;
+ dispose(hnext);
+ end;
+ { Stop }
+ exit;
+ end;
+ if (hp^.temptype=tt_free) then
+ hprevfree:=hp;
+ hprev:=hp;
+ hp:=hp^.next;
+ end;
+ end;
+
+
+ procedure ttgobj.gettemp(list: TAsmList; size, alignment : longint;temptype:ttemptype;out ref : treference);
+ var
+ varalign : shortint;
+ begin
+ varalign:=used_align(alignment,current_settings.alignment.localalignmin,current_settings.alignment.localalignmax);
+ { can't use reference_reset_base, because that will let tgobj depend
+ on cgobj (PFV) }
+ fillchar(ref,sizeof(ref),0);
+ ref.base:=current_procinfo.framepointer;
+ ref.offset:=alloctemp(list,size,varalign,temptype,nil);
+ ref.alignment:=varalign;
+ end;
+
+
+ procedure ttgobj.gettemptyped(list: TAsmList; def:tdef;temptype:ttemptype;out ref : treference);
+ var
+ varalign : shortint;
+ begin
+ varalign:=def.alignment;
+ varalign:=used_align(varalign,current_settings.alignment.localalignmin,current_settings.alignment.localalignmax);
+ { can't use reference_reset_base, because that will let tgobj depend
+ on cgobj (PFV) }
+ fillchar(ref,sizeof(ref),0);
+ ref.base:=current_procinfo.framepointer;
+ ref.offset:=alloctemp(list,def.size,varalign,temptype,def);
+ ref.alignment:=varalign;
+ end;
+
+
+ function ttgobj.istemp(const ref : treference) : boolean;
+ begin
+ { ref.index = R_NO was missing
+ led to problems with local arrays
+ with lower bound > 0 (PM) }
+ if direction = 1 then
+ begin
+ istemp:=(ref.base=current_procinfo.framepointer) and
+ (ref.index=NR_NO) and
+ (ref.offset>=firsttemp);
+ end
+ else
+ begin
+ istemp:=(ref.base=current_procinfo.framepointer) and
+ (ref.index=NR_NO) and
+ (ref.offset<firsttemp);
+ end;
+ end;
+
+
+ function ttgobj.sizeoftemp(list: TAsmList; const ref: treference): longint;
+ var
+ hp : ptemprecord;
+ begin
+ SizeOfTemp := -1;
+ hp:=templist;
+ while assigned(hp) do
+ begin
+ if (hp^.pos=ref.offset) then
+ begin
+ sizeoftemp := hp^.size;
+ exit;
+ end;
+ hp := hp^.next;
+ end;
+{$ifdef EXTDEBUG}
+ comment(v_debug,'tgobj: (SizeOfTemp) temp at pos '+tostr(ref.offset)+' not found !');
+ list.concat(tai_tempalloc.allocinfo(ref.offset,0,'temp not found'));
+{$endif}
+ end;
+
+
+ function ttgobj.changetemptype(list: tasmList; const ref:treference; temptype:ttemptype):boolean;
+ var
+ hp : ptemprecord;
+ begin
+ ChangeTempType:=false;
+ hp:=templist;
+ while assigned(hp) do
+ begin
+ if (hp^.pos=ref.offset) then
+ begin
+ if hp^.temptype<>tt_free then
+ begin
+{$ifdef EXTDEBUG}
+ if hp^.temptype=temptype then
+ Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
+ ' at pos '+tostr(ref.offset)+ ' is already of the correct type !');
+ list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'type changed to '+TempTypeStr[temptype]));
+{$endif}
+ ChangeTempType:=true;
+ hp^.temptype:=temptype;
+ end
+ else
+ begin
+{$ifdef EXTDEBUG}
+ Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
+ ' at pos '+tostr(ref.offset)+ ' is already freed !');
+ list.concat(tai_tempalloc.allocinfo(hp^.pos,hp^.size,'temp is already freed'));
+{$endif}
+ end;
+ exit;
+ end;
+ hp:=hp^.next;
+ end;
+{$ifdef EXTDEBUG}
+ Comment(V_Warning,'tgobj: (ChangeTempType) temp'+
+ ' at pos '+tostr(ref.offset)+ ' not found !');
+ list.concat(tai_tempalloc.allocinfo(ref.offset,0,'temp not found'));
+{$endif}
+ end;
+
+
+ function ttgobj.gettypeoftemp(const ref:treference): ttemptype;
+ var
+ hp : ptemprecord;
+ begin
+ hp:=templist;
+ while assigned(hp) do
+ begin
+ if (hp^.pos=ref.offset) then
+ begin
+ if hp^.temptype<>tt_free then
+ result:=hp^.temptype
+ else
+ internalerror(2007020810);
+ exit;
+ end;
+ hp:=hp^.next;
+ end;
+ result:=tt_none;
+ end;
+
+
+ procedure ttgobj.UnGetTemp(list: TAsmList; const ref : treference);
+ begin
+ FreeTemp(list,ref.offset,[tt_normal,tt_noreuse,tt_persistent]);
+ end;
+
+
+ procedure ttgobj.UnGetIfTemp(list: TAsmList; const ref : treference);
+ begin
+ if istemp(ref) then
+ FreeTemp(list,ref.offset,[tt_normal]);
+ end;
+
+
+ procedure ttgobj.getlocal(list: TAsmList; size : longint;def:tdef;var ref : treference);
+ begin
+ getlocal(list, size, def.alignment, def, ref);
+ end;
+
+
+ procedure ttgobj.getlocal(list: TAsmList; size : longint; alignment : shortint; def:tdef;var ref : treference);
+ begin
+ alignment:=used_align(alignment,current_settings.alignment.localalignmin,current_settings.alignment.localalignmax);
+ { can't use reference_reset_base, because that will let tgobj depend
+ on cgobj (PFV) }
+ fillchar(ref,sizeof(ref),0);
+ ref.base:=current_procinfo.framepointer;
+ ref.offset:=alloctemp(list,size,alignment,tt_persistent,nil);
+ ref.alignment:=alignment;
+ end;
+
+
+ procedure ttgobj.UnGetLocal(list: TAsmList; const ref : treference);
+ begin
+ FreeTemp(list,ref.offset,[tt_persistent]);
+ end;
+
+end.