+ support for nested procedures for the JVM target

o since the JVM target has no stack/framepointer that can be passed
     on to nested routines, all local variables and parameters accessed
     from nested routines are grouped into a local record whose address
     is passed to nested routines. The same technique is also required
     for LLVM in the future

git-svn-id: branches/jvmbackend@18588 -
This commit is contained in:
Jonas Maebe 2011-08-20 08:12:59 +00:00
parent 874095a1c0
commit 8fa42c206f
14 changed files with 607 additions and 55 deletions

2
.gitattributes vendored
View File

@ -348,6 +348,8 @@ compiler/ncginl.pas svneol=native#text/plain
compiler/ncgld.pas svneol=native#text/plain
compiler/ncgmat.pas svneol=native#text/plain
compiler/ncgmem.pas svneol=native#text/plain
compiler/ncgnstld.pas svneol=native#text/plain
compiler/ncgnstmm.pas svneol=native#text/plain
compiler/ncgobjc.pas svneol=native#text/plain
compiler/ncgopt.pas svneol=native#text/plain
compiler/ncgrtti.pas svneol=native#text/plain

View File

@ -29,10 +29,10 @@ uses
globtype,
symtype,
cgutils,
node, ncgld;
node, ncgld, ncgnstld;
type
tjvmloadnode = class(tcgloadnode)
tjvmloadnode = class(tcgnestloadnode)
function is_addr_param_load: boolean; override;
procedure pass_generate_code; override;
end;

View File

@ -28,7 +28,7 @@ interface
uses
globtype,
cgbase,cpubase,
node,nmem,ncgmem;
node,nmem,ncgmem,ncgnstmm;
type
tjvmaddrnode = class(tcgaddrnode)
@ -45,10 +45,6 @@ interface
procedure pass_generate_code; override;
end;
tjvmloadparentfpnode = class(tcgloadparentfpnode)
procedure pass_generate_code;override;
end;
tjvmvecnode = class(tcgvecnode)
function pass_1: tnode; override;
procedure pass_generate_code;override;
@ -121,7 +117,8 @@ implementation
end
else
begin
if not jvmimplicitpointertype(left.resultdef) then
if not(nf_internal in flags) and
not jvmimplicitpointertype(left.resultdef) then
begin
CGMessage(parser_e_illegal_expression);
exit
@ -134,7 +131,8 @@ implementation
procedure tjvmaddrnode.pass_generate_code;
begin
secondpass(left);
if jvmimplicitpointertype(left.resultdef) then
if jvmimplicitpointertype(left.resultdef) or
(nf_internal in flags) then
begin
{ this is basically a typecast: the left node is an implicit
pointer, and we typecast it to a regular 'pointer'
@ -143,6 +141,7 @@ implementation
end
else
begin
{ procvar }
{$ifndef nounsupported}
location_reset(location,LOC_REGISTER,OS_ADDR);
location.register:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlobject);
@ -168,19 +167,6 @@ implementation
end;
{ tjvmloadparentfpnode }
procedure tjvmloadparentfpnode.pass_generate_code;
begin
{$ifndef nounsupported}
location_reset(location,LOC_REGISTER,OS_ADDR);
location.register:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlobject);
hlcg.a_load_const_reg(current_asmdata.CurrAsmList,java_jlobject,0,location.register);
{$else}
internalerror(2011041301);
{$endif}
end;
{*****************************************************************************
TJVMVECNODE
*****************************************************************************}
@ -298,6 +284,5 @@ begin
cderefnode:=tjvmderefnode;
caddrnode:=tjvmaddrnode;
cvecnode:=tjvmvecnode;
cloadparentfpnode:=tjvmloadparentfpnode;
cloadvmtaddrnode:=tjvmloadvmtaddrnode;
end.

View File

@ -241,7 +241,11 @@ implementation
else if jvmimplicitpointertype(tpointerdef(def).pointeddef) then
result:=jvmaddencodedtype(tpointerdef(def).pointeddef,false,encodedstr,forcesignature,founderror)
else
result:=false;
begin
{ used for internal pointer constructs }
encodedstr:=encodedstr+'[';
result:=jvmaddencodedtype(tpointerdef(def).pointeddef,false,encodedstr,forcesignature,founderror);
end;
end;
floatdef :
begin

View File

@ -33,6 +33,9 @@ interface
type
tcgloadnode = class(tloadnode)
protected
procedure generate_nested_access(vs: tsym);virtual;
public
procedure pass_generate_code;override;
procedure generate_picvaraccess;virtual;
procedure changereflocation(const ref: treference);
@ -235,6 +238,20 @@ implementation
end;
procedure tcgloadnode.generate_nested_access(vs: tsym);
var
{ paramter declared as tsym to reduce interface unit dependencies }
lvs: tabstractnormalvarsym absolute vs;
begin
secondpass(left);
if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
internalerror(200309286);
if lvs.localloc.loc<>LOC_REFERENCE then
internalerror(200409241);
reference_reset_base(location.reference,left.location.register,lvs.localloc.reference.offset,lvs.localloc.reference.alignment);
end;
procedure tcgloadnode.pass_generate_code;
var
hregister : tregister;
@ -412,19 +429,10 @@ implementation
localvarsym :
begin
vs:=tabstractnormalvarsym(symtableentry);
{$if not defined(jvm) or defined(nounsupported)}
{ Nested variable }
if assigned(left) then
begin
secondpass(left);
if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
internalerror(200309286);
if vs.localloc.loc<>LOC_REFERENCE then
internalerror(200409241);
reference_reset_base(location.reference,left.location.register,vs.localloc.reference.offset,vs.localloc.reference.alignment);
end
generate_nested_access(vs)
else
{$endif}
location:=vs.localloc;
{ handle call by reference variables when they are not

View File

@ -151,14 +151,6 @@ implementation
hsym : tparavarsym;
href : treference;
begin
{$ifdef jvm}
{$ifndef nounsupported}
location_reset(location,LOC_REGISTER,OS_ADDR);
location.register:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlobject);
hlcg.a_load_const_reg(current_asmdata.CurrAsmList,java_jlobject,0,location.register);
exit;
{$endif nounsupported}
{$endif jvm}
if (current_procinfo.procdef.parast.symtablelevel=parentpd.parast.symtablelevel) then
begin
location_reset(location,LOC_REGISTER,OS_ADDR);

197
compiler/ncgnstld.pas Normal file
View File

@ -0,0 +1,197 @@
{
Copyright (c) 2011 by Jonas Maebe
Support for load nodes on targets that have to group all local variables
and parameters accessed by nested routines into structs (and then pass the
address of these structs to nested routines rather than the frame pointer,
and access the local variables as fields thereof)
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 ncgnstld;
{$i fpcdefs.inc}
interface
uses
node,
symtype,
nld,
ncgld;
type
tcgnestloadnode = class(tcgloadnode)
protected
nestsym: tsym;
procedure generate_nested_access(vs: tsym);override;
public
function pass_typecheck: tnode; override;
function pass_1:tnode;override;
function dogetcopy: tnode; override;
function docompare(p: tnode): boolean; override;
end;
implementation
uses
cutils,verbose,globtype,globals,systems,constexp,
symnot,
defutil,defcmp,
htypechk,pass_1,procinfo,paramgr,
cpuinfo,
symconst,symbase,symsym,symdef,symtable,symcreat,
ncon,ninl,ncnv,nmem,ncal,nutils,nbas,
pass_2,cgbase
;
{*****************************************************************************
TCGNESTLOADNODE
*****************************************************************************}
procedure tcgnestloadnode.generate_nested_access(vs: tsym);
begin
{ left has been transformed into a string of accesses that result in
the location of the original variable's copy in the appropriate
parentfpstruct (via tcgnestloadparentfpnode.pass_1). In case it is a
var/out/constref parameter, that "copy" will have been a copy of the
address so the normal handling of such parameters in ncgld is ok) }
secondpass(left);
location:=left.location;
end;
function tcgnestloadnode.pass_typecheck: tnode;
var
nestedvars: tsym;
begin
result:=inherited pass_typecheck;
if assigned(result) then
exit;
case symtableentry.typ of
paravarsym,
localvarsym :
begin
{ Nested variable? Then we have to move it to a structure that
can be passed by reference to nested routines }
if assigned(current_procinfo) and
(symtable.symtabletype in [localsymtable,parasymtable]) and
((symtable.symtablelevel<>current_procinfo.procdef.parast.symtablelevel) or
{ also redirect loads of locals/paras that have been moved to
the parentfpstruct inside the routine in which they were
originally declared, except in the initialisation code for
the parentfpstruct (nf_internal flag) }
(tabstractnormalvarsym(symtableentry).inparentfpstruct and
not(nf_internal in flags))) then
begin
{ get struct holding all locals accessed by nested routines }
nestedvars:=tprocdef(symtable.defowner).parentfpstruct;
{ don't add the parentfpstruct to itself! }
if nestedvars=symtableentry then
exit;
if not assigned(nestedvars) then
begin
{ create this struct }
build_parentfpstruct(tprocdef(symtable.defowner));
nestedvars:=tprocdef(symtable.defowner).parentfpstruct;
end;
{ store result for use in pass_1 }
nestsym:=maybe_add_sym_to_parentfpstruct(tprocdef(symtableentry.owner.defowner),symtableentry,resultdef,is_addr_param_load);
{ left normally holds the parentfp node. If it's not assigned,
this is an access to a local variable/para from the routine
in which it was actually declared -> redirect to its
equivalent in the parentfp struct }
if not assigned(left) then
begin
left:=caddrnode.create_internal(cloadnode.create(tprocdef(symtableentry.owner.defowner).parentfpstruct,tprocdef(symtableentry.owner.defowner).parentfpstruct.owner));
include(left.flags,nf_typedaddr);
end;
typecheckpass(left);
end;
end;
end;
end;
function tcgnestloadnode.pass_1:tnode;
var
thissym,
nestedvars: tsym;
nestedvarsdef: tdef;
begin
result:=inherited;
if assigned(result) then
exit;
case symtableentry.typ of
paravarsym,
localvarsym :
begin
{ Nested variable? Then we have to move it to a structure that
can be passed by reference to nested routines }
if assigned(current_procinfo) and
(symtable.symtabletype in [localsymtable,parasymtable]) and
((symtable.symtablelevel<>current_procinfo.procdef.parast.symtablelevel) or
(tabstractnormalvarsym(symtableentry).inparentfpstruct and
not(nf_internal in flags))) then
begin
{ get struct holding all locals accessed by nested routines }
nestedvars:=tprocdef(symtable.defowner).parentfpstruct;
if not assigned(nestedvars) then
begin
{ create this struct }
build_parentfpstruct(tprocdef(symtable.defowner));
nestedvars:=tprocdef(symtable.defowner).parentfpstruct;
end;
nestedvarsdef:=tlocalvarsym(nestedvars).vardef;
if nestedvars<>symtableentry then
thissym:=nestsym
else
thissym:=find_sym_in_parentfpstruct(tprocdef(symtableentry.owner.defowner),symtableentry);
if not assigned(thissym) then
internalerror(2011060406);
{ firstpass the parentfpnode. This will transform it into
a load of the appropriate parentfpstruct }
if not assigned(left) then
internalerror(2011060104);
firstpass(left);
{ subscript it to get the variable }
left:=csubscriptnode.create(thissym,cderefnode.create(left));
firstpass(left);
end;
end;
end;
end;
function tcgnestloadnode.dogetcopy: tnode;
begin
result:=inherited dogetcopy;
tcgnestloadnode(result).nestsym:=nestsym;
end;
function tcgnestloadnode.docompare(p: tnode): boolean;
begin
result:=
inherited docompare(p) and
(tcgnestloadnode(p).nestsym=nestsym);
end;
begin
cloadnode:=tcgnestloadnode;
end.

130
compiler/ncgnstmm.pas Normal file
View File

@ -0,0 +1,130 @@
{
Copyright (c) 1998-2002 by Florian Klaempfl
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 ncgnstmm;
{$i fpcdefs.inc}
interface
uses
globtype,cgbase,cpuinfo,cpubase,
node,ncgmem;
type
tcgnestloadparentfpnode = class(tcgloadparentfpnode)
function pass_typecheck: tnode; override;
function pass_1: tnode; override;
procedure pass_generate_code;override;
end;
implementation
uses
systems,
cutils,cclasses,verbose,globals,constexp,
symconst,symdef,symsym,symtable,symcreat,defutil,paramgr,
aasmbase,aasmtai,aasmdata,
procinfo,pass_2,parabase,
pass_1,ncnv,nmem,nld,ncon,nadd,nutils,
cgutils,cgobj,hlcgobj,
tgobj,ncgutil,objcgutl
;
{*****************************************************************************
TCGLOADPARENTFPNODE
*****************************************************************************}
function tcgnestloadparentfpnode.pass_typecheck: tnode;
var
hsym : tparavarsym;
currpi,
nextpi : tprocinfo;
begin
result:=inherited;
if assigned(result) then
exit;
currpi:=current_procinfo.parent;
while (currpi.procdef.parast.symtablelevel>=parentpd.parast.symtablelevel) do
begin
if not assigned(currpi.procdef.parentfpstruct) then
build_parentfpstruct(currpi.procdef);
currpi:=currpi.parent;
end;
{ mark all parent parentfp parameters for inclusion in the struct that
holds all locals accessed from nested routines }
currpi:=current_procinfo.parent;
nextpi:=currpi.parent;
while (currpi.procdef.parast.symtablelevel>parentpd.parast.symtablelevel) do
begin
hsym:=tparavarsym(currpi.procdef.parast.Find('parentfp'));
maybe_add_sym_to_parentfpstruct(currpi.procdef,hsym,nextpi.procdef.parentfpstructptrtype,false);
currpi:=nextpi;
nextpi:=nextpi.parent;
end;
end;
function tcgnestloadparentfpnode.pass_1: tnode;
var
fsym : tfieldvarsym;
hsym : tparavarsym;
currpi,
nextpi : tprocinfo;
begin
result:=nil;
if not assigned(current_procinfo.procdef.parentfpstruct) then
begin
hsym:=tparavarsym(current_procinfo.procdef.parast.Find('parentfp'));
result:=cloadnode.create(hsym,hsym.owner);
end
else
begin
result:=caddrnode.create_internal(cloadnode.create(current_procinfo.procdef.parentfpstruct,current_procinfo.procdef.parentfpstruct.owner));
include(result.flags,nf_typedaddr);
end;
{ mark all parent parentfp parameters for inclusion in the struct that
holds all locals accessed from nested routines }
currpi:=current_procinfo.parent;
nextpi:=currpi.parent;
while (currpi.procdef.parast.symtablelevel>parentpd.parast.symtablelevel) do
begin
hsym:=tparavarsym(currpi.procdef.parast.Find('parentfp'));
fsym:=tfieldvarsym(find_sym_in_parentfpstruct(currpi.procdef,hsym));
if not assigned(fsym) then
internalerror(2011060405);
result:=csubscriptnode.create(fsym,cderefnode.create(result));
currpi:=nextpi;
nextpi:=nextpi.parent;
end;
end;
procedure tcgnestloadparentfpnode.pass_generate_code;
begin
{ should be handled in pass 1 }
internalerror(2011060202);
end;
begin
cloadparentfpnode:=tcgnestloadparentfpnode;
end.

View File

@ -87,7 +87,7 @@ implementation
systems,fpccrc,
cpuinfo,
{ symtable }
symbase,symtable,defutil,defcmp,paramgr,cpupara,
symbase,symtable,symcreat,defutil,defcmp,paramgr,cpupara,
{ pass 1 }
fmodule,node,htypechk,
nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw,
@ -229,12 +229,21 @@ implementation
{$endif i386}
else
paranr:=paranr_parentfp_delphi_cc;
{ Generate result variable accessing function result, it
can't be put in a register since it must be accessable
from the framepointer }
vs:=tparavarsym.create('$parentfp',paranr,vs_value
,voidpointertype,[vo_is_parentfp,vo_is_hidden_para]);
vs.varregable:=vr_none;
{ Generate frame pointer. It can't be put in a register since it
must be accessable from nested routines }
if not(target_info.system in systems_fpnestedstruct) then
begin
vs:=tparavarsym.create('$parentfp',paranr,vs_value
,voidpointertype,[vo_is_parentfp,vo_is_hidden_para]);
vs.varregable:=vr_none;
end
else
begin
if not assigned(tprocdef(pd.owner.defowner).parentfpstruct) then
build_parentfpstruct(tprocdef(pd.owner.defowner));
vs:=tparavarsym.create('$parentfp',paranr,vs_value
,tprocdef(pd.owner.defowner).parentfpstructptrtype,[vo_is_parentfp,vo_is_hidden_para]);
end;
pd.parast.insert(vs);
current_tokenpos:=storepos;

View File

@ -245,7 +245,9 @@ implementation
end
else
begin
{ parse routine body }
block:=statement_block(_BEGIN);
{ initialized variables }
if current_procinfo.procdef.localst.symtabletype=localsymtable then
begin
{ initialization of local variables with their initial
@ -255,6 +257,17 @@ implementation
current_procinfo.procdef.localst.SymList.ForEachCall(@initializevars,block);
current_filepos:=oldfilepos;
end;
if assigned(current_procinfo.procdef.parentfpstruct) then
begin
{ we only do this after the code has been parsed because
otherwise for-loop counters moved to the struct cause
errors; we still do it nevertheless to prevent false
"unused" symbols warnings and to assist debug info
generation }
redirect_parentfpstruct_local_syms(current_procinfo.procdef);
{ finish the parentfpstruct (add padding, ...) }
finish_parentfpstruct(current_procinfo.procdef);
end;
end;
block:=cnodeutils.wrap_proc_body(current_procinfo.procdef,block);
end;
@ -1486,6 +1499,14 @@ implementation
{ Finish type checking pass }
do_typecheckpass(code);
if assigned(procdef.parentfpinitblock) then
begin
tblocknode(code).left:=cstatementnode.create(procdef.parentfpinitblock,tblocknode(code).left);
do_typecheckpass(tblocknode(code).left);
procdef.parentfpinitblock:=nil;
end;
end;
{ Check for unused labels, forwards, symbols for procedures. Static

View File

@ -28,7 +28,7 @@ interface
uses
finput,tokens,scanner,
symconst,symdef,symbase;
symconst,symbase,symtype,symdef;
type
@ -70,14 +70,40 @@ interface
procedure finish_copied_procdef(var pd: tprocdef; const realname: string; newparentst: tsymtable; newstruct: tabstractrecorddef);
{ create "parent frame pointer" record skeleton for procdef, in which local
variables and parameters from pd accessed from nested routines can be
stored }
procedure build_parentfpstruct(pd: tprocdef);
{ checks whether sym (a local or para of pd) already has a counterpart in
pd's parentfpstruct, and if not adds a new field to the struct with type
"vardef" (can be different from sym's type in case it's a call-by-reference
parameter, which is indicated by addrparam). If it already has a field in
the parentfpstruct, this field is returned. }
function maybe_add_sym_to_parentfpstruct(pd: tprocdef; sym: tsym; vardef: tdef; addrparam: boolean): tsym;
{ given a localvarsym or paravarsym of pd, returns the field of the
parentfpstruct corresponding to this sym }
function find_sym_in_parentfpstruct(pd: tprocdef; sym: tsym): tsym;
{ replaces all local and paravarsyms that have been mirrored in the
parentfpstruct with aliasvarsyms that redirect to these fields (used to
make sure that references to these syms in the owning procdef itself also
use the ones in the parentfpstructs) }
procedure redirect_parentfpstruct_local_syms(pd: tprocdef);
{ finalises the parentfpstruct (alignment padding, ...) }
procedure finish_parentfpstruct(pd: tprocdef);
implementation
uses
cutils,globtype,globals,verbose,systems,comphook,
symtype,symsym,symtable,defutil,
cutils,globtype,globals,verbose,systems,comphook,fmodule,
symsym,symtable,defutil,
pbase,pdecobj,pdecsub,psub,
defcmp;
node,nbas,nld,nmem,
defcmp,
paramgr
{$ifdef jvm}
,pjvm
{$endif};
procedure replace_scanner(const tempname: string; out sstate: tscannerstate);
var
@ -366,7 +392,12 @@ implementation
for i:=0 to st.deflist.count-1 do
begin
def:=tdef(st.deflist[i]);
if (is_javaclass(def) and
if (def.typ=procdef) and
assigned(tprocdef(def).localst) and
{ not true for the "main" procedure, whose localsymtable is the staticsymtable }
(tprocdef(def).localst.symtabletype=localsymtable) then
add_synthetic_method_implementations(tprocdef(def).localst)
else if (is_javaclass(def) and
not(oo_is_external in tobjectdef(def).objectoptions)) or
(def.typ=recorddef) then
begin
@ -386,7 +417,6 @@ implementation
sym: tsym;
parasym: tparavarsym;
ps: tprocsym;
hdef: tdef;
stname: string;
i: longint;
begin
@ -433,5 +463,158 @@ implementation
end;
procedure build_parentfpstruct(pd: tprocdef);
var
nestedvars: tsym;
nestedvarsst: tsymtable;
pnestedvarsdef,
nestedvarsdef: tdef;
old_symtablestack: tsymtablestack;
begin
{ make sure the defs are not registered in the current symtablestack,
because they may be for a parent procdef (changeowner does remove a def
from the symtable in which it was originally created, so that by itself
is not enough) }
old_symtablestack:=symtablestack;
symtablestack:=old_symtablestack.getcopyuntil(current_module.localsymtable);
{ create struct to hold local variables and parameters that are
accessed from within nested routines }
nestedvarsst:=trecordsymtable.create(current_module.realmodulename^+'$$_fpc_nestedvars$'+tostr(pd.procsym.symid),current_settings.alignment.localalignmax);
nestedvarsdef:=trecorddef.create(nestedvarsst.name^,nestedvarsst);
{$ifdef jvm}
jvm_guarantee_record_typesym(nestedvarsdef,nestedvarsdef.owner);
{ don't add clone/FpcDeepCopy, because the field names are not all
representable in source form and we don't need them anyway }
symtablestack.push(trecorddef(nestedvarsdef).symtable);
maybe_add_public_default_java_constructor(trecorddef(nestedvarsdef));
symtablestack.pop(trecorddef(nestedvarsdef).symtable);
{$endif}
symtablestack.free;
symtablestack:=old_symtablestack.getcopyuntil(pd.localst);
pnestedvarsdef:=tpointerdef.create(nestedvarsdef);
nestedvars:=tlocalvarsym.create('$nestedvars',vs_var,nestedvarsdef,[]);
pd.localst.insert(nestedvars);
pd.parentfpstruct:=nestedvars;
pd.parentfpstructptrtype:=pnestedvarsdef;
pd.parentfpinitblock:=cblocknode.create(nil);
symtablestack.free;
symtablestack:=old_symtablestack;
end;
function maybe_add_sym_to_parentfpstruct(pd: tprocdef; sym: tsym; vardef: tdef; addrparam: boolean): tsym;
var
fieldvardef,
nestedvarsdef: tdef;
nestedvarsst: tsymtable;
initcode: tnode;
old_filepos: tfileposinfo;
begin
nestedvarsdef:=tlocalvarsym(pd.parentfpstruct).vardef;
result:=search_struct_member(trecorddef(nestedvarsdef),sym.name);
if not assigned(result) then
begin
{ mark that this symbol is mirrored in the parentfpstruct }
tabstractnormalvarsym(sym).inparentfpstruct:=true;
{ add field to the struct holding all locals accessed
by nested routines }
nestedvarsst:=trecorddef(nestedvarsdef).symtable;
{ indicate whether or not this is a var/out/constref/... parameter }
if addrparam then
fieldvardef:=tpointerdef.create(vardef)
else
fieldvardef:=vardef;
result:=tfieldvarsym.create(sym.realname,vs_value,fieldvardef,[]);
if nestedvarsst.symlist.count=0 then
include(tfieldvarsym(result).varoptions,vo_is_first_field);
nestedvarsst.insert(result);
trecordsymtable(nestedvarsst).addfield(tfieldvarsym(result),vis_public);
{ add initialization with original value if it's a parameter }
if (sym.typ=paravarsym) then
begin
old_filepos:=current_filepos;
fillchar(current_filepos,sizeof(current_filepos),0);
initcode:=cloadnode.create(sym,sym.owner);
{ indicate that this load should not be transformed into a load
from the parentfpstruct, but instead should load the original
value }
include(initcode.flags,nf_internal);
{ in case it's a var/out/constref parameter, store the address of the
parameter in the struct }
if addrparam then
begin
initcode:=caddrnode.create_internal(initcode);
include(initcode.flags,nf_typedaddr);
end;
initcode:=cassignmentnode.create(
csubscriptnode.create(result,cloadnode.create(pd.parentfpstruct,pd.parentfpstruct.owner)),
initcode);
tblocknode(pd.parentfpinitblock).left:=cstatementnode.create
(initcode,tblocknode(pd.parentfpinitblock).left);
current_filepos:=old_filepos;
end;
end;
end;
procedure redirect_parentfpstruct_local_syms(pd: tprocdef);
var
nestedvarsdef: trecorddef;
sl: tpropaccesslist;
fsym,
lsym,
aliassym: tsym;
i: longint;
begin
nestedvarsdef:=trecorddef(tlocalvarsym(pd.parentfpstruct).vardef);
for i:=0 to nestedvarsdef.symtable.symlist.count-1 do
begin
fsym:=tsym(nestedvarsdef.symtable.symlist[i]);
if fsym.typ<>fieldvarsym then
continue;
lsym:=tsym(pd.localst.find(fsym.name));
if not assigned(lsym) then
lsym:=tsym(pd.parast.find(fsym.name));
if not assigned(lsym) then
internalerror(2011060408);
{ add an absolute variable that redirects to the field }
sl:=tpropaccesslist.create;
sl.addsym(sl_load,pd.parentfpstruct);
sl.addsym(sl_subscript,tfieldvarsym(fsym));
aliassym:=tabsolutevarsym.create_ref(lsym.name,tfieldvarsym(fsym).vardef,sl);
{ hide the original variable (can't delete, because there
may be other loadnodes that reference it)
-- only for locals; hiding parameters changes the
function signature }
if lsym.typ<>paravarsym then
hidesym(lsym);
{ insert the absolute variable in the localst of the
routine; ignore duplicates, because this will also check the
parasymtable and we want to override parameters with our local
versions }
pd.localst.insert(aliassym,false);
end;
end;
function find_sym_in_parentfpstruct(pd: tprocdef; sym: tsym): tsym;
var
nestedvarsdef: tdef;
begin
nestedvarsdef:=tlocalvarsym(pd.parentfpstruct).vardef;
result:=search_struct_member(trecorddef(nestedvarsdef),sym.name);
end;
procedure finish_parentfpstruct(pd: tprocdef);
begin
trecordsymtable(trecorddef(tlocalvarsym(pd.parentfpstruct).vardef).symtable).addalignmentpadding;
end;
end.

View File

@ -559,6 +559,19 @@ interface
easily write out all methods grouped per class }
exprasmlist : TAsmList;
{$endif jvm}
{ temporary reference to structure containing copies of all local
variables and parameters accessed by nested routines; reference to
this structure is passed as "parent frame pointer" on targets that
lack this concept (at least JVM and LLVM); no need to save to/
restore from ppu, since nested routines are always in the same
unit (no need to save to ppu) }
parentfpstruct: tsym;
{ pointer to parentfpstruct's type (not yet valid during parsing, so
cannot be used for $parentfp parameter) (no need to save to ppu) }
parentfpstructptrtype: tdef;
{ code to copy the parameters accessed from nested routines into the
parentfpstruct (no need to save to ppu) }
parentfpinitblock: tnode;
{$ifdef oldregvars}
regvarinfo: pregvarinfo;
{$endif oldregvars}

View File

@ -173,6 +173,7 @@ interface
defaultconstsymderef : tderef;
localloc : TLocation; { register/reference for local var }
initialloc : TLocation; { initial location so it can still be initialized later after the location was changed by SSA }
inparentfpstruct : boolean; { migrated to a parentfpstruct because of nested access (not written to ppu, because not important and would change interface crc) }
constructor create(st:tsymtyp;const n : string;vsp:tvarspez;def:tdef;vopts:tvaroptions);
constructor ppuload(st:tsymtyp;ppufile:tcompilerppufile);
procedure ppuwrite(ppufile:tcompilerppufile);override;

View File

@ -323,6 +323,13 @@ interface
system_jvm_java32
];
{ all systems that don't use a built-in framepointer for accessing nested
variables, but emulate it by wrapping nested variables in records
whose address is passed around }
systems_fpnestedstruct = [
system_jvm_java32
];
cpu2str : array[TSystemCpu] of string[10] =
('','i386','m68k','alpha','powerpc','sparc','vm','ia64','x86_64',
'mips','arm', 'powerpc64', 'avr', 'mipsel','jvm');