{ Copyright (c) 1998-2011 by Florian Klaempfl and Jonas Maebe Generate JVM inline 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 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 njvminl; {$i fpcdefs.inc} interface uses cpubase, node,ninl,ncginl; type tjvminlinenode = class(tcginlinenode) protected function typecheck_length(var handled: boolean): tnode; function typecheck_high(var handled: boolean): tnode; function typecheck_new(var handled: boolean): tnode; function first_copy: tnode; override; function first_assigned: tnode; override; function first_get_frame: tnode; override; function first_box: tnode; override; function first_unbox: tnode; override; function first_setlength_array: tnode; public { typecheck override to intercept handling } function pass_typecheck: tnode; override; { first pass override so that the code generator will actually generate these nodes. } function first_sqr_real: tnode; override; function first_trunc_real: tnode; override; function first_new: tnode; override; function first_IncludeExclude: tnode; override; function first_setlength: tnode; override; function first_length: tnode; override; procedure second_length; override; procedure second_sqr_real; override; procedure second_trunc_real; override; procedure second_new; override; procedure second_setlength; override; protected procedure load_fpu_location; end; implementation uses cutils,globals,verbose,globtype,constexp,fmodule,compinnr, aasmbase,aasmtai,aasmdata,aasmcpu, symtype,symconst,symdef,symsym,symcpu,symtable,jvmdef, defutil, nadd,nbas,ncon,ncnv,nmat,nmem,ncal,nld,nflw,nutils, cgbase,pass_1,pass_2, cpuinfo,ncgutil, cgutils,hlcgobj,hlcgcpu; {***************************************************************************** tjvminlinenode *****************************************************************************} function tjvminlinenode.typecheck_length(var handled: boolean): tnode; begin result:=nil; typecheckpass(left); if is_open_array(left.resultdef) or is_dynamic_array(left.resultdef) or is_array_of_const(left.resultdef) then begin resultdef:=s32inttype; handled:=true; end; end; function tjvminlinenode.typecheck_high(var handled: boolean): tnode; begin result:=nil; typecheckpass(left); if is_dynamic_array(left.resultdef) or is_open_array(left.resultdef) or is_array_of_const(left.resultdef) then begin { replace with pred(length(arr)) } result:=cinlinenode.create(in_pred_x,false, cinlinenode.create(in_length_x,false,left)); left:=nil; handled:=true; end; end; function tjvminlinenode.typecheck_new(var handled: boolean): tnode; var para: tcallparanode; elemdef: tdef; begin result:=nil; { normally never exists; used by the JVM backend to create new arrays because it requires special opcodes } tcallparanode(left).get_paratype; if is_dynamic_array(left.resultdef) then begin para:=tcallparanode(left); { need at least one extra parameter in addition to the array } if not assigned(para.right) then internalerror(2011012206); elemdef:=tarraydef(left.resultdef).elementdef; while elemdef.typ=arraydef do begin { if we have less length specifiers than dimensions, make the last array an array of length 0 } if not assigned(para.right) then begin para.right:=ccallparanode.create( cordconstnode.create(0,s32inttype,false),nil); tcallparanode(para.right).get_paratype; break; end else begin inserttypeconv(tcallparanode(para.right).left,s32inttype); tcallparanode(para.right).get_paratype; end; para:=tcallparanode(para.right); elemdef:=tarraydef(elemdef).elementdef; end; resultdef:=left.resultdef; handled:=true; end; end; function tjvminlinenode.first_copy: tnode; var ppn: tcallparanode; arr, len, start: tnode; eledef: tdef; counter, ndims: longint; finaltype: char; begin if is_dynamic_array(resultdef) then begin ppn:=tcallparanode(left); counter:=1; while assigned(ppn.right) do begin inc(counter); ppn:=tcallparanode(ppn.right); end; if (counter=3) then begin len:=tcallparanode(left).left; tcallparanode(left).left:=nil; start:=tcallparanode(tcallparanode(left).right).left; tcallparanode(tcallparanode(left).right).left:=nil; { free the original start/len paras and remove them } ppn:=tcallparanode(left); left:=tcallparanode(tcallparanode(left).right).right; tcallparanode(ppn.right).right:=nil; ppn.free; end else begin { use special -1,-1 argument to copy the whole array } len:=genintconstnode(-1); start:=genintconstnode(-1); end; { currently there is one parameter left: the array itself } arr:=tcallparanode(left).left; tcallparanode(left).left:=nil; { in case it's a dynamic array of static arrays, get the dimensions of the static array components } eledef:=tarraydef(resultdef).elementdef; ndims:=1; while (eledef.typ=arraydef) and not is_dynamic_array(eledef) do begin inc(ndims); eledef:=tarraydef(eledef).elementdef; end; { get the final element kind } finaltype:=jvmarrtype_setlength(eledef); { construct the call to fpc_dynarray_copy(src: JLObject; start, len: longint; ndim: longint; eletype: jchar) } result:=ccallnode.createintern('FPC_DYNARRAY_COPY', ccallparanode.create(cordconstnode.create(ord(finaltype),cwidechartype,false), ccallparanode.create(genintconstnode(ndims), ccallparanode.create(len, ccallparanode.create(start, ccallparanode.create(ctypeconvnode.create_explicit(arr,java_jlobject),nil) ) ) ) ) ); inserttypeconv_explicit(result,resultdef); end else result:=inherited first_copy; end; function tjvminlinenode.first_assigned: tnode; begin { on the JVM target, empty arrays can also be <> nil but have length 0 instead. Since assigned(dynarray) is only used to determine whether the length is <> 0 on other targets, replace this expression here } if is_dynamic_array(tcallparanode(left).left.resultdef) then begin result:=caddnode.create(unequaln,cinlinenode.create( in_length_x,false,tcallparanode(left).left),genintconstnode(0)); tcallparanode(left).left:=nil; end else result:=inherited; end; function tjvminlinenode.first_get_frame: tnode; begin { no frame pointer on the JVM target } result:=cnilnode.create; end; function tjvminlinenode.first_box: tnode; var boxdef, boxparadef: tdef; begin { get class wrapper type } jvmgetboxtype(left.resultdef,boxdef,boxparadef,true); { created wrapped instance } inserttypeconv_explicit(tcallparanode(left).left,boxparadef); result:=ccallnode.createinternmethod( cloadvmtaddrnode.create(ctypenode.create(tobjectdef(boxdef))),'CREATE',left); { reused } left:=nil; end; function tjvminlinenode.first_unbox: tnode; var val: tnode; boxdef, boxparadef: tdef; begin jvmgetboxtype(resultdef,boxdef,boxparadef,true); val:=tcallparanode(tcallparanode(left).right).left; tcallparanode(tcallparanode(left).right).left:=nil; { typecast to the boxing type } val:=ctypeconvnode.create_explicit(val,boxdef); { call the unboxing method } val:=ccallnode.createinternmethod(val,jvmgetunboxmethod(resultdef),nil); { add type conversion for shortint -> byte etc } inserttypeconv_explicit(val,resultdef); result:=val; end; function tjvminlinenode.pass_typecheck: tnode; var handled: boolean; begin result:=nil; handled:=false; case inlinenumber of in_length_x: begin result:=typecheck_length(handled); end; in_high_x: begin result:=typecheck_high(handled); end; in_new_x: begin result:=typecheck_new(handled); end; in_sizeof_x: begin { can't get the size of the data of a class/object } if left.resultdef.typ in [objectdef,classrefdef] then Message(parser_e_illegal_expression); end; else ; end; if not handled then result:=inherited pass_typecheck; end; (* function tjvminlinenode.first_sqrt_real : tnode; begin if (current_settings.cputype >= cpu_PPC970) then begin expectloc:=LOC_FPUREGISTER; first_sqrt_real := nil; end else result:=inherited first_sqrt_real; end; *) function tjvminlinenode.first_sqr_real : tnode; begin expectloc:=LOC_FPUREGISTER; first_sqr_real:=nil; end; function tjvminlinenode.first_trunc_real : tnode; begin expectloc:=LOC_REGISTER; first_trunc_real:=nil; end; function tjvminlinenode.first_new: tnode; begin { skip the array; it's a type node } tcallparanode(tcallparanode(left).right).firstcallparan; expectloc:=LOC_REGISTER; result:=nil; end; function tjvminlinenode.first_IncludeExclude: tnode; var setpara: tnode; valuepara: tcallparanode; seteledef: tdef; procname: string[6]; begin setpara:=tcallparanode(left).left; tcallparanode(left).left:=nil; valuepara:=tcallparanode(tcallparanode(left).right); tcallparanode(left).right:=nil; seteledef:=tsetdef(setpara.resultdef).elementdef; setpara:=caddrnode.create_internal(setpara); include(taddrnode(setpara).addrnodeflags,anf_typedaddr); if seteledef.typ=enumdef then begin inserttypeconv_explicit(setpara,java_juenumset); inserttypeconv_explicit(valuepara.left,tcpuenumdef(tenumdef(seteledef).getbasedef).classdef); end else begin inserttypeconv_explicit(setpara,java_jubitset); inserttypeconv_explicit(valuepara.left,s32inttype); end; if inlinenumber=in_include_x_y then procname:='ADD' else procname:='REMOVE'; result:=ccallnode.createinternmethod(setpara,procname,valuepara); end; function tjvminlinenode.first_setlength_array: tnode; var assignmenttarget, ppn, newparas: tnode; newnode: tnode; eledef, objarraydef: tdef; ndims: longint; finaltype: char; setlenroutine: string; lefttemp: ttempcreatenode; newblock: tblocknode; newstatement: tstatementnode; begin { first parameter is the array, the rest are the dimensions } newparas:=tcallparanode(left).right; tcallparanode(left).right:=nil; { count the number of specified dimensions, and determine the type of the final one } ppn:=newparas; eledef:=tarraydef(left.resultdef).elementdef; { ppn already points to the first dimension } ndims:=1; while assigned(tcallparanode(ppn).right) do begin inc(ndims); eledef:=tarraydef(eledef).elementdef; ppn:=tcallparanode(ppn).right; end; { in case it's a dynamic array of static arrays, we must also allocate the static arrays! } while (eledef.typ=arraydef) and not is_dynamic_array(eledef) do begin inc(ndims); tcallparanode(ppn).right:= ccallparanode.create( genintconstnode(tarraydef(eledef).elecount),nil); ppn:=tcallparanode(ppn).right; eledef:=tarraydef(eledef).elementdef; end; { prepend type parameter for the array } newparas:=ccallparanode.create(ctypenode.create(left.resultdef),newparas); ttypenode(tcallparanode(newparas).left).allowed:=true; { node to create the new array } newnode:=cinlinenode.create(in_new_x,false,newparas); { Common parameters for setlength helper } { start with org (save assignmenttarget itself to assign the result back to) } { store left into a temp in case it may contain a function call (which must not be evaluated twice) } newblock:=nil; newstatement:=nil; lefttemp:=maybereplacewithtempref(tcallparanode(left).left,newblock,newstatement,tcallparanode(left).left.resultdef.size,false); if assigned(lefttemp) then begin assignmenttarget:=ctemprefnode.create(lefttemp); typecheckpass(tnode(assignmenttarget)); end else assignmenttarget:=tcallparanode(left).left.getcopy; newparas:=left; left:=nil; finaltype:=jvmarrtype_setlength(eledef); { since the setlength prototypes require certain types, insert explicit type conversions where necessary } objarraydef:=nil; if (ndims>1) then begin { expects array of JLObject } setlenroutine:='FPC_SETLENGTH_DYNARR_MULTIDIM'; objarraydef:=search_system_type('TJOBJECTARRAY').typedef end else begin case finaltype of 'R': begin { expects array of FpcBaseRecord } setlenroutine:='FPC_SETLENGTH_DYNARR_JRECORD'; objarraydef:=search_system_type('TJRECORDARRAY').typedef; end; 'T': begin { expects array of ShortstringClass } setlenroutine:='FPC_SETLENGTH_DYNARR_JSHORTSTRING'; objarraydef:=search_system_type('TSHORTSTRINGARRAY').typedef; end; else begin { expects JLObject } setlenroutine:='FPC_SETLENGTH_DYNARR_GENERIC'; objarraydef:=java_jlobject; end end; end; tcallparanode(newparas).left:=ctypeconvnode.create_explicit(tcallparanode(newparas).left,objarraydef); newnode:=ctypeconvnode.create_explicit(newnode,objarraydef); { prepend new } newparas:=ccallparanode.create(newnode,newparas); { prepend deepcopy } newparas:=ccallparanode.create(cordconstnode.create(0,pasbool1type,false),newparas); { call the right setlength helper } if ndims>1 then begin { create proper parameters, from right to left: eletype=finaltype, ndim=ndims, deepcopy=false, new=newnode, assignmenttarget=tcallparanode(left).left } { prepend ndim } newparas:=ccallparanode.create(cordconstnode.create(ndims,s32inttype,false),newparas); { prepend eletype } newparas:=ccallparanode.create(cordconstnode.create(ord(finaltype),cwidechartype,false),newparas); end else begin { create proper parameters, from right to left: deepcopy=false, new=newnode, assignmenttarget=tcallparnode(left).left -> already done in common part above } end; result:=ccallnode.createintern(setlenroutine,newparas); { assign result back to org (no call-by-reference for Java) } result:=cassignmentnode.create(assignmenttarget, ctypeconvnode.create_explicit(result,assignmenttarget.resultdef)); if assigned(lefttemp) then begin addstatement(newstatement,result); addstatement(newstatement,ctempdeletenode.create(lefttemp)); result:=newblock; end; end; function tjvminlinenode.first_setlength: tnode; begin { reverse the parameter order so we can process them more easily } reverseparameters(tcallparanode(left)); { treat setlength(x,0) specially: used to init uninitialised locations } if not is_shortstring(left.resultdef) and not assigned(tcallparanode(tcallparanode(left).right).right) and is_constintnode(tcallparanode(tcallparanode(left).right).left) and (tordconstnode(tcallparanode(tcallparanode(left).right).left).value=0) then begin result:=nil; expectloc:=LOC_VOID; exit; end; { strings are handled the same as on other platforms } if left.resultdef.typ=stringdef then begin reverseparameters(tcallparanode(left)); result:=inherited first_setlength; exit; end; case left.resultdef.typ of arraydef: result:=first_setlength_array; else internalerror(2011031204); end; end; function tjvminlinenode.first_length: tnode; var newblock: tblocknode; newstatement: tstatementnode; lefttemp, lentemp: ttempcreatenode; ifcond, stringtemp, stringnonnull, stringnull: tnode; psym: tsym; stringclass: tdef; begin if is_wide_or_unicode_string(left.resultdef) or is_ansistring(left.resultdef) then begin { if assigned(stringclass(left)) then lentemp:=stringclass(left).length() else lentemp:=0; --> return lentemp } if is_ansistring(left.resultdef) then stringclass:=java_ansistring else stringclass:=java_jlstring; newblock:=internalstatements(newstatement); { store left into a temp since it may contain a function call (which must not be evaluated twice) } if node_complexity(left)>4 then begin lefttemp:=ctempcreatenode.create_value(stringclass,stringclass.size,tt_persistent,true,ctypeconvnode.create_explicit(left,stringclass)); addstatement(newstatement,lefttemp); stringtemp:=ctemprefnode.create(lefttemp) end else begin lefttemp:=nil; stringtemp:=ctypeconvnode.create_explicit(left,stringclass); end; left:=nil; lentemp:=ctempcreatenode.create(s32inttype,s32inttype.size,tt_persistent,true); addstatement(newstatement,lentemp); { if-condition: assigned(stringclass(stringvar))? } ifcond:=cinlinenode.create(in_assigned_x,false, ccallparanode.create(stringtemp.getcopy,nil)); { then-path: call length() method } psym:=search_struct_member(tabstractrecorddef(stringclass),'LENGTH'); if not assigned(psym) or (psym.typ<>procsym) then internalerror(2011031403); stringnonnull:=cassignmentnode.create( ctemprefnode.create(lentemp), ccallnode.create(nil,tprocsym(psym),psym.owner,stringtemp,[],nil)); { else-path: length is 0 } stringnull:=cassignmentnode.create( ctemprefnode.create(lentemp), genintconstnode(0)); { complete if-statement } addstatement(newstatement,cifnode.create(ifcond,stringnonnull,stringnull)); { free lefttemp } if assigned(lefttemp) then addstatement(newstatement,ctempdeletenode.create(lefttemp)); { return len temp } addstatement(newstatement,ctempdeletenode.create_normal_temp(lentemp)); addstatement(newstatement,ctemprefnode.create(lentemp)); result:=newblock; end else if is_shortstring(left.resultdef) then begin psym:=search_struct_member(tabstractrecorddef(java_shortstring),'LENGTH'); if not assigned(psym) or (psym.typ<>procsym) then internalerror(2011052402); result:= ccallnode.create(nil,tprocsym(psym),psym.owner, ctypeconvnode.create_explicit(caddrnode.create_internal(left),java_shortstring),[],nil); { reused } left:=nil; end { should be no other string types } else if left.resultdef.typ=stringdef then internalerror(2011052403) else result:=inherited first_length; end; procedure tjvminlinenode.second_length; begin if is_dynamic_array(left.resultdef) or is_open_array(left.resultdef) or is_array_of_const(left.resultdef) then begin location_reset(location,LOC_REGISTER,OS_S32); location.register:=hlcg.getintregister(current_asmdata.CurrAsmList,s32inttype); secondpass(left); thlcgjvm(hlcg).g_getarraylen(current_asmdata.CurrAsmList,left.location); thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,resultdef,location.register); end else internalerror(2011012004); end; (* function tjvminlinenode.first_round_real : tnode; begin if (current_settings.cputype >= cpu_PPC970) then begin expectloc:=LOC_REFERENCE; first_round_real := nil; end else result:=inherited first_round_real; end; *) { load the FPU value on the evaluation stack } procedure tjvminlinenode.load_fpu_location; begin secondpass(left); thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,left.resultdef,left.location); end; (* procedure tjvminlinenode.second_sqrt_real; begin if (current_settings.cputype < cpu_PPC970) then internalerror(2007020910); location.loc:=LOC_FPUREGISTER; load_fpu_location; case left.location.size of OS_F32: current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FSQRTS,location.register, left.location.register)); OS_F64: current_asmdata.CurrAsmList.concat(taicpu.op_reg_reg(A_FSQRT,location.register, left.location.register)); else inherited; end; end; *) procedure tjvminlinenode.second_sqr_real; begin load_fpu_location; location_reset(location,LOC_FPUREGISTER,location.size); location.register:=hlcg.getfpuregister(current_asmdata.CurrAsmList,resultdef); case left.location.size of OS_F32: begin current_asmdata.CurrAsmList.concat(taicpu.op_none(a_dup)); thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_fmul)); thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1); end; OS_F64: begin current_asmdata.CurrAsmList.concat(taicpu.op_none(a_dup2)); thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,2); current_asmdata.CurrAsmList.concat(taicpu.op_none(a_dmul)); thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,2); end; else internalerror(2011010804); end; thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,resultdef,location.register); end; procedure tjvminlinenode.second_trunc_real; begin load_fpu_location; location_reset(location,LOC_REGISTER,left.location.size); location.register:=hlcg.getintregister(current_asmdata.CurrAsmList,resultdef); case left.location.size of OS_F32: begin current_asmdata.CurrAsmList.concat(taicpu.op_none(a_f2l)); { 32 bit float -> 64 bit int: +1 stack slot } thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1); end; OS_F64: begin { 64 bit float -> 64 bit int: same number of stack slots } current_asmdata.CurrAsmList.concat(taicpu.op_none(a_d2l)); end; else internalerror(2011010805); end; thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,resultdef,location.register); end; procedure tjvminlinenode.second_new; var arr: tnode; hp: tcallparanode; paracount: longint; begin hp:=tcallparanode(left); { we don't second pass this one, it's only a type node } arr:=hp.left; if not is_dynamic_array(arr.resultdef) then internalerror(2011012204); hp:=tcallparanode(hp.right); if not assigned(hp) then internalerror(2011012205); paracount:=0; { put all the dimensions on the stack } repeat inc(paracount); secondpass(hp.left); thlcgjvm(hlcg).a_load_loc_stack(current_asmdata.CurrAsmList,hp.left.resultdef,hp.left.location); hp:=tcallparanode(hp.right); until not assigned(hp); { create the array } thlcgjvm(hlcg).g_newarray(current_asmdata.CurrAsmList,arr.resultdef,paracount); location_reset(location,LOC_REGISTER,OS_ADDR); location.register:=hlcg.getaddressregister(current_asmdata.CurrAsmList,resultdef); thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,arr.resultdef,location.register); end; procedure tjvminlinenode.second_setlength; var target: tnode; lenpara: tnode; emptystr: ansichar; tmpreg: tregister; begin target:=tcallparanode(left).left; lenpara:=tcallparanode(tcallparanode(left).right).left; if assigned(tcallparanode(tcallparanode(left).right).right) or not is_constintnode(lenpara) or (tordconstnode(lenpara).value<>0) then internalerror(2011031801); secondpass(target); { can't directly load from stack to destination, because if target is a reference then its address must be placed on the stack before the value } tmpreg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,target.resultdef); if is_wide_or_unicode_string(target.resultdef) then begin emptystr:=#0; current_asmdata.CurrAsmList.concat(taicpu.op_string(a_ldc,0,@emptystr)); thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,1); end else if is_ansistring(target.resultdef) then thlcgjvm(hlcg).a_load_const_stack(current_asmdata.CurrAsmList,java_jlobject,0,R_ADDRESSREGISTER) else if is_dynamic_array(target.resultdef) then begin thlcgjvm(hlcg).a_load_const_stack(current_asmdata.CurrAsmList,s32inttype,0,R_INTREGISTER); thlcgjvm(hlcg).g_newarray(current_asmdata.CurrAsmList,target.resultdef,1); end else internalerror(2011031401); thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,target.resultdef,tmpreg); thlcgjvm(hlcg).a_load_reg_loc(current_asmdata.CurrAsmList,target.resultdef,target.resultdef,tmpreg,target.location); end; begin cinlinenode:=tjvminlinenode; end.