{ $Id$ Copyright (c) 1998-2002 by Florian Klaempfl This unit exports some help routines for the code generation 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 cgbase; {$i fpcdefs.inc} interface uses { common } cclasses, { global } globals,verbose, { symtable } symconst,symtype,symdef,symsym, { aasm } cpubase,cpuinfo,cginfo,aasmbase,aasmtai ; const {# bitmask indicating if the procedure uses asm } pi_uses_asm = $1; {# bitmask indicating if the procedure is exported by an unit } pi_is_global = $2; {# bitmask indicating if the procedure does a call } pi_do_call = $4; {# bitmask indicating if the procedure is an operator } pi_operator = $8; {# bitmask indicating if the procedure is an external C function } pi_c_import = $10; {# bitmask indicating if the procedure has a try statement = no register optimization } pi_uses_exceptions = $20; {# bitmask indicating if the procedure is declared as @var(assembler), don't optimize} pi_is_assembler = $40; {# bitmask indicating if the procedure contains data which needs to be finalized } pi_needs_implicit_finally = $80; type pprocinfo = ^tprocinfo; tprocinfo = object {# pointer to parent in nested procedures } parent : pprocinfo; {# current class, if we are in a method } _class : tobjectdef; {# the definition of the routine itself } procdef : tprocdef; {# frame pointer offset??? } framepointer_offset : longint; { self pointer offset???? } selfpointer_offset : longint; {# result value offset in stack (functions only) } return_offset : longint; {# firsttemp position } firsttemp_offset : longint; {# parameter offset in stack } para_offset : longint; {# some collected informations about the procedure see pi_xxxx above } flags : longint; {# register used as frame pointer } framepointer : tregister; {# true, if the procedure is exported by a unit } globalsymbol : boolean; {# true, if the procedure should be exported (only OS/2) } exported : boolean; {# true, if we can not use fast exit code } no_fast_exit : boolean; aktproccode,aktentrycode, aktexitcode,aktlocaldata : taasmoutput; constructor init; destructor done; end; pregvarinfo = ^tregvarinfo; tregvarinfo = record regvars : array[1..maxvarregs] of tvarsym; regvars_para : array[1..maxvarregs] of boolean; regvars_refs : array[1..maxvarregs] of longint; fpuregvars : array[1..maxfpuvarregs] of tvarsym; fpuregvars_para : array[1..maxfpuvarregs] of boolean; fpuregvars_refs : array[1..maxfpuvarregs] of longint; end; var {# information about the current sub routine being parsed (@var(pprocinfo))} procinfo : pprocinfo; { labels for BREAK and CONTINUE } aktbreaklabel,aktcontinuelabel : tasmlabel; { label when the result is true or false } truelabel,falselabel : tasmlabel; { label to leave the sub routine } aktexitlabel : tasmlabel; { also an exit label, only used we need to clear only the stack } aktexit2label : tasmlabel; {# only used in constructor for fail keyword or if getmem fails } faillabel : tasmlabel; quickexitlabel : tasmlabel; {# true, if there was an error while code generation occurs } codegenerror : boolean; { save the size of pushed parameter, needed for aligning } pushedparasize : longint; { message calls with codegenerror support } procedure cgmessage(t : longint); procedure cgmessage1(t : longint;const s : string); procedure cgmessage2(t : longint;const s1,s2 : string); procedure cgmessage3(t : longint;const s1,s2,s3 : string); procedure CGMessagePos(const pos:tfileposinfo;t:longint); procedure CGMessagePos1(const pos:tfileposinfo;t:longint;const s1:string); procedure CGMessagePos2(const pos:tfileposinfo;t:longint;const s1,s2:string); procedure CGMessagePos3(const pos:tfileposinfo;t:longint;const s1,s2,s3:string); { initialize respectively terminates the code generator } { for a new module or procedure } procedure codegen_doneprocedure; procedure codegen_donemodule; procedure codegen_newmodule; procedure codegen_newprocedure; {# From a definition return the abstract code generator size (@var(tcgsize) enum). It is to note that the value returned can be @var(OS_NO) } function def_cgsize(def: tdef): tcgsize; function int_cgsize(const l: aword): tcgsize; {# return the inverse condition of opcmp } function inverse_opcmp(opcmp: topcmp): topcmp; {# return whether op is commutative } function commutativeop(op: topcg): boolean; implementation uses systems, cresstr, defbase {$ifdef fixLeaksOnError} ,comphook {$endif fixLeaksOnError} ; {$ifdef fixLeaksOnError} var procinfoStack: TStack; hcodegen_old_do_stop: tstopprocedure; {$endif fixLeaksOnError} {***************************************************************************** override the message calls to set codegenerror *****************************************************************************} procedure cgmessage(t : longint); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=Errorcount; verbose.Message(t); codegenerror:=olderrorcount<>Errorcount; end; end; procedure cgmessage1(t : longint;const s : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=Errorcount; verbose.Message1(t,s); codegenerror:=olderrorcount<>Errorcount; end; end; procedure cgmessage2(t : longint;const s1,s2 : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=Errorcount; verbose.Message2(t,s1,s2); codegenerror:=olderrorcount<>Errorcount; end; end; procedure cgmessage3(t : longint;const s1,s2,s3 : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=Errorcount; verbose.Message3(t,s1,s2,s3); codegenerror:=olderrorcount<>Errorcount; end; end; procedure cgmessagepos(const pos:tfileposinfo;t : longint); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=Errorcount; verbose.MessagePos(pos,t); codegenerror:=olderrorcount<>Errorcount; end; end; procedure cgmessagepos1(const pos:tfileposinfo;t : longint;const s1 : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=Errorcount; verbose.MessagePos1(pos,t,s1); codegenerror:=olderrorcount<>Errorcount; end; end; procedure cgmessagepos2(const pos:tfileposinfo;t : longint;const s1,s2 : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=Errorcount; verbose.MessagePos2(pos,t,s1,s2); codegenerror:=olderrorcount<>Errorcount; end; end; procedure cgmessagepos3(const pos:tfileposinfo;t : longint;const s1,s2,s3 : string); var olderrorcount : longint; begin if not(codegenerror) then begin olderrorcount:=Errorcount; verbose.MessagePos3(pos,t,s1,s2,s3); codegenerror:=olderrorcount<>Errorcount; end; end; {**************************************************************************** TProcInfo ****************************************************************************} constructor tprocinfo.init; begin parent:=nil; _class:=nil; procdef:=nil; framepointer_offset:=0; selfpointer_offset:=0; return_offset:=0; firsttemp_offset:=0; para_offset:=0; flags:=0; framepointer:=R_NO; globalsymbol:=false; exported:=false; no_fast_exit:=false; aktentrycode:=Taasmoutput.Create; aktexitcode:=Taasmoutput.Create; aktproccode:=Taasmoutput.Create; aktlocaldata:=Taasmoutput.Create; end; destructor tprocinfo.done; begin aktentrycode.free; aktexitcode.free; aktproccode.free; aktlocaldata.free; end; {***************************************************************************** initialize/terminate the codegen for procedure and modules *****************************************************************************} procedure codegen_newprocedure; begin aktbreaklabel:=nil; aktcontinuelabel:=nil; { aktexitlabel:=0; is store in oldaktexitlabel so it must not be reset to zero before this storage !} { new procinfo } new(procinfo,init); {$ifdef fixLeaksOnError} procinfoStack.push(procinfo); {$endif fixLeaksOnError} end; procedure codegen_doneprocedure; begin {$ifdef fixLeaksOnError} if procinfo <> procinfoStack.pop then writeln('problem with procinfoStack!'); {$endif fixLeaksOnError} dispose(procinfo,done); procinfo:=nil; end; procedure codegen_newmodule; begin exprasmlist:=taasmoutput.create; datasegment:=taasmoutput.create; codesegment:=taasmoutput.create; bsssegment:=taasmoutput.create; debuglist:=taasmoutput.create; withdebuglist:=taasmoutput.create; consts:=taasmoutput.create; rttilist:=taasmoutput.create; ResourceStringList:=Nil; importssection:=nil; exportssection:=nil; resourcesection:=nil; { assembler symbols } asmsymbollist:=tdictionary.create; asmsymbollist.usehash; { resourcestrings } ResourceStrings:=TResourceStrings.Create; end; procedure codegen_donemodule; {$ifdef MEMDEBUG} var d : tmemdebug; {$endif} begin {$ifdef MEMDEBUG} d:=tmemdebug.create('asmlist'); {$endif} exprasmlist.free; codesegment.free; bsssegment.free; datasegment.free; debuglist.free; withdebuglist.free; consts.free; rttilist.free; if assigned(ResourceStringList) then ResourceStringList.free; if assigned(importssection) then importssection.free; if assigned(exportssection) then exportssection.free; if assigned(resourcesection) then resourcesection.free; {$ifdef MEMDEBUG} d.free; {$endif} { assembler symbols } {$ifdef MEMDEBUG} d:=tmemdebug.create('asmsymbol'); {$endif} asmsymbollist.free; {$ifdef MEMDEBUG} d.free; {$endif} { resource strings } ResourceStrings.free; end; function def_cgsize(def: tdef): tcgsize; begin case def.deftype of orddef, enumdef, setdef: begin result := int_cgsize(def.size); if is_signed(def) then result := tcgsize(ord(result)+(ord(OS_S8)-ord(OS_8))); end; classrefdef, pointerdef, procvardef: result := OS_ADDR; stringdef : begin if is_ansistring(def) or is_widestring(def) then result := OS_ADDR else result := OS_NO; end; objectdef : begin if is_class_or_interface(def) then result := OS_ADDR else result := OS_NO; end; floatdef: result := tfloat2tcgsize[tfloatdef(def).typ]; recorddef : result:=int_cgsize(def.size); arraydef : begin if not is_special_array(def) then result := int_cgsize(def.size) else result := OS_NO; end; else begin { undefined size } result:=OS_NO; end; end; end; function int_cgsize(const l: aword): tcgsize; begin case l of 1 : result := OS_8; 2 : result := OS_16; 3,4 : result := OS_32; 5..8 : result := OS_64; else result:=OS_NO; end; end; function inverse_opcmp(opcmp: topcmp): topcmp; const list: array[TOpCmp] of TOpCmp = (OC_NONE,OC_NE,OC_LTE,OC_GTE,OC_LT,OC_GT,OC_EQ,OC_A,OC_AE, OC_B,OC_BE); begin inverse_opcmp := list[opcmp]; end; function commutativeop(op: topcg): boolean; const list: array[topcg] of boolean = (true,true,true,false,false,true,true,false,false, true,false,false,false,false,true); begin commutativeop := list[op]; end; {$ifdef fixLeaksOnError} procedure hcodegen_do_stop; var p: pprocinfo; begin p := pprocinfo(procinfoStack.pop); while p <> nil Do begin dispose(p,done); p := pprocinfo(procinfoStack.pop); end; procinfoStack.done; do_stop := hcodegen_old_do_stop; do_stop{$ifdef FPCPROCVAR}(){$endif}; end; begin hcodegen_old_do_stop := do_stop; do_stop := {$ifdef FPCPROCVAR}@{$endif}hcodegen_do_stop; procinfoStack.init; {$endif fixLeaksOnError} end. { $Log$ Revision 1.19 2002-07-20 11:57:53 florian * types.pas renamed to defbase.pas because D6 contains a types unit so this would conflicts if D6 programms are compiled + Willamette/SSE2 instructions to assembler added Revision 1.18 2002/07/01 18:46:22 peter * internal linker * reorganized aasm layer Revision 1.17 2002/05/20 13:30:40 carl * bugfix of hdisponen (base must be set, not index) * more portability fixes Revision 1.16 2002/05/18 13:34:05 peter * readded missing revisions Revision 1.15 2002/05/16 19:46:35 carl + defines.inc -> fpcdefs.inc to avoid conflicts if compiling by hand + try to fix temp allocation (still in ifdef) + generic constructor calls + start of tassembler / tmodulebase class cleanup Revision 1.13 2002/04/25 20:16:38 peter * moved more routines from cga/n386util Revision 1.12 2002/04/21 15:28:06 carl - remove duplicate constants - move some constants to cginfo Revision 1.11 2002/04/20 21:32:23 carl + generic FPC_CHECKPOINTER + first parameter offset in stack now portable * rename some constants + move some cpu stuff to other units - remove unused constents * fix stacksize for some targets * fix generic size problems which depend now on EXTEND_SIZE constant Revision 1.10 2002/04/07 09:13:39 carl + documentation - remove unused variables Revision 1.9 2002/04/04 19:05:54 peter * removed unused units * use tlocation.size in cg.a_*loc*() routines Revision 1.8 2002/04/02 17:11:27 peter * tlocation,treference update * LOC_CONSTANT added for better constant handling * secondadd splitted in multiple routines * location_force_reg added for loading a location to a register of a specified size * secondassignment parses now first the right and then the left node (this is compatible with Kylix). This saves a lot of push/pop especially with string operations * adapted some routines to use the new cg methods Revision 1.7 2002/03/31 20:26:33 jonas + a_loadfpu_* and a_loadmm_* methods in tcg * register allocation is now handled by a class and is mostly processor independent (+rgobj.pas and i386/rgcpu.pas) * temp allocation is now handled by a class (+tgobj.pas, -i386\tgcpu.pas) * some small improvements and fixes to the optimizer * some register allocation fixes * some fpuvaroffset fixes in the unary minus node * push/popusedregisters is now called rg.save/restoreusedregisters and (for i386) uses temps instead of push/pop's when using -Op3 (that code is also better optimizable) * fixed and optimized register saving/restoring for new/dispose nodes * LOC_FPU locations now also require their "register" field to be set to R_ST, not R_ST0 (the latter is used for LOC_CFPUREGISTER locations only) - list field removed of the tnode class because it's not used currently and can cause hard-to-find bugs Revision 1.6 2002/03/04 19:10:11 peter * removed compiler warnings }