{ $Id$ Copyright (c) 1998-2000 by Florian Klaempfl Generate i386 assembler for in memory related nodes 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 n386mem; {$i defines.inc} interface uses node,nmem,ncgmem; type ti386newnode = class(tnewnode) procedure pass_2;override; end; ti386addrnode = class(tcgaddrnode) procedure pass_2;override; end; ti386simplenewdisposenode = class(tsimplenewdisposenode) procedure pass_2;override; end; ti386derefnode = class(tcgderefnode) procedure pass_2;override; end; ti386vecnode = class(tvecnode) procedure pass_2;override; end; implementation uses {$ifdef delphi} sysutils, {$endif} globtype,systems, cutils,verbose,globals, symconst,symtype,symdef,symsym,symtable,aasm,types, cgbase,temp_gen,pass_2, pass_1,nld,ncon,nadd, cpubase,cpuasm, cga,tgcpu,n386util; {***************************************************************************** TI386NEWNODE *****************************************************************************} procedure ti386newnode.pass_2; var pushed : tpushed; r : preference; begin if assigned(left) then begin secondpass(left); location.register:=left.location.register; end else begin pushusedregisters(pushed,$ff); gettempofsizereference(target_info.size_of_pointer,location.reference); { determines the size of the mem block } push_int(tpointerdef(resulttype.def).pointertype.def.size); emit_push_lea_loc(location,false); saveregvars($ff); emitcall('FPC_GETMEM'); if tpointerdef(resulttype.def).pointertype.def.needs_inittable then begin new(r); reset_reference(r^); r^.symbol:=tstoreddef(tpointerdef(resulttype.def).pointertype.def).get_rtti_label(initrtti); emitpushreferenceaddr(r^); dispose(r); { push pointer we just allocated, we need to initialize the data located at that pointer not the pointer self (PFV) } emit_push_loc(location); emitcall('FPC_INITIALIZE'); end; popusedregisters(pushed); { may be load ESI } maybe_loadself; end; if codegenerror then exit; end; {***************************************************************************** TI386ADDRNODE *****************************************************************************} procedure ti386addrnode.pass_2; begin inherited pass_2; { for use of other segments } if left.location.reference.segment<>R_NO then location.segment:=left.location.reference.segment; end; {***************************************************************************** TI386SIMPLENEWDISPOSENODE *****************************************************************************} procedure ti386simplenewdisposenode.pass_2; var pushed : tpushed; r : preference; begin secondpass(left); if codegenerror then exit; pushusedregisters(pushed,$ff); saveregvars($ff); { call the mem handling procedures } case nodetype of simpledisposen: begin if tpointerdef(left.resulttype.def).pointertype.def.needs_inittable then begin new(r); reset_reference(r^); r^.symbol:=tstoreddef(tpointerdef(left.resulttype.def).pointertype.def).get_rtti_label(initrtti); emitpushreferenceaddr(r^); dispose(r); { push pointer adress } emit_push_loc(left.location); emitcall('FPC_FINALIZE'); end; emit_push_loc(left.location); emitcall('FPC_FREEMEM'); end; simplenewn: begin { determines the size of the mem block } push_int(tpointerdef(left.resulttype.def).pointertype.def.size); emit_push_lea_loc(left.location,true); emitcall('FPC_GETMEM'); if tpointerdef(left.resulttype.def).pointertype.def.needs_inittable then begin new(r); reset_reference(r^); r^.symbol:=tstoreddef(tpointerdef(left.resulttype.def).pointertype.def).get_rtti_label(initrtti); emitpushreferenceaddr(r^); dispose(r); emit_push_loc(left.location); emitcall('FPC_INITIALIZE'); end; end; end; popusedregisters(pushed); { may be load ESI } maybe_loadself; end; {***************************************************************************** TI386DEREFNODE *****************************************************************************} procedure ti386derefnode.pass_2; begin inherited pass_2; if tpointerdef(left.resulttype.def).is_far then location.reference.segment:=R_FS; if not tpointerdef(left.resulttype.def).is_far and (cs_gdb_heaptrc in aktglobalswitches) and (cs_checkpointer in aktglobalswitches) then begin emit_reg( A_PUSH,S_L,location.reference.base); emitcall('FPC_CHECKPOINTER'); end; end; {***************************************************************************** TI386VECNODE *****************************************************************************} procedure ti386vecnode.pass_2; var is_pushed : boolean; ind,hr : tregister; //_p : tnode; function get_mul_size:longint; begin if nf_memindex in flags then get_mul_size:=1 else begin if (left.resulttype.def.deftype=arraydef) then get_mul_size:=tarraydef(left.resulttype.def).elesize else get_mul_size:=resulttype.def.size; end end; procedure calc_emit_mul; var l1,l2 : longint; begin l1:=get_mul_size; case l1 of 1,2,4,8 : location.reference.scalefactor:=l1; else begin if ispowerof2(l1,l2) then emit_const_reg(A_SHL,S_L,l2,ind) else emit_const_reg(A_IMUL,S_L,l1,ind); end; end; end; var extraoffset : longint; { rl stores the resulttype.def of the left node, this is necessary } { to detect if it is an ansistring } { because in constant nodes which constant index } { the left tree is removed } t : tnode; hp : preference; href : treference; tai : Taicpu; srsym : tsym; pushed : tpushed; hightree : tnode; hl,otl,ofl : tasmlabel; begin secondpass(left); { we load the array reference to location } { an ansistring needs to be dereferenced } if is_ansistring(left.resulttype.def) or is_widestring(left.resulttype.def) then begin reset_reference(location.reference); if nf_callunique in flags then begin if left.location.loc<>LOC_REFERENCE then begin CGMessage(cg_e_illegal_expression); exit; end; pushusedregisters(pushed,$ff); emitpushreferenceaddr(left.location.reference); saveregvars($ff); if is_ansistring(left.resulttype.def) then emitcall('FPC_ANSISTR_UNIQUE') else emitcall('FPC_WIDESTR_UNIQUE'); maybe_loadself; popusedregisters(pushed); end; if left.location.loc in [LOC_REGISTER,LOC_CREGISTER] then begin location.reference.base:=left.location.register; end else begin del_reference(left.location.reference); location.reference.base:=getregister32; emit_ref_reg(A_MOV,S_L, newreference(left.location.reference), location.reference.base); end; { check for a zero length string, we can use the ansistring routine here } if (cs_check_range in aktlocalswitches) then begin pushusedregisters(pushed,$ff); emit_reg(A_PUSH,S_L,location.reference.base); saveregvars($ff); emitcall('FPC_ANSISTR_CHECKZERO'); maybe_loadself; popusedregisters(pushed); end; if is_ansistring(left.resulttype.def) then { in ansistrings S[1] is pchar(S)[0] !! } dec(location.reference.offset) else begin { in widestrings S[1] is pwchar(S)[0] !! } dec(location.reference.offset,2); // emit_const_reg(A_SHL,S_L, // 1,location.reference.base); end; { we've also to keep left up-to-date, because it is used } { if a constant array index occurs, subject to change (FK) } set_location(left.location,location); end else if is_dynamic_array(left.resulttype.def) then { ... also a dynamic string } begin reset_reference(location.reference); if left.location.loc in [LOC_REGISTER,LOC_CREGISTER] then begin location.reference.base:=left.location.register; end else begin del_reference(left.location.reference); location.reference.base:=getregister32; emit_ref_reg(A_MOV,S_L, newreference(left.location.reference), location.reference.base); end; {$warning FIXME} { check for a zero length string, we can use the ansistring routine here } if (cs_check_range in aktlocalswitches) then begin pushusedregisters(pushed,$ff); emit_reg(A_PUSH,S_L,location.reference.base); saveregvars($ff); emitcall('FPC_ANSISTR_CHECKZERO'); maybe_loadself; popusedregisters(pushed); end; { we've also to keep left up-to-date, because it is used } { if a constant array index occurs, subject to change (FK) } set_location(left.location,location); end else set_location(location,left.location); { offset can only differ from 0 if arraydef } if (left.resulttype.def.deftype=arraydef) and not(is_dynamic_array(left.resulttype.def)) then dec(location.reference.offset, get_mul_size*tarraydef(left.resulttype.def).lowrange); if right.nodetype=ordconstn then begin { offset can only differ from 0 if arraydef } if (left.resulttype.def.deftype=arraydef) then begin if not(is_open_array(left.resulttype.def)) and not(is_array_of_const(left.resulttype.def)) and not(is_dynamic_array(left.resulttype.def)) then begin if (tordconstnode(right).value>tarraydef(left.resulttype.def).highrange) or (tordconstnode(right).valueLOC_REFERENCE) and (location.loc<>LOC_MEM) then CGMessage(cg_e_illegal_expression); if (right.location.loc=LOC_JUMP) then begin otl:=truelabel; getlabel(truelabel); ofl:=falselabel; getlabel(falselabel); end; is_pushed:=maybe_push(right.registers32,self,false); secondpass(right); if is_pushed then restore(self,false); { here we change the location of right and the update was forgotten so it led to wrong code in emitrangecheck later PM so make range check before } if cs_check_range in aktlocalswitches then begin if left.resulttype.def.deftype=arraydef then begin if is_open_array(left.resulttype.def) or is_array_of_const(left.resulttype.def) then begin reset_reference(href); tarraydef(left.resulttype.def).genrangecheck; href.symbol:=newasmsymbol(tarraydef(left.resulttype.def).getrangecheckstring); href.offset:=4; srsym:=searchsymonlyin(tloadnode(left).symtable, 'high'+tvarsym(tloadnode(left).symtableentry).name); hightree:=cloadnode.create(tvarsym(srsym),tloadnode(left).symtable); firstpass(hightree); secondpass(hightree); emit_mov_loc_ref(hightree.location,href,S_L,true); hightree.free; hightree:=nil; end; emitrangecheck(right,left.resulttype.def); end; end; case right.location.loc of LOC_REGISTER: begin ind:=right.location.register; case right.resulttype.def.size of 1: begin hr:=reg8toreg32(ind); emit_reg_reg(A_MOVZX,S_BL,ind,hr); ind:=hr; end; 2: begin hr:=reg16toreg32(ind); emit_reg_reg(A_MOVZX,S_WL,ind,hr); ind:=hr; end; end; end; LOC_CREGISTER: begin ind:=getregister32; case right.resulttype.def.size of 1: emit_reg_reg(A_MOVZX,S_BL,right.location.register,ind); 2: emit_reg_reg(A_MOVZX,S_WL,right.location.register,ind); 4: emit_reg_reg(A_MOV,S_L,right.location.register,ind); end; end; LOC_FLAGS: begin ind:=getregister32; emit_flag2reg(right.location.resflags,reg32toreg8(ind)); emit_reg_reg(A_MOVZX,S_BL,reg32toreg8(ind),ind); end; LOC_JUMP : begin ind:=getregister32; emitlab(truelabel); truelabel:=otl; emit_const_reg(A_MOV,S_L,1,ind); getlabel(hl); emitjmp(C_None,hl); emitlab(falselabel); falselabel:=ofl; emit_reg_reg(A_XOR,S_L,ind,ind); emitlab(hl); end; LOC_REFERENCE,LOC_MEM : begin del_reference(right.location.reference); ind:=getregister32; { Booleans are stored in an 8 bit memory location, so the use of MOVL is not correct } case right.resulttype.def.size of 1 : tai:=Taicpu.Op_ref_reg(A_MOVZX,S_BL,newreference(right.location.reference),ind); 2 : tai:=Taicpu.Op_ref_reg(A_MOVZX,S_WL,newreference(right.location.reference),ind); 4 : tai:=Taicpu.Op_ref_reg(A_MOV,S_L,newreference(right.location.reference),ind); end; exprasmList.concat(tai); end; else internalerror(5913428); end; { produce possible range check code: } if cs_check_range in aktlocalswitches then begin if left.resulttype.def.deftype=arraydef then begin { done defore (PM) } end else if (left.resulttype.def.deftype=stringdef) then begin case tstringdef(left.resulttype.def).string_typ of { it's the same for ansi- and wide strings } st_widestring, st_ansistring: begin pushusedregisters(pushed,$ff); emit_reg(A_PUSH,S_L,ind); hp:=newreference(location.reference); dec(hp^.offset,7); emit_ref(A_PUSH,S_L,hp); saveregvars($ff); emitcall('FPC_ANSISTR_RANGECHECK'); popusedregisters(pushed); maybe_loadself; end; st_shortstring: begin {!!!!!!!!!!!!!!!!!} end; st_longstring: begin {!!!!!!!!!!!!!!!!!} end; end; end; end; if location.reference.index=R_NO then begin location.reference.index:=ind; calc_emit_mul; end else begin if location.reference.base=R_NO then begin case location.reference.scalefactor of 2 : emit_const_reg(A_SHL,S_L,1,location.reference.index); 4 : emit_const_reg(A_SHL,S_L,2,location.reference.index); 8 : emit_const_reg(A_SHL,S_L,3,location.reference.index); end; calc_emit_mul; location.reference.base:=location.reference.index; location.reference.index:=ind; end else begin emit_ref_reg( A_LEA,S_L,newreference(location.reference), location.reference.index); ungetregister32(location.reference.base); { the symbol offset is loaded, } { so release the symbol name and set symbol } { to nil } location.reference.symbol:=nil; location.reference.offset:=0; calc_emit_mul; location.reference.base:=location.reference.index; location.reference.index:=ind; end; end; if nf_memseg in flags then location.reference.segment:=R_FS; end; end; begin cnewnode:=ti386newnode; csimplenewdisposenode:=ti386simplenewdisposenode; caddrnode:=ti386addrnode; cderefnode:=ti386derefnode; cvecnode:=ti386vecnode; end. { $Log$ Revision 1.18 2001-12-03 21:48:43 peter * freemem change to value parameter * torddef low/high range changed to int64 Revision 1.17 2001/09/30 16:17:17 jonas * made most constant and mem handling processor independent Revision 1.16 2001/08/30 20:13:57 peter * rtti/init table updates * rttisym for reusable global rtti/init info * support published for interfaces Revision 1.15 2001/08/26 13:37:00 florian * some cg reorganisation * some PPC updates Revision 1.14 2001/07/08 21:00:18 peter * various widestring updates, it works now mostly without charset mapping supported Revision 1.13 2001/04/18 22:02:03 peter * registration of targets and assemblers Revision 1.12 2001/04/13 01:22:19 peter * symtable change to classes * range check generation and errors fixed, make cycle DEBUG=1 works * memory leaks fixed Revision 1.11 2001/04/02 21:20:38 peter * resulttype rewrite Revision 1.10 2001/03/11 22:58:52 peter * getsym redesign, removed the globals srsym,srsymtable Revision 1.9 2001/02/02 22:38:00 peter * fixed crash with new(precord), merged Revision 1.8 2000/12/25 00:07:33 peter + new tlinkedlist class (merge of old tstringqueue,tcontainer and tlinkedlist objects) Revision 1.7 2000/12/05 11:44:33 jonas + new integer regvar handling, should be much more efficient Revision 1.6 2000/11/29 00:30:48 florian * unused units removed from uses clause * some changes for widestrings Revision 1.5 2000/11/04 14:25:24 florian + merged Attila's changes for interfaces, not tested yet Revision 1.4 2000/10/31 22:02:57 peter * symtable splitted, no real code changes Revision 1.3 2000/10/31 14:18:53 jonas * merged double deleting of left location when using a temp in secondwith (merged from fixes branch). This also fixes web bug1194 Revision 1.2 2000/10/21 18:16:13 florian * a lot of changes: - basic dyn. array support - basic C++ support - some work for interfaces done .... Revision 1.1 2000/10/15 09:33:32 peter * moved n386*.pas to i386/ cpu_target dir Revision 1.2 2000/10/14 21:52:54 peter * fixed memory leaks Revision 1.1 2000/10/14 10:14:49 peter * moehrendorf oct 2000 rewrite }