cpupara.pas 20 KB

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