njvmcal.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. {
  2. Copyright (c) 2011 by Jonas Maebe
  3. JVM-specific code for call nodes
  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 njvmcal;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. cgbase,
  22. symtype,symdef,
  23. ncgcal;
  24. type
  25. tjvmcallparanode = class(tcgcallparanode)
  26. protected
  27. outcopybasereg: tregister;
  28. procedure push_formal_para; override;
  29. procedure push_copyout_para; override;
  30. procedure handleformalcopyoutpara(orgparadef: tdef); override;
  31. procedure load_arrayref_para(useparadef: tdef);
  32. end;
  33. { tjvmcallnode }
  34. tjvmcallnode = class(tcgcallnode)
  35. protected
  36. procedure extra_pre_call_code; override;
  37. procedure set_result_location(realresdef: tstoreddef); override;
  38. procedure do_release_unused_return_value;override;
  39. procedure extra_post_call_code; override;
  40. end;
  41. implementation
  42. uses
  43. verbose,globtype,constexp,
  44. symconst,defutil,ncal,
  45. cgutils,tgobj,procinfo,
  46. cpubase,aasmdata,aasmcpu,
  47. hlcgobj,hlcgcpu,
  48. pass_1,node,nutils,nbas,ncnv,ncon,ninl,nld,nmem,
  49. jvmdef;
  50. {*****************************************************************************
  51. TJVMCALLPARANODE
  52. *****************************************************************************}
  53. procedure tjvmcallparanode.load_arrayref_para(useparadef: tdef);
  54. var
  55. arrayloc: tlocation;
  56. arrayref: treference;
  57. begin
  58. { cannot be a regular array or record, because those are passed by
  59. plain reference (since they are reference types at the Java level,
  60. but not at the Pascal level) -> no special initialisation necessary }
  61. outcopybasereg:=hlcg.getaddressregister(current_asmdata.CurrAsmList,java_jlobject);
  62. thlcgjvm(hlcg).a_load_stack_reg(current_asmdata.CurrAsmList,java_jlobject,outcopybasereg);
  63. reference_reset_base(arrayref,outcopybasereg,0,4);
  64. arrayref.arrayreftype:=art_indexconst;
  65. arrayref.indexoffset:=0;
  66. { load the current parameter value into the array in case it's not an
  67. out-parameter; if it's an out-parameter the contents must be nil
  68. but that's already ok, since the anewarray opcode takes care of that }
  69. if (parasym.varspez<>vs_out) then
  70. hlcg.a_load_loc_ref(current_asmdata.CurrAsmList,useparadef,useparadef,left.location,arrayref);
  71. { store the array reference into the parameter location (don't change
  72. left.location, we may need it for copy-back after the call) }
  73. location_reset(arrayloc,LOC_REGISTER,OS_ADDR);
  74. arrayloc.register:=outcopybasereg;
  75. hlcg.gen_load_loc_cgpara(current_asmdata.CurrAsmList,java_jlobject,arrayloc,tempcgpara)
  76. end;
  77. procedure tjvmcallparanode.push_formal_para;
  78. begin
  79. { primitive values are boxed, so in all cases this is a pointer to
  80. something and since it cannot be changed (or is not supposed to be
  81. changed anyway), we don't have to create a temporary array to hold a
  82. pointer to this value and can just pass the pointer to this value
  83. directly.
  84. In case the value can be changed (formal var/out), then we have
  85. already created a temporary array of one element that holds the boxed
  86. (or in case of a non-primitive type: original) value. The reason is
  87. that copying it back out may be a complex operation which we don't
  88. want to handle at the code generator level.
  89. -> always push a value parameter (which is either an array of one
  90. element, or an object) }
  91. push_value_para
  92. end;
  93. procedure tjvmcallparanode.push_copyout_para;
  94. var
  95. mangledname: string;
  96. primitivetype: boolean;
  97. opc: tasmop;
  98. begin
  99. { create an array with one element of the parameter type }
  100. thlcgjvm(hlcg).a_load_const_stack(current_asmdata.CurrAsmList,s32inttype,1,R_INTREGISTER);
  101. mangledname:=jvmarrtype(left.resultdef,primitivetype);
  102. if primitivetype then
  103. opc:=a_newarray
  104. else
  105. opc:=a_anewarray;
  106. { doesn't change stack height: one int replaced by one reference }
  107. current_asmdata.CurrAsmList.concat(taicpu.op_sym(opc,current_asmdata.RefAsmSymbol(mangledname)));
  108. load_arrayref_para(left.resultdef);
  109. end;
  110. procedure getparabasenodes(p: tnode; out basenode: tnode; out parent: tunarynode);
  111. begin
  112. parent:=nil;
  113. while assigned(p) do
  114. begin
  115. case p.nodetype of
  116. inlinen:
  117. begin
  118. if tinlinenode(p).inlinenumber=in_box_x then
  119. begin
  120. parent:=tunarynode(p);
  121. p:=parent.left;
  122. end
  123. else
  124. break;
  125. end;
  126. subscriptn,
  127. vecn:
  128. begin
  129. break;
  130. end;
  131. typeconvn:
  132. begin
  133. parent:=tunarynode(p);
  134. { skip typeconversions that don't change the node type }
  135. p:=p.actualtargetnode;
  136. end;
  137. derefn:
  138. begin
  139. parent:=tunarynode(p);
  140. p:=tunarynode(p).left;
  141. end
  142. else
  143. break;
  144. end;
  145. end;
  146. basenode:=p;
  147. end;
  148. function replacewithtemps(var orgnode, copiednode: tnode): ttempcreatenode;
  149. begin
  150. result:=ctempcreatenode.create_value(
  151. orgnode.resultdef,orgnode.resultdef.size,
  152. tt_persistent,true,orgnode);
  153. { this right is reused while constructing the temp }
  154. orgnode:=ctemprefnode.create(result);
  155. typecheckpass(orgnode);
  156. { this right is not reused }
  157. copiednode.free;
  158. copiednode:=ctemprefnode.create(result);
  159. typecheckpass(copiednode);
  160. end;
  161. procedure tjvmcallparanode.handleformalcopyoutpara(orgparadef: tdef);
  162. var
  163. paravaltemp,
  164. arraytemp,
  165. indextemp: ttempcreatenode;
  166. arrdef: tarraydef;
  167. initstat,
  168. finistat: tstatementnode;
  169. leftcopy: tnode;
  170. realpara, copyrealpara, tempn, assignmenttempn: tnode;
  171. realparaparent,copyrealparaparent: tunarynode;
  172. derefbasedef: tdef;
  173. deref: boolean;
  174. begin
  175. fparainit:=internalstatements(initstat);
  176. { In general, we now create a temp array of one element, assign left
  177. (or its address in case of a jvmimplicitpointertype) to it, replace
  178. the parameter with this array, and add code to paracopyback that
  179. extracts the value from the array again and assigns it to the original
  180. variable.
  181. Complications
  182. a) in case the parameter involves calling a function, it must not
  183. be called twice, so take the address of the location (since this
  184. is a var/out parameter, taking the address is conceptually
  185. always possible)
  186. b) in case this is an element of a string, we can't take the address
  187. in JVM code, so we then have to take the address of the string
  188. (which conceptually may not be possible since it can be a
  189. property or so) and store the index value into a temp, and
  190. reconstruct the vecn in te paracopyback code from this data
  191. (it's similar for normal var/out parameters)
  192. }
  193. { we'll replace a bunch of stuff in the parameter with temprefnodes,
  194. but we can't take a getcopy for the assignment afterwards of this
  195. result since a getcopy will always assume that we are copying the
  196. init/deletenodes too and that the temprefnodes have to point to the
  197. new temps -> get a copy of the parameter in advance, and then replace
  198. the nodes in the copy with temps just like in the original para }
  199. leftcopy:=left.getcopy;
  200. { get the real parameter source in case of type conversions. This is
  201. the same logic as for set_unique(). The parent is where we have to
  202. replace realpara with the temp that replaces it. }
  203. getparabasenodes(left,realpara,realparaparent);
  204. getparabasenodes(leftcopy,copyrealpara,copyrealparaparent);
  205. { assign either the parameter's address (in case it's an implicit
  206. pointer type) or the parameter itself (in case it's a primitive or
  207. actual pointer/object type) to the temp }
  208. deref:=false;
  209. if jvmimplicitpointertype(realpara.resultdef) then
  210. begin
  211. derefbasedef:=realpara.resultdef;
  212. realpara:=caddrnode.create_internal(realpara);
  213. include(realpara.flags,nf_typedaddr);
  214. typecheckpass(realpara);
  215. { we'll have to reference the parameter again in the expression }
  216. deref:=true;
  217. end;
  218. paravaltemp:=nil;
  219. { make sure we don't replace simple loadnodes with a temp, because
  220. in case of passing e.g. stringvar[3] to a formal var/out parameter,
  221. we add "stringvar[3]:=<result>" afterwards. Because Java strings are
  222. immutable, this is translated into "stringvar:=stringvar.setChar(3,
  223. <result>)". So if we replace stringvar with a temp, this will change
  224. the temp rather than stringvar. }
  225. indextemp:=nil;
  226. if (realpara.nodetype=vecn) then
  227. begin
  228. if node_complexity(tvecnode(realpara).left)>1 then
  229. begin
  230. paravaltemp:=replacewithtemps(tvecnode(realpara).left,
  231. tvecnode(copyrealpara).left);
  232. addstatement(initstat,paravaltemp);
  233. end;
  234. { in case of an array index, also replace the index with a temp if
  235. necessary/useful }
  236. if (node_complexity(tvecnode(realpara).right)>1) then
  237. begin
  238. indextemp:=replacewithtemps(tvecnode(realpara).right,
  239. tvecnode(copyrealpara).right);
  240. addstatement(initstat,indextemp);
  241. end;
  242. end
  243. else
  244. begin
  245. paravaltemp:=ctempcreatenode.create_value(
  246. realpara.resultdef,java_jlobject.size,tt_persistent,true,realpara);
  247. addstatement(initstat,paravaltemp);
  248. { replace the parameter in the parameter expression with this temp }
  249. tempn:=ctemprefnode.create(paravaltemp);
  250. assignmenttempn:=ctemprefnode.create(paravaltemp);
  251. { will be spliced in the middle of a tree that has already been
  252. typecheckpassed }
  253. typecheckpass(tempn);
  254. typecheckpass(assignmenttempn);
  255. if assigned(realparaparent) then
  256. begin
  257. { left has been reused in paravaltemp (it's realpara itself) ->
  258. don't free }
  259. realparaparent.left:=tempn;
  260. { the left's copy is not reused }
  261. copyrealparaparent.left.free;
  262. copyrealparaparent.left:=assignmenttempn;
  263. end
  264. else
  265. begin
  266. { left has been reused in paravaltemp (it's realpara itself) ->
  267. don't free }
  268. left:=tempn;
  269. { leftcopy can remain the same }
  270. assignmenttempn.free;
  271. end;
  272. end;
  273. { create the array temp that and assign the parameter value (typecasted
  274. to java_jlobject) }
  275. arrdef:=tarraydef.create(0,1,s32inttype);
  276. arrdef.elementdef:=java_jlobject;
  277. arraytemp:=ctempcreatenode.create(arrdef,java_jlobject.size,
  278. tt_persistent,true);
  279. addstatement(initstat,arraytemp);
  280. { wrap the primitive type in an object container
  281. if required }
  282. if (left.resultdef.typ in [orddef,floatdef]) then
  283. begin
  284. left:=cinlinenode.create(in_box_x,false,ccallparanode.create(left,nil));
  285. typecheckpass(left);
  286. end;
  287. addstatement(initstat,cassignmentnode.create(
  288. cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(0)),
  289. ctypeconvnode.create_explicit(left,java_jlobject)));
  290. { replace the parameter with the array }
  291. left:=ctemprefnode.create(arraytemp);
  292. { add the extraction of the parameter and assign it back to the
  293. original location }
  294. fparacopyback:=internalstatements(finistat);
  295. tempn:=cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(0));
  296. { unbox if necessary }
  297. if orgparadef.typ in [orddef,floatdef] then
  298. tempn:=cinlinenode.create(in_unbox_x_y,false,ccallparanode.create(
  299. ctypenode.create(orgparadef),ccallparanode.create(tempn,nil)));
  300. if (deref) then
  301. begin
  302. inserttypeconv_explicit(tempn,getpointerdef(derefbasedef));
  303. tempn:=cderefnode.create(tempn);
  304. end;
  305. addstatement(finistat,cassignmentnode.create(leftcopy,
  306. ctypeconvnode.create_explicit(tempn,orgparadef)));
  307. if assigned(indextemp) then
  308. addstatement(finistat,ctempdeletenode.create(indextemp));
  309. addstatement(finistat,ctempdeletenode.create(arraytemp));
  310. if assigned(paravaltemp) then
  311. addstatement(finistat,ctempdeletenode.create(paravaltemp));
  312. typecheckpass(fparainit);
  313. typecheckpass(left);
  314. typecheckpass(fparacopyback);
  315. end;
  316. {*****************************************************************************
  317. TJVMCALLNODE
  318. *****************************************************************************}
  319. procedure tjvmcallnode.extra_pre_call_code;
  320. begin
  321. { when calling a constructor, first create a new instance, except
  322. when calling it from another constructor (because then this has
  323. already been done before calling the current constructor) }
  324. if procdefinition.typ<>procdef then
  325. exit;
  326. if tabstractprocdef(procdefinition).proctypeoption<>potype_constructor then
  327. exit;
  328. if not(methodpointer.resultdef.typ in [classrefdef,recorddef]) then
  329. exit;
  330. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(tabstractrecorddef(tabstractprocdef(procdefinition).owner.defowner).jvm_full_typename(true))));
  331. { the constructor doesn't return anything, so put a duplicate of the
  332. self pointer on the evaluation stack for use as function result
  333. after the constructor has run }
  334. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_dup));
  335. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,2);
  336. end;
  337. procedure tjvmcallnode.set_result_location(realresdef: tstoreddef);
  338. begin
  339. location_reset_ref(location,LOC_REFERENCE,def_cgsize(realresdef),1);
  340. { in case of jvmimplicitpointertype(), the function will have allocated
  341. it already and we don't have to allocate it again here }
  342. if not jvmimplicitpointertype(realresdef) then
  343. tg.gethltemp(current_asmdata.CurrAsmList,realresdef,realresdef.size,tt_normal,location.reference)
  344. else
  345. tg.gethltemp(current_asmdata.CurrAsmList,java_jlobject,java_jlobject.size,tt_normal,location.reference);
  346. end;
  347. procedure tjvmcallnode.do_release_unused_return_value;
  348. begin
  349. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and
  350. (current_procinfo.procdef.proctypeoption=potype_constructor) then
  351. exit;
  352. if (location.loc=LOC_REFERENCE) then
  353. tg.ungetiftemp(current_asmdata.CurrAsmList,location.reference);
  354. if assigned(funcretnode) then
  355. exit;
  356. case resultdef.size of
  357. 0:
  358. ;
  359. 1..4:
  360. begin
  361. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop));
  362. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  363. end;
  364. 8:
  365. begin
  366. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop2));
  367. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,2);
  368. end
  369. else
  370. internalerror(2011010305);
  371. end;
  372. end;
  373. procedure tjvmcallnode.extra_post_call_code;
  374. var
  375. totalremovesize: longint;
  376. realresdef: tdef;
  377. ppn: tjvmcallparanode;
  378. pararef: treference;
  379. begin
  380. if not assigned(typedef) then
  381. realresdef:=tstoreddef(resultdef)
  382. else
  383. realresdef:=tstoreddef(typedef);
  384. { a constructor doesn't actually return a value in the jvm }
  385. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) then
  386. totalremovesize:=pushedparasize
  387. else
  388. { even a byte takes up a full stackslot -> align size to multiple of 4 }
  389. totalremovesize:=pushedparasize-(align(realresdef.size,4) shr 2);
  390. { remove parameters from internal evaluation stack counter (in case of
  391. e.g. no parameters and a result, it can also increase) }
  392. if totalremovesize>0 then
  393. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,totalremovesize)
  394. else if totalremovesize<0 then
  395. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,-totalremovesize);
  396. { if this was an inherited constructor call, initialise all fields that
  397. are wrapped types following it }
  398. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and
  399. (cnf_inherited in callnodeflags) then
  400. thlcgjvm(hlcg).gen_initialize_fields_code(current_asmdata.CurrAsmList);
  401. { copy back the copyout parameter values, if any }
  402. { Release temps from parameters }
  403. ppn:=tjvmcallparanode(left);
  404. while assigned(ppn) do
  405. begin
  406. if assigned(ppn.left) then
  407. begin
  408. if (ppn.outcopybasereg<>NR_NO) then
  409. begin
  410. reference_reset_base(pararef,NR_NO,0,4);
  411. pararef.arrayreftype:=art_indexconst;
  412. pararef.base:=ppn.outcopybasereg;
  413. pararef.indexoffset:=0;
  414. { the value has to be copied back into persistent storage }
  415. if (ppn.parasym.vardef.typ<>formaldef) then
  416. begin
  417. case ppn.left.location.loc of
  418. LOC_REFERENCE:
  419. hlcg.a_load_ref_ref(current_asmdata.CurrAsmList,ppn.left.resultdef,ppn.left.resultdef,pararef,ppn.left.location.reference);
  420. LOC_CREGISTER:
  421. hlcg.a_load_ref_reg(current_asmdata.CurrAsmList,ppn.left.resultdef,ppn.left.resultdef,pararef,ppn.left.location.register);
  422. else
  423. internalerror(2011051201);
  424. end;
  425. end
  426. else
  427. begin
  428. { extracting values from foramldef parameters is done
  429. by the generic code }
  430. end;
  431. end;
  432. end;
  433. ppn:=tjvmcallparanode(ppn.right);
  434. end;
  435. end;
  436. begin
  437. ccallnode:=tjvmcallnode;
  438. ccallparanode:=tjvmcallparanode;
  439. end.