{ $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; function maybe_push(needed : byte;p : tnode;isint64 : boolean) : 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 pushsetelement(p : tnode); procedure push_value_para(p:tnode;inlined,is_cdecl:boolean; para_offset:longint;alignment : longint); procedure loadshortstring(source,dest : tnode); procedure loadlongstring(p:tbinarynode); procedure loadansi2short(source,dest : tnode); procedure loadinterfacecom(p: tbinarynode); procedure maketojumpbool(p : tnode); procedure emitoverflowcheck(p:tnode); procedure emitrangecheck(p:tnode;todef:pdef); procedure firstcomplex(p : tbinarynode); implementation uses globtype,globals,systems,verbose, cutils,cobjects, aasm,cpubase,cpuasm, symconst,symdef,symsym,symtable, {$ifdef GDB} gdb, {$endif GDB} types, ncon,nld, pass_1,pass_2, hcodegen,tgeni386,temp_gen, cgai386; {***************************************************************************** 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 needed>usablereg32 then begin if (p.location.loc=LOC_REGISTER) then begin if isint64 then begin {$ifdef TEMPS_NOT_PUSH} gettempofsizereference(href,8); p.temp_offset:=href.offset; href.offset:=href.offset+4; exprasmlist^.concat(new(paicpu,op_reg(A_MOV,S_L,p.location.registerhigh,href))); href.offset:=href.offset-4; {$else TEMPS_NOT_PUSH} exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.registerhigh))); {$endif TEMPS_NOT_PUSH} ungetregister32(p.location.registerhigh); end {$ifdef TEMPS_NOT_PUSH} else begin gettempofsizereference(href,4); p.temp_offset:=href.offset; end {$endif TEMPS_NOT_PUSH} ; pushed:=true; {$ifdef TEMPS_NOT_PUSH} exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,p.location.register,href))); {$else TEMPS_NOT_PUSH} exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.register))); {$endif TEMPS_NOT_PUSH} ungetregister32(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 del_reference(p.location.reference); getexplicitregister32(R_EDI); emit_ref_reg(A_LEA,S_L,newreference(p.location.reference),R_EDI); {$ifdef TEMPS_NOT_PUSH} gettempofsizereference(href,4); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,href))); p.temp_offset:=href.offset; {$else TEMPS_NOT_PUSH} exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,R_EDI))); {$endif TEMPS_NOT_PUSH} ungetregister32(R_EDI); pushed:=true; end else pushed:=false; end else pushed:=false; maybe_push:=pushed; end; {$ifdef TEMPS_NOT_PUSH} function maybe_savetotemp(needed : byte;p : tnode;isint64 : boolean) : boolean; var pushed : boolean; href : treference; begin if needed>usablereg32 then begin if (p^.location.loc=LOC_REGISTER) then begin if isint64(p^.resulttype) then begin gettempofsizereference(href,8); p^.temp_offset:=href.offset; href.offset:=href.offset+4; exprasmlist^.concat(new(paicpu,op_reg(A_MOV,S_L,p^.location.registerhigh,href))); href.offset:=href.offset-4; ungetregister32(p^.location.registerhigh); end else begin gettempofsizereference(href,4); p^.temp_offset:=href.offset; end; pushed:=true; exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,p^.location.register,href))); ungetregister32(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 del_reference(p^.location.reference); getexplicitregister32(R_EDI); emit_ref_reg(A_LEA,S_L,newreference(p^.location.reference), R_EDI); gettempofsizereference(href,4); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,href))); ungetregister32(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 hregister:=getregister32; {$ifdef TEMPS_NOT_PUSH} reset_reference(href); href.base:=procinfo^.frame_pointer; href.offset:=p.temp_offset; emit_ref_reg(A_MOV,S_L,href,hregister); {$else TEMPS_NOT_PUSH} exprasmlist^.concat(new(paicpu,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:=getregister32; {$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(new(paicpu,op_reg(A_POP,S_L,p.location.registerhigh))); {$endif TEMPS_NOT_PUSH} end; end else begin reset_reference(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} ungetiftemp(href); {$endif TEMPS_NOT_PUSH} end; {$ifdef TEMPS_NOT_PUSH} procedure restorefromtemp(p : tnode;isint64 : boolean); var hregister : tregister; href : treference; begin hregister:=getregister32; reset_reference(href); href.base:=procinfo^.frame_pointer; 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:=getregister32; 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; ungetiftemp(href); end; {$endif TEMPS_NOT_PUSH} procedure pushsetelement(p : tnode); var hr,hr16,hr32 : tregister; begin { copy the element on the stack, slightly complicated } if p.nodetype=ordconstn then begin if target_os.stackalignment=4 then exprasmlist^.concat(new(paicpu,op_const(A_PUSH,S_L,tordconstnode(p).value))) else exprasmlist^.concat(new(paicpu,op_const(A_PUSH,S_W,tordconstnode(p).value))); end else begin case p.location.loc of LOC_REGISTER, LOC_CREGISTER : begin hr:=p.location.register; case hr of R_EAX,R_EBX,R_ECX,R_EDX,R_EDI,R_ESI,R_ESP : begin hr16:=reg32toreg16(hr); hr32:=hr; end; R_AX,R_BX,R_CX,R_DX,R_DI,R_SI,R_SP : begin hr16:=hr; hr32:=reg16toreg32(hr); end; R_AL,R_BL,R_CL,R_DL : begin hr16:=reg8toreg16(hr); hr32:=reg8toreg32(hr); end; end; if target_os.stackalignment=4 then exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,hr32))) else exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_W,hr16))); ungetregister32(hr32); end; else begin { you can't push more bytes than the size of the element, } { because this may cross a page boundary and you'll get a } { sigsegv (JM) } emit_push_mem_size(p.location.reference,1); del_reference(p.location.reference); end; end; end; end; procedure push_value_para(p:tnode;inlined,is_cdecl:boolean; para_offset:longint;alignment : longint); var tempreference : treference; r : preference; opsize : topsize; op : tasmop; hreg : tregister; size : longint; hlabel : pasmlabel; begin case p.location.loc of LOC_REGISTER, LOC_CREGISTER: begin case p.location.register of R_EAX,R_EBX,R_ECX,R_EDX,R_ESI, R_EDI,R_ESP,R_EBP : begin if p.resulttype^.size=8 then begin inc(pushedparasize,8); if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L, p.location.registerlow,r))); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize+4); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L, p.location.registerhigh,r))); end else exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.registerhigh))); ungetregister32(p.location.registerhigh); exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.registerlow))); ungetregister32(p.location.registerlow); end else begin inc(pushedparasize,4); if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L, p.location.register,r))); end else exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.location.register))); ungetregister32(p.location.register); end; end; R_AX,R_BX,R_CX,R_DX,R_SI,R_DI: begin if alignment=4 then begin opsize:=S_L; hreg:=reg16toreg32(p.location.register); inc(pushedparasize,4); end else begin opsize:=S_W; hreg:=p.location.register; inc(pushedparasize,2); end; if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r))); end else exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,opsize,hreg))); ungetregister32(reg16toreg32(p.location.register)); end; R_AL,R_BL,R_CL,R_DL: begin if alignment=4 then begin opsize:=S_L; hreg:=reg8toreg32(p.location.register); inc(pushedparasize,4); end else begin opsize:=S_W; hreg:=reg8toreg16(p.location.register); inc(pushedparasize,2); end; { we must push always 16 bit } if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r))); end else exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,opsize,hreg))); ungetregister32(reg8toreg32(p.location.register)); end; else internalerror(1899); end; end; LOC_FPU: begin size:=align(pfloatdef(p.resulttype)^.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(new(pai_force_line,init)); {$endif GDB} r:=new_reference(R_ESP,0); floatstoreops(pfloatdef(p.resulttype)^.typ,op,opsize); { this is the easiest case for inlined !! } if inlined then begin r^.base:=procinfo^.framepointer; r^.offset:=para_offset-pushedparasize; end; exprasmlist^.concat(new(paicpu,op_ref(op,opsize,r))); dec(fpuvaroffset); end; LOC_CFPUREGISTER: begin exprasmlist^.concat(new(paicpu,op_reg(A_FLD,S_NO, correct_fpuregister(p.location.register,fpuvaroffset)))); size:=align(pfloatdef(p.resulttype)^.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(new(pai_force_line,init)); {$endif GDB} r:=new_reference(R_ESP,0); floatstoreops(pfloatdef(p.resulttype)^.typ,op,opsize); { this is the easiest case for inlined !! } if inlined then begin r^.base:=procinfo^.framepointer; r^.offset:=para_offset-pushedparasize; end; exprasmlist^.concat(new(paicpu,op_ref(op,opsize,r))); end; LOC_REFERENCE,LOC_MEM: begin tempreference:=p.location.reference; del_reference(p.location.reference); case p.resulttype^.deftype of enumdef, orddef : begin case p.resulttype^.size of 8 : begin inc(pushedparasize,8); if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); getexplicitregister32(R_EDI); inc(tempreference.offset,4); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize+4); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); end else begin inc(tempreference.offset,4); emit_push_mem(tempreference); dec(tempreference.offset,4); emit_push_mem(tempreference); end; end; 4 : begin inc(pushedparasize,4); if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); end else emit_push_mem(tempreference); end; 1,2 : begin if alignment=4 then begin opsize:=S_L; hreg:=R_EDI; inc(pushedparasize,4); end else begin opsize:=S_W; hreg:=R_DI; inc(pushedparasize,2); end; if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,opsize, newreference(tempreference),hreg); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r))); ungetregister32(R_EDI); end else emit_push_mem_size(tempreference,p.resulttype^.size); end; else internalerror(234231); end; end; floatdef : begin case pfloatdef(p.resulttype)^.typ of f32bit, s32real : begin inc(pushedparasize,4); if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); end else emit_push_mem(tempreference); end; s64real, s64comp : begin inc(pushedparasize,4); inc(tempreference.offset,4); if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); end else emit_push_mem(tempreference); inc(pushedparasize,4); dec(tempreference.offset,4); if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); end else emit_push_mem(tempreference); end; s80real : begin inc(pushedparasize,4); if alignment=4 then inc(tempreference.offset,8) else inc(tempreference.offset,6); if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); end else emit_push_mem(tempreference); dec(tempreference.offset,4); inc(pushedparasize,4); if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); end else emit_push_mem(tempreference); if alignment=4 then begin opsize:=S_L; hreg:=R_EDI; inc(pushedparasize,4); dec(tempreference.offset,4); end else begin opsize:=S_W; hreg:=R_DI; inc(pushedparasize,2); dec(tempreference.offset,2); end; if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,opsize, newreference(tempreference),hreg); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r))); ungetregister32(R_EDI); end else exprasmlist^.concat(new(paicpu,op_ref(A_PUSH,opsize, newreference(tempreference)))); end; end; end; pointerdef, procvardef, classrefdef: begin inc(pushedparasize,4); if inlined then begin getexplicitregister32(R_EDI); emit_ref_reg(A_MOV,S_L, newreference(tempreference),R_EDI); r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,S_L,R_EDI,r))); ungetregister32(R_EDI); end else emit_push_mem(tempreference); end; arraydef, recorddef, stringdef, setdef, objectdef : begin { even some structured types are 32 bit } if is_widestring(p.resulttype) or is_ansistring(p.resulttype) or is_smallset(p.resulttype) or ((p.resulttype^.deftype in [recorddef,arraydef]) and ( (p.resulttype^.deftype<>arraydef) or not (parraydef(p.resulttype)^.IsConstructor or parraydef(p.resulttype)^.isArrayOfConst or is_open_array(p.resulttype)) ) and (p.resulttype^.size<=4) ) or is_class(p.resulttype) or is_interface(p.resulttype) then begin if (p.resulttype^.size>2) or ((alignment=4) and (p.resulttype^.size>0)) then begin inc(pushedparasize,4); if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); concatcopy(tempreference,r^,4,false,false); end else emit_push_mem(tempreference); end else begin if p.resulttype^.size>0 then begin inc(pushedparasize,2); if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); concatcopy(tempreference,r^,2,false,false); end else exprasmlist^.concat(new(paicpu,op_ref(A_PUSH,S_W,newreference(tempreference)))); end; end; end { call by value open array ? } else if is_cdecl then begin { push on stack } size:=align(p.resulttype^.size,alignment); inc(pushedparasize,size); emit_const_reg(A_SUB,S_L,size,R_ESP); r:=new_reference(R_ESP,0); concatcopy(tempreference,r^,size,false,false); end else internalerror(8954); end; else CGMessage(cg_e_illegal_expression); end; end; LOC_JUMP: begin getlabel(hlabel); if alignment=4 then begin opsize:=S_L; inc(pushedparasize,4); end else begin opsize:=S_W; inc(pushedparasize,2); end; emitlab(truelabel); if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); emit_const_ref(A_MOV,opsize,1,r); end else exprasmlist^.concat(new(paicpu,op_const(A_PUSH,opsize,1))); emitjmp(C_None,hlabel); emitlab(falselabel); if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); emit_const_ref(A_MOV,opsize,0,r); end else exprasmlist^.concat(new(paicpu,op_const(A_PUSH,opsize,0))); emitlab(hlabel); end; LOC_FLAGS: begin if not(R_EAX in unused) then begin getexplicitregister32(R_EDI); emit_reg_reg(A_MOV,S_L,R_EAX,R_EDI); end; emit_flag2reg(p.location.resflags,R_AL); emit_reg_reg(A_MOVZX,S_BW,R_AL,R_AX); if alignment=4 then begin opsize:=S_L; hreg:=R_EAX; inc(pushedparasize,4); end else begin opsize:=S_W; hreg:=R_AX; inc(pushedparasize,2); end; if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOV,opsize,hreg,r))); end else exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,opsize,hreg))); if not(R_EAX in unused) then begin emit_reg_reg(A_MOV,S_L,R_EDI,R_EAX); ungetregister32(R_EDI); end; end; {$ifdef SUPPORT_MMX} LOC_MMXREGISTER, LOC_CMMXREGISTER: begin inc(pushedparasize,8); { was missing !!! (PM) } emit_const_reg( A_SUB,S_L,8,R_ESP); {$ifdef GDB} if (cs_debuginfo in aktmoduleswitches) and (exprasmlist^.first=exprasmlist^.last) then exprasmlist^.concat(new(pai_force_line,init)); {$endif GDB} if inlined then begin r:=new_reference(procinfo^.framepointer,para_offset-pushedparasize); exprasmlist^.concat(new(paicpu,op_reg_ref(A_MOVQ,S_NO, p.location.register,r))); end else begin r:=new_reference(R_ESP,0); exprasmlist^.concat(new(paicpu,op_reg_ref( A_MOVQ,S_NO,p.location.register,r))); end; end; {$endif SUPPORT_MMX} end; end; {***************************************************************************** Emit Functions *****************************************************************************} procedure maketojumpbool(p : tnode); { produces jumps to true respectively false labels using boolean expressions } var opsize : topsize; storepos : tfileposinfo; begin if nf_error in p.flags then exit; storepos:=aktfilepos; aktfilepos:=p.fileinfo; if is_boolean(p.resulttype) then begin if is_constboolnode(p) then begin if tordconstnode(p).value<>0 then emitjmp(C_None,truelabel) else emitjmp(C_None,falselabel); end else begin opsize:=def_opsize(p.resulttype); case p.location.loc of LOC_CREGISTER,LOC_REGISTER : begin emit_reg_reg(A_OR,opsize,p.location.register, p.location.register); ungetregister(p.location.register); emitjmp(C_NZ,truelabel); emitjmp(C_None,falselabel); end; LOC_MEM,LOC_REFERENCE : begin emit_const_ref( A_CMP,opsize,0,newreference(p.location.reference)); del_reference(p.location.reference); emitjmp(C_NZ,truelabel); emitjmp(C_None,falselabel); end; LOC_FLAGS : begin emitjmp(flag_2_cond[p.location.resflags],truelabel); emitjmp(C_None,falselabel); end; end; end; end else CGMessage(type_e_mismatch); aktfilepos:=storepos; end; { produces if necessary overflowcode } procedure emitoverflowcheck(p:tnode); var hl : pasmlabel; begin if not(cs_check_overflow in aktlocalswitches) then exit; getlabel(hl); if not ((p.resulttype^.deftype=pointerdef) or ((p.resulttype^.deftype=orddef) and (porddef(p.resulttype)^.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; { produces range check code, while one of the operands is a 64 bit integer } procedure emitrangecheck64(p : tnode;todef : pdef); begin CGMessage(cg_w_64bit_range_check_not_supported); {internalerror(28699);} end; { produces if necessary rangecheckcode } procedure emitrangecheck(p:tnode;todef:pdef); { generate range checking code for the value at location t. The type used is the checked against todefs ranges. fromdef (p.resulttype) is the original type used at that location, when both defs are equal the check is also insert (needed for succ,pref,inc,dec) } var neglabel, poslabel : pasmlabel; href : treference; rstr : string; hreg : tregister; opsize : topsize; op : tasmop; fromdef : pdef; lto,hto, lfrom,hfrom : longint; doublebound, is_reg, popecx : boolean; begin { range checking on and range checkable value? } if not(cs_check_range in aktlocalswitches) or not(todef^.deftype in [orddef,enumdef,arraydef]) then exit; { only check when assigning to scalar, subranges are different, when todef=fromdef then the check is always generated } fromdef:=p.resulttype; if is_64bitint(fromdef) or is_64bitint(todef) then begin emitrangecheck64(p,todef); exit; end; {we also need lto and hto when checking if we need to use doublebound! (JM)} getrange(todef,lto,hto); if todef<>fromdef then begin getrange(p.resulttype,lfrom,hfrom); { first check for not being u32bit, then if the to is bigger than from } if (lto=hfrom) then exit; end; { generate the rangecheck code for the def where we are going to store the result } doublebound:=false; case todef^.deftype of orddef : begin porddef(todef)^.genrangecheck; rstr:=porddef(todef)^.getrangecheckstring; doublebound:=(porddef(todef)^.typ=u32bit) and (lto>hto); end; enumdef : begin penumdef(todef)^.genrangecheck; rstr:=penumdef(todef)^.getrangecheckstring; end; arraydef : begin parraydef(todef)^.genrangecheck; rstr:=parraydef(todef)^.getrangecheckstring; doublebound:=(lto>hto); end; end; { get op and opsize } opsize:=def2def_opsize(fromdef,u32bitdef); if opsize in [S_B,S_W,S_L] then op:=A_MOV else if is_signed(fromdef) then op:=A_MOVSX else op:=A_MOVZX; is_reg:=(p.location.loc in [LOC_REGISTER,LOC_CREGISTER]); if is_reg then hreg:=p.location.register; if not target_os.use_bound_instruction then begin { FPC_BOUNDCHECK needs to be called with %ecx - value %edi - pointer to the ranges } popecx:=false; if not(is_reg) or (p.location.register<>R_ECX) then begin if not(R_ECX in unused) then begin exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,R_ECX))); popecx:=true; end else exprasmlist^.concat(new(pairegalloc,alloc(R_ECX))); if is_reg then emit_reg_reg(op,opsize,p.location.register,R_ECX) else emit_ref_reg(op,opsize,newreference(p.location.reference),R_ECX); end; if doublebound then begin getlabel(neglabel); getlabel(poslabel); emit_reg_reg(A_OR,S_L,R_ECX,R_ECX); emitjmp(C_L,neglabel); end; { insert bound instruction only } getexplicitregister32(R_EDI); exprasmlist^.concat(new(paicpu,op_sym_ofs_reg(A_MOV,S_L,newasmsymbol(rstr),0,R_EDI))); emitcall('FPC_BOUNDCHECK'); ungetregister32(R_EDI); { u32bit needs 2 checks } if doublebound then begin emitjmp(C_None,poslabel); emitlab(neglabel); getexplicitregister32(R_EDI); exprasmlist^.concat(new(paicpu,op_sym_ofs_reg(A_MOV,S_L,newasmsymbol(rstr),8,R_EDI))); emitcall('FPC_BOUNDCHECK'); ungetregister32(R_EDI); emitlab(poslabel); end; if popecx then exprasmlist^.concat(new(paicpu,op_reg(A_POP,S_L,R_ECX))) else exprasmlist^.concat(new(pairegalloc,dealloc(R_ECX))); end else begin reset_reference(href); href.symbol:=newasmsymbol(rstr); { load the value in a register } if is_reg then begin { be sure that hreg is a 32 bit reg, if not load it in %edi } if p.location.register in [R_EAX..R_EDI] then hreg:=p.location.register else begin getexplicitregister32(R_EDI); emit_reg_reg(op,opsize,p.location.register,R_EDI); hreg:=R_EDI; end; end else begin getexplicitregister32(R_EDI); emit_ref_reg(op,opsize,newreference(p.location.reference),R_EDI); hreg:=R_EDI; end; if doublebound then begin getlabel(neglabel); getlabel(poslabel); emit_reg_reg(A_TEST,S_L,hreg,hreg); emitjmp(C_L,neglabel); end; { insert bound instruction only } exprasmlist^.concat(new(paicpu,op_reg_ref(A_BOUND,S_L,hreg,newreference(href)))); { u32bit needs 2 checks } if doublebound then begin href.offset:=8; emitjmp(C_None,poslabel); emitlab(neglabel); exprasmlist^.concat(new(paicpu,op_reg_ref(A_BOUND,S_L,hreg,newreference(href)))); emitlab(poslabel); end; if hreg = R_EDI then ungetregister32(R_EDI); end; 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^.deftype=orddef) and (porddef(p.left.resulttype)^.typ in [bool8bit,bool16bit,bool32bit]) then begin { p.swaped:=false} if nf_swaped in p.flags then internalerror(234234); end else if (p.left.registers32 } { problems with the optimizer (JM) } del_reference(p.left.location.reference); ungettemp:=false; case p.right.location.loc of LOC_REGISTER,LOC_CREGISTER: begin pushusedregisters(pushed, $ff xor ($80 shr byte(p.right.location.register))); exprasmlist^.concat(new(paicpu,op_reg(A_PUSH,S_L,p.right.location.register))); ungetregister32(p.right.location.register); end; LOC_REFERENCE,LOC_MEM: begin pushusedregisters(pushed,$ff xor ($80 shr byte(p.right.location.reference.base)) xor ($80 shr byte(p.right.location.reference.index))); emit_push_mem(p.right.location.reference); del_reference(p.right.location.reference); ungettemp:=true; end; end; emitpushreferenceaddr(p.left.location.reference); del_reference(p.left.location.reference); emitcall('FPC_INTF_ASSIGN'); maybe_loadesi; popusedregisters(pushed); if ungettemp then ungetiftemp(p.right.location.reference); end; end. { $Log$ Revision 1.3 2000-11-04 14:25:25 florian + merged Attila's changes for interfaces, not tested yet Revision 1.2 2000/10/31 22:02:57 peter * symtable splitted, no real code changes Revision 1.1 2000/10/15 09:33:32 peter * moved n386*.pas to i386/ cpu_target dir Revision 1.3 2000/10/14 21:52:54 peter * fixed memory leaks Revision 1.2 2000/10/14 10:14:50 peter * moehrendorf oct 2000 rewrite Revision 1.1 2000/10/01 19:58:40 peter * new file }