mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-28 09:22:35 +02:00
+ "reference" temps that sort of implement pointer-style functionality for
platforms that don't support pointers (by make a copy of all registers part of a reference) git-svn-id: branches/jvmbackend@18377 -
This commit is contained in:
parent
0e87627218
commit
a2a6b2fd1d
@ -412,6 +412,15 @@ unit hlcgobj;
|
||||
The default implementation issues a jump instruction to the external name. }
|
||||
// procedure g_external_wrapper(list : TAsmList; procdef: tprocdef; const externalname: string); virtual;
|
||||
|
||||
{ create "safe copy" of a tlocation that can be used later: all
|
||||
registers used in the tlocation are copied to new ones, so that
|
||||
even if the original ones change, things stay the same (except if
|
||||
the original location was already a register, then the register is
|
||||
kept). Must only be used on lvalue locations.
|
||||
It's intended as some kind of replacement for a_loadaddr_ref_reg()
|
||||
for targets without pointers. }
|
||||
procedure g_reference_loc(list: TAsmList; def: tdef; const fromloc: tlocation; out toloc: tlocation); virtual; abstract;
|
||||
|
||||
|
||||
{ routines migrated from ncgutil }
|
||||
|
||||
|
@ -1181,7 +1181,7 @@ implementation
|
||||
case hp.nodetype of
|
||||
temprefn :
|
||||
begin
|
||||
valid_for_assign := true;
|
||||
valid_for_assign := not(ti_readonly in ttemprefnode(hp).tempinfo^.flags);
|
||||
exit;
|
||||
end;
|
||||
derefn :
|
||||
|
@ -111,6 +111,8 @@ uses
|
||||
procedure a_op_ref_stack(list : TAsmList;op: topcg; size: tdef;const ref: treference);
|
||||
procedure a_op_loc_stack(list : TAsmList;op: topcg; size: tdef;const loc: tlocation);
|
||||
|
||||
procedure g_reference_loc(list: TAsmList; def: tdef; const fromloc: tlocation; out toloc: tlocation); override;
|
||||
|
||||
{ this routine expects that all values are already massaged into the
|
||||
required form (sign bits xor'ed for gt/lt comparisons for OS_32/OS_64,
|
||||
see http://stackoverflow.com/questions/4068973/c-performing-signed-comparison-in-unsigned-variables-without-casting ) }
|
||||
@ -461,6 +463,64 @@ implementation
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure thlcgjvm.g_reference_loc(list: TAsmList; def: tdef; const fromloc: tlocation; out toloc: tlocation);
|
||||
|
||||
procedure handle_reg_move(regsize: tdef; const fromreg: tregister; out toreg: tregister; regtyp: tregistertype);
|
||||
begin
|
||||
case regtyp of
|
||||
R_INTREGISTER:
|
||||
toreg:=getintregister(list,regsize);
|
||||
R_ADDRESSREGISTER:
|
||||
toreg:=getaddressregister(list,regsize);
|
||||
R_FPUREGISTER:
|
||||
toreg:=getfpuregister(list,regsize);
|
||||
end;
|
||||
a_load_reg_reg(list,regsize,regsize,fromreg,toreg);
|
||||
end;
|
||||
|
||||
begin
|
||||
toloc:=fromloc;
|
||||
case fromloc.loc of
|
||||
{ volatile location, can't get a permanent reference }
|
||||
LOC_REGISTER,
|
||||
LOC_FPUREGISTER:
|
||||
internalerror(2011031406);
|
||||
LOC_CONSTANT:
|
||||
{ finished }
|
||||
;
|
||||
LOC_CREGISTER:
|
||||
handle_reg_move(def,fromloc.reference.index,toloc.reference.index,R_INTREGISTER);
|
||||
LOC_CFPUREGISTER:
|
||||
handle_reg_move(def,fromloc.reference.index,toloc.reference.index,R_FPUREGISTER);
|
||||
{ although LOC_CREFERENCE cannot be an lvalue, we may want to take a
|
||||
reference to such a location for multiple reading }
|
||||
LOC_CREFERENCE,
|
||||
LOC_REFERENCE:
|
||||
begin
|
||||
if (fromloc.reference.base<>NR_NO) and
|
||||
(fromloc.reference.base<>current_procinfo.framepointer) and
|
||||
(fromloc.reference.base<>NR_STACK_POINTER_REG) then
|
||||
handle_reg_move(java_jlobject,fromloc.reference.base,toloc.reference.base,R_ADDRESSREGISTER);
|
||||
case fromloc.reference.arrayreftype of
|
||||
art_indexreg:
|
||||
begin
|
||||
{ all array indices in Java are 32 bit ints }
|
||||
handle_reg_move(s32inttype,fromloc.reference.index,toloc.reference.index,R_INTREGISTER);
|
||||
end;
|
||||
art_indexref:
|
||||
begin
|
||||
if (fromloc.reference.indexbase<>NR_NO) and
|
||||
(fromloc.reference.indexbase<>NR_STACK_POINTER_REG) then
|
||||
handle_reg_move(s32inttype,fromloc.reference.indexbase,toloc.reference.indexbase,R_ADDRESSREGISTER);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
else
|
||||
internalerror(2011031407);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure thlcgjvm.a_cmp_stack_label(list: TAsmlist; size: tdef; cmp_op: topcmp; lab: tasmlabel);
|
||||
const
|
||||
opcmp2icmp: array[topcmp] of tasmop = (A_None,
|
||||
|
@ -94,12 +94,57 @@ interface
|
||||
|
||||
ttempcreatenode = class;
|
||||
|
||||
ttempinfoflag = (ti_may_be_in_reg,ti_valid,ti_nextref_set_hookoncopy_nil,
|
||||
ti_addr_taken,ti_executeinitialisation);
|
||||
ttempinfoflag = (
|
||||
{ temp can be kept in a register as far as the original creator is
|
||||
concerned }
|
||||
ti_may_be_in_reg,
|
||||
{ the ttempcreatenode has been process and the temp's location is
|
||||
valid (-> the ttempdeletenode has not yet been processed, or
|
||||
in case it's a "create_to_normal()" one, the final ttemprefnode
|
||||
has not yet been processed) }
|
||||
ti_valid,
|
||||
{ when performing a getcopy of a nodetree, we have to hook up the
|
||||
copies of ttemprefnodes and ttempdestroynode to the copied
|
||||
ttempinfo. this is done by setting hookoncopy in the original
|
||||
ttempinfo to point to the new one. if the temp is deleted via a
|
||||
regular ttempdeletenode, the hookoncopy is simply set to nil once
|
||||
it's processed. otherwise, it sets the ti_nextref_set_hookoncopy_nil
|
||||
and after processing the final ttemprefnode, hookoncopy is set to nil
|
||||
}
|
||||
ti_nextref_set_hookoncopy_nil,
|
||||
{ the address of this temp is taken (-> cannot be kept in a register,
|
||||
even if the creator didn't mind)
|
||||
}
|
||||
ti_addr_taken,
|
||||
{ temps can get an extra node tree that contains the value to which
|
||||
they should be initialised when they are created. this initialisation
|
||||
has to be performed right before the first reference to the temp.
|
||||
this flag indicates that the ttempcreatenode has been
|
||||
processed by pass_generate_code, but that the first ttemprefnode
|
||||
hasn't yet and hence will have to perform the initialisation
|
||||
}
|
||||
ti_executeinitialisation,
|
||||
{ in case an expression like "inc(x[func()],1)" is translated into
|
||||
a regular addition, you have to create a temp to hold the address
|
||||
representing x[func()], since otherwise func() will be called twice
|
||||
and that can spell trouble in case it has side effects. on platforms
|
||||
without pointers, we cannot just take the address though. this flag
|
||||
has to be combined with ti_executeinitialisation above and will,
|
||||
rather than loading the value at the calculated location and store
|
||||
it in the temp, keep a copy of the calculated location if possible
|
||||
and required (not possible for regvars, because SSA may change their
|
||||
register, but not required for them either since calculating their
|
||||
location has no side-effects
|
||||
}
|
||||
ti_reference,
|
||||
{ this temp only allows reading (makes it possible to safely use as
|
||||
reference under more circumstances)
|
||||
}
|
||||
ti_readonly);
|
||||
ttempinfoflags = set of ttempinfoflag;
|
||||
|
||||
const
|
||||
tempinfostoreflags = [ti_may_be_in_reg,ti_addr_taken];
|
||||
tempinfostoreflags = [ti_may_be_in_reg,ti_addr_taken,ti_reference,ti_readonly];
|
||||
|
||||
type
|
||||
{ to allow access to the location by temp references even after the temp has }
|
||||
@ -136,6 +181,7 @@ interface
|
||||
constructor create(_typedef: tdef; _size: tcgint; _temptype: ttemptype;allowreg:boolean); virtual;
|
||||
constructor create_withnode(_typedef: tdef; _size: tcgint; _temptype: ttemptype; allowreg:boolean; withnode: tnode); virtual;
|
||||
constructor create_value(_typedef:tdef; _size: tcgint; _temptype: ttemptype;allowreg:boolean; templvalue: tnode);
|
||||
constructor create_reference(_typedef:tdef; _size: tcgint; _temptype: ttemptype;allowreg:boolean; templvalue: tnode; readonly: boolean);
|
||||
constructor ppuload(t:tnodetype;ppufile:tcompilerppufile);override;
|
||||
procedure ppuwrite(ppufile:tcompilerppufile);override;
|
||||
procedure buildderefimpl;override;
|
||||
@ -209,6 +255,9 @@ interface
|
||||
function laststatement(block:tblocknode):tstatementnode;
|
||||
procedure addstatement(var laststatement:tstatementnode;n:tnode);
|
||||
|
||||
{ if the complexity of n is "high", creates a reference temp to n's
|
||||
location and replace n with a ttemprefnode referring to that location }
|
||||
function maybereplacewithtempref(var n: tnode; size: ASizeInt; readonly: boolean): ttempcreatenode;
|
||||
|
||||
implementation
|
||||
|
||||
@ -251,6 +300,20 @@ implementation
|
||||
end;
|
||||
|
||||
|
||||
function maybereplacewithtempref(var n: tnode; size: ASizeInt; readonly: boolean): ttempcreatenode;
|
||||
begin
|
||||
result:=nil;
|
||||
if node_complexity(n) > 4 then
|
||||
begin
|
||||
result:=ctempcreatenode.create_reference(n.resultdef,size,tt_persistent,true,n,readonly);
|
||||
typecheckpass(tnode(result));
|
||||
n:=ctemprefnode.create(result);
|
||||
typecheckpass(n);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
{*****************************************************************************
|
||||
TFIRSTNOTHING
|
||||
*****************************************************************************}
|
||||
@ -734,6 +797,19 @@ implementation
|
||||
end;
|
||||
|
||||
|
||||
constructor ttempcreatenode.create_reference(_typedef: tdef; _size: tcgint; _temptype: ttemptype; allowreg: boolean; templvalue: tnode; readonly: boolean);
|
||||
begin
|
||||
// store in ppuwrite
|
||||
self.create(_typedef,_size,_temptype,allowreg);
|
||||
ftemplvalue:=templvalue;
|
||||
// no assignment node, just the tempvalue
|
||||
tempinfo^.tempinitcode:=ftemplvalue;
|
||||
include(tempinfo^.flags,ti_reference);
|
||||
if readonly then
|
||||
include(tempinfo^.flags,ti_readonly);
|
||||
end;
|
||||
|
||||
|
||||
function ttempcreatenode.dogetcopy: tnode;
|
||||
var
|
||||
n: ttempcreatenode;
|
||||
|
@ -395,23 +395,28 @@ interface
|
||||
if (ti_valid in tempinfo^.flags) then
|
||||
internalerror(200108222);
|
||||
|
||||
{ get a (persistent) temp }
|
||||
if is_managed_type(tempinfo^.typedef) then
|
||||
{ in case of ti_reference, the location will be initialised using the
|
||||
location of the tempinitnode once the first temprefnode is processed }
|
||||
if not(ti_reference in tempinfo^.flags) then
|
||||
begin
|
||||
location_reset_ref(tempinfo^.location,LOC_REFERENCE,def_cgsize(tempinfo^.typedef),0);
|
||||
tg.GetTempTyped(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.temptype,tempinfo^.location.reference);
|
||||
{ the temp could have been used previously either because the memory location was reused or
|
||||
because we're in a loop }
|
||||
hlcg.g_finalize(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.location.reference);
|
||||
end
|
||||
else if (ti_may_be_in_reg in tempinfo^.flags) then
|
||||
begin
|
||||
location_allocate_register(current_asmdata.CurrAsmList,tempinfo^.location,tempinfo^.typedef,tempinfo^.temptype = tt_persistent);
|
||||
end
|
||||
else
|
||||
begin
|
||||
location_reset_ref(tempinfo^.location,LOC_REFERENCE,def_cgsize(tempinfo^.typedef),0);
|
||||
tg.gethltemp(current_asmdata.CurrAsmList,tempinfo^.typedef,size,tempinfo^.temptype,tempinfo^.location.reference);
|
||||
{ get a (persistent) temp }
|
||||
if is_managed_type(tempinfo^.typedef) then
|
||||
begin
|
||||
location_reset_ref(tempinfo^.location,LOC_REFERENCE,def_cgsize(tempinfo^.typedef),0);
|
||||
tg.GetTempTyped(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.temptype,tempinfo^.location.reference);
|
||||
{ the temp could have been used previously either because the memory location was reused or
|
||||
because we're in a loop }
|
||||
hlcg.g_finalize(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.location.reference);
|
||||
end
|
||||
else if (ti_may_be_in_reg in tempinfo^.flags) then
|
||||
begin
|
||||
location_allocate_register(current_asmdata.CurrAsmList,tempinfo^.location,tempinfo^.typedef,tempinfo^.temptype = tt_persistent);
|
||||
end
|
||||
else
|
||||
begin
|
||||
location_reset_ref(tempinfo^.location,LOC_REFERENCE,def_cgsize(tempinfo^.typedef),0);
|
||||
tg.gethltemp(current_asmdata.CurrAsmList,tempinfo^.typedef,size,tempinfo^.temptype,tempinfo^.location.reference);
|
||||
end;
|
||||
end;
|
||||
include(tempinfo^.flags,ti_valid);
|
||||
if assigned(tempinfo^.tempinitcode) then
|
||||
@ -430,6 +435,27 @@ interface
|
||||
{ avoid recursion }
|
||||
exclude(tempinfo^.flags, ti_executeinitialisation);
|
||||
secondpass(tempinfo^.tempinitcode);
|
||||
if (ti_reference in tempinfo^.flags) then
|
||||
begin
|
||||
case tempinfo^.tempinitcode.location.loc of
|
||||
LOC_CREGISTER,
|
||||
LOC_CFPUREGISTER,
|
||||
LOC_CMMREGISTER,
|
||||
LOC_CSUBSETREG:
|
||||
begin
|
||||
{ although it's ok if we need this value multiple times
|
||||
for reading, it's not in case of writing (because the
|
||||
register could change due to SSA -> storing to the saved
|
||||
register afterwards would be wrong). }
|
||||
if not(ti_readonly in tempinfo^.flags) then
|
||||
internalerror(2011031407);
|
||||
end;
|
||||
{ in case reference contains CREGISTERS, that doesn't matter:
|
||||
we want to write to the location indicated by the current
|
||||
value of those registers, and we can save those values }
|
||||
end;
|
||||
hlcg.g_reference_loc(current_asmdata.CurrAsmList,tempinfo^.typedef,tempinfo^.tempinitcode.location,tempinfo^.location);
|
||||
end;
|
||||
end;
|
||||
{ check if the temp is valid }
|
||||
if not(ti_valid in tempinfo^.flags) then
|
||||
|
Loading…
Reference in New Issue
Block a user