njvmcal.pas 23 KB

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