cpupara.pas 21 KB

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