njvmld.pas 12 KB

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