{ $Id$ Copyright (c) 1998-2000 by Florian Klaempfl This unit handles the codegeneration pass 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. **************************************************************************** } {$ifdef TP} {$E+,F+,N+} {$endif} unit pass_2; interface uses tree; { produces assembler for the expression in variable p } { and produces an assembler node at the end } procedure generatecode(var _p : ptree); { produces the actual code } function do_secondpass(p : pnode) : boolean; procedure secondpass(p : pnode); implementation uses globtype,systems, cobjects,verbose,comphook,globals,files, symconst,symtable,types,aasm,scanner, pass_1,tgobj,cgbase,cgobj,tgcpu,cpuasm,cpubase,convtree {$ifdef GDB} ,gdb {$endif} ; type perrornode = ^terrornode; terrornode = object(tnode) constructor init; procedure secondpass;virtual; end; tstatementnode = object(tbinarynode) procedure secondpass;virtual; end; tblocknode = object(tunarynode) procedure secondpass;virtual; end; tasmnode = object(tnode) p_asm : paasmoutput; object_preserved : boolean; procedure secondpass;virtual; end; {**************************************************************************** TERRORNODE ****************************************************************************} constructor terrornode.init; begin inherited init; treetype:=errorn; end; procedure terrornode.secondpass; begin error:=true; codegenerror:=true; end; {**************************************************************************** TSTATEMENTNODE ****************************************************************************} procedure tstatementnode.secondpass; var hp : pbinarynode; oldrl : plinkedlist; begin hp:=@self; while assigned(hp) do begin if assigned(hp^.right) then begin tg.cleartempgen; oldrl:=temptoremove; temptoremove:=new(plinkedlist,init); hp^.right^.secondpass; { release temp. ansi strings } cg^.g_removetemps(exprasmlist,temptoremove); dispose(temptoremove,done); temptoremove:=oldrl; end; hp:=pbinarynode(hp^.left); end; end; procedure tblocknode.secondpass; begin { do second pass on left node } if assigned(left) then left^.secondpass; end; procedure tasmnode.secondpass; begin exprasmlist^.concatlist(p_asm); if not object_preserved then cg^.g_maybe_loadself(exprasmlist); end; function generateexprlist(p : pnode) : plinkedlist; var l : plinkedlist; begin l:=new(plinkedlist,init); p^.concattolist(l); generateexprlist:=l; end; procedure secondpass(p : pnode); var oldcodegenerror : boolean; oldlocalswitches : tlocalswitches; oldpos : tfileposinfo; l : plinkedlist; hp : pnode; begin if not(p^.error) then begin oldcodegenerror:=codegenerror; oldlocalswitches:=aktlocalswitches; oldpos:=aktfilepos; aktfilepos:=p^.fileinfo; aktlocalswitches:=p^.localswitches; codegenerror:=false; { do we have a list of statements? } if p^.treetype=statementn then begin l:=generateexprlist(p); { here we should do CSE and node reordering } hp:=pnode(l^.first); while assigned(hp) do begin if assigned(hp^.parent) then begin if nf_needs_truefalselabel in hp^.parent^.flags then begin if not(assigned(punarynode(hp^.parent)^.truelabel)) then getlabel(punarynode(hp^.parent)^.truelabel); if not(assigned(punarynode(hp^.parent)^.falselabel)) then getlabel(punarynode(hp^.parent)^.falselabel); truelabel:=punarynode(hp^.parent)^.truelabel; falselabel:=punarynode(hp^.parent)^.falselabel; end; end; hp^.secondpass; hp:=pnode(hp^.next); end; end else p^.secondpass; p^.error:=codegenerror; codegenerror:=codegenerror or oldcodegenerror; aktlocalswitches:=oldlocalswitches; aktfilepos:=oldpos; end else codegenerror:=true; end; function do_secondpass(p : pnode) : boolean; begin codegenerror:=false; if not(p^.error) then secondpass(p); do_secondpass:=codegenerror; end; var regvars : array[1..maxvarregs] of pvarsym; regvars_para : array[1..maxvarregs] of boolean; regvars_refs : array[1..maxvarregs] of longint; parasym : boolean; procedure searchregvars(p : pnamedindexobject); var i,j,k : longint; begin if (pvarsym(p)^.typ=varsym) and (vo_regable in pvarsym(p)^.varoptions) then begin { walk through all momentary register variables } for i:=1 to maxvarregs do begin { free register ? } if regvars[i]=nil then begin regvars[i]:=pvarsym(p); regvars_para[i]:=parasym; break; end; { else throw out a variable ? } j:=pvarsym(p)^.refs; { parameter get a less value } if parasym then begin if cs_littlesize in aktglobalswitches then dec(j,1) else dec(j,100); end; if (j>regvars_refs[i]) and (j>0) then begin for k:=maxvarregs-1 downto i do begin regvars[k+1]:=regvars[k]; regvars_para[k+1]:=regvars_para[k]; end; { calc the new refs pvarsym(p)^.refs:=j; } regvars[i]:=pvarsym(p); regvars_para[i]:=parasym; regvars_refs[i]:=j; break; end; end; end; end; procedure generatecode(var _p : ptree); var i : longint; hr : preference; {$ifdef i386} regsize : topsize; {$endif i386} p : pnode; label nextreg; begin temptoremove:=nil; tg.cleartempgen; { when size optimization only count occurrence } if cs_littlesize in aktglobalswitches then t_times:=1 else { reference for repetition is 100 } t_times:=100; { clear register count } tg.clearregistercount; use_esp_stackframe:=false; if not(do_firstpass(_p)) then begin p:=convtree2node(_p); { max. optimizations } { only if no asm is used } { and no try statement } if (cs_regalloc in aktglobalswitches) and ((procinfo^.flags and (pi_uses_asm or pi_uses_exceptions))=0) then begin { can we omit the stack frame ? } { conditions: 1. procedure (not main block) 2. no constructor or destructor 3. no call to other procedures 4. no interrupt handler } if assigned(aktprocsym) then begin if not(aktprocsym^.definition^.proctypeoption in [potype_constructor,potype_destructor]) and not(po_interrupt in aktprocsym^.definition^.procoptions) and ((procinfo^.flags and pi_do_call)=0) and (lexlevel>=normal_function_level) then begin { use ESP as frame pointer } procinfo^.framepointer:=stack_pointer; use_esp_stackframe:=true; { calc parameter distance new } dec(procinfo^.framepointer_offset,pointersize); dec(procinfo^.selfpointer_offset,pointersize); { is this correct ???} { retoffset can be negativ for results in eax !! } { the value should be decreased only if positive } if procinfo^.return_offset>=0 then dec(procinfo^.return_offset,4); dec(procinfo^.para_offset,4); aktprocsym^.definition^.parast^.address_fixup:=procinfo^.para_offset; end; end; if (p^.registersint=regvars[i]^.refs then begin regvars[i]:=nil; goto nextreg; end; { register is no longer available for } { expressions } { search the register which is the most } { unused } exclude(tg.availabletempregsint,varregs[i]); tg.is_reg_var[varregs[i]]:=true; dec(tg.c_countusableregsint); { possibly no 32 bit register are needed } { call by reference/const ? } {!!!!!!!!!!!!!! if (regvars[i]^.varspez=vs_var) or ((regvars[i]^.varspez=vs_const) and dont_copy_const_param(regvars[i]^.definition)) then begin regvars[i]^.reg:=varregs[i]; regsize:=sizepostfix_pointer; end else if (regvars[i]^.definition^.deftype=orddef) and (porddef(regvars[i]^.definition)^.size=1) then begin regvars[i]^.reg:=regtoreg8(varregs[i]); regsize:=S_B; end else if (regvars[i]^.definition^.deftype=orddef) and (porddef(regvars[i]^.definition)^.size=2) then begin regvars[i]^.reg:=regtoreg16(varregs[i]); regsize:=S_W; end else if (regvars[i]^.definition^.deftype=orddef) and (porddef(regvars[i]^.definition)^.size=4) then begin regvars[i]^.reg:=regtoreg32(varregs[i]); regsize:=S_L; end else if (cf_registers64 in cpuflags) and (regvars[i]^.definition^.deftype=orddef) and (porddef(regvars[i]^.definition)^.size=8) then begin regvars[i]^.reg:=regtoreg64(varregs[i]); regsize:=S_Q; end; } { parameter must be load } if regvars_para[i] then begin { procinfo is there actual, } { because we can't never be in a } { nested procedure } { when loading parameter to reg } new(hr); reset_reference(hr^); hr^.offset:=pvarsym(regvars[i])^.address+procinfo^.para_offset; hr^.base:=procinfo^.framepointer; {$ifdef i386} procinfo^.aktentrycode^.concat(new(paicpu,op_ref_reg(A_MOV,regsize, hr,regvars[i]^.reg))); {$endif i386} {$ifdef m68k} procinfo.aktentrycode^.concat(new(pai68k,op_ref_reg(A_MOVE,regsize, hr,regvars[i]^.reg))); {$endif m68k} tg.unusedregsint:=tg.unusedregsint - [regvars[i]^.reg]; end; { procedure uses this register } include(tg.usedinproc,varregs[i]); end; nextreg: {$ifdef i386} { dummy } regsize:=S_W; {$endif i386} end; if (status.verbosity and v_debug)=v_debug then begin for i:=1 to maxvarregs do begin if assigned(regvars[i]) then Message3(cg_d_register_weight,reg2str(regvars[i]^.reg), tostr(regvars[i]^.refs),regvars[i]^.name); end; end; end; end; if assigned(aktprocsym) and (pocall_inline in aktprocsym^.definition^.proccalloptions) then make_const_global:=true; do_secondpass(p); if assigned(procinfo^.def) then procinfo^.def^.fpu_used:=p^.registersfpu; { all registers can be used again } tg.resetusableregisters; end; procinfo^.aktproccode^.concatlist(exprasmlist); make_const_global:=false; end; end. { $Log$ Revision 1.1 2000-07-13 06:30:08 michael + Initial import Revision 1.11 2000/02/20 20:49:46 florian * newcg is compiling * fixed the dup id problem reported by Paul Y. Revision 1.10 2000/01/07 01:14:54 peter * updated copyright to 2000 Revision 1.9 1999/12/06 18:17:10 peter * newcg compiler compiles again Revision 1.8 1999/10/12 21:20:47 florian * new codegenerator compiles again Revision 1.7 1999/08/25 12:00:13 jonas * changed pai386, paippc and paiapha (same for tai*) to paicpu (taicpu) Revision 1.6 1999/08/05 14:58:15 florian * some fixes for the floating point registers * more things for the new code generator Revision 1.5 1999/08/04 00:23:58 florian * renamed i386asm and i386base to cpuasm and cpubase Revision 1.4 1999/08/03 17:09:46 florian * the alpha compiler can be compiled now Revision 1.3 1999/08/03 00:30:36 florian * again a fix for the alpha Revision 1.2 1999/08/03 00:28:03 florian * some updates to compile for the alpha Revision 1.1 1999/08/03 00:07:16 florian * initial revision }