fpc/compiler/i386/n386cal.pas
peter 3bcfe33002 * fixed stackcheck that would be called recursively when a stack
error was found
  * generic changeregsize(reg,size) for i386 register resizing
  * removed some more routines from cga unit
  * fixed returnvalue handling
  * fixed default stacksize of linux and go32v2, 8kb was a bit small :-)
2002-04-15 19:44:18 +00:00

1696 lines
76 KiB
ObjectPascal

{
$Id$
Copyright (c) 1998-2000 by Florian Klaempfl
Generate i386 assembler for in call 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 bymethodpointer
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 n386cal;
{$i defines.inc}
interface
{ $define AnsiStrRef}
uses
symdef,node,ncal;
type
ti386callparanode = class(tcallparanode)
procedure secondcallparan(defcoll : TParaItem;
push_from_left_to_right,inlined,is_cdecl : boolean;
para_alignment,para_offset : longint);override;
end;
ti386callnode = class(tcallnode)
procedure pass_2;override;
end;
ti386procinlinenode = class(tprocinlinenode)
procedure pass_2;override;
end;
implementation
uses
globtype,systems,
cutils,verbose,globals,
symconst,symbase,symsym,symtable,aasm,types,
{$ifdef GDB}
{$ifdef delphi}
sysutils,
{$else}
strings,
{$endif}
gdb,
{$endif GDB}
cginfo,cgbase,pass_2,
cpubase,
nmem,nld,ncnv,
tainst,cga,cgobj,tgobj,n386ld,n386util,regvars,rgobj,rgcpu,cg64f32,cgcpu;
{*****************************************************************************
TI386CALLPARANODE
*****************************************************************************}
procedure ti386callparanode.secondcallparan(defcoll : TParaItem;
push_from_left_to_right,inlined,is_cdecl : boolean;para_alignment,para_offset : longint);
procedure maybe_push_high;
begin
{ open array ? }
{ defcoll.data can be nil for read/write }
if assigned(defcoll.paratype.def) and
assigned(hightree) then
begin
secondpass(hightree);
{ this is a longint anyway ! }
push_value_para(hightree,inlined,false,para_offset,4);
end;
end;
var
otlabel,oflabel : tasmlabel;
{ temporary variables: }
tempdeftype : tdeftype;
href : treference;
begin
{ set default para_alignment to target_info.stackalignment }
if para_alignment=0 then
para_alignment:=aktalignment.paraalign;
{ push from left to right if specified }
if push_from_left_to_right and assigned(right) then
begin
if (nf_varargs_para in flags) then
tcallparanode(right).secondcallparan(defcoll,push_from_left_to_right,
inlined,is_cdecl,para_alignment,para_offset)
else
tcallparanode(right).secondcallparan(TParaItem(defcoll.next),push_from_left_to_right,
inlined,is_cdecl,para_alignment,para_offset);
end;
otlabel:=truelabel;
oflabel:=falselabel;
getlabel(truelabel);
getlabel(falselabel);
secondpass(left);
{ handle varargs first, because defcoll is not valid }
if (nf_varargs_para in flags) then
begin
if push_addr_param(left.resulttype.def) then
begin
inc(pushedparasize,4);
emitpushreferenceaddr(left.location.reference);
location_release(exprasmlist,left.location);
end
else
push_value_para(left,inlined,is_cdecl,para_offset,para_alignment);
end
{ filter array constructor with c styled args }
else if is_array_constructor(left.resulttype.def) and (nf_cargs in left.flags) then
begin
{ nothing, everything is already pushed }
end
{ in codegen.handleread.. defcoll.data is set to nil }
else if assigned(defcoll.paratype.def) and
(defcoll.paratype.def.deftype=formaldef) then
begin
{ allow @var }
inc(pushedparasize,4);
if (left.nodetype=addrn) and
(not(nf_procvarload in left.flags)) then
begin
{ always a register }
if inlined then
begin
reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize);
emit_reg_ref(A_MOV,S_L,left.location.register,href);
end
else
emit_reg(A_PUSH,S_L,left.location.register);
rg.ungetregisterint(exprasmlist,left.location.register);
end
else
begin
if not(left.location.loc in [LOC_CREFERENCE,LOC_REFERENCE]) then
CGMessage(type_e_mismatch)
else
begin
if inlined then
begin
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_ref_reg(A_LEA,S_L,left.location.reference,R_EDI);
reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize);
emit_reg_ref(A_MOV,S_L,R_EDI,href);
rg.ungetregisterint(exprasmlist,R_EDI);
end
else
cg.a_paramaddr_ref(exprasmlist,left.location.reference,-1);
location_release(exprasmlist,left.location);
end;
end;
end
{ handle call by reference parameter }
else if (defcoll.paratyp in [vs_var,vs_out]) then
begin
{ get temp for constants }
if left.location.loc=LOC_CONSTANT then
begin
tg.gettempofsizereference(exprasmlist,left.resulttype.def.size,href);
cg.a_load_loc_ref(exprasmlist,left.location,href);
location_reset(left.location,LOC_REFERENCE,def_cgsize(left.resulttype.def));
left.location.reference:=href;
end;
if (left.location.loc<>LOC_REFERENCE) then
begin
{ passing self to a var parameter is allowed in
TP and delphi }
if not((left.location.loc=LOC_CREFERENCE) and
(left.nodetype=selfn)) then
internalerror(200106041);
end;
maybe_push_high;
if (defcoll.paratyp=vs_out) and
assigned(defcoll.paratype.def) and
not is_class(defcoll.paratype.def) and
defcoll.paratype.def.needs_inittable then
finalize(defcoll.paratype.def,left.location.reference,false);
inc(pushedparasize,4);
if inlined then
begin
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_ref_reg(A_LEA,S_L,left.location.reference,R_EDI);
reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize);
emit_reg_ref(A_MOV,S_L,R_EDI,href);
rg.ungetregisterint(exprasmlist,R_EDI);
end
else
cg.a_paramaddr_ref(exprasmlist,left.location.reference,-1);
location_release(exprasmlist,left.location);
end
else
begin
tempdeftype:=resulttype.def.deftype;
if tempdeftype=filedef then
CGMessage(cg_e_file_must_call_by_reference);
{ open array must always push the address, this is needed to
also push addr of small open arrays and with cdecl functions (PFV) }
if (
assigned(defcoll.paratype.def) and
(is_open_array(defcoll.paratype.def) or
is_array_of_const(defcoll.paratype.def))
) or
(
push_addr_param(resulttype.def) and
not is_cdecl
) then
begin
if not(left.location.loc in [LOC_CREFERENCE,LOC_REFERENCE]) then
begin
{ allow passing nil to a procvardef (methodpointer) }
if (left.nodetype=typeconvn) and
(left.resulttype.def.deftype=procvardef) and
(ttypeconvnode(left).left.nodetype=niln) then
begin
tg.gettempofsizereference(exprasmlist,tcgsize2size[left.location.size],href);
cg.a_load_loc_ref(exprasmlist,left.location,href);
location_reset(left.location,LOC_REFERENCE,left.location.size);
left.location.reference:=href;
end
else
internalerror(200204011);
end;
maybe_push_high;
inc(pushedparasize,4);
if inlined then
begin
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_ref_reg(A_LEA,S_L,left.location.reference,R_EDI);
reference_reset_base(href,procinfo^.framepointer,para_offset-pushedparasize);
emit_reg_ref(A_MOV,S_L,R_EDI,href);
rg.ungetregisterint(exprasmlist,R_EDI);
end
else
cg.a_paramaddr_ref(exprasmlist,left.location.reference,-1);
location_release(exprasmlist,left.location);
end
else
begin
push_value_para(left,inlined,is_cdecl,
para_offset,para_alignment);
end;
end;
truelabel:=otlabel;
falselabel:=oflabel;
{ push from right to left }
if not push_from_left_to_right and assigned(right) then
begin
if (nf_varargs_para in flags) then
tcallparanode(right).secondcallparan(defcoll,push_from_left_to_right,
inlined,is_cdecl,para_alignment,para_offset)
else
tcallparanode(right).secondcallparan(TParaItem(defcoll.next),push_from_left_to_right,
inlined,is_cdecl,para_alignment,para_offset);
end;
end;
{*****************************************************************************
TI386CALLNODE
*****************************************************************************}
procedure ti386callnode.pass_2;
var
regs_to_push : tregisterset;
unusedstate: pointer;
pushed : tpushedsaved;
funcretref,refcountedtemp : treference;
hregister : tregister;
oldpushedparasize : longint;
{ true if ESI must be loaded again after the subroutine }
loadesi : boolean;
{ true if a virtual method must be called directly }
no_virtual_call : boolean;
{ true if we produce a con- or destrutor in a call }
is_con_or_destructor : boolean;
{ true if a constructor is called again }
extended_new : boolean;
{ adress returned from an I/O-error }
iolabel : tasmlabel;
{ lexlevel count }
i : longint;
{ help reference pointer }
href : treference;
hp : tnode;
pp : tbinarynode;
params : tnode;
inlined : boolean;
inlinecode : tprocinlinenode;
store_parast_fixup,
para_alignment,
para_offset : longint;
cgsize : tcgsize;
{ instruction for alignement correction }
{ corr : paicpu;}
{ we must pop this size also after !! }
{ must_pop : boolean; }
pop_size : longint;
{$ifdef OPTALIGN}
pop_esp : boolean;
push_size : longint;
{$endif OPTALIGN}
pop_allowed : boolean;
constructorfailed : tasmlabel;
label
dont_call;
begin
extended_new:=false;
iolabel:=nil;
inlinecode:=nil;
inlined:=false;
loadesi:=true;
no_virtual_call:=false;
rg.saveunusedstate(unusedstate);
{ if we allocate the temp. location for ansi- or widestrings }
{ already here, we avoid later a push/pop }
if is_widestring(resulttype.def) then
begin
tg.gettempwidestringreference(exprasmlist,refcountedtemp);
decrstringref(resulttype.def,refcountedtemp);
end
else if is_ansistring(resulttype.def) then
begin
tg.gettempansistringreference(exprasmlist,refcountedtemp);
decrstringref(resulttype.def,refcountedtemp);
end;
if (procdefinition.proccalloption in [pocall_cdecl,pocall_cppdecl,pocall_stdcall]) then
para_alignment:=4
else
para_alignment:=aktalignment.paraalign;
if not assigned(procdefinition) then
exit;
{ Deciding whether we may still need the parameters happens next (JM) }
if assigned(left) then
params:=left.getcopy
else params := nil;
if (procdefinition.proccalloption=pocall_inline) then
begin
inlined:=true;
inlinecode:=tprocinlinenode(right);
right:=nil;
{ set it to the same lexical level as the local symtable, becuase
the para's are stored there }
tprocdef(procdefinition).parast.symtablelevel:=aktprocdef.localst.symtablelevel;
if assigned(params) then
inlinecode.para_offset:=tg.gettempofsizepersistant(exprasmlist,inlinecode.para_size);
store_parast_fixup:=tprocdef(procdefinition).parast.address_fixup;
tprocdef(procdefinition).parast.address_fixup:=inlinecode.para_offset;
{$ifdef extdebug}
Comment(V_debug,
'inlined parasymtable is at offset '
+tostr(tprocdef(procdefinition).parast.address_fixup));
exprasmList.concat(Tai_asm_comment.Create(
strpnew('inlined parasymtable is at offset '
+tostr(tprocdef(procdefinition).parast.address_fixup))));
{$endif extdebug}
end;
{ only if no proc var }
if inlined or
not(assigned(right)) then
is_con_or_destructor:=(procdefinition.proctypeoption in [potype_constructor,potype_destructor]);
{ proc variables destroy all registers }
if (inlined or
(right=nil)) and
{ virtual methods too }
not(po_virtualmethod in procdefinition.procoptions) then
begin
if (cs_check_io in aktlocalswitches) and
(po_iocheck in procdefinition.procoptions) and
not(po_iocheck in aktprocdef.procoptions) then
begin
getaddrlabel(iolabel);
emitlab(iolabel);
end
else
iolabel:=nil;
{ save all used registers }
regs_to_push := tprocdef(procdefinition).usedregisters;
rg.saveusedregisters(exprasmlist,pushed,regs_to_push);
{ give used registers through }
rg.usedinproc:=rg.usedinproc + tprocdef(procdefinition).usedregisters;
end
else
begin
regs_to_push := all_registers;
rg.saveusedregisters(exprasmlist,pushed,regs_to_push);
rg.usedinproc:=all_registers;
{ no IO check for methods and procedure variables }
iolabel:=nil;
end;
{ generate the code for the parameter and push them }
oldpushedparasize:=pushedparasize;
pushedparasize:=0;
pop_size:=0;
{ no inc esp for inlined procedure
and for objects constructors PM }
if inlined or
((procdefinition.proctypeoption=potype_constructor) and
{ quick'n'dirty check if it is a class or an object }
(resulttype.def.deftype=orddef)) then
pop_allowed:=false
else
pop_allowed:=true;
if pop_allowed then
begin
{ Old pushedsize aligned on 4 ? }
i:=oldpushedparasize and 3;
if i>0 then
inc(pop_size,4-i);
{ This parasize aligned on 4 ? }
i:=procdefinition.para_size(para_alignment) and 3;
if i>0 then
inc(pop_size,4-i);
{ insert the opcode and update pushedparasize }
{ never push 4 or more !! }
pop_size:=pop_size mod 4;
if pop_size>0 then
begin
inc(pushedparasize,pop_size);
emit_const_reg(A_SUB,S_L,pop_size,R_ESP);
{$ifdef GDB}
if (cs_debuginfo in aktmoduleswitches) and
(exprasmList.first=exprasmList.last) then
exprasmList.concat(Tai_force_line.Create);
{$endif GDB}
end;
end;
{$ifdef OPTALIGN}
if pop_allowed and (cs_align in aktglobalswitches) then
begin
pop_esp:=true;
push_size:=procdefinition.para_size(para_alignment);
{ !!!! here we have to take care of return type, self
and nested procedures
}
inc(push_size,12);
emit_reg_reg(A_MOV,S_L,R_ESP,R_EDI);
if (push_size mod 8)=0 then
emit_const_reg(A_AND,S_L,$fffffff8,R_ESP)
else
begin
emit_const_reg(A_SUB,S_L,push_size,R_ESP);
emit_const_reg(A_AND,S_L,$fffffff8,R_ESP);
emit_const_reg(A_SUB,S_L,push_size,R_ESP);
end;
emit_reg(A_PUSH,S_L,R_EDI);
end
else
pop_esp:=false;
{$endif OPTALIGN}
if (not is_void(resulttype.def)) and
ret_in_param(resulttype.def) then
begin
funcretref.symbol:=nil;
{$ifdef test_dest_loc}
if dest_loc_known and (dest_loc_tree=p) and
(dest_loc.loc in [LOC_REFERENCE,LOC_MEM]) then
begin
funcretref:=dest_loc.reference;
if assigned(dest_loc.reference.symbol) then
funcretref.symbol:=stringdup(dest_loc.reference.symbol^);
in_dest_loc:=true;
end
else
{$endif test_dest_loc}
if inlined then
begin
reference_reset(funcretref);
funcretref.offset:=tg.gettempofsizepersistant(exprasmlist,resulttype.def.size);
funcretref.base:=procinfo^.framepointer;
{$ifdef extdebug}
Comment(V_debug,'function return value is at offset '
+tostr(funcretref.offset));
exprasmlist.concat(tai_asm_comment.create(
strpnew('function return value is at offset '
+tostr(funcretref.offset))));
{$endif extdebug}
end
else
tg.gettempofsizereference(exprasmlist,resulttype.def.size,funcretref);
end;
if assigned(params) then
begin
{ be found elsewhere }
if inlined then
para_offset:=tprocdef(procdefinition).parast.address_fixup+
tprocdef(procdefinition).parast.datasize
else
para_offset:=0;
if not(inlined) and
assigned(right) then
tcallparanode(params).secondcallparan(TParaItem(tabstractprocdef(right.resulttype.def).Para.first),
(po_leftright in procdefinition.procoptions),inlined,
(procdefinition.proccalloption in [pocall_cdecl,pocall_cppdecl]),
para_alignment,para_offset)
else
tcallparanode(params).secondcallparan(TParaItem(procdefinition.Para.first),
(po_leftright in procdefinition.procoptions),inlined,
(procdefinition.proccalloption in [pocall_cdecl,pocall_cppdecl]),
para_alignment,para_offset);
end;
if inlined then
inlinecode.retoffset:=tg.gettempofsizepersistant(exprasmlist,Align(resulttype.def.size,aktalignment.paraalign));
if ret_in_param(resulttype.def) then
begin
{ This must not be counted for C code
complex return address is removed from stack
by function itself ! }
{$ifdef OLD_C_STACK}
inc(pushedparasize,4); { lets try without it PM }
{$endif not OLD_C_STACK}
if inlined then
begin
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_ref_reg(A_LEA,S_L,funcretref,R_EDI);
reference_reset_base(href,procinfo^.framepointer,inlinecode.retoffset);
emit_reg_ref(A_MOV,S_L,R_EDI,href);
rg.ungetregisterint(exprasmlist,R_EDI);
end
else
emitpushreferenceaddr(funcretref);
end;
{ procedure variable ? }
if inlined or
(right=nil) then
begin
{ overloaded operator has no symtable }
{ push self }
if assigned(symtableproc) and
(symtableproc.symtabletype=withsymtable) then
begin
{ dirty trick to avoid the secondcall below }
methodpointer:=ccallparanode.create(nil,nil);
location_reset(methodpointer.location,LOC_REGISTER,OS_ADDR);
rg.getexplicitregisterint(exprasmlist,R_ESI);
methodpointer.location.register:=R_ESI;
{ ARGHHH this is wrong !!!
if we can init from base class for a child
class that the wrong VMT will be
transfered to constructor !! }
methodpointer.resulttype:=
twithnode(twithsymtable(symtableproc).withnode).left.resulttype;
{ make a reference }
href:=twithnode(twithsymtable(symtableproc).withnode).withreference;
if ((not(nf_islocal in twithnode(twithsymtable(symtableproc).withnode).flags)) and
(not twithsymtable(symtableproc).direct_with)) or
is_class_or_interface(methodpointer.resulttype.def) then
emit_ref_reg(A_MOV,S_L,href,R_ESI)
else
emit_ref_reg(A_LEA,S_L,href,R_ESI);
end;
{ push self }
if assigned(symtableproc) and
((symtableproc.symtabletype=objectsymtable) or
(symtableproc.symtabletype=withsymtable)) then
begin
if assigned(methodpointer) then
begin
{
if methodpointer^.resulttype.def=classrefdef then
begin
two possibilities:
1. constructor
2. class method
end
else }
begin
case methodpointer.nodetype of
typen:
begin
{ direct call to inherited method }
if (po_abstractmethod in procdefinition.procoptions) then
begin
CGMessage(cg_e_cant_call_abstract_method);
goto dont_call;
end;
{ generate no virtual call }
no_virtual_call:=true;
if (sp_static in symtableprocentry.symoptions) then
begin
{ well lets put the VMT address directly into ESI }
{ it is kind of dirty but that is the simplest }
{ way to accept virtual static functions (PM) }
loadesi:=true;
{ if no VMT just use $0 bug0214 PM }
rg.getexplicitregisterint(exprasmlist,R_ESI);
if not(oo_has_vmt in tobjectdef(methodpointer.resulttype.def).objectoptions) then
emit_const_reg(A_MOV,S_L,0,R_ESI)
else
begin
emit_sym_ofs_reg(A_MOV,S_L,
newasmsymbol(tobjectdef(methodpointer.resulttype.def).vmt_mangledname),
0,R_ESI);
end;
{ emit_reg(A_PUSH,S_L,R_ESI);
this is done below !! }
end
else
{ this is a member call, so ESI isn't modfied }
loadesi:=false;
{ a class destructor needs a flag }
if is_class(tobjectdef(methodpointer.resulttype.def)) and
(procdefinition.proctypeoption=potype_destructor) then
begin
push_int(0);
emit_reg(A_PUSH,S_L,R_ESI);
end;
if not(is_con_or_destructor and
is_class(methodpointer.resulttype.def) and
(procdefinition.proctypeoption in [potype_constructor,potype_destructor])
) then
emit_reg(A_PUSH,S_L,R_ESI);
{ if an inherited con- or destructor should be }
{ called in a con- or destructor then a warning }
{ will be made }
{ con- and destructors need a pointer to the vmt }
if is_con_or_destructor and
is_object(methodpointer.resulttype.def) and
assigned(aktprocdef) then
begin
if not(aktprocdef.proctypeoption in
[potype_constructor,potype_destructor]) then
CGMessage(cg_w_member_cd_call_from_method);
end;
{ class destructors get there flag above }
{ constructor flags ? }
if is_con_or_destructor and
not(
is_class(methodpointer.resulttype.def) and
assigned(aktprocdef) and
(aktprocdef.proctypeoption=potype_destructor)) then
begin
{ a constructor needs also a flag }
if is_class(methodpointer.resulttype.def) then
push_int(0);
push_int(0);
end;
end;
hnewn:
begin
{ extended syntax of new }
{ ESI must be zero }
rg.getexplicitregisterint(exprasmlist,R_ESI);
emit_reg_reg(A_XOR,S_L,R_ESI,R_ESI);
emit_reg(A_PUSH,S_L,R_ESI);
{ insert the vmt }
emit_sym(A_PUSH,S_L,
newasmsymbol(tobjectdef(methodpointer.resulttype.def).vmt_mangledname));
extended_new:=true;
end;
hdisposen:
begin
secondpass(methodpointer);
{ destructor with extended syntax called from dispose }
{ hdisposen always deliver LOC_REFERENCE }
rg.getexplicitregisterint(exprasmlist,R_ESI);
emit_ref_reg(A_LEA,S_L,methodpointer.location.reference,R_ESI);
reference_release(exprasmlist,methodpointer.location.reference);
emit_reg(A_PUSH,S_L,R_ESI);
emit_sym(A_PUSH,S_L,
newasmsymbol(tobjectdef(methodpointer.resulttype.def).vmt_mangledname));
end;
else
begin
{ call to an instance member }
if (symtableproc.symtabletype<>withsymtable) then
begin
secondpass(methodpointer);
rg.getexplicitregisterint(exprasmlist,R_ESI);
case methodpointer.location.loc of
LOC_CREGISTER,
LOC_REGISTER:
begin
emit_reg_reg(A_MOV,S_L,methodpointer.location.register,R_ESI);
rg.ungetregisterint(exprasmlist,methodpointer.location.register);
end;
else
begin
if (methodpointer.resulttype.def.deftype=classrefdef) or
is_class_or_interface(methodpointer.resulttype.def) then
emit_ref_reg(A_MOV,S_L,methodpointer.location.reference,R_ESI)
else
emit_ref_reg(A_LEA,S_L,methodpointer.location.reference,R_ESI);
reference_release(exprasmlist,methodpointer.location.reference);
end;
end;
end;
{ when calling a class method, we have to load ESI with the VMT !
But, not for a class method via self }
if not(po_containsself in procdefinition.procoptions) then
begin
if (po_classmethod in procdefinition.procoptions) and
not(methodpointer.resulttype.def.deftype=classrefdef) then
begin
{ class method needs current VMT }
rg.getexplicitregisterint(exprasmlist,R_ESI);
reference_reset_base(href,R_ESI,tprocdef(procdefinition)._class.vmt_offset);
emit_ref_reg(A_MOV,S_L,href,R_ESI);
end;
{ direct call to destructor: remove data }
if (procdefinition.proctypeoption=potype_destructor) and
is_class(methodpointer.resulttype.def) then
emit_const(A_PUSH,S_L,1);
{ direct call to class constructor, don't allocate memory }
if (procdefinition.proctypeoption=potype_constructor) and
is_class(methodpointer.resulttype.def) then
begin
emit_const(A_PUSH,S_L,0);
emit_const(A_PUSH,S_L,0);
end
else
begin
{ constructor call via classreference => allocate memory }
if (procdefinition.proctypeoption=potype_constructor) and
(methodpointer.resulttype.def.deftype=classrefdef) and
is_class(tclassrefdef(methodpointer.resulttype.def).pointertype.def) then
emit_const(A_PUSH,S_L,1);
emit_reg(A_PUSH,S_L,R_ESI);
end;
end;
if is_con_or_destructor then
begin
{ classes don't get a VMT pointer pushed }
if is_object(methodpointer.resulttype.def) then
begin
if (procdefinition.proctypeoption=potype_constructor) then
begin
{ it's no bad idea, to insert the VMT }
emit_sym(A_PUSH,S_L,newasmsymbol(
tobjectdef(methodpointer.resulttype.def).vmt_mangledname));
end
{ destructors haven't to dispose the instance, if this is }
{ a direct call }
else
push_int(0);
end;
end;
end;
end;
end;
end
else
begin
if (po_classmethod in procdefinition.procoptions) and
not(
assigned(aktprocdef) and
(po_classmethod in aktprocdef.procoptions)
) then
begin
{ class method needs current VMT }
rg.getexplicitregisterint(exprasmlist,R_ESI);
reference_reset_base(href,R_ESI,tprocdef(procdefinition)._class.vmt_offset);
emit_ref_reg(A_MOV,S_L,href,R_ESI);
end
else
begin
{ member call, ESI isn't modified }
loadesi:=false;
end;
{ direct call to destructor: don't remove data! }
if is_class(procinfo^._class) then
begin
if (procdefinition.proctypeoption=potype_destructor) then
begin
emit_const(A_PUSH,S_L,0);
emit_reg(A_PUSH,S_L,R_ESI);
end
else if (procdefinition.proctypeoption=potype_constructor) then
begin
emit_const(A_PUSH,S_L,0);
emit_const(A_PUSH,S_L,0);
end
else
emit_reg(A_PUSH,S_L,R_ESI);
end
else if is_object(procinfo^._class) then
begin
emit_reg(A_PUSH,S_L,R_ESI);
if is_con_or_destructor then
begin
if (procdefinition.proctypeoption=potype_constructor) then
begin
{ it's no bad idea, to insert the VMT }
emit_sym(A_PUSH,S_L,newasmsymbol(
procinfo^._class.vmt_mangledname));
end
{ destructors haven't to dispose the instance, if this is }
{ a direct call }
else
push_int(0);
end;
end
else
Internalerror(200006165);
end;
end;
{ call to BeforeDestruction? }
if (procdefinition.proctypeoption=potype_destructor) and
assigned(methodpointer) and
(methodpointer.nodetype<>typen) and
is_class(tobjectdef(methodpointer.resulttype.def)) and
(inlined or
(right=nil)) then
begin
emit_reg(A_PUSH,S_L,R_ESI);
reference_reset_base(href,R_ESI,0);
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_ref_reg(A_MOV,S_L,href,R_EDI);
reference_reset_base(href,R_EDI,72);
emit_ref(A_CALL,S_NO,href);
rg.ungetregisterint(exprasmlist,R_EDI);
end;
{ push base pointer ?}
{ never when inlining, since if necessary, the base pointer }
{ can/will be gottten from the current procedure's symtable }
{ (JM) }
if not inlined then
if (lexlevel>=normal_function_level) and assigned(tprocdef(procdefinition).parast) and
((tprocdef(procdefinition).parast.symtablelevel)>normal_function_level) then
begin
{ if we call a nested function in a method, we must }
{ push also SELF! }
{ THAT'S NOT TRUE, we have to load ESI via frame pointer }
{ access }
{
begin
loadesi:=false;
emit_reg(A_PUSH,S_L,R_ESI);
end;
}
if lexlevel=(tprocdef(procdefinition).parast.symtablelevel) then
begin
reference_reset_base(href,procinfo^.framepointer,procinfo^.framepointer_offset);
emit_ref(A_PUSH,S_L,href)
end
{ this is only true if the difference is one !!
but it cannot be more !! }
else if (lexlevel=(tprocdef(procdefinition).parast.symtablelevel)-1) then
begin
emit_reg(A_PUSH,S_L,procinfo^.framepointer)
end
else if (lexlevel>(tprocdef(procdefinition).parast.symtablelevel)) then
begin
hregister:=rg.getregisterint(exprasmlist);
reference_reset_base(href,procinfo^.framepointer,procinfo^.framepointer_offset);
emit_ref_reg(A_MOV,S_L,href,hregister);
for i:=(tprocdef(procdefinition).parast.symtablelevel) to lexlevel-1 do
begin
{we should get the correct frame_pointer_offset at each level
how can we do this !!! }
reference_reset_base(href,hregister,procinfo^.framepointer_offset);
emit_ref_reg(A_MOV,S_L,href,hregister);
end;
emit_reg(A_PUSH,S_L,hregister);
rg.ungetregisterint(exprasmlist,hregister);
end
else
internalerror(25000);
end;
rg.saveregvars(exprasmlist,regs_to_push);
if (po_virtualmethod in procdefinition.procoptions) and
not(no_virtual_call) then
begin
{ static functions contain the vmt_address in ESI }
{ also class methods }
{ Here it is quite tricky because it also depends }
{ on the methodpointer PM }
rg.getexplicitregisterint(exprasmlist,R_ESI);
if assigned(aktprocdef) then
begin
if (((sp_static in aktprocdef.procsym.symoptions) or
(po_classmethod in aktprocdef.procoptions)) and
((methodpointer=nil) or (methodpointer.nodetype=typen)))
or
(po_staticmethod in procdefinition.procoptions) or
((procdefinition.proctypeoption=potype_constructor) and
{ esi contains the vmt if we call a constructor via a class ref }
assigned(methodpointer) and
(methodpointer.resulttype.def.deftype=classrefdef)
) or
{ is_interface(tprocdef(procdefinition)._class) or }
{ ESI is loaded earlier }
(po_classmethod in procdefinition.procoptions) then
begin
reference_reset_base(href,R_ESI,0);
end
else
begin
{ this is one point where we need vmt_offset (PM) }
reference_reset_base(href,R_ESI,tprocdef(procdefinition)._class.vmt_offset);
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_ref_reg(A_MOV,S_L,href,R_EDI);
reference_reset_base(href,R_EDI,0);
end;
end
else
{ aktprocdef should be assigned, also in main program }
internalerror(12345);
{
begin
new(r);
reset_reference(r^);
r^.base:=R_ESI;
emit_ref_reg(A_MOV,S_L,r,R_EDI);
new(r);
reset_reference(r^);
r^.base:=R_EDI;
end;
}
if tprocdef(procdefinition).extnumber=-1 then
internalerror(44584);
href.offset:=tprocdef(procdefinition)._class.vmtmethodoffset(tprocdef(procdefinition).extnumber);
if not(is_interface(tprocdef(procdefinition)._class)) and
not(is_cppclass(tprocdef(procdefinition)._class)) then
begin
if (cs_check_object_ext in aktlocalswitches) then
begin
emit_sym(A_PUSH,S_L,
newasmsymbol(tprocdef(procdefinition)._class.vmt_mangledname));
emit_reg(A_PUSH,S_L,href.base);
emitcall('FPC_CHECK_OBJECT_EXT');
end
else if (cs_check_range in aktlocalswitches) then
begin
emit_reg(A_PUSH,S_L,href.base);
emitcall('FPC_CHECK_OBJECT');
end;
end;
emit_ref(A_CALL,S_NO,href);
rg.ungetregisterint(exprasmlist,R_EDI);
end
else if not inlined then
begin
{ We can call interrupts from within the smae code
by just pushing the flags and CS PM }
if (po_interrupt in procdefinition.procoptions) then
begin
emit_none(A_PUSHF,S_L);
emit_reg(A_PUSH,S_L,R_CS);
end;
emitcall(tprocdef(procdefinition).mangledname);
end
else { inlined proc }
{ inlined code is in inlinecode }
begin
{ process the inlinecode }
secondpass(inlinecode);
{ free the args }
if tprocdef(procdefinition).parast.datasize>0 then
tg.ungetpersistanttemp(exprasmlist,tprocdef(procdefinition).parast.address_fixup);
end;
end
else
{ now procedure variable case }
begin
secondpass(right);
if (po_interrupt in procdefinition.procoptions) then
begin
emit_none(A_PUSHF,S_L);
emit_reg(A_PUSH,S_L,R_CS);
end;
{ procedure of object? }
if (po_methodpointer in procdefinition.procoptions) then
begin
{ method pointer can't be in a register }
hregister:=R_NO;
{ do some hacking if we call a method pointer }
{ which is a class member }
{ else ESI is overwritten ! }
if (right.location.reference.base=R_ESI) or
(right.location.reference.index=R_ESI) then
begin
reference_release(exprasmlist,right.location.reference);
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_ref_reg(A_MOV,S_L,right.location.reference,R_EDI);
hregister:=R_EDI;
end;
{ load self, but not if it's already explicitly pushed }
if not(po_containsself in procdefinition.procoptions) then
begin
{ load ESI }
inc(right.location.reference.offset,4);
rg.getexplicitregisterint(exprasmlist,R_ESI);
emit_ref_reg(A_MOV,S_L,right.location.reference,R_ESI);
dec(right.location.reference.offset,4);
{ push self pointer }
emit_reg(A_PUSH,S_L,R_ESI);
end;
rg.saveregvars(exprasmlist,ALL_REGISTERS);
if hregister=R_NO then
emit_ref(A_CALL,S_NO,right.location.reference)
else
begin
emit_reg(A_CALL,S_NO,hregister);
rg.ungetregisterint(exprasmlist,hregister);
end;
reference_release(exprasmlist,right.location.reference);
end
else
begin
rg.saveregvars(exprasmlist,ALL_REGISTERS);
case right.location.loc of
LOC_REGISTER,LOC_CREGISTER:
emit_reg(A_CALL,S_NO,right.location.register);
LOC_REFERENCE,LOC_CREFERENCE :
emit_ref(A_CALL,S_NO,right.location.reference);
else
internalerror(200203311);
end;
location_release(exprasmlist,right.location);
end;
end;
{ this was only for normal functions
displaced here so we also get
it to work for procvars PM }
if (not inlined) and (po_clearstack in procdefinition.procoptions) then
begin
{ we also add the pop_size which is included in pushedparasize }
pop_size:=0;
{ better than an add on all processors }
if pushedparasize=4 then
begin
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_reg(A_POP,S_L,R_EDI);
rg.ungetregisterint(exprasmlist,R_EDI);
end
{ the pentium has two pipes and pop reg is pairable }
{ but the registers must be different! }
else if (pushedparasize=8) and
not(cs_littlesize in aktglobalswitches) and
(aktoptprocessor=ClassP5) and
(procinfo^._class=nil) then
begin
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_reg(A_POP,S_L,R_EDI);
rg.ungetregisterint(exprasmlist,R_EDI);
exprasmList.concat(Tairegalloc.Alloc(R_ESI));
emit_reg(A_POP,S_L,R_ESI);
exprasmList.concat(Tairegalloc.DeAlloc(R_ESI));
end
else if pushedparasize<>0 then
emit_const_reg(A_ADD,S_L,pushedparasize,R_ESP);
end;
{$ifdef OPTALIGN}
if pop_esp then
emit_reg(A_POP,S_L,R_ESP);
{$endif OPTALIGN}
dont_call:
pushedparasize:=oldpushedparasize;
rg.restoreunusedstate(unusedstate);
{$ifdef TEMPREGDEBUG}
testregisters32;
{$endif TEMPREGDEBUG}
{ a constructor could be a function with boolean result }
{ if calling constructor called fail we
must jump directly to quickexitlabel PM
but only if it is a call of an inherited constructor }
if (inlined or
(right=nil)) and
(procdefinition.proctypeoption=potype_constructor) and
assigned(methodpointer) and
(methodpointer.nodetype=typen) and
(aktprocdef.proctypeoption=potype_constructor) then
begin
emitjmp(C_Z,faillabel);
end;
{ call to AfterConstruction? }
if is_class(resulttype.def) and
(inlined or
(right=nil)) and
(procdefinition.proctypeoption=potype_constructor) and
assigned(methodpointer) and
(methodpointer.nodetype<>typen) then
begin
getlabel(constructorfailed);
emitjmp(C_Z,constructorfailed);
emit_reg(A_PUSH,S_L,R_ESI);
reference_reset_base(href,R_ESI,0);
rg.getexplicitregisterint(exprasmlist,R_EDI);
emit_ref_reg(A_MOV,S_L,href,R_EDI);
reference_reset_base(href,R_EDI,68);
emit_ref(A_CALL,S_NO,href);
rg.ungetregisterint(exprasmlist,R_EDI);
exprasmList.concat(Tairegalloc.Alloc(R_EAX));
emitlab(constructorfailed);
emit_reg_reg(A_MOV,S_L,R_ESI,R_EAX);
end;
{ handle function results }
if (not is_void(resulttype.def)) then
begin
{ structured results are easy to handle.... }
{ needed also when result_no_used !! }
if ret_in_param(resulttype.def) then
begin
location_reset(location,LOC_CREFERENCE,def_cgsize(resulttype.def));
location.reference.symbol:=nil;
location.reference:=funcretref;
end
else
{ ansi/widestrings must be registered, so we can dispose them }
if is_ansistring(resulttype.def) or
is_widestring(resulttype.def) then
begin
location_reset(location,LOC_CREFERENCE,OS_ADDR);
location.reference:=refcountedtemp;
cg.a_reg_alloc(exprasmlist,accumulator);
cg.a_load_reg_ref(exprasmlist,OS_ADDR,accumulator,location.reference);
cg.a_reg_dealloc(exprasmlist,accumulator);
end
else
{ we have only to handle the result if it is used }
if (nf_return_value_used in flags) then
begin
case resulttype.def.deftype of
enumdef,
orddef :
begin
cgsize:=def_cgsize(resulttype.def);
{ an object constructor is a function with boolean result }
if (inlined or (right=nil)) and
(procdefinition.proctypeoption=potype_constructor) then
begin
if extended_new then
cgsize:=OS_INT
else
begin
cgsize:=OS_NO;
{ this fails if popsize > 0 PM }
location_reset(location,LOC_FLAGS,OS_NO);
location.resflags:=F_NE;
end;
end;
if cgsize<>OS_NO then
begin
location_reset(location,LOC_REGISTER,cgsize);
cg.a_reg_alloc(exprasmlist,accumulator);
if cgsize in [OS_64,OS_S64] then
begin
cg.a_reg_alloc(exprasmlist,accumulatorhigh);
if accumulatorhigh in rg.unusedregsint then
begin
location.registerhigh:=rg.getexplicitregisterint(exprasmlist,accumulatorhigh);
location.registerlow:=rg.getexplicitregisterint(exprasmlist,accumulator);
end
else
begin
location.registerhigh:=rg.getexplicitregisterint(exprasmlist,accumulatorhigh);
location.registerlow:=rg.getexplicitregisterint(exprasmlist,accumulator);
end;
tcg64f32(cg).a_load64_reg_reg(exprasmlist,accumulator,accumulatorhigh,
location.registerlow,location.registerhigh);
end
else
begin
location.register:=rg.getexplicitregisterint(exprasmlist,accumulator);
hregister:=changeregsize(accumulator,TCGSize2Opsize[cgsize]);
location.register:=changeregsize(location.register,TCGSize2Opsize[cgsize]);
cg.a_load_reg_reg(exprasmlist,cgsize,hregister,location.register);
end;
end;
end;
floatdef :
begin
location_reset(location,LOC_FPUREGISTER,def_cgsize(resulttype.def));
location.register:=R_ST;
inc(trgcpu(rg).fpuvaroffset);
end;
else
begin
location_reset(location,LOC_REGISTER,OS_INT);
location.register:=rg.getexplicitregisterint(exprasmlist,accumulator);
cg.a_load_reg_reg(exprasmlist,OS_INT,accumulator,location.register);
end;
end;
end;
end;
{ perhaps i/o check ? }
if iolabel<>nil then
begin
emit_sym(A_PUSH,S_L,iolabel);
emitcall('FPC_IOCHECK');
end;
if pop_size>0 then
emit_const_reg(A_ADD,S_L,pop_size,R_ESP);
{ restore registers }
rg.restoreusedregisters(exprasmlist,pushed);
{ at last, restore instance pointer (SELF) }
if loadesi then
maybe_loadself;
pp:=tbinarynode(params);
while assigned(pp) do
begin
if assigned(pp.left) then
begin
if (pp.left.location.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
tg.ungetiftemp(exprasmlist,pp.left.location.reference);
{ process also all nodes of an array of const }
if pp.left.nodetype=arrayconstructorn then
begin
if assigned(tarrayconstructornode(pp.left).left) then
begin
hp:=pp.left;
while assigned(hp) do
begin
if (tarrayconstructornode(tunarynode(hp).left).location.loc in [LOC_REFERENCE,LOC_CREFERENCE]) then
tg.ungetiftemp(exprasmlist,tarrayconstructornode(hp).left.location.reference);
hp:=tbinarynode(hp).right;
end;
end;
end;
end;
pp:=tbinarynode(pp.right);
end;
if inlined then
begin
tg.ungetpersistanttemp(exprasmlist,inlinecode.retoffset);
tprocdef(procdefinition).parast.address_fixup:=store_parast_fixup;
right:=inlinecode;
end;
if assigned(params) then
params.free;
{ from now on the result can be freed normally }
if inlined and ret_in_param(resulttype.def) then
tg.persistanttemptonormal(funcretref.offset);
{ if return value is not used }
if (not(nf_return_value_used in flags)) and (not is_void(resulttype.def)) then
begin
if location.loc in [LOC_CREFERENCE,LOC_REFERENCE] then
begin
{ data which must be finalized ? }
if (resulttype.def.needs_inittable) then
finalize(resulttype.def,location.reference,false);
{ release unused temp }
tg.ungetiftemp(exprasmlist,location.reference)
end
else if location.loc=LOC_FPUREGISTER then
begin
{ release FPU stack }
emit_reg(A_FSTP,S_NO,R_ST);
{
dec(trgcpu(rg).fpuvaroffset);
do NOT decrement as the increment before
is not called for unused results PM }
end;
end;
end;
{*****************************************************************************
TI386PROCINLINENODE
*****************************************************************************}
procedure ti386procinlinenode.pass_2;
var st : tsymtable;
oldprocdef : tprocdef;
ps, i : longint;
tmpreg: tregister;
oldprocinfo : pprocinfo;
oldinlining_procedure,
nostackframe,make_global : boolean;
inlineentrycode,inlineexitcode : TAAsmoutput;
oldexitlabel,oldexit2label,oldquickexitlabel:tasmlabel;
oldregstate: pointer;
{$ifdef GDB}
startlabel,endlabel : tasmlabel;
pp : pchar;
mangled_length : longint;
{$endif GDB}
begin
{ deallocate the registers used for the current procedure's regvars }
if assigned(aktprocdef.regvarinfo) then
begin
with pregvarinfo(aktprocdef.regvarinfo)^ do
for i := 1 to maxvarregs do
if assigned(regvars[i]) then
store_regvar(exprasmlist,regvars[i].reg);
rg.saveStateForInline(oldregstate);
{ make sure the register allocator knows what the regvars in the }
{ inlined code block are (JM) }
rg.resetusableregisters;
rg.clearregistercount;
rg.cleartempgen;
if assigned(inlineprocdef.regvarinfo) then
with pregvarinfo(inlineprocdef.regvarinfo)^ do
for i := 1 to maxvarregs do
if assigned(regvars[i]) then
begin
tmpreg:=changeregsize(regvars[i].reg,S_L);
rg.makeregvar(tmpreg);
end;
end;
oldinlining_procedure:=inlining_procedure;
oldexitlabel:=aktexitlabel;
oldexit2label:=aktexit2label;
oldquickexitlabel:=quickexitlabel;
getlabel(aktexitlabel);
getlabel(aktexit2label);
{ we're inlining a procedure }
inlining_procedure:=true;
{ save old procinfo }
oldprocdef:=aktprocdef;
getmem(oldprocinfo,sizeof(tprocinfo));
move(procinfo^,oldprocinfo^,sizeof(tprocinfo));
{ set new procinfo }
aktprocdef:=inlineprocdef;
procinfo^.return_offset:=retoffset;
procinfo^.para_offset:=para_offset;
procinfo^.no_fast_exit:=false;
{ arg space has been filled by the parent secondcall }
st:=aktprocdef.localst;
{ set it to the same lexical level }
st.symtablelevel:=oldprocdef.localst.symtablelevel;
if st.datasize>0 then
begin
st.address_fixup:=tg.gettempofsizepersistant(exprasmlist,st.datasize)+st.datasize;
{$ifdef extdebug}
Comment(V_debug,'local symtable is at offset '+tostr(st.address_fixup));
exprasmList.concat(Tai_asm_comment.Create(strpnew(
'local symtable is at offset '+tostr(st.address_fixup))));
{$endif extdebug}
end;
exprasmList.concat(Tai_Marker.Create(InlineStart));
{$ifdef extdebug}
exprasmList.concat(Tai_asm_comment.Create(strpnew('Start of inlined proc')));
{$endif extdebug}
{$ifdef GDB}
if (cs_debuginfo in aktmoduleswitches) then
begin
getaddrlabel(startlabel);
getaddrlabel(endlabel);
emitlab(startlabel);
inlineprocdef.localst.symtabletype:=inlinelocalsymtable;
inlineprocdef.parast.symtabletype:=inlineparasymtable;
{ Here we must include the para and local symtable info }
inlineprocdef.concatstabto(withdebuglist);
{ set it back for safety }
inlineprocdef.localst.symtabletype:=localsymtable;
inlineprocdef.parast.symtabletype:=parasymtable;
mangled_length:=length(oldprocdef.mangledname);
getmem(pp,mangled_length+50);
strpcopy(pp,'192,0,0,'+startlabel.name);
if (target_info.use_function_relative_addresses) then
begin
strpcopy(strend(pp),'-');
strpcopy(strend(pp),oldprocdef.mangledname);
end;
withdebugList.concat(Tai_stabn.Create(strnew(pp)));
end;
{$endif GDB}
{ takes care of local data initialization }
inlineentrycode:=TAAsmoutput.Create;
inlineexitcode:=TAAsmoutput.Create;
ps:=para_size;
make_global:=false; { to avoid warning }
genentrycode(inlineentrycode,make_global,0,ps,nostackframe,true);
if po_assembler in aktprocdef.procoptions then
inlineentrycode.insert(Tai_marker.Create(asmblockstart));
exprasmList.concatlist(inlineentrycode);
secondpass(inlinetree);
genexitcode(inlineexitcode,0,false,true);
if po_assembler in aktprocdef.procoptions then
inlineexitcode.concat(Tai_marker.Create(asmblockend));
exprasmList.concatlist(inlineexitcode);
inlineentrycode.free;
inlineexitcode.free;
{$ifdef extdebug}
exprasmList.concat(Tai_asm_comment.Create(strpnew('End of inlined proc')));
{$endif extdebug}
exprasmList.concat(Tai_Marker.Create(InlineEnd));
{we can free the local data now, reset also the fixup address }
if st.datasize>0 then
begin
tg.ungetpersistanttemp(exprasmlist,st.address_fixup-st.datasize);
st.address_fixup:=0;
end;
{ restore procinfo }
move(oldprocinfo^,procinfo^,sizeof(tprocinfo));
freemem(oldprocinfo,sizeof(tprocinfo));
{$ifdef GDB}
if (cs_debuginfo in aktmoduleswitches) then
begin
emitlab(endlabel);
strpcopy(pp,'224,0,0,'+endlabel.name);
if (target_info.use_function_relative_addresses) then
begin
strpcopy(strend(pp),'-');
strpcopy(strend(pp),oldprocdef.mangledname);
end;
withdebugList.concat(Tai_stabn.Create(strnew(pp)));
freemem(pp,mangled_length+50);
end;
{$endif GDB}
{ restore }
aktprocdef:=oldprocdef;
aktexitlabel:=oldexitlabel;
aktexit2label:=oldexit2label;
quickexitlabel:=oldquickexitlabel;
inlining_procedure:=oldinlining_procedure;
{ reallocate the registers used for the current procedure's regvars, }
{ since they may have been used and then deallocated in the inlined }
{ procedure (JM) }
if assigned(aktprocdef.regvarinfo) then
begin
rg.restoreStateAfterInline(oldregstate);
end;
end;
begin
ccallparanode:=ti386callparanode;
ccallnode:=ti386callnode;
cprocinlinenode:=ti386procinlinenode;
end.
{
$Log$
Revision 1.45 2002-04-15 19:44:21 peter
* fixed stackcheck that would be called recursively when a stack
error was found
* generic changeregsize(reg,size) for i386 register resizing
* removed some more routines from cga unit
* fixed returnvalue handling
* fixed default stacksize of linux and go32v2, 8kb was a bit small :-)
Revision 1.44 2002/04/04 19:06:10 peter
* removed unused units
* use tlocation.size in cg.a_*loc*() routines
Revision 1.43 2002/04/02 17:11:35 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.42 2002/03/31 20:26:38 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.41 2002/03/04 19:10:13 peter
* removed compiler warnings
Revision 1.40 2001/12/31 09:53:15 jonas
* changed remaining "getregister32" calls to "getregisterint"
Revision 1.39 2001/12/29 15:32:13 jonas
* powerpc/cgcpu.pas compiles :)
* several powerpc-related fixes
* cpuasm unit is now based on common tainst unit
+ nppcmat unit for powerpc (almost complete)
Revision 1.38 2001/11/18 00:00:34 florian
* handling of ansi- and widestring results improved
Revision 1.37 2001/11/02 23:24:40 peter
* fixed crash with inlining after aktprocdef change
Revision 1.36 2001/11/02 22:58:09 peter
* procsym definition rewrite
Revision 1.35 2001/10/25 21:22:41 peter
* calling convention rewrite
Revision 1.34 2001/10/21 12:33:07 peter
* array access for properties added
Revision 1.33 2001/09/09 08:50:15 jonas
* when calling an inline procedure inside a nested procedure, the
framepointer was being pushed on the stack, but this pushed framepointer
was never used nor removed from the stack again after the inlining was
done. It's now simply not pushed anymore, because the inlined procedure
can get the previous framepointer from the procedure in which it is being
inlined (merged)
Revision 1.32 2001/09/01 23:02:30 jonas
* i386*: call and jmp read their first operand
* cgcal: deallocate hlper register only after call statement (fixes bug
with "procedure of object" and optimizer reported to bugrep on
2001/08/30) ('merged')
Revision 1.31 2001/08/29 12:18:08 jonas
+ new createinternres() constructor for tcallnode to support setting a
custom resulttype
* compilerproc typeconversions now set the resulttype from the type
conversion for the generated call node, because the resulttype of
of the compilerproc helper isn't always exact (e.g. the ones that
return shortstrings, actually return a shortstring[x], where x is
specified by the typeconversion node)
* ti386callnode.pass_2 now always uses resulttype instead of
procsym.definition.rettype (so the custom resulttype, if any, is
always used). Note that this "rettype" stuff is only for use with
compilerprocs.
Revision 1.30 2001/08/26 13:36:56 florian
* some cg reorganisation
* some PPC updates
Revision 1.29 2001/08/19 21:11:21 florian
* some bugs fix:
- overload; with external procedures fixed
- better selection of routine to do an overloaded
type case
- ... some more
Revision 1.28 2001/08/06 21:40:50 peter
* funcret moved from tprocinfo to tprocdef
Revision 1.27 2001/07/08 21:00:16 peter
* various widestring updates, it works now mostly without charset
mapping supported
Revision 1.26 2001/07/01 20:16:20 peter
* alignmentinfo record added
* -Oa argument supports more alignment settings that can be specified
per type: PROC,LOOP,VARMIN,VARMAX,CONSTMIN,CONSTMAX,RECORDMIN
RECORDMAX,LOCALMIN,LOCALMAX. It is possible to set the mimimum
required alignment and the maximum usefull alignment. The final
alignment will be choosen per variable size dependent on these
settings
Revision 1.25 2001/06/04 11:48:02 peter
* better const to var checking
Revision 1.24 2001/05/19 21:22:53 peter
* function returning int64 inlining fixed
Revision 1.23 2001/05/16 15:11:42 jonas
* added missign begin..end pair (noticed by Carl)
Revision 1.22 2001/04/18 22:02:01 peter
* registration of targets and assemblers
Revision 1.21 2001/04/13 01:22:18 peter
* symtable change to classes
* range check generation and errors fixed, make cycle DEBUG=1 works
* memory leaks fixed
Revision 1.20 2001/04/02 21:20:36 peter
* resulttype rewrite
Revision 1.19 2001/03/11 22:58:51 peter
* getsym redesign, removed the globals srsym,srsymtable
Revision 1.18 2001/01/27 21:29:35 florian
* behavior -Oa optimized
Revision 1.17 2001/01/08 21:46:46 peter
* don't push high value for open array with cdecl;external;
Revision 1.16 2000/12/25 00:07:32 peter
+ new tlinkedlist class (merge of old tstringqueue,tcontainer and
tlinkedlist objects)
Revision 1.15 2000/12/09 10:45:40 florian
* AfterConstructor isn't called anymore when a constructor failed
Revision 1.14 2000/12/07 17:19:46 jonas
* new constant handling: from now on, hex constants >$7fffffff are
parsed as unsigned constants (otherwise, $80000000 got sign extended
and became $ffffffff80000000), all constants in the longint range
become longints, all constants >$7fffffff and <=cardinal($ffffffff)
are cardinals and the rest are int64's.
* added lots of longint typecast to prevent range check errors in the
compiler and rtl
* type casts of symbolic ordinal constants are now preserved
* fixed bug where the original resulttype.def wasn't restored correctly
after doing a 64bit rangecheck
Revision 1.13 2000/12/05 11:44:33 jonas
+ new integer regvar handling, should be much more efficient
Revision 1.12 2000/12/03 22:26:54 florian
* fixed web buzg 1275: problem with int64 functions results
Revision 1.11 2000/11/29 00:30:46 florian
* unused units removed from uses clause
* some changes for widestrings
Revision 1.10 2000/11/23 13:26:34 jonas
* fix for webbug 1066/1126
Revision 1.9 2000/11/22 15:12:06 jonas
* fixed inline-related problems (partially "merges")
Revision 1.8 2000/11/17 09:54:58 florian
* INT_CHECK_OBJECT_* isn't applied to interfaces anymore
Revision 1.7 2000/11/12 23:24:14 florian
* interfaces are basically running
Revision 1.6 2000/11/07 23:40:49 florian
+ AfterConstruction and BeforeDestruction impemented
Revision 1.5 2000/11/06 23:15:01 peter
* added copyvaluepara call again
Revision 1.4 2000/11/04 14:25:23 florian
+ merged Attila's changes for interfaces, not tested yet
Revision 1.3 2000/11/04 13:12:14 jonas
* check for nil pointers before calling getcopy
Revision 1.2 2000/10/31 22:02:56 peter
* symtable splitted, no real code changes
Revision 1.1 2000/10/15 09:33:31 peter
* moved n386*.pas to i386/ cpu_target dir
Revision 1.2 2000/10/14 10:14:48 peter
* moehrendorf oct 2000 rewrite
Revision 1.1 2000/10/10 17:31:56 florian
* initial revision
}