2
0

cpupara.pas 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. {
  2. Copyright (c) 2002 by Florian Klaempfl
  3. Generates the argument location information for 680x0
  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. { Generates the argument location information for 680x0.
  18. }
  19. unit cpupara;
  20. {$i fpcdefs.inc}
  21. interface
  22. uses
  23. globtype,
  24. cpubase,
  25. aasmdata,
  26. symconst,symtype,symdef,symsym,
  27. parabase,paramgr,cgbase,cgutils;
  28. type
  29. { Returns the location for the nr-st 32 Bit int parameter
  30. if every parameter before is an 32 Bit int parameter as well
  31. and if the calling conventions for the helper routines of the
  32. rtl are used.
  33. }
  34. tcpuparamanager = class(tparamanager)
  35. procedure getintparaloc(pd : tabstractprocdef; nr : longint; var cgpara : tcgpara);override;
  36. function create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;override;
  37. function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  38. function get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;override;
  39. procedure createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara);override;
  40. function create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;override;
  41. function parseparaloc(p : tparavarsym;const s : string) : boolean;override;
  42. function parsefuncretloc(p : tabstractprocdef; const s : string) : boolean;override;
  43. function get_volatile_registers_int(calloption:tproccalloption):tcpuregisterset;override;
  44. function get_volatile_registers_address(calloption:tproccalloption):tcpuregisterset;override;
  45. private
  46. procedure init_values(var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword);
  47. function create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist;
  48. var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword):longint;
  49. end;
  50. implementation
  51. uses
  52. verbose,
  53. globals,
  54. systems,
  55. cpuinfo,
  56. defutil;
  57. procedure tcpuparamanager.getintparaloc(pd : tabstractprocdef; nr : longint; var cgpara : tcgpara);
  58. var
  59. paraloc : pcgparalocation;
  60. psym: tparavarsym;
  61. pdef: tdef;
  62. begin
  63. if nr<1 then
  64. internalerror(2002070801);
  65. psym:=tparavarsym(pd.paras[nr-1]);
  66. pdef:=psym.vardef;
  67. if push_addr_param(psym.varspez,pdef,pd.proccalloption) then
  68. pdef:=getpointerdef(pdef);
  69. cgpara.reset;
  70. cgpara.size:=def_cgsize(pdef);
  71. cgpara.intsize:=tcgsize2size[cgpara.size];
  72. cgpara.alignment:=std_param_align;
  73. cgpara.def:=pdef;
  74. paraloc:=cgpara.add_location;
  75. with paraloc^ do
  76. begin
  77. { warning : THIS ONLY WORKS WITH INTERNAL ROUTINES,
  78. WHICH MUST ALWAYS PASS 4-BYTE PARAMETERS!!
  79. }
  80. loc:=LOC_REFERENCE;
  81. reference.index:=NR_STACK_POINTER_REG;
  82. reference.offset:=target_info.first_parm_offset+nr*4;
  83. size:=def_cgsize(pdef);
  84. def:=pdef;
  85. end;
  86. end;
  87. function getparaloc(p : tdef) : tcgloc;
  88. begin
  89. result:=LOC_REFERENCE;
  90. (* Later, the LOC_REFERENCE is in most cases changed into LOC_REGISTER
  91. if push_addr_param for the def is true
  92. case p.typ of
  93. orddef:
  94. result:=LOC_REGISTER;
  95. floatdef:
  96. result:=LOC_FPUREGISTER;
  97. enumdef:
  98. result:=LOC_REGISTER;
  99. pointerdef:
  100. result:=LOC_REGISTER;
  101. formaldef:
  102. result:=LOC_REGISTER;
  103. classrefdef:
  104. result:=LOC_REGISTER;
  105. recorddef:
  106. if (target_info.abi<>abi_powerpc_aix) then
  107. result:=LOC_REFERENCE
  108. else
  109. result:=LOC_REGISTER;
  110. objectdef:
  111. if is_object(p) then
  112. result:=LOC_REFERENCE
  113. else
  114. result:=LOC_REGISTER;
  115. stringdef:
  116. if is_shortstring(p) or is_longstring(p) then
  117. result:=LOC_REFERENCE
  118. else
  119. result:=LOC_REGISTER;
  120. procvardef:
  121. if (po_methodpointer in tprocvardef(p).procoptions) then
  122. result:=LOC_REFERENCE
  123. else
  124. result:=LOC_REGISTER;
  125. filedef:
  126. result:=LOC_REGISTER;
  127. arraydef:
  128. result:=LOC_REFERENCE;
  129. setdef:
  130. if is_smallset(p) then
  131. result:=LOC_REGISTER
  132. else
  133. result:=LOC_REFERENCE;
  134. variantdef:
  135. result:=LOC_REFERENCE;
  136. { avoid problems with errornous definitions }
  137. errordef:
  138. result:=LOC_REGISTER;
  139. else
  140. internalerror(2002071001);
  141. end;
  142. *)
  143. end;
  144. { TODO: copied from ppc cg, needs work}
  145. function tcpuparamanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  146. begin
  147. result:=false;
  148. { var,out,constref always require address }
  149. if varspez in [vs_var,vs_out,vs_constref] then
  150. begin
  151. result:=true;
  152. exit;
  153. end;
  154. case def.typ of
  155. variantdef,
  156. formaldef :
  157. result:=true;
  158. recorddef:
  159. result:=true;
  160. arraydef:
  161. result:=(tarraydef(def).highrange>=tarraydef(def).lowrange) or
  162. is_open_array(def) or
  163. is_array_of_const(def) or
  164. is_array_constructor(def);
  165. objectdef :
  166. result:=is_object(def);
  167. setdef :
  168. result:=not is_smallset(def);
  169. stringdef :
  170. result:=tstringdef(def).stringtype in [st_shortstring,st_longstring];
  171. procvardef :
  172. result:=po_methodpointer in tprocvardef(def).procoptions;
  173. end;
  174. end;
  175. procedure tcpuparamanager.init_values(var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword);
  176. begin
  177. cur_stack_offset:=8;
  178. curintreg:=RS_D0;
  179. curfloatreg:=RS_FP0;
  180. end;
  181. function tcpuparamanager.get_funcretloc(p : tabstractprocdef; side: tcallercallee; forcetempdef: tdef): tcgpara;
  182. var
  183. paraloc : pcgparalocation;
  184. retcgsize : tcgsize;
  185. begin
  186. if set_common_funcretloc_info(p,forcetempdef,retcgsize,result) then
  187. exit;
  188. paraloc:=result.add_location;
  189. { Return in FPU register? }
  190. if not (cs_fp_emulation in current_settings.moduleswitches) and
  191. not (current_settings.fputype=fpu_soft) and (result.def.typ=floatdef) then
  192. begin
  193. paraloc^.loc:=LOC_FPUREGISTER;
  194. paraloc^.register:=NR_FPU_RESULT_REG;
  195. paraloc^.size:=retcgsize;
  196. paraloc^.def:=result.def;
  197. end
  198. else
  199. { Return in register }
  200. begin
  201. if retcgsize in [OS_64,OS_S64] then
  202. begin
  203. { low 32bits }
  204. paraloc^.loc:=LOC_REGISTER;
  205. paraloc^.size:=OS_32;
  206. paraloc^.def:=u32inttype;
  207. if side=callerside then
  208. paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG
  209. else
  210. paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG;
  211. { high 32bits }
  212. paraloc:=result.add_location;
  213. paraloc^.loc:=LOC_REGISTER;
  214. paraloc^.size:=OS_32;
  215. paraloc^.def:=u32inttype;
  216. if side=calleeside then
  217. paraloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG
  218. else
  219. paraloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG;
  220. end
  221. else
  222. begin
  223. paraloc^.loc:=LOC_REGISTER;
  224. paraloc^.size:=retcgsize;
  225. paraloc^.def:=result.def;
  226. if side=callerside then
  227. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize))
  228. else
  229. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize));
  230. end;
  231. end;
  232. end;
  233. function tcpuparamanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  234. var
  235. cur_stack_offset: aword;
  236. curintreg, curfloatreg: tsuperregister;
  237. begin
  238. init_values(curintreg,curfloatreg,cur_stack_offset);
  239. result:=create_paraloc_info_intern(p,side,p.paras,curintreg,curfloatreg,cur_stack_offset);
  240. create_funcretloc_info(p,side);
  241. end;
  242. function tcpuparamanager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist;
  243. var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword):longint;
  244. var
  245. paraloc : pcgparalocation;
  246. hp : tparavarsym;
  247. paracgsize : tcgsize;
  248. paralen : aint;
  249. parasize : longint;
  250. paradef : tdef;
  251. i : longint;
  252. loc : tcgloc;
  253. nextintreg,
  254. nextfloatreg : tsuperregister;
  255. stack_offset : longint;
  256. firstparaloc : boolean;
  257. begin
  258. result:=0;
  259. nextintreg:=curintreg;
  260. nextfloatreg:=curfloatreg;
  261. stack_offset:=cur_stack_offset;
  262. parasize:=0;
  263. for i:=0 to paras.count-1 do
  264. begin
  265. hp:=tparavarsym(paras[i]);
  266. paradef:=hp.vardef;
  267. { syscall for AmigaOS can have already a paraloc set }
  268. if (vo_has_explicit_paraloc in hp.varoptions) then
  269. begin
  270. if not(vo_is_syscall_lib in hp.varoptions) then
  271. internalerror(200506051);
  272. continue;
  273. end;
  274. hp.paraloc[side].reset;
  275. { currently only support C-style array of const }
  276. if (p.proccalloption in cstylearrayofconst) and
  277. is_array_of_const(paradef) then
  278. begin
  279. {$ifdef DEBUG_CHARLIE}
  280. writeln('loc register');
  281. {$endif DEBUG_CHARLIE}
  282. paraloc:=hp.paraloc[side].add_location;
  283. { hack: the paraloc must be valid, but is not actually used }
  284. paraloc^.loc:=LOC_REGISTER;
  285. paraloc^.register:=NR_D0;
  286. paraloc^.size:=OS_ADDR;
  287. paraloc^.def:=voidpointertype;
  288. break;
  289. end;
  290. if push_addr_param(hp.varspez,paradef,p.proccalloption) then
  291. begin
  292. {$ifdef DEBUG_CHARLIE}
  293. writeln('loc register');
  294. {$endif DEBUG_CHARLIE}
  295. paradef:=getpointerdef(paradef);
  296. loc:=LOC_REGISTER;
  297. paracgsize := OS_ADDR;
  298. paralen := tcgsize2size[OS_ADDR];
  299. end
  300. else
  301. begin
  302. if not is_special_array(paradef) then
  303. paralen:=paradef.size
  304. else
  305. paralen:=tcgsize2size[def_cgsize(paradef)];
  306. loc:=getparaloc(paradef);
  307. paracgsize:=def_cgsize(paradef);
  308. { for things like formaldef }
  309. if (paracgsize=OS_NO) then
  310. begin
  311. paracgsize:=OS_ADDR;
  312. paralen := tcgsize2size[OS_ADDR];
  313. end;
  314. end;
  315. hp.paraloc[side].alignment:=std_param_align;
  316. hp.paraloc[side].size:=paracgsize;
  317. hp.paraloc[side].intsize:=paralen;
  318. hp.paraloc[side].def:=paradef;
  319. if (paralen = 0) then
  320. if (paradef.typ = recorddef) then
  321. begin
  322. paraloc:=hp.paraloc[side].add_location;
  323. paraloc^.loc := LOC_VOID;
  324. end
  325. else
  326. internalerror(200506052);
  327. firstparaloc:=true;
  328. { can become < 0 for e.g. 3-byte records }
  329. while (paralen > 0) do
  330. begin
  331. paraloc:=hp.paraloc[side].add_location;
  332. (*
  333. by default, the m68k doesn't know any register parameters (FK)
  334. if (loc = LOC_REGISTER) and
  335. (nextintreg <= RS_D2) then
  336. begin
  337. //writeln('loc register');
  338. paraloc^.loc := loc;
  339. { make sure we don't lose whether or not the type is signed }
  340. if (paradef.typ <> orddef) then
  341. paracgsize := int_cgsize(paralen);
  342. if (paracgsize in [OS_NO,OS_64,OS_S64]) then
  343. paraloc^.size := OS_INT
  344. else
  345. paraloc^.size := paracgsize;
  346. paraloc^.register:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
  347. inc(nextintreg);
  348. dec(paralen,tcgsize2size[paraloc^.size]);
  349. end
  350. else if (loc = LOC_FPUREGISTER) and
  351. (nextfloatreg <= RS_FP2) then
  352. begin
  353. // writeln('loc fpuregister');
  354. paraloc^.loc:=loc;
  355. paraloc^.size := paracgsize;
  356. paraloc^.register:=newreg(R_FPUREGISTER,nextfloatreg,R_SUBWHOLE);
  357. inc(nextfloatreg);
  358. dec(paralen,tcgsize2size[paraloc^.size]);
  359. end
  360. else { LOC_REFERENCE }
  361. *)
  362. begin
  363. {$ifdef DEBUG_CHARLIE}
  364. writeln('loc reference');
  365. {$endif DEBUG_CHARLIE}
  366. paraloc^.loc:=LOC_REFERENCE;
  367. paraloc^.def:=get_paraloc_def(paradef,paralen,firstparaloc);
  368. if paradef.typ<>orddef then
  369. paracgsize:=int_cgsize(paralen);
  370. if paracgsize=OS_NO then
  371. paraloc^.size:=OS_INT
  372. else
  373. paraloc^.size:=paracgsize;
  374. if (side = callerside) then
  375. paraloc^.reference.index:=NR_STACK_POINTER_REG
  376. else
  377. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  378. paraloc^.reference.offset:=stack_offset;
  379. inc(stack_offset,align(paralen,4));
  380. paralen := 0;
  381. end;
  382. firstparaloc:=false;
  383. end;
  384. end;
  385. result:=stack_offset;
  386. // writeln('stack offset:',stack_offset);
  387. end;
  388. {
  389. if push_addr_param(hp.varspez,paradef,p.proccalloption) then
  390. paracgsize:=OS_ADDR
  391. else
  392. begin
  393. paracgsize:=def_cgsize(paradef);
  394. if paracgsize=OS_NO then
  395. paracgsize:=OS_ADDR;
  396. end;
  397. hp.paraloc[side].size:=paracgsize;
  398. hp.paraloc[side].Alignment:=std_param_align;
  399. paraloc:=hp.paraloc[side].add_location;
  400. paraloc^.size:=paracgsize;
  401. paraloc^.loc:=LOC_REFERENCE;
  402. if side=callerside then
  403. paraloc^.reference.index:=NR_STACK_POINTER_REG
  404. else
  405. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  406. paraloc^.reference.offset:=target_info.first_parm_offset+parasize;
  407. end;
  408. create_funcretloc_info(p,side);
  409. result:=parasize;
  410. end;
  411. }
  412. function tcpuparamanager.parsefuncretloc(p : tabstractprocdef; const s : string) : boolean;
  413. begin
  414. result:=false;
  415. case target_info.system of
  416. system_m68k_amiga:
  417. begin
  418. if s='D0' then
  419. p.exp_funcretloc:=NR_D0
  420. else if s='D1' then
  421. p.exp_funcretloc:=NR_D1
  422. else if s='D2' then
  423. p.exp_funcretloc:=NR_D2
  424. else if s='D3' then
  425. p.exp_funcretloc:=NR_D3
  426. else if s='D4' then
  427. p.exp_funcretloc:=NR_D4
  428. else if s='D5' then
  429. p.exp_funcretloc:=NR_D5
  430. else if s='D6' then
  431. p.exp_funcretloc:=NR_D6
  432. else if s='D7' then
  433. p.exp_funcretloc:=NR_D7
  434. else if s='A0' then
  435. p.exp_funcretloc:=NR_A0
  436. else if s='A1' then
  437. p.exp_funcretloc:=NR_A1
  438. else if s='A2' then
  439. p.exp_funcretloc:=NR_A2
  440. else if s='A3' then
  441. p.exp_funcretloc:=NR_A3
  442. else if s='A4' then
  443. p.exp_funcretloc:=NR_A4
  444. else if s='A5' then
  445. p.exp_funcretloc:=NR_A5
  446. { 'A6' is problematic, since it's the frame pointer in fpc,
  447. so it should be saved before a call! }
  448. else if s='A6' then
  449. p.exp_funcretloc:=NR_A6
  450. { 'A7' is the stack pointer on 68k, can't be overwritten by API calls }
  451. else
  452. p.exp_funcretloc:=NR_NO;
  453. if p.exp_funcretloc<>NR_NO then result:=true;
  454. end;
  455. else
  456. internalerror(2005121801);
  457. end;
  458. end;
  459. function tcpuparamanager.get_volatile_registers_int(calloption:tproccalloption):tcpuregisterset;
  460. begin
  461. { d0 and d1 are considered volatile (ToDo: results in "procedure too
  462. complex when compiling unicodedata.pas) }
  463. //Result:=[RS_D0,RS_D1];
  464. Result:=[RS_D0..RS_D7];
  465. end;
  466. function tcpuparamanager.get_volatile_registers_address(calloption:tproccalloption):tcpuregisterset;
  467. begin
  468. { a0 and a1 are considered volatile }
  469. Result:=[RS_A0,RS_A1];
  470. end;
  471. function tcpuparamanager.parseparaloc(p : tparavarsym;const s : string) : boolean;
  472. var
  473. paraloc : pcgparalocation;
  474. begin
  475. result:=false;
  476. case target_info.system of
  477. system_m68k_amiga:
  478. begin
  479. p.paraloc[callerside].alignment:=4;
  480. paraloc:=p.paraloc[callerside].add_location;
  481. paraloc^.loc:=LOC_REGISTER;
  482. paraloc^.size:=def_cgsize(p.vardef);
  483. paraloc^.def:=p.vardef;
  484. { pattern is always uppercase'd }
  485. if s='D0' then
  486. paraloc^.register:=NR_D0
  487. else if s='D1' then
  488. paraloc^.register:=NR_D1
  489. else if s='D2' then
  490. paraloc^.register:=NR_D2
  491. else if s='D3' then
  492. paraloc^.register:=NR_D3
  493. else if s='D4' then
  494. paraloc^.register:=NR_D4
  495. else if s='D5' then
  496. paraloc^.register:=NR_D5
  497. else if s='D6' then
  498. paraloc^.register:=NR_D6
  499. else if s='D7' then
  500. paraloc^.register:=NR_D7
  501. else if s='A0' then
  502. paraloc^.register:=NR_A0
  503. else if s='A1' then
  504. paraloc^.register:=NR_A1
  505. else if s='A2' then
  506. paraloc^.register:=NR_A2
  507. else if s='A3' then
  508. paraloc^.register:=NR_A3
  509. else if s='A4' then
  510. paraloc^.register:=NR_A4
  511. else if s='A5' then
  512. paraloc^.register:=NR_A5
  513. { 'A6' is problematic, since it's the frame pointer in fpc,
  514. so it should be saved before a call! }
  515. else if s='A6' then
  516. paraloc^.register:=NR_A6
  517. { 'A7' is the stack pointer on 68k, can't be overwritten by API calls }
  518. else
  519. exit;
  520. { copy to callee side }
  521. p.paraloc[calleeside].add_location^:=paraloc^;
  522. end;
  523. else
  524. internalerror(200405092);
  525. end;
  526. result:=true;
  527. end;
  528. procedure tcpuparamanager.createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara);
  529. var
  530. paraloc : pcgparalocation;
  531. begin
  532. paraloc:=parasym.paraloc[callerside].location;
  533. { Never a need for temps when value is pushed (calls inside parameters
  534. will simply allocate even more stack space for their parameters) }
  535. if not(use_fixed_stack) then
  536. can_use_final_stack_loc:=true;
  537. inherited createtempparaloc(list,calloption,parasym,can_use_final_stack_loc,cgpara);
  538. end;
  539. function tcpuparamanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;
  540. var
  541. cur_stack_offset: aword;
  542. curintreg, curfloatreg: tsuperregister;
  543. begin
  544. init_values(curintreg,curfloatreg,cur_stack_offset);
  545. result:=create_paraloc_info_intern(p,callerside,p.paras,curintreg,curfloatreg,cur_stack_offset);
  546. if (p.proccalloption in cstylearrayofconst) then
  547. { just continue loading the parameters in the registers }
  548. result:=create_paraloc_info_intern(p,callerside,varargspara,curintreg,curfloatreg,cur_stack_offset)
  549. else
  550. internalerror(200410231);
  551. end;
  552. begin
  553. paramanager:=tcpuparamanager.create;
  554. end.