+ support for targets that require by-reference value parameters to be

be copied on the caller instead of callee side
   o mark Darwin/Aarch64 as such a target (any AArch64 target will be like
     that normally, as its ABI specifies this behaviour)
   o don't mark by-reference value parameters on such targets as
     vo_has_local_copy, since a) they don't have one (the copy is on the
     caller side), and b) this ensures that all code handling such
     parameters automatically knows that they are still by reference
     after the init code has run
   o when making the copies on the caller side, don't increase the
     reference count for managed types except for variants, just like
     is done when making the copies on the callee side. This is because
     the reference count increasing code on the callee side only runs
     for non-assembler functions, and we cannot know 100% certain on the
     caller side whether the called function is assembler or not (e.g. in
     case of externally declared functions)
   o maybe over time we can reuse the Pascal code in
     tcallparanode.copy_value_by_ref_para to replace the equivalent code
     in hlcgobj and ncgutil also on the caller side for other targets

git-svn-id: trunk@29870 -
This commit is contained in:
Jonas Maebe 2015-02-23 22:50:20 +00:00
parent bedcc5cbb1
commit c6ba0bb6fb
5 changed files with 251 additions and 7 deletions

View File

@ -4523,7 +4523,11 @@ implementation
if (tparavarsym(p).varspez=vs_value) then if (tparavarsym(p).varspez=vs_value) then
begin begin
include(current_procinfo.flags,pi_needs_implicit_finally); include(current_procinfo.flags,pi_needs_implicit_finally);
location_get_data_ref(list,tparavarsym(p).vardef,tparavarsym(p).localloc,href,is_open_array(tparavarsym(p).vardef),sizeof(pint)); location_get_data_ref(list,tparavarsym(p).vardef,tparavarsym(p).localloc,href,
is_open_array(tparavarsym(p).vardef) or
((target_info.system in systems_caller_copy_addr_value_para) and
paramanager.push_addr_param(vs_value,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption)),
sizeof(pint));
if is_open_array(tparavarsym(p).vardef) then if is_open_array(tparavarsym(p).vardef) then
begin begin
if paramanager.push_high_param(tparavarsym(p).varspez,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption) then if paramanager.push_high_param(tparavarsym(p).varspez,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption) then
@ -4543,7 +4547,8 @@ implementation
end; end;
end; end;
{ open arrays can contain elements requiring init/final code, so the else has been removed here } { open arrays can contain elements requiring init/final code, so the else has been removed here }
if (tparavarsym(p).varspez=vs_value) and if not(target_info.system in systems_caller_copy_addr_value_para) and
(tparavarsym(p).varspez=vs_value) and
(is_open_array(tparavarsym(p).vardef) or (is_open_array(tparavarsym(p).vardef) or
is_array_of_const(tparavarsym(p).vardef)) then is_array_of_const(tparavarsym(p).vardef)) then
begin begin
@ -4581,7 +4586,11 @@ implementation
if not((tparavarsym(p).vardef.typ=variantdef) and if not((tparavarsym(p).vardef.typ=variantdef) and
paramanager.push_addr_param(tparavarsym(p).varspez,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption)) then paramanager.push_addr_param(tparavarsym(p).varspez,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption)) then
begin begin
location_get_data_ref(list,tparavarsym(p).vardef,tparavarsym(p).initialloc,href,is_open_array(tparavarsym(p).vardef),sizeof(pint)); location_get_data_ref(list,tparavarsym(p).vardef,tparavarsym(p).initialloc,href,
is_open_array(tparavarsym(p).vardef) or
((target_info.system in systems_caller_copy_addr_value_para) and
paramanager.push_addr_param(vs_value,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption)),
sizeof(pint));
if is_open_array(tparavarsym(p).vardef) then if is_open_array(tparavarsym(p).vardef) then
begin begin
if paramanager.push_high_param(tparavarsym(p).varspez,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption) then if paramanager.push_high_param(tparavarsym(p).varspez,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption) then
@ -4656,7 +4665,8 @@ implementation
{ generate copies of call by value parameters, must be done before { generate copies of call by value parameters, must be done before
the initialization and body is parsed because the refcounts are the initialization and body is parsed because the refcounts are
incremented using the local copies } incremented using the local copies }
current_procinfo.procdef.parast.SymList.ForEachCall(@g_copyvalueparas,list); if not(target_info.system in systems_caller_copy_addr_value_para) then
current_procinfo.procdef.parast.SymList.ForEachCall(@g_copyvalueparas,list);
if not(po_assembler in current_procinfo.procdef.procoptions) then if not(po_assembler in current_procinfo.procdef.procoptions) then
begin begin

View File

@ -201,6 +201,10 @@ interface
fparainit, fparainit,
fparacopyback: tnode; fparacopyback: tnode;
procedure handlemanagedbyrefpara(orgparadef: tdef);virtual;abstract; procedure handlemanagedbyrefpara(orgparadef: tdef);virtual;abstract;
{ on some targets, value parameters that are passed by reference must
be copied to a temp location by the caller (and then a reference to
this temp location must be passed) }
procedure copy_value_by_ref_para;
public public
callparaflags : tcallparaflags; callparaflags : tcallparaflags;
parasym : tparavarsym; parasym : tparavarsym;
@ -592,6 +596,208 @@ implementation
TCALLPARANODE TCALLPARANODE
****************************************************************************} ****************************************************************************}
procedure tcallparanode.copy_value_by_ref_para;
var
initstat,
copybackstat,
finistat: tstatementnode;
finiblock: tblocknode;
paratemp: ttempcreatenode;
arraysize,
arraybegin: tnode;
lefttemp: ttempcreatenode;
vardatatype,
temparraydef: tdef;
begin
{ this routine is for targets where by-reference value parameters need
to be copied by the caller. It's basically the node-level equivalent
of thlcgobj.g_copyvalueparas }
{ in case of an array constructor, we don't need a copy since the array
constructor itself is already constructed on the fly (and hence if
it's modified by the caller, that's no problem) }
if not is_array_constructor(left.resultdef) then
begin
fparainit:=internalstatements(initstat);
fparacopyback:=internalstatements(copybackstat);
finiblock:=internalstatements(finistat);
paratemp:=nil;
{ making a copy of an open array, an array of const or a dynamic
array requires dynamic memory allocation since we don't know the
size at compile time }
if is_open_array(left.resultdef) or
is_array_of_const(left.resultdef) or
(is_dynamic_array(left.resultdef) and
is_open_array(parasym.vardef)) then
begin
paratemp:=ctempcreatenode.create(voidpointertype,voidpointertype.size,tt_persistent,true);
if is_dynamic_array(left.resultdef) then
begin
{ note that in insert_typeconv, this dynamic array was
already converted into an open array (-> dereferenced)
and then its resultdef was restored to the original
dynamic array one -> get the address before treating it
as a dynamic array here }
{ first restore the actual resultdef of left }
temparraydef:=left.resultdef;
left.resultdef:=parasym.vardef;
{ get its address }
lefttemp:=ctempcreatenode.create(voidpointertype,voidpointertype.size,tt_persistent,true);
addstatement(initstat,lefttemp);
addstatement(finistat,ctempdeletenode.create(lefttemp));
addstatement(initstat,
cassignmentnode.create(
ctemprefnode.create(lefttemp),
caddrnode.create_internal(left)
)
);
{ restore the resultdef }
left.resultdef:=temparraydef;
{ now treat that address (correctly) as the original
dynamic array to get its start and length }
arraybegin:=cvecnode.create(
ctypeconvnode.create_explicit(ctemprefnode.create(lefttemp),
left.resultdef),
genintconstnode(0)
);
arraysize:=caddnode.create(muln,
geninlinenode(in_length_x,false,
ctypeconvnode.create_explicit(ctemprefnode.create(lefttemp),
left.resultdef)
),
genintconstnode(tarraydef(left.resultdef).elementdef.size)
);
end
else
begin
{ no problem here that left is used multiple times, as
sizeof() will simply evaluate to the high parameter }
arraybegin:=left.getcopy;
arraysize:=geninlinenode(in_sizeof_x,false,left);
end;
addstatement(initstat,paratemp);
{ paratemp:=getmem(sizeof(para)) }
addstatement(initstat,
cassignmentnode.create(
ctemprefnode.create(paratemp),
ccallnode.createintern('fpc_getmem',
ccallparanode.create(
arraysize.getcopy,nil
)
)
)
);
{ move(para,temp,sizeof(arr)) (no "left.getcopy" below because
we replace left afterwards) }
addstatement(initstat,
ccallnode.createintern('MOVE',
ccallparanode.create(
arraysize,
ccallparanode.create(
cderefnode.create(ctemprefnode.create(paratemp)),
ccallparanode.create(
arraybegin,nil
)
)
)
)
);
{ no reference count increases, that's still done on the callee
side because for compatibility with targets that perform this
copy on the callee side, that should only be done for non-
assember functions (and we can't know that 100% certain here,
e.g. in case of external declarations) (*) }
{ free the memory again after the call: freemem(paratemp) }
addstatement(finistat,
ccallnode.createintern('fpc_freemem',
ccallparanode.create(
ctemprefnode.create(paratemp),nil
)
)
);
{ replace the original parameter with a dereference of the
temp typecasted to the same type as the original parameter
(don't free left, it has been reused above) }
left:=ctypeconvnode.create_internal(
cderefnode.create(ctemprefnode.create(paratemp)),
left.resultdef);
end
else if is_shortstring(parasym.vardef) then
begin
{ the shortstring parameter may have a different size than the
parameter type -> assign and truncate/extend }
paratemp:=ctempcreatenode.create(parasym.vardef,parasym.vardef.size,tt_persistent,false);
addstatement(initstat,paratemp);
{ assign shortstring }
addstatement(initstat,
cassignmentnode.create(
ctemprefnode.create(paratemp),left
)
);
{ replace parameter with temp (don't free left, it has been
reused above) }
left:=ctemprefnode.create(paratemp);
end
else if parasym.vardef.typ=variantdef then
begin
vardatatype:=search_system_type('TVARDATA').typedef;
paratemp:=ctempcreatenode.create(vardatatype,vardatatype.size,tt_persistent,false);
addstatement(initstat,paratemp);
addstatement(initstat,
ccallnode.createintern('fpc_variant_copy_overwrite',
ccallparanode.create(
ctypeconvnode.create_explicit(ctemprefnode.create(paratemp),
vardatatype
),
ccallparanode.create(ctypeconvnode.create_explicit(left,
vardatatype),
nil
)
)
)
);
{ replace parameter with temp (don't free left, it has been
reused above) }
left:=ctypeconvnode.create_explicit(ctemprefnode.create(paratemp),parasym.vardef);
end
else if is_managed_type(left.resultdef) then
begin
{ don't increase/decrease the reference count here, will be done by
the callee (see (*) above) -> typecast to array of byte
for the assignment to the temp }
temparraydef:=getarraydef(u8inttype,left.resultdef.size);
paratemp:=ctempcreatenode.create(temparraydef,temparraydef.size,tt_persistent,false);
addstatement(initstat,paratemp);
addstatement(initstat,
cassignmentnode.create(
ctemprefnode.create(paratemp),
ctypeconvnode.create_internal(left,temparraydef)
)
);
left:=ctypeconvnode.create_explicit(ctemprefnode.create(paratemp),left.resultdef);
end
else
begin
paratemp:=ctempcreatenode.create(left.resultdef,left.resultdef.size,tt_persistent,false);
addstatement(initstat,paratemp);
addstatement(initstat,
cassignmentnode.create(ctemprefnode.create(paratemp),left)
);
{ replace parameter with temp (don't free left, it has been
reused above) }
left:=ctemprefnode.create(paratemp);
end;
addstatement(finistat,ctempdeletenode.create(paratemp));
addstatement(copybackstat,finiblock);
firstpass(fparainit);
firstpass(left);
firstpass(fparacopyback);
end;
end;
constructor tcallparanode.create(expr,next : tnode); constructor tcallparanode.create(expr,next : tnode);
begin begin
@ -721,6 +927,7 @@ implementation
tcallparanode(right).firstcallparan; tcallparanode(right).firstcallparan;
if not assigned(left.resultdef) then if not assigned(left.resultdef) then
get_paratype; get_paratype;
if assigned(parasym) and if assigned(parasym) and
(target_info.system in systems_managed_vm) and (target_info.system in systems_managed_vm) and
(parasym.varspez in [vs_var,vs_out,vs_constref]) and (parasym.varspez in [vs_var,vs_out,vs_constref]) and
@ -729,6 +936,23 @@ implementation
(left.nodetype<>nothingn) then (left.nodetype<>nothingn) then
handlemanagedbyrefpara(left.resultdef); handlemanagedbyrefpara(left.resultdef);
{ for targets that have to copy "value parameters by reference" on the
caller side
aktcallnode may not be assigned in case firstcallparan is called for
fake parameters to inline nodes (in that case, we don't have a real
call and hence no "caller side" either)
}
if assigned(aktcallnode) and
(target_info.system in systems_caller_copy_addr_value_para) and
((assigned(parasym) and
(parasym.varspez=vs_value)) or
(cpf_varargs_para in callparaflags)) and
(left.nodetype<>nothingn) and
paramanager.push_addr_param(vs_value,left.resultdef,
aktcallnode.procdefinition.proccalloption) then
copy_value_by_ref_para;
{ does it need to load RTTI? } { does it need to load RTTI? }
if assigned(parasym) and (parasym.varspez=vs_out) and if assigned(parasym) and (parasym.varspez=vs_out) and
(cs_create_pic in current_settings.moduleswitches) and (cs_create_pic in current_settings.moduleswitches) and

View File

@ -698,7 +698,11 @@ implementation
if not((tparavarsym(p).vardef.typ=variantdef) and if not((tparavarsym(p).vardef.typ=variantdef) and
paramanager.push_addr_param(tparavarsym(p).varspez,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption)) then paramanager.push_addr_param(tparavarsym(p).varspez,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption)) then
begin begin
hlcg.location_get_data_ref(list,tparavarsym(p).vardef,tparavarsym(p).initialloc,href,is_open_array(tparavarsym(p).vardef),sizeof(pint)); hlcg.location_get_data_ref(list,tparavarsym(p).vardef,tparavarsym(p).initialloc,href,
is_open_array(tparavarsym(p).vardef) or
((target_info.system in systems_caller_copy_addr_value_para) and
paramanager.push_addr_param(vs_value,tparavarsym(p).vardef,current_procinfo.procdef.proccalloption)),
sizeof(pint));
if is_open_array(tparavarsym(p).vardef) then if is_open_array(tparavarsym(p).vardef) then
begin begin
{ open arrays do not contain correct element count in their rtti, { open arrays do not contain correct element count in their rtti,
@ -1330,7 +1334,8 @@ implementation
{ generate copies of call by value parameters, must be done before { generate copies of call by value parameters, must be done before
the initialization and body is parsed because the refcounts are the initialization and body is parsed because the refcounts are
incremented using the local copies } incremented using the local copies }
current_procinfo.procdef.parast.SymList.ForEachCall(@copyvalueparas,list); if not(target_info.system in systems_caller_copy_addr_value_para) then
current_procinfo.procdef.parast.SymList.ForEachCall(@copyvalueparas,list);
{$ifdef powerpc} {$ifdef powerpc}
{ unget the register that contains the stack pointer before the procedure entry, } { unget the register that contains the stack pointer before the procedure entry, }
{ which is used to access the parameters in their original callee-side location } { which is used to access the parameters in their original callee-side location }

View File

@ -335,7 +335,8 @@ implementation
if (varspez=vs_value) and if (varspez=vs_value) and
paramanager.push_addr_param(varspez,vardef,pd.proccalloption) and paramanager.push_addr_param(varspez,vardef,pd.proccalloption) and
not(is_open_array(vardef) or not(is_open_array(vardef) or
is_array_of_const(vardef)) then is_array_of_const(vardef)) and
not(target_info.system in systems_caller_copy_addr_value_para) then
include(varoptions,vo_has_local_copy); include(varoptions,vo_has_local_copy);
{ needs high parameter ? } { needs high parameter ? }

View File

@ -338,6 +338,10 @@ interface
system_jvm_android32 system_jvm_android32
]; ];
{ all systems where a value parameter passed by reference must be copied
on the caller side rather than on the callee side }
systems_caller_copy_addr_value_para = [system_aarch64_darwin];
{ pointer checking (requires special code in FPC_CHECKPOINTER, { pointer checking (requires special code in FPC_CHECKPOINTER,
and can never work for libc-based targets or any other program and can never work for libc-based targets or any other program
linking to an external library) linking to an external library)