1 {
2     Copyright (c) 2011 by Jonas Maebe
3 
4     Generate JVM assembler for nodes that handle loads and assignments
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20  ****************************************************************************
21 }
22 unit njvmld;
23 
24 {$I fpcdefs.inc}
25 
26 interface
27 
28 uses
29   globtype,
30   aasmdata,
31   symtype,
32   cgutils,
33   node, ncgld, ncgnstld;
34 
35 type
36   tjvmloadnode = class(tcgnestloadnode)
37    protected
is_copyout_addr_param_loadnull38     function is_copyout_addr_param_load: boolean;
handle_threadvar_accessnull39     function handle_threadvar_access: tnode; override;
keep_param_address_in_nested_structnull40     function keep_param_address_in_nested_struct: boolean; override;
41    public
is_addr_param_loadnull42     function is_addr_param_load: boolean; override;
43     procedure pass_generate_code; override;
44   end;
45 
46   tjvmassignmentnode  = class(tcgassignmentnode)
47    protected
direct_shortstring_assignmentnull48     function direct_shortstring_assignment: boolean; override;
maybechangetempnull49     function maybechangetemp(list: TAsmList; var n: tnode; const newref: treference): boolean;override;
50    public
pass_1null51     function pass_1: tnode; override;
52   end;
53 
54   tjvmarrayconstructornode = class(tcgarrayconstructornode)
55    protected
56     procedure makearrayref(var ref: treference; eledef: tdef); override;
57     procedure advancearrayoffset(var ref: treference; elesize: asizeint); override;
58     procedure wrapmanagedvarrec(var n: tnode);override;
59   end;
60 
61 implementation
62 
63 uses
64   verbose,globals,compinnr,
65   nbas,nld,ncal,ncon,ninl,nmem,ncnv,nutils,
66   symconst,symsym,symdef,symtable,defutil,jvmdef,
67   paramgr,
68   pass_1,
69   cpubase,cgbase,hlcgobj,cpuinfo;
70 
71 { tjvmassignmentnode }
72 
tjvmassignmentnode.direct_shortstring_assignmentnull73 function tjvmassignmentnode.direct_shortstring_assignment: boolean;
74   begin
75     if maybe_find_real_class_definition(right.resultdef,false)=java_jlstring then
76       inserttypeconv_explicit(right,cunicodestringtype);
77     result:=right.resultdef.typ=stringdef;
78   end;
79 
80 
tjvmassignmentnode.maybechangetempnull81 function tjvmassignmentnode.maybechangetemp(list: TAsmList; var n: tnode; const newref: treference): boolean;
82   begin
83     { don't do this when compiling for Dalvik, because it can invalidate the
84       debug information (which Dalvik uses as extra type information) }
85     if current_settings.cputype<>cpu_dalvik then
86       result:=inherited
87     else
88       result:=false;
89   end;
90 
91 
tjvmassignmentnode.pass_1null92 function tjvmassignmentnode.pass_1: tnode;
93   var
94     block: tblocknode;
95     tempn: ttempcreatenode;
96     stat: tstatementnode;
97     target: tnode;
98     psym: tsym;
99   begin
100     { intercept writes to string elements, because Java strings are immutable
101       -> detour via StringBuilder
102     }
103     target:=actualtargetnode(@left)^;
104     if (target.nodetype=vecn) and
105        (is_wide_or_unicode_string(tvecnode(target).left.resultdef) or
106         is_ansistring(tvecnode(target).left.resultdef)) then
107       begin
108         { prevent errors in case of an expression such as
109             word(unicodestr[x]):=1234;
110         }
111         if is_wide_or_unicode_string(tvecnode(target).left.resultdef) then
112           inserttypeconv_explicit(right,cwidechartype)
113         else
114           inserttypeconv_explicit(right,cansichartype);
115         result:=ccallnode.createintern('fpc_'+tstringdef(tvecnode(target).left.resultdef).stringtypname+'_setchar',
116           ccallparanode.create(right,
117             ccallparanode.create(tvecnode(target).right,
118               ccallparanode.create(tvecnode(target).left.getcopy,nil))));
119         result:=cassignmentnode.create(tvecnode(target).left,result);
120         right:=nil;
121         tvecnode(target).left:=nil;
122         tvecnode(target).right:=nil;
123         exit;
124       end
125     else if (target.nodetype=vecn) and
126        is_shortstring(tvecnode(target).left.resultdef) then
127       begin
128         { prevent errors in case of an expression such as
129             byte(str[x]):=12;
130         }
131         inserttypeconv_explicit(right,cansichartype);
132         { call ShortstringClass(@shortstring).setChar(index,char) }
133         tvecnode(target).left:=caddrnode.create_internal(tvecnode(target).left);
134         { avoid useless typecheck when casting to shortstringclass }
135         include(taddrnode(tvecnode(target).left).addrnodeflags,anf_typedaddr);
136         inserttypeconv_explicit(tvecnode(target).left,java_shortstring);
137         psym:=search_struct_member(tabstractrecorddef(java_shortstring),'SETCHAR');
138         if not assigned(psym) or
139            (psym.typ<>procsym) then
140           internalerror(2011052408);
141         result:=
142           ccallnode.create(
143             ccallparanode.create(right,
144               ccallparanode.create(tvecnode(target).right,nil)),
145             tprocsym(psym),psym.owner,tvecnode(target).left,[],nil);
146         right:=nil;
147         tvecnode(target).left:=nil;
148         tvecnode(target).right:=nil;
149         exit;
150       end
151     else if target.resultdef.typ=formaldef then
152       begin
153         if right.resultdef.typ in [orddef,floatdef] then
154           right:=cinlinenode.create(in_box_x,false,right)
155         else if jvmimplicitpointertype(right.resultdef) then
156           begin
157             { we have to assign the address of a deep copy of the type to the
158               object in the formalpara -> create a temp, assign the value to
159               the temp, then assign the address in the temp to the para }
160             block:=internalstatements(stat);
161             tempn:=ctempcreatenode.create_value(right.resultdef,right.resultdef.size,
162               tt_persistent,false,right);
163             addstatement(stat,tempn);
164             right:=caddrnode.create(ctemprefnode.create(tempn));
165             inserttypeconv_explicit(right,java_jlobject);
166             addstatement(stat,ctempdeletenode.create_normal_temp(tempn));
167             addstatement(stat,ctypeconvnode.create_explicit(
168               caddrnode.create(ctemprefnode.create(tempn)),java_jlobject));
169             right:=block;
170           end;
171         typecheckpass(right);
172         result:=inherited;
173         exit;
174       end
175     else
176       result:=inherited;
177   end;
178 
179 
tjvmloadnode.is_copyout_addr_param_loadnull180 function tjvmloadnode.is_copyout_addr_param_load: boolean;
181   begin
182     result:=
183       { passed via array of one element }
184       ((symtable.symtabletype=parasymtable) and
185        (symtableentry.typ=paravarsym) and
186        paramanager.push_copyout_param(tparavarsym(symtableentry).varspez,resultdef,tprocdef(symtable.defowner).proccalloption));
187   end;
188 
189 
tjvmloadnode.handle_threadvar_accessnull190 function tjvmloadnode.handle_threadvar_access: tnode;
191   var
192     vs: tsym;
193   begin
194     { get the variable wrapping the threadvar }
195     vs:=tsym(symtable.find(symtableentry.name+'$THREADVAR'));
196     if not assigned(vs) or
197        (vs.typ<>staticvarsym) then
198       internalerror(2011082201);
199     { get a read/write reference to the threadvar value }
200     result:=cloadnode.create(vs,vs.owner);
201     typecheckpass(result);
202     result:=ccallnode.createinternmethod(result,'GETREADWRITEREFERENCE',nil);
203     if not(tstaticvarsym(symtableentry).vardef.typ in [orddef,floatdef]) and
204        not jvmimplicitpointertype(tstaticvarsym(symtableentry).vardef) then
205       begin
206         { in these cases, the threadvar was internally constructed as an
207           "array of jlobject", while the variable itself is a different kind of
208           pointer (dynarmic array, class, interface, pointer type). We cannot
209           typecast an "array of jlobject" to e.g. an "array of array of byte",
210           even if all elements inside the array are "array of byte" (since the
211           outer array type is simply different) -> first dereference (= select
212           the array element) and then typecast to the result type. This works
213           even on the left-hand side because then we get e.g.
214             jlobject(threavarinstance.getreadwritereference^):=value;
215 
216           threavarinstance.getreadwritereference returns a ppointer in these
217           cases.
218         }
219         result:=cderefnode.create(result);
220         result:=ctypeconvnode.create_explicit(result,resultdef);
221       end
222     else
223       begin
224         result:=ctypeconvnode.create_explicit(result,cpointerdef.getreusable(resultdef));
225         result:=cderefnode.create(result);
226       end;
227   end;
228 
229 
tjvmloadnode.keep_param_address_in_nested_structnull230 function tjvmloadnode.keep_param_address_in_nested_struct: boolean;
231   begin
232     { we don't need an extra load when implicit pointer types  are passed as
233       var/out/constref parameter (since they are already pointers). However,
234       when transfering them into a nestedfp struct, we do want to transfer the
235       pointer and not make a deep copy in case they are var/out/constref (since
236       changes made to the var/out parameter should propagate up) }
237     result:=
238      is_addr_param_load or
239      ((symtableentry.typ=paravarsym) and
240       jvmimplicitpointertype(tparavarsym(symtableentry).vardef) and
241       (tparavarsym(symtableentry).varspez in [vs_var,vs_constref,vs_out]));
242   end;
243 
244 
tjvmloadnode.is_addr_param_loadnull245 function tjvmloadnode.is_addr_param_load: boolean;
246   begin
247     result:=
248       (inherited is_addr_param_load and
249        not jvmimplicitpointertype(tparavarsym(symtableentry).vardef) and
250        (tparavarsym(symtableentry).vardef.typ<>formaldef)) or
251       is_copyout_addr_param_load;
252   end;
253 
254 
255 procedure tjvmloadnode.pass_generate_code;
256   begin
257     if is_copyout_addr_param_load then
258       begin
259         { in case of nested access, load address of field in nestedfpstruct }
260         if assigned(left) then
261           generate_nested_access(tabstractnormalvarsym(symtableentry));
262         location_reset_ref(location,LOC_REFERENCE,def_cgsize(resultdef),4,[]);
263         location.reference.arrayreftype:=art_indexconst;
264         location.reference.base:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlobject);
265         location.reference.indexoffset:=0;
266         { load the field from the nestedfpstruct, or the parameter location.
267           In both cases, the result is an array of one element containing the
268           parameter value }
269         if assigned(left) then
270           hlcg.a_load_loc_reg(current_asmdata.CurrAsmList,java_jlobject,java_jlobject,left.location,location.reference.base)
271         else
272           hlcg.a_load_loc_reg(current_asmdata.CurrAsmList,java_jlobject,java_jlobject,tparavarsym(symtableentry).localloc,location.reference.base);
273       end
274     else if symtableentry.typ=procsym then
275       { handled in tjvmcnvnode.first_proc_to_procvar }
276       internalerror(2011072408)
277     else
278       inherited pass_generate_code;
279   end;
280 
281 
282 { tjvmarrayconstructornode }
283 
284 procedure tjvmarrayconstructornode.makearrayref(var ref: treference; eledef: tdef);
285   var
286     basereg: tregister;
287   begin
288     { arrays are implicitly dereferenced }
289     basereg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlobject);
290     hlcg.a_load_ref_reg(current_asmdata.CurrAsmList,java_jlobject,java_jlobject,ref,basereg);
291     reference_reset_base(ref,basereg,0,ctempposinvalid,1,[]);
292     ref.arrayreftype:=art_indexconst;
293     ref.indexoffset:=0;
294   end;
295 
296 
297 procedure tjvmarrayconstructornode.advancearrayoffset(var ref: treference; elesize: asizeint);
298   begin
299     inc(ref.indexoffset);
300   end;
301 
302 
303 procedure tjvmarrayconstructornode.wrapmanagedvarrec(var n: tnode);
304   var
305     varrecdef: trecorddef;
306     block: tblocknode;
307     stat: tstatementnode;
308     temp: ttempcreatenode;
309   begin
310     varrecdef:=trecorddef(search_system_type('TVARREC').typedef);
311     block:=internalstatements(stat);
312     temp:=ctempcreatenode.create(varrecdef,varrecdef.size,tt_persistent,false);
313     addstatement(stat,temp);
314     addstatement(stat,
315       ccallnode.createinternmethod(
316         ctemprefnode.create(temp),'INIT',ccallparanode.create(n,nil)));
317     { note: this will not free the record contents, but just let its reference
318       on the stack be reused -- which is ok, because the reference will be
319       stored into the open array parameter }
320     addstatement(stat,ctempdeletenode.create_normal_temp(temp));
321     addstatement(stat,ctemprefnode.create(temp));
322     n:=block;
323     firstpass(n);
324   end;
325 
326 
327 begin
328   cloadnode:=tjvmloadnode;
329   cassignmentnode:=tjvmassignmentnode;
330   carrayconstructornode:=tjvmarrayconstructornode;
331 end.
332 
333