njvmcal.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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. node,ncal,ncgcal;
  24. type
  25. tjvmcallparanode = class(tcgcallparanode)
  26. protected
  27. procedure push_formal_para; override;
  28. procedure push_copyout_para; override;
  29. procedure handlemanagedbyrefpara(orgparadef: tdef); override;
  30. end;
  31. { tjvmcallnode }
  32. tjvmcallnode = class(tcgcallnode)
  33. protected
  34. procedure wrapcomplexinlinepara(para: tcallparanode); override;
  35. procedure extra_pre_call_code; override;
  36. procedure set_result_location(realresdef: tstoreddef); override;
  37. procedure do_release_unused_return_value;override;
  38. procedure extra_post_call_code; override;
  39. function dispatch_procvar: tnode;
  40. procedure remove_hidden_paras;
  41. public
  42. function pass_1: tnode; override;
  43. end;
  44. implementation
  45. uses
  46. verbose,globtype,constexp,cutils,
  47. symconst,symtable,symsym,defutil,
  48. cgutils,tgobj,procinfo,htypechk,
  49. cpubase,aasmdata,aasmcpu,
  50. hlcgobj,hlcgcpu,
  51. pass_1,nutils,nbas,ncnv,ncon,ninl,nld,nmem,
  52. jvmdef;
  53. {*****************************************************************************
  54. TJVMCALLPARANODE
  55. *****************************************************************************}
  56. procedure tjvmcallparanode.push_formal_para;
  57. begin
  58. { primitive values are boxed, so in all cases this is a pointer to
  59. something and since it cannot be changed (or is not supposed to be
  60. changed anyway), we don't have to create a temporary array to hold a
  61. pointer to this value and can just pass the pointer to this value
  62. directly.
  63. In case the value can be changed (formal var/out), then we have
  64. already created a temporary array of one element that holds the boxed
  65. (or in case of a non-primitive type: original) value. The reason is
  66. that copying it back out may be a complex operation which we don't
  67. want to handle at the code generator level.
  68. -> always push a value parameter (which is either an array of one
  69. element, or an object) }
  70. push_value_para
  71. end;
  72. procedure tjvmcallparanode.push_copyout_para;
  73. begin
  74. { everything is wrapped and replaced by handlemanagedbyrefpara() in
  75. pass_1 }
  76. push_value_para;
  77. end;
  78. procedure getparabasenodes(p: tnode; out basenode: tnode; out parent: tunarynode);
  79. begin
  80. parent:=nil;
  81. while assigned(p) do
  82. begin
  83. case p.nodetype of
  84. inlinen:
  85. begin
  86. if tinlinenode(p).inlinenumber=in_box_x then
  87. begin
  88. parent:=tunarynode(p);
  89. p:=parent.left;
  90. end
  91. else
  92. break;
  93. end;
  94. subscriptn,
  95. vecn:
  96. begin
  97. break;
  98. end;
  99. typeconvn:
  100. begin
  101. parent:=tunarynode(p);
  102. { skip typeconversions that don't change the node type }
  103. p:=p.actualtargetnode;
  104. end;
  105. derefn:
  106. begin
  107. parent:=tunarynode(p);
  108. p:=tunarynode(p).left;
  109. end
  110. else
  111. break;
  112. end;
  113. end;
  114. basenode:=p;
  115. end;
  116. function replacewithtemp(var orgnode:tnode): ttempcreatenode;
  117. begin
  118. if valid_for_var(orgnode,false) then
  119. result:=ctempcreatenode.create_reference(
  120. orgnode.resultdef,orgnode.resultdef.size,
  121. tt_persistent,true,orgnode,true)
  122. else
  123. result:=ctempcreatenode.create_value(
  124. orgnode.resultdef,orgnode.resultdef.size,
  125. tt_persistent,true,orgnode);
  126. { this node is reused while constructing the temp }
  127. orgnode:=ctemprefnode.create(result);
  128. typecheckpass(orgnode);
  129. end;
  130. procedure tjvmcallparanode.handlemanagedbyrefpara(orgparadef: tdef);
  131. var
  132. arrdef: tarraydef;
  133. arreledef: tdef;
  134. initstat,
  135. copybackstat,
  136. finistat: tstatementnode;
  137. finiblock: tblocknode;
  138. realpara, tempn: tnode;
  139. realparaparent: tunarynode;
  140. realparatemp, arraytemp: ttempcreatenode;
  141. leftcopy: tnode;
  142. implicitptrpara: boolean;
  143. begin
  144. { implicit pointer types are already pointers -> no need to stuff them
  145. in an array to pass them by reference (except in case of a formal
  146. parameter, in which case everything is passed in an array since the
  147. callee can't know what was passed in) }
  148. if jvmimplicitpointertype(orgparadef) and
  149. (parasym.vardef.typ<>formaldef) then
  150. exit;
  151. fparainit:=internalstatements(initstat);
  152. fparacopyback:=internalstatements(copybackstat);
  153. finiblock:=internalstatements(finistat);
  154. getparabasenodes(left,realpara,realparaparent);
  155. { make sure we can get a copy of left safely, so we can use it both
  156. to load the original parameter value and to assign the result again
  157. afterwards (if required) }
  158. { special case for access to string character, because those are
  159. translated into function calls that differ depending on which side of
  160. an assignment they are on }
  161. if (realpara.nodetype=vecn) and
  162. (tvecnode(realpara).left.resultdef.typ=stringdef) then
  163. begin
  164. if node_complexity(tvecnode(realpara).left)>1 then
  165. begin
  166. realparatemp:=replacewithtemp(tvecnode(realpara).left);
  167. addstatement(initstat,realparatemp);
  168. addstatement(finistat,ctempdeletenode.create(realparatemp));
  169. end;
  170. if node_complexity(tvecnode(realpara).right)>1 then
  171. begin
  172. realparatemp:=replacewithtemp(tvecnode(realpara).right);
  173. addstatement(initstat,realparatemp);
  174. addstatement(finistat,ctempdeletenode.create(realparatemp));
  175. end;
  176. end
  177. else
  178. begin
  179. { general case: if it's possible that there's a function call
  180. involved, use a temp to prevent double evaluations }
  181. if assigned(realparaparent) then
  182. begin
  183. realparatemp:=replacewithtemp(realparaparent.left);
  184. addstatement(initstat,realparatemp);
  185. addstatement(finistat,ctempdeletenode.create(realparatemp));
  186. end;
  187. end;
  188. { create a copy of the original left (with temps already substituted),
  189. so we can use it if required to handle copying the return value back }
  190. leftcopy:=left.getcopy;
  191. implicitptrpara:=jvmimplicitpointertype(orgparadef);
  192. { create the array temp that that will serve as the paramter }
  193. if parasym.vardef.typ=formaldef then
  194. arreledef:=java_jlobject
  195. else if implicitptrpara then
  196. arreledef:=getpointerdef(orgparadef)
  197. else
  198. arreledef:=parasym.vardef;
  199. arrdef:=getsingletonarraydef(arreledef);
  200. { the -1 means "use the array's element count to determine the number
  201. of elements" in the JVM temp generator }
  202. arraytemp:=ctempcreatenode.create(arrdef,-1,tt_persistent,true);
  203. addstatement(initstat,arraytemp);
  204. addstatement(finistat,ctempdeletenode.create(arraytemp));
  205. { in case of a non-out parameter, pass in the original value (also
  206. always in case of implicitpointer type, since that pointer points to
  207. the data that will be changed by the callee) }
  208. if (parasym.varspez<>vs_out) or
  209. ((parasym.vardef.typ<>formaldef) and
  210. implicitptrpara) then
  211. begin
  212. if implicitptrpara then
  213. begin
  214. { pass pointer to the struct }
  215. left:=caddrnode.create_internal(left);
  216. include(left.flags,nf_typedaddr);
  217. typecheckpass(left);
  218. end;
  219. { wrap the primitive type in an object container
  220. if required }
  221. if parasym.vardef.typ=formaldef then
  222. begin
  223. if (left.resultdef.typ in [orddef,floatdef]) then
  224. begin
  225. left:=cinlinenode.create(in_box_x,false,ccallparanode.create(left,nil));
  226. typecheckpass(left);
  227. end;
  228. left:=ctypeconvnode.create_explicit(left,java_jlobject);
  229. end;
  230. { put the parameter value in the array }
  231. addstatement(initstat,cassignmentnode.create(
  232. cvecnode.create(ctemprefnode.create(arraytemp),genintconstnode(0)),
  233. left));
  234. end
  235. else
  236. left.free;
  237. { replace the parameter with the temp array }
  238. left:=ctemprefnode.create(arraytemp);
  239. { generate the code to copy back the changed value into the original
  240. parameter in case of var/out.
  241. In case of a formaldef, changes to the parameter in the callee change
  242. the pointer inside the array -> we have to copy back the changes in
  243. all cases.
  244. In case of a regular parameter, we only have to copy things back in
  245. case it's not an implicit pointer type. The reason is that for
  246. implicit pointer types, any changes will have been directly applied
  247. to the original parameter via the implicit pointer that we passed in }
  248. if (parasym.varspez in [vs_var,vs_out]) and
  249. ((parasym.vardef.typ=formaldef) or
  250. not implicitptrpara) then
  251. begin
  252. { add the extraction of the parameter and assign it back to the
  253. original location }
  254. tempn:=ctemprefnode.create(arraytemp);
  255. tempn:=cvecnode.create(tempn,genintconstnode(0));
  256. { unbox if necessary }
  257. if parasym.vardef.typ=formaldef then
  258. begin
  259. if orgparadef.typ in [orddef,floatdef] then
  260. tempn:=cinlinenode.create(in_unbox_x_y,false,ccallparanode.create(
  261. ctypenode.create(orgparadef),ccallparanode.create(tempn,nil)))
  262. else if implicitptrpara then
  263. tempn:=ctypeconvnode.create_explicit(tempn,getpointerdef(orgparadef))
  264. end;
  265. if implicitptrpara then
  266. tempn:=cderefnode.create(tempn);
  267. addstatement(copybackstat,cassignmentnode.create(leftcopy,
  268. ctypeconvnode.create_explicit(tempn,orgparadef)));
  269. end
  270. else
  271. leftcopy.free;
  272. addstatement(copybackstat,finiblock);
  273. firstpass(fparainit);
  274. firstpass(left);
  275. firstpass(fparacopyback);
  276. end;
  277. {*****************************************************************************
  278. TJVMCALLNODE
  279. *****************************************************************************}
  280. procedure tjvmcallnode.wrapcomplexinlinepara(para: tcallparanode);
  281. var
  282. tempnode: ttempcreatenode;
  283. begin
  284. { don't use caddrnodes for the JVM target, because we can't take the
  285. address of every kind of type (e.g., of ansistrings). A temp-reference
  286. node does work for any kind of memory reference (and the expectloc
  287. is LOC_(C)REFERENCE when this routine is called), but is not (yet)
  288. supported for other targets }
  289. tempnode:=ctempcreatenode.create_reference(para.parasym.vardef,para.parasym.vardef.size,
  290. tt_persistent,tparavarsym(para.parasym).is_regvar(false),para.left,false);
  291. addstatement(inlineinitstatement,tempnode);
  292. addstatement(inlinecleanupstatement,ctempdeletenode.create(tempnode));
  293. para.left:=ctemprefnode.create(tempnode);
  294. { inherit addr_taken flag }
  295. if (tabstractvarsym(para.parasym).addr_taken) then
  296. include(tempnode.tempinfo^.flags,ti_addr_taken);
  297. end;
  298. procedure tjvmcallnode.extra_pre_call_code;
  299. begin
  300. { when calling a constructor, first create a new instance, except
  301. when calling it from another constructor (because then this has
  302. already been done before calling the current constructor) }
  303. if procdefinition.typ<>procdef then
  304. exit;
  305. if tabstractprocdef(procdefinition).proctypeoption<>potype_constructor then
  306. exit;
  307. if not(methodpointer.resultdef.typ in [classrefdef,recorddef]) then
  308. exit;
  309. current_asmdata.CurrAsmList.concat(taicpu.op_sym(a_new,current_asmdata.RefAsmSymbol(tabstractrecorddef(tabstractprocdef(procdefinition).owner.defowner).jvm_full_typename(true))));
  310. { the constructor doesn't return anything, so put a duplicate of the
  311. self pointer on the evaluation stack for use as function result
  312. after the constructor has run }
  313. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_dup));
  314. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,2);
  315. end;
  316. procedure tjvmcallnode.set_result_location(realresdef: tstoreddef);
  317. begin
  318. location_reset_ref(location,LOC_REFERENCE,def_cgsize(realresdef),1);
  319. { in case of jvmimplicitpointertype(), the function will have allocated
  320. it already and we don't have to allocate it again here }
  321. if not jvmimplicitpointertype(realresdef) then
  322. tg.gethltemp(current_asmdata.CurrAsmList,realresdef,realresdef.size,tt_normal,location.reference)
  323. else
  324. tg.gethltemp(current_asmdata.CurrAsmList,java_jlobject,java_jlobject.size,tt_normal,location.reference);
  325. end;
  326. procedure tjvmcallnode.do_release_unused_return_value;
  327. begin
  328. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and
  329. (current_procinfo.procdef.proctypeoption=potype_constructor) then
  330. exit;
  331. if (location.loc=LOC_REFERENCE) then
  332. tg.ungetiftemp(current_asmdata.CurrAsmList,location.reference);
  333. if assigned(funcretnode) then
  334. exit;
  335. case resultdef.size of
  336. 0:
  337. ;
  338. 1..4:
  339. begin
  340. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop));
  341. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,1);
  342. end;
  343. 8:
  344. begin
  345. current_asmdata.CurrAsmList.concat(taicpu.op_none(a_pop2));
  346. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,2);
  347. end
  348. else
  349. internalerror(2011010305);
  350. end;
  351. end;
  352. procedure tjvmcallnode.extra_post_call_code;
  353. var
  354. totalremovesize: longint;
  355. realresdef: tdef;
  356. begin
  357. if not assigned(typedef) then
  358. realresdef:=tstoreddef(resultdef)
  359. else
  360. realresdef:=tstoreddef(typedef);
  361. { a constructor doesn't actually return a value in the jvm }
  362. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) then
  363. totalremovesize:=pushedparasize
  364. else
  365. begin
  366. { zero-extend unsigned 8/16 bit returns (we have to return them
  367. sign-extended to keep the Android verifier happy, and even if that
  368. one did not exist a plain Java routine could return a
  369. sign-extended value) }
  370. if cnf_return_value_used in callnodeflags then
  371. thlcgjvm(hlcg).maybe_resize_stack_para_val(current_asmdata.CurrAsmList,realresdef,false);
  372. { even a byte takes up a full stackslot -> align size to multiple of 4 }
  373. totalremovesize:=pushedparasize-(align(realresdef.size,4) shr 2);
  374. end;
  375. { remove parameters from internal evaluation stack counter (in case of
  376. e.g. no parameters and a result, it can also increase) }
  377. if totalremovesize>0 then
  378. thlcgjvm(hlcg).decstack(current_asmdata.CurrAsmList,totalremovesize)
  379. else if totalremovesize<0 then
  380. thlcgjvm(hlcg).incstack(current_asmdata.CurrAsmList,-totalremovesize);
  381. { if this was an inherited constructor call, initialise all fields that
  382. are wrapped types following it }
  383. if (tabstractprocdef(procdefinition).proctypeoption=potype_constructor) and
  384. (cnf_inherited in callnodeflags) then
  385. thlcgjvm(hlcg).gen_initialize_fields_code(current_asmdata.CurrAsmList);
  386. end;
  387. procedure tjvmcallnode.remove_hidden_paras;
  388. var
  389. prevpara, para, nextpara: tcallparanode;
  390. begin
  391. prevpara:=nil;
  392. para:=tcallparanode(left);
  393. while assigned(para) do
  394. begin
  395. nextpara:=tcallparanode(para.right);
  396. if vo_is_hidden_para in para.parasym.varoptions then
  397. begin
  398. if assigned(prevpara) then
  399. prevpara.right:=nextpara
  400. else
  401. left:=nextpara;
  402. para.right:=nil;
  403. para.free;
  404. end
  405. else
  406. prevpara:=para;
  407. para:=nextpara;
  408. end;
  409. end;
  410. function tjvmcallnode.dispatch_procvar: tnode;
  411. var
  412. pdclass: tobjectdef;
  413. begin
  414. pdclass:=tprocvardef(right.resultdef).classdef;
  415. { convert procvar type into corresponding class }
  416. if not tprocvardef(right.resultdef).is_addressonly then
  417. begin
  418. right:=caddrnode.create_internal(right);
  419. include(right.flags,nf_typedaddr);
  420. end;
  421. right:=ctypeconvnode.create_explicit(right,pdclass);
  422. include(right.flags,nf_load_procvar);
  423. typecheckpass(right);
  424. { call the invoke method with these parameters. It will take care of the
  425. wrapping and typeconversions; first filter out the automatically added
  426. hidden parameters though }
  427. remove_hidden_paras;
  428. result:=ccallnode.createinternmethod(right,'INVOKE',left);
  429. { reused }
  430. left:=nil;
  431. right:=nil;
  432. end;
  433. function tjvmcallnode.pass_1: tnode;
  434. var
  435. sym: tsym;
  436. wrappername: shortstring;
  437. begin
  438. { transform procvar calls }
  439. if assigned(right) then
  440. result:=dispatch_procvar
  441. else
  442. begin
  443. { replace virtual class method and constructor calls in case they may
  444. be indirect; make sure we don't replace the callthrough to the
  445. original constructor with another call to the wrapper }
  446. if (procdefinition.typ=procdef) and
  447. (current_procinfo.procdef.synthetickind<>tsk_callthrough) and
  448. not(cnf_inherited in callnodeflags) and
  449. ((procdefinition.proctypeoption=potype_constructor) or
  450. (po_classmethod in procdefinition.procoptions)) and
  451. (po_virtualmethod in procdefinition.procoptions) and
  452. (methodpointer.nodetype<>loadvmtaddrn) then
  453. begin
  454. wrappername:=symtableprocentry.name+'__FPCVIRTUALCLASSMETHOD__';
  455. sym:=
  456. search_struct_member(tobjectdef(procdefinition.owner.defowner),
  457. wrappername);
  458. if not assigned(sym) or
  459. (sym.typ<>procsym) then
  460. internalerror(2011072801);
  461. { do not simply replace the procsym/procdef in case we could
  462. in theory do that, because the parameter nodes have already
  463. been bound to the current procdef's parasyms }
  464. remove_hidden_paras;
  465. result:=ccallnode.create(left,tprocsym(sym),symtableproc,methodpointer,callnodeflags);
  466. result.flags:=flags;
  467. left:=nil;
  468. methodpointer:=nil;
  469. exit;
  470. end;
  471. result:=inherited pass_1;
  472. if assigned(result) then
  473. exit;
  474. end;
  475. end;
  476. begin
  477. ccallnode:=tjvmcallnode;
  478. ccallparanode:=tjvmcallparanode;
  479. end.