cpupara.pas 21 KB

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