cpupara.pas 21 KB

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