mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-09 21:48:35 +02:00
524 lines
20 KiB
ObjectPascal
524 lines
20 KiB
ObjectPascal
{
|
||
Copyright (c) 2000-2011 by Florian Klaempfl and Jonas Maebe
|
||
|
||
Code generation for add nodes on the JVM
|
||
|
||
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 njvmadd;
|
||
|
||
{$i fpcdefs.inc}
|
||
|
||
interface
|
||
|
||
uses
|
||
cgbase,
|
||
node,ncgadd,cpubase;
|
||
|
||
type
|
||
|
||
{ tjvmaddnode }
|
||
|
||
tjvmaddnode = class(tcgaddnode)
|
||
function pass_1: tnode;override;
|
||
protected
|
||
function jvm_first_addset: tnode;
|
||
|
||
procedure second_generic_compare(unsigned: boolean);
|
||
|
||
procedure pass_left_right;override;
|
||
procedure second_addfloat;override;
|
||
procedure second_cmpfloat;override;
|
||
procedure second_cmpboolean;override;
|
||
procedure second_cmp64bit;override;
|
||
procedure second_add64bit; override;
|
||
procedure second_cmpordinal;override;
|
||
end;
|
||
|
||
implementation
|
||
|
||
uses
|
||
systems,
|
||
cutils,verbose,constexp,globtype,compinnr,
|
||
symconst,symtable,symdef,symcpu,
|
||
paramgr,procinfo,pass_1,
|
||
aasmbase,aasmtai,aasmdata,aasmcpu,defutil,
|
||
hlcgobj,hlcgcpu,cgutils,
|
||
cpupara,
|
||
nbas,ncon,nset,nadd,ncal,ncnv,ninl,nld,nmat,nmem,
|
||
njvmcon,
|
||
cgobj;
|
||
|
||
{*****************************************************************************
|
||
tjvmaddnode
|
||
*****************************************************************************}
|
||
|
||
function tjvmaddnode.pass_1: tnode;
|
||
begin
|
||
{ special handling for enums: they're classes in the JVM -> get their
|
||
ordinal value to compare them (do before calling inherited pass_1,
|
||
because pass_1 will convert enum constants from ordinals into class
|
||
instances) }
|
||
if (left.resultdef.typ=enumdef) and
|
||
(right.resultdef.typ=enumdef) then
|
||
begin
|
||
{ enums can only be compared at this stage (add/sub is only allowed
|
||
in constant expressions) }
|
||
if not is_boolean(resultdef) then
|
||
internalerror(2011062603);
|
||
inserttypeconv_explicit(left,s32inttype);
|
||
inserttypeconv_explicit(right,s32inttype);
|
||
end;
|
||
{ special handling for sets: all sets are JUBitSet/JUEnumSet on the JVM
|
||
target to ease interoperability with Java code }
|
||
if left.resultdef.typ=setdef then
|
||
begin
|
||
result:=jvm_first_addset;
|
||
exit;
|
||
end;
|
||
{ special handling for comparing a dynamic array to nil: dynamic arrays
|
||
can be empty on the jvm target and not be different from nil at the
|
||
same time (array of 0 elements) -> change into length check }
|
||
if is_dynamic_array(left.resultdef) and
|
||
(right.nodetype=niln) then
|
||
begin
|
||
result:=caddnode.create(nodetype,cinlinenode.create(in_length_x,false,left),genintconstnode(0));
|
||
left:=nil;
|
||
exit;
|
||
end;
|
||
if is_dynamic_array(right.resultdef) and
|
||
(left.nodetype=niln) then
|
||
begin
|
||
result:=caddnode.create(nodetype,cinlinenode.create(in_length_x,false,right),genintconstnode(0));
|
||
right:=nil;
|
||
exit;
|
||
end;
|
||
result:=inherited pass_1;
|
||
if expectloc=LOC_FLAGS then
|
||
expectloc:=LOC_JUMP;
|
||
end;
|
||
|
||
|
||
function tjvmaddnode.jvm_first_addset: tnode;
|
||
|
||
procedure call_set_helper_paras(const n : string; isenum: boolean; paras: tcallparanode);
|
||
var
|
||
block: tblocknode;
|
||
stat: tstatementnode;
|
||
temp: ttempcreatenode;
|
||
begin
|
||
result:=ccallnode.createinternmethod(left,'CLONE',nil);
|
||
if isenum then
|
||
inserttypeconv_explicit(result,java_juenumset)
|
||
else
|
||
inserttypeconv_explicit(result,java_jubitset);
|
||
if isenum then
|
||
begin
|
||
{ all enum instance methods return a boolean, while we are
|
||
interested in the resulting set }
|
||
block:=internalstatements(stat);
|
||
temp:=ctempcreatenode.create(java_juenumset,4,tt_persistent,true);
|
||
addstatement(stat,temp);
|
||
addstatement(stat,cassignmentnode.create(
|
||
ctemprefnode.create(temp),result));
|
||
addstatement(stat,ccallnode.createinternmethod(
|
||
ctemprefnode.create(temp),n,paras));
|
||
addstatement(stat,ctempdeletenode.create_normal_temp(temp));
|
||
addstatement(stat,ctemprefnode.create(temp));
|
||
result:=block;
|
||
end
|
||
else
|
||
result:=ccallnode.createinternmethod(result,n,paras);
|
||
end;
|
||
|
||
procedure call_set_helper(const n: string; isenum: boolean);
|
||
begin
|
||
call_set_helper_paras(n,isenum,ccallparanode.create(right,nil));
|
||
end;
|
||
|
||
var
|
||
procname: string;
|
||
tmpn: tnode;
|
||
paras: tcallparanode;
|
||
isenum: boolean;
|
||
begin
|
||
isenum:=
|
||
(assigned(tsetdef(left.resultdef).elementdef) and
|
||
(tsetdef(left.resultdef).elementdef.typ=enumdef)) or
|
||
((right.nodetype=setelementn) and
|
||
(tsetelementnode(right).left.resultdef.typ=enumdef)) or
|
||
((right.resultdef.typ=setdef) and
|
||
assigned(tsetdef(right.resultdef).elementdef) and
|
||
(tsetdef(right.resultdef).elementdef.typ=enumdef));
|
||
{ don't destroy optimization opportunity }
|
||
if not((nodetype=addn) and
|
||
(right.nodetype=setelementn) and
|
||
is_emptyset(left)) then
|
||
begin
|
||
left:=caddrnode.create_internal(left);
|
||
include(taddrnode(left).addrnodeflags,anf_typedaddr);
|
||
if isenum then
|
||
begin
|
||
inserttypeconv_explicit(left,java_juenumset);
|
||
if right.resultdef.typ=setdef then
|
||
begin
|
||
right:=caddrnode.create_internal(right);
|
||
include(taddrnode(right).addrnodeflags,anf_typedaddr);
|
||
inserttypeconv_explicit(right,java_juenumset);
|
||
end;
|
||
end
|
||
else
|
||
begin
|
||
inserttypeconv_explicit(left,java_jubitset);
|
||
if right.resultdef.typ=setdef then
|
||
begin
|
||
right:=caddrnode.create_internal(right);
|
||
include(taddrnode(right).addrnodeflags,anf_typedaddr);
|
||
inserttypeconv_explicit(right,java_jubitset);
|
||
end;
|
||
end;
|
||
end
|
||
else
|
||
tjvmsetconstnode(left).setconsttype:=sct_notransform;
|
||
firstpass(left);
|
||
firstpass(right);
|
||
case nodetype of
|
||
equaln,unequaln,lten,gten:
|
||
begin
|
||
case nodetype of
|
||
equaln,unequaln:
|
||
procname:='EQUALS';
|
||
lten,gten:
|
||
begin
|
||
{ (left <= right) = (right >= left) }
|
||
if nodetype=lten then
|
||
begin
|
||
tmpn:=left;
|
||
left:=right;
|
||
right:=tmpn;
|
||
end;
|
||
procname:='CONTAINSALL'
|
||
end;
|
||
else
|
||
internalerror(2013120114);
|
||
end;
|
||
result:=ccallnode.createinternmethod(left,procname,ccallparanode.create(right,nil));
|
||
{ for an unequaln, we have to negate the result of equals }
|
||
if nodetype=unequaln then
|
||
result:=cnotnode.create(result);
|
||
end;
|
||
addn:
|
||
begin
|
||
{ optimize first loading of a set }
|
||
if (right.nodetype=setelementn) and
|
||
is_emptyset(left) then
|
||
begin
|
||
paras:=nil;
|
||
procname:='OF';
|
||
if isenum then
|
||
begin
|
||
inserttypeconv_explicit(tsetelementnode(right).left,tcpuenumdef(tenumdef(tsetelementnode(right).left.resultdef).getbasedef).classdef);
|
||
result:=cloadvmtaddrnode.create(ctypenode.create(java_juenumset));
|
||
end
|
||
else
|
||
begin
|
||
{ for boolean, char, etc }
|
||
inserttypeconv_explicit(tsetelementnode(right).left,s32inttype);
|
||
result:=cloadvmtaddrnode.create(ctypenode.create(java_jubitset));
|
||
end;
|
||
paras:=ccallparanode.create(tsetelementnode(right).left,nil);
|
||
tsetelementnode(right).left:=nil;
|
||
if assigned(tsetelementnode(right).right) then
|
||
begin
|
||
procname:='RANGE';
|
||
if isenum then
|
||
begin
|
||
inserttypeconv_explicit(tsetelementnode(right).right,tcpuenumdef(tenumdef(tsetelementnode(right).right.resultdef).getbasedef).classdef);
|
||
end
|
||
else
|
||
begin
|
||
inserttypeconv_explicit(tsetelementnode(right).right,s32inttype);
|
||
end;
|
||
paras:=ccallparanode.create(tsetelementnode(right).right,paras);
|
||
tsetelementnode(right).right:=nil;
|
||
end;
|
||
right.free;
|
||
result:=ccallnode.createinternmethod(result,procname,paras)
|
||
end
|
||
else
|
||
begin
|
||
if right.nodetype=setelementn then
|
||
begin
|
||
paras:=nil;
|
||
{ get a copy of left to add to }
|
||
procname:='ADD';
|
||
if isenum then
|
||
begin
|
||
inserttypeconv_explicit(tsetelementnode(right).left,tcpuenumdef(tenumdef(tsetelementnode(right).left.resultdef).getbasedef).classdef);
|
||
end
|
||
else
|
||
begin
|
||
{ for boolean, char, etc }
|
||
inserttypeconv_explicit(tsetelementnode(right).left,s32inttype);
|
||
end;
|
||
paras:=ccallparanode.create(tsetelementnode(right).left,paras);
|
||
tsetelementnode(right).left:=nil;
|
||
if assigned(tsetelementnode(right).right) then
|
||
begin
|
||
procname:='ADDALL';
|
||
{ create a set containing the range via the class
|
||
factory method, then add all of its elements }
|
||
if isenum then
|
||
begin
|
||
inserttypeconv_explicit(tsetelementnode(right).right,tcpuenumdef(tenumdef(tsetelementnode(right).right.resultdef).getbasedef).classdef);
|
||
tmpn:=cloadvmtaddrnode.create(ctypenode.create(java_juenumset));
|
||
end
|
||
else
|
||
begin
|
||
inserttypeconv_explicit(tsetelementnode(right).right,s32inttype);
|
||
tmpn:=cloadvmtaddrnode.create(ctypenode.create(java_jubitset));
|
||
end;
|
||
paras:=ccallparanode.create(ccallnode.createinternmethod(tmpn,'RANGE',ccallparanode.create(tsetelementnode(right).right,paras)),nil);
|
||
tsetelementnode(right).right:=nil;
|
||
end;
|
||
call_set_helper_paras(procname,isenum,paras);
|
||
end
|
||
else
|
||
call_set_helper('ADDALL',isenum)
|
||
end
|
||
end;
|
||
subn:
|
||
call_set_helper('REMOVEALL',isenum);
|
||
symdifn:
|
||
if isenum then
|
||
begin
|
||
{ "s1 xor s2" is the same as "(s1 + s2) - (s1 * s2)"
|
||
-> call helper to prevent double evaluations }
|
||
result:=ccallnode.createintern('fpc_enumset_symdif',
|
||
ccallparanode.create(right,ccallparanode.create(left,nil)));
|
||
left:=nil;
|
||
right:=nil;
|
||
end
|
||
else
|
||
call_set_helper('SYMDIF',isenum);
|
||
muln:
|
||
call_set_helper('RETAINALL',isenum)
|
||
else
|
||
internalerror(2011062807);
|
||
end;
|
||
{ convert helper result back to original set type for further expression
|
||
evaluation }
|
||
if not is_boolean(resultdef) then
|
||
begin
|
||
inserttypeconv_explicit(result,cpointerdef.getreusable(resultdef));
|
||
result:=cderefnode.create(result);
|
||
end;
|
||
{ left and right are reused as parameters }
|
||
left:=nil;
|
||
right:=nil;
|
||
end;
|
||
|
||
|
||
procedure tjvmaddnode.second_generic_compare(unsigned: boolean);
|
||
var
|
||
truelabel,
|
||
falselabel: tasmlabel;
|
||
cmpop: TOpCmp;
|
||
begin
|
||
truelabel:=nil;
|
||
falselabel:=nil;
|
||
pass_left_right;
|
||
{ swap the operands to make it easier for the optimizer to optimize
|
||
the operand stack slot reloading in case both are in a register }
|
||
if (left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) and
|
||
(right.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
|
||
swapleftright;
|
||
cmpop:=cmpnode2topcmp(unsigned);
|
||
if (nf_swapped in flags) then
|
||
cmpop:=swap_opcmp(cmpop);
|
||
|
||
current_asmdata.getjumplabel(truelabel);
|
||
current_asmdata.getjumplabel(falselabel);
|
||
location_reset_jump(location,truelabel,falselabel);
|
||
|
||
if left.location.loc in [LOC_REGISTER,LOC_CREGISTER] then
|
||
hlcg.a_cmp_loc_reg_label(current_asmdata.CurrAsmList,left.resultdef,cmpop,right.location,left.location.register,location.truelabel)
|
||
else case right.location.loc of
|
||
LOC_REGISTER,LOC_CREGISTER:
|
||
hlcg.a_cmp_reg_loc_label(current_asmdata.CurrAsmList,left.resultdef,cmpop,right.location.register,left.location,location.truelabel);
|
||
LOC_REFERENCE,LOC_CREFERENCE:
|
||
hlcg.a_cmp_ref_loc_label(current_asmdata.CurrAsmList,left.resultdef,cmpop,right.location.reference,left.location,location.truelabel);
|
||
LOC_CONSTANT:
|
||
hlcg.a_cmp_const_loc_label(current_asmdata.CurrAsmList,left.resultdef,cmpop,right.location.value,left.location,location.truelabel);
|
||
else
|
||
internalerror(2011010413);
|
||
end;
|
||
hlcg.a_jmp_always(current_asmdata.CurrAsmList,location.falselabel);
|
||
end;
|
||
|
||
procedure tjvmaddnode.pass_left_right;
|
||
begin
|
||
if not((nodetype in [orn,andn]) and
|
||
is_boolean(left.resultdef)) then
|
||
swapleftright;
|
||
inherited pass_left_right;
|
||
end;
|
||
|
||
|
||
procedure tjvmaddnode.second_addfloat;
|
||
var
|
||
op : TAsmOp;
|
||
commutative : boolean;
|
||
begin
|
||
pass_left_right;
|
||
|
||
location_reset(location,LOC_FPUREGISTER,def_cgsize(resultdef));
|
||
location.register:=hlcg.getfpuregister(current_asmdata.CurrAsmList,resultdef);
|
||
|
||
commutative:=false;
|
||
case nodetype of
|
||
addn :
|
||
begin
|
||
if location.size=OS_F64 then
|
||
op:=a_dadd
|
||
else
|
||
op:=a_fadd;
|
||
commutative:=true;
|
||
end;
|
||
muln :
|
||
begin
|
||
if location.size=OS_F64 then
|
||
op:=a_dmul
|
||
else
|
||
op:=a_fmul;
|
||
commutative:=true;
|
||
end;
|
||
subn :
|
||
begin
|
||
if location.size=OS_F64 then
|
||
op:=a_dsub
|
||
else
|
||
op:=a_fsub;
|
||
end;
|
||
slashn :
|
||
begin
|
||
if location.size=OS_F64 then
|
||
op:=a_ddiv
|
||
else
|
||
op:=a_fdiv;
|
||
end;
|
||
else
|
||
internalerror(2011010402);
|
||
end;
|
||
|
||
{ swap the operands to make it easier for the optimizer to optimize
|
||
the operand stack slot reloading (non-commutative operations must
|
||
always be in the correct order though) }
|
||
if (commutative and
|
||
(left.location.loc in [LOC_FPUREGISTER,LOC_CFPUREGISTER]) and
|
||
(right.location.loc in [LOC_FPUREGISTER,LOC_CFPUREGISTER])) or
|
||
(not commutative and
|
||
(nf_swapped in flags)) then
|
||
swapleftright;
|
||
|
||
thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
|
||
thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,right.resultdef,right.location);
|
||
|
||
current_asmdata.CurrAsmList.concat(taicpu.op_none(op));
|
||
thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1+ord(location.size=OS_F64));
|
||
{ could be optimized in the future by keeping the results on the stack,
|
||
if we add code to swap the operands when necessary (a_swap for
|
||
singles, store/load/load for doubles since there is no swap for
|
||
2-slot elements -- also adjust expectloc in that case! }
|
||
thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,resultdef,location.register);
|
||
end;
|
||
|
||
|
||
procedure tjvmaddnode.second_cmpfloat;
|
||
var
|
||
truelabel,
|
||
falselabel: tasmlabel;
|
||
op: tasmop;
|
||
cmpop: TOpCmp;
|
||
begin
|
||
truelabel:=nil;
|
||
falselabel:=nil;
|
||
pass_left_right;
|
||
{ swap the operands to make it easier for the optimizer to optimize
|
||
the operand stack slot reloading in case both are in a register }
|
||
if (left.location.loc in [LOC_FPUREGISTER,LOC_CFPUREGISTER]) and
|
||
(right.location.loc in [LOC_FPUREGISTER,LOC_CFPUREGISTER]) then
|
||
swapleftright;
|
||
cmpop:=cmpnode2topcmp(false);
|
||
if (nf_swapped in flags) then
|
||
cmpop:=swap_opcmp(cmpop);
|
||
|
||
current_asmdata.getjumplabel(truelabel);
|
||
current_asmdata.getjumplabel(falselabel);
|
||
location_reset_jump(location,truelabel,falselabel);
|
||
|
||
thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location);
|
||
thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,right.resultdef,right.location);
|
||
|
||
{ compares two floating point values and puts 1/0/-1 on stack depending
|
||
on whether value1 >/=/< value2 }
|
||
if left.location.size=OS_F64 then
|
||
{ make sure that comparisons with NaNs always return false for </> }
|
||
if nodetype in [ltn,lten] then
|
||
op:=a_dcmpg
|
||
else
|
||
op:=a_dcmpl
|
||
else if nodetype in [ltn,lten] then
|
||
op:=a_fcmpg
|
||
else
|
||
op:=a_fcmpl;
|
||
current_asmdata.CurrAsmList.concat(taicpu.op_none(op));
|
||
thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,(1+ord(left.location.size=OS_F64))*2-1);
|
||
|
||
current_asmdata.CurrAsmList.concat(taicpu.op_sym(opcmp2if[cmpop],location.truelabel));
|
||
thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
|
||
hlcg.a_jmp_always(current_asmdata.CurrAsmList,location.falselabel);
|
||
end;
|
||
|
||
|
||
procedure tjvmaddnode.second_cmpboolean;
|
||
begin
|
||
second_generic_compare(true);
|
||
end;
|
||
|
||
|
||
procedure tjvmaddnode.second_cmp64bit;
|
||
begin
|
||
second_generic_compare(not is_signed(left.resultdef));
|
||
end;
|
||
|
||
|
||
procedure tjvmaddnode.second_add64bit;
|
||
begin
|
||
second_opordinal;
|
||
end;
|
||
|
||
|
||
procedure tjvmaddnode.second_cmpordinal;
|
||
begin
|
||
second_generic_compare(not is_signed(left.resultdef));
|
||
end;
|
||
|
||
begin
|
||
caddnode:=tjvmaddnode;
|
||
end.
|