{ $Id$ Copyright (c) 1998-2000 by Florian Klaempfl Helper routines for the i386 code 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. **************************************************************************** } unit n386util; {$i defines.inc} interface uses symtype,node,cpubase,cginfo; function maybe_push(needed : byte;p : tnode;isint64 : boolean) : boolean; function maybe_pushfpu(needed : byte;p : tnode) : boolean; {$ifdef TEMPS_NOT_PUSH} function maybe_savetotemp(needed : byte;p : tnode;isint64 : boolean) : boolean; {$endif TEMPS_NOT_PUSH} procedure restore(p : tnode;isint64 : boolean); {$ifdef TEMPS_NOT_PUSH} procedure restorefromtemp(p : tnode;isint64 : boolean); {$endif TEMPS_NOT_PUSH} procedure push_value_para(p:tnode;inlined,is_cdecl:boolean; para_offset:longint;alignment : longint); procedure emitoverflowcheck(p:tnode); procedure firstcomplex(p : tbinarynode); implementation uses globtype,globals,systems,verbose, cutils, aasm,cpuasm, symconst,symdef,symsym,symtable, {$ifdef GDB} gdb, {$endif GDB} types, ncgutil,ncon,nld, pass_1,pass_2, cgbase,tgobj, cga,regvars,cgobj,cg64f32,rgobj,rgcpu,cgcpu; {***************************************************************************** Emit Push Functions *****************************************************************************} function maybe_push(needed : byte;p : tnode;isint64 : boolean) : boolean; var pushed : boolean; {hregister : tregister; } {$ifdef TEMPS_NOT_PUSH} href : treference; {$endif TEMPS_NOT_PUSH} begin if p.location.loc = LOC_CREGISTER then begin maybe_push := true; exit; end; if needed>rg.countunusedregsint then begin if (p.location.loc=LOC_REGISTER) then begin if isint64 then begin {$ifdef TEMPS_NOT_PUSH} tg.gettempofsizereference(exprasmlist,href,8); p.temp_offset:=href.offset; href.offset:=href.offset+4; exprasmList.concat(Taicpu.Op_reg(A_MOV,S_L,p.location.registerhigh,href)); href.offset:=href.offset-4; {$else TEMPS_NOT_PUSH} exprasmList.concat(Taicpu.Op_reg(A_PUSH,S_L,p.location.registerhigh)); {$endif TEMPS_NOT_PUSH} rg.ungetregisterint(exprasmlist,p.location.registerhigh); end {$ifdef TEMPS_NOT_PUSH} else begin tg.gettempofsizereference(exprasmlist,href,4); p.temp_offset:=href.offset; end {$endif TEMPS_NOT_PUSH} ; pushed:=true; {$ifdef TEMPS_NOT_PUSH} exprasmList.concat(Taicpu.Op_reg_ref(A_MOV,S_L,p.location.register,href)); {$else TEMPS_NOT_PUSH} exprasmList.concat(Taicpu.Op_reg(A_PUSH,S_L,p.location.register)); {$endif TEMPS_NOT_PUSH} rg.ungetregisterint(exprasmlist,p.location.register); end else if (p.location.loc in [LOC_CREFERENCE,LOC_REFERENCE]) and ((p.location.reference.base<>R_NO) or (p.location.reference.index<>R_NO) ) then begin reference_release(exprasmlist,p.location.reference); rg.getexplicitregisterint(exprasmlist,R_EDI); emit_ref_reg(A_LEA,S_L,p.location.reference,R_EDI); {$ifdef TEMPS_NOT_PUSH} tg.gettempofsizereference(exprasmlist,href,4); exprasmList.concat(Taicpu.Op_reg_ref(A_MOV,S_L,R_EDI,href)); p.temp_offset:=href.offset; {$else TEMPS_NOT_PUSH} exprasmList.concat(Taicpu.Op_reg(A_PUSH,S_L,R_EDI)); {$endif TEMPS_NOT_PUSH} rg.ungetregisterint(exprasmlist,R_EDI); pushed:=true; end else pushed:=false; end else pushed:=false; maybe_push:=pushed; end; function maybe_pushfpu(needed : byte;p : tnode) : boolean; begin if needed>=maxfpuregs then begin if p.location.loc = LOC_FPUREGISTER then begin location_force_mem(p.location); maybe_pushfpu:=true; end else maybe_pushfpu:=false; end else maybe_pushfpu:=false; end; {$ifdef TEMPS_NOT_PUSH} function maybe_savetotemp(needed : byte;p : tnode;isint64 : boolean) : boolean; var pushed : boolean; href : treference; begin if needed>rg.unusedregsint then begin if (p^.location.loc=LOC_REGISTER) then begin if isint64(p^.resulttype.def) then begin tg.gettempofsizereference(exprasmlist,href,8); p^.temp_offset:=href.offset; href.offset:=href.offset+4; exprasmList.concat(Taicpu.Op_reg(A_MOV,S_L,p^.location.registerhigh,href)); href.offset:=href.offset-4; rg.ungetregisterint(exprasmlist,p^.location.registerhigh); end else begin tg.gettempofsizereference(exprasmlist,href,4); p^.temp_offset:=href.offset; end; pushed:=true; exprasmList.concat(Taicpu.Op_reg_ref(A_MOV,S_L,p^.location.register,href)); rg.ungetregisterint(exprasmlist,p^.location.register); end else if (p^.location.loc in [LOC_MEM,LOC_REFERENCE]) and ((p^.location.reference.base<>R_NO) or (p^.location.reference.index<>R_NO) ) then begin reference_release(p^.location.reference); rg.getexplicitregisterint(exprasmlist,R_EDI); emit_ref_reg(A_LEA,S_L,reference_copy(p^.location.reference), R_EDI); tg.gettempofsizereference(exprasmlist,href,4); exprasmList.concat(Taicpu.Op_reg_ref(A_MOV,S_L,R_EDI,href)); rg.ungetregisterint(exprasmlist,R_EDI); p^.temp_offset:=href.offset; pushed:=true; end else pushed:=false; end else pushed:=false; maybe_push:=pushed; end; {$endif TEMPS_NOT_PUSH} procedure restore(p : tnode;isint64 : boolean); var hregister : tregister; {$ifdef TEMPS_NOT_PUSH} href : treference; {$endif TEMPS_NOT_PUSH} begin if p.location.loc = LOC_CREGISTER then begin load_regvar_reg(exprasmlist,p.location.register); exit; end; hregister:=rg.getregisterint(exprasmlist); {$ifdef TEMPS_NOT_PUSH} reset_reference(href); href.base:=procinfo^.frame_pointer_reg; href.offset:=p.temp_offset; emit_ref_reg(A_MOV,S_L,href,hregister); {$else TEMPS_NOT_PUSH} exprasmList.concat(Taicpu.Op_reg(A_POP,S_L,hregister)); {$endif TEMPS_NOT_PUSH} if (p.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then begin p.location.register:=hregister; if isint64 then begin p.location.registerhigh:=rg.getregisterint(exprasmlist); {$ifdef TEMPS_NOT_PUSH} href.offset:=p.temp_offset+4; emit_ref_reg(A_MOV,S_L,p.location.registerhigh); { set correctly for release ! } href.offset:=p.temp_offset; {$else TEMPS_NOT_PUSH} exprasmList.concat(Taicpu.Op_reg(A_POP,S_L,p.location.registerhigh)); {$endif TEMPS_NOT_PUSH} end; end else begin reference_reset(p.location.reference); { any reasons why this was moved into the index register ? } { normally usage of base register is much better (FK) } p.location.reference.base:=hregister; { Why is this done? We can never be sure about p.left because otherwise secondload fails !!! set_location(p.left^.location,p.location);} end; {$ifdef TEMPS_NOT_PUSH} tg.ungetiftemp(exprasmlist,href); {$endif TEMPS_NOT_PUSH} end; {$ifdef TEMPS_NOT_PUSH} procedure restorefromtemp(p : tnode;isint64 : boolean); var hregister : tregister; href : treference; begin hregister:=rg.getregisterint(exprasmlist); reset_reference(href); href.base:=procinfo^.frame_pointer_reg; href.offset:=p.temp_offset; emit_ref_reg(A_MOV,S_L,href,hregister); if (p.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then begin p.location.register:=hregister; if isint64 then begin p.location.registerhigh:=rg.getregisterint(exprasmlist); href.offset:=p.temp_offset+4; emit_ref_reg(A_MOV,S_L,p.location.registerhigh); { set correctly for release ! } href.offset:=p.temp_offset; end; end else begin reset_reference(p.location.reference); p.location.reference.base:=hregister; { Why is this done? We can never be sure about p^.left because otherwise secondload fails PM set_location(p^.left^.location,p^.location);} end; tg.ungetiftemp(exprasmlist,href); end; {$endif TEMPS_NOT_PUSH} procedure push_value_para(p:tnode;inlined,is_cdecl:boolean; para_offset:longint;alignment : longint); var tempreference : treference; href : treference; hreg : tregister; sizetopush, size : longint; cgsize : tcgsize; begin { Move flags and jump in register to make it less complex } if p.location.loc in [LOC_FLAGS,LOC_JUMP] then location_force_reg(p.location,def_cgsize(p.resulttype.def),false); { Handle Floating point types differently } if p.resulttype.def.deftype=floatdef then begin case p.location.loc of LOC_FPUREGISTER, LOC_CFPUREGISTER: begin size:=align(tfloatdef(p.resulttype.def).size,alignment); inc(pushedparasize,size); if not inlined then emit_const_reg(A_SUB,S_L,size,R_ESP); {$ifdef GDB} if (cs_debuginfo in aktmoduleswitches) and (exprasmList.first=exprasmList.last) then exprasmList.concat(Tai_force_line.Create); {$endif GDB} { this is the easiest case for inlined !! } if inlined then reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize) else reference_reset_base(href,R_ESP,0); cg.a_loadfpu_reg_ref(exprasmlist, def_cgsize(p.resulttype.def),p.location.register,href); end; LOC_REFERENCE, LOC_CREFERENCE : begin sizetopush:=align(p.resulttype.def.size,alignment); tempreference:=p.location.reference; inc(tempreference.offset,sizetopush); while (sizetopush>0) do begin if sizetopush>=4 then begin cgsize:=OS_32; inc(pushedparasize,4); dec(tempreference.offset,4); dec(sizetopush,4); end else begin cgsize:=OS_16; inc(pushedparasize,2); dec(tempreference.offset,2); dec(sizetopush,2); end; if inlined then begin reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize); cg.a_load_ref_ref(exprasmlist,cgsize,tempreference,href); end else cg.a_param_ref(exprasmlist,cgsize,tempreference,-1); end; end; else internalerror(200204243); end; end else begin { call by value open array ? } if is_cdecl and push_addr_param(p.resulttype.def) then begin if not (p.location.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then internalerror(200204241); { push on stack } size:=align(p.resulttype.def.size,alignment); inc(pushedparasize,size); emit_const_reg(A_SUB,S_L,size,R_ESP); reference_reset_base(href,R_ESP,0); cg.g_concatcopy(exprasmlist,p.location.reference,href,size,false,false); end else begin case p.location.loc of LOC_CONSTANT, LOC_REGISTER, LOC_CREGISTER, LOC_REFERENCE, LOC_CREFERENCE : begin cgsize:=def_cgsize(p.resulttype.def); if cgsize in [OS_64,OS_S64] then begin inc(pushedparasize,8); if inlined then begin reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize); tcg64f32(cg).a_load64_loc_ref(exprasmlist,p.location,href); end else tcg64f32(cg).a_param64_loc(exprasmlist,p.location,-1); end else begin case cgsize of OS_8,OS_S8 : begin if alignment=4 then cgsize:=OS_32 else cgsize:=OS_16; end; OS_16,OS_S16 : begin if alignment=4 then cgsize:=OS_32; end; end; { update register to use to match alignment } if p.location.loc in [LOC_REGISTER,LOC_CREGISTER] then begin hreg:=p.location.register; p.location.register:=rg.makeregsize(p.location.register,cgsize); end; inc(pushedparasize,alignment); if inlined then begin reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize); cg.a_load_loc_ref(exprasmlist,p.location,href); end else cg.a_param_loc(exprasmlist,p.location,-1); { restore old register } if p.location.loc in [LOC_REGISTER,LOC_CREGISTER] then p.location.register:=hreg; end; location_release(exprasmlist,p.location); end; {$ifdef SUPPORT_MMX} LOC_MMXREGISTER, LOC_CMMXREGISTER: begin inc(pushedparasize,8); if inlined then begin reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize); cg.a_loadmm_reg_ref(exprasmlist,p.location.register,href); end else cg.a_parammm_reg(exprasmlist,p.location.register); end; {$endif SUPPORT_MMX} else internalerror(200204241); end; end; end; end; {***************************************************************************** Emit Functions *****************************************************************************} { produces if necessary overflowcode } procedure emitoverflowcheck(p:tnode); var hl : tasmlabel; begin if not(cs_check_overflow in aktlocalswitches) then exit; getlabel(hl); if not ((p.resulttype.def.deftype=pointerdef) or ((p.resulttype.def.deftype=orddef) and (torddef(p.resulttype.def).typ in [u64bit,u16bit,u32bit,u8bit,uchar, bool8bit,bool16bit,bool32bit]))) then emitjmp(C_NO,hl) else emitjmp(C_NB,hl); emitcall('FPC_OVERFLOW'); emitlab(hl); end; { DO NOT RELY on the fact that the tnode is not yet swaped because of inlining code PM } procedure firstcomplex(p : tbinarynode); var hp : tnode; begin { always calculate boolean AND and OR from left to right } if (p.nodetype in [orn,andn]) and (p.left.resulttype.def.deftype=orddef) and (torddef(p.left.resulttype.def).typ in [bool8bit,bool16bit,bool32bit]) then begin { p.swaped:=false} if nf_swaped in p.flags then internalerror(234234); end else if (((p.location.loc=LOC_FPUREGISTER) and (p.right.registersfpu > p.left.registersfpu)) or ((((p.left.registersfpu = 0) and (p.right.registersfpu = 0)) or (p.location.loc<>LOC_FPUREGISTER)) and (p.left.registers32