cpupara.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  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;
  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,cgutils,
  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 (result.def.typ=floatdef) then
  182. begin
  183. paraloc^.loc:=LOC_FPUREGISTER;
  184. paraloc^.register:=NR_FPU_RESULT_REG;
  185. paraloc^.size:=retcgsize;
  186. end
  187. else
  188. { Return in register }
  189. begin
  190. if retcgsize in [OS_64,OS_S64] then
  191. begin
  192. { low 32bits }
  193. paraloc^.loc:=LOC_REGISTER;
  194. paraloc^.size:=OS_32;
  195. if side=callerside then
  196. paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG
  197. else
  198. paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG;
  199. { high 32bits }
  200. paraloc:=result.add_location;
  201. paraloc^.loc:=LOC_REGISTER;
  202. paraloc^.size:=OS_32;
  203. if side=calleeside then
  204. paraloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG
  205. else
  206. paraloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG;
  207. end
  208. else
  209. begin
  210. paraloc^.loc:=LOC_REGISTER;
  211. paraloc^.size:=retcgsize;
  212. if side=callerside then
  213. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(R_INTREGISTER,retcgsize))
  214. else
  215. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(R_INTREGISTER,retcgsize));
  216. end;
  217. end;
  218. end;
  219. function tm68kparamanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  220. var
  221. cur_stack_offset: aword;
  222. curintreg, curfloatreg: tsuperregister;
  223. begin
  224. init_values(curintreg,curfloatreg,cur_stack_offset);
  225. result:=create_paraloc_info_intern(p,side,p.paras,curintreg,curfloatreg,cur_stack_offset);
  226. create_funcretloc_info(p,side);
  227. end;
  228. function tm68kparamanager.create_paraloc_info_intern(p : tabstractprocdef; side: tcallercallee; paras: tparalist;
  229. var curintreg, curfloatreg: tsuperregister; var cur_stack_offset: aword):longint;
  230. var
  231. paraloc : pcgparalocation;
  232. hp : tparavarsym;
  233. paracgsize : tcgsize;
  234. paralen : aint;
  235. parasize : longint;
  236. paradef : tdef;
  237. i : longint;
  238. loc : tcgloc;
  239. nextintreg,
  240. nextfloatreg : tsuperregister;
  241. stack_offset : longint;
  242. begin
  243. result:=0;
  244. nextintreg:=curintreg;
  245. nextfloatreg:=curfloatreg;
  246. stack_offset:=cur_stack_offset;
  247. parasize:=0;
  248. for i:=0 to p.paras.count-1 do
  249. begin
  250. hp:=tparavarsym(paras[i]);
  251. paradef:=hp.vardef;
  252. { syscall for AmigaOS can have already a paraloc set }
  253. if (vo_has_explicit_paraloc in hp.varoptions) then
  254. begin
  255. if not(vo_is_syscall_lib in hp.varoptions) then
  256. internalerror(200506051);
  257. continue;
  258. end;
  259. hp.paraloc[side].reset;
  260. { currently only support C-style array of const }
  261. if (p.proccalloption in cstylearrayofconst) and
  262. is_array_of_const(paradef) then
  263. begin
  264. {$ifdef DEBUG_CHARLIE}
  265. writeln('loc register');
  266. {$endif DEBUG_CHARLIE}
  267. paraloc:=hp.paraloc[side].add_location;
  268. { hack: the paraloc must be valid, but is not actually used }
  269. paraloc^.loc:=LOC_REGISTER;
  270. paraloc^.register:=NR_D0;
  271. paraloc^.size:=OS_ADDR;
  272. break;
  273. end;
  274. if push_addr_param(hp.varspez,paradef,p.proccalloption) then
  275. begin
  276. {$ifdef DEBUG_CHARLIE}
  277. writeln('loc register');
  278. {$endif DEBUG_CHARLIE}
  279. paradef:=getpointerdef(paradef);
  280. loc:=LOC_REGISTER;
  281. paracgsize := OS_ADDR;
  282. paralen := tcgsize2size[OS_ADDR];
  283. end
  284. else
  285. begin
  286. if not is_special_array(paradef) then
  287. paralen:=paradef.size
  288. else
  289. paralen:=tcgsize2size[def_cgsize(paradef)];
  290. loc:=getparaloc(paradef);
  291. paracgsize:=def_cgsize(paradef);
  292. { for things like formaldef }
  293. if (paracgsize=OS_NO) then
  294. begin
  295. paracgsize:=OS_ADDR;
  296. paralen := tcgsize2size[OS_ADDR];
  297. end;
  298. end;
  299. hp.paraloc[side].alignment:=std_param_align;
  300. hp.paraloc[side].size:=paracgsize;
  301. hp.paraloc[side].intsize:=paralen;
  302. hp.paraloc[side].def:=paradef;
  303. if (paralen = 0) then
  304. if (paradef.typ = recorddef) then
  305. begin
  306. paraloc:=hp.paraloc[side].add_location;
  307. paraloc^.loc := LOC_VOID;
  308. end
  309. else
  310. internalerror(200506052);
  311. { can become < 0 for e.g. 3-byte records }
  312. while (paralen > 0) do
  313. begin
  314. paraloc:=hp.paraloc[side].add_location;
  315. (*
  316. by default, the m68k doesn't know any register parameters (FK)
  317. if (loc = LOC_REGISTER) and
  318. (nextintreg <= RS_D2) then
  319. begin
  320. //writeln('loc register');
  321. paraloc^.loc := loc;
  322. { make sure we don't lose whether or not the type is signed }
  323. if (paradef.typ <> orddef) then
  324. paracgsize := int_cgsize(paralen);
  325. if (paracgsize in [OS_NO,OS_64,OS_S64]) then
  326. paraloc^.size := OS_INT
  327. else
  328. paraloc^.size := paracgsize;
  329. paraloc^.register:=newreg(R_INTREGISTER,nextintreg,R_SUBNONE);
  330. inc(nextintreg);
  331. dec(paralen,tcgsize2size[paraloc^.size]);
  332. end
  333. else if (loc = LOC_FPUREGISTER) and
  334. (nextfloatreg <= RS_FP2) then
  335. begin
  336. // writeln('loc fpuregister');
  337. paraloc^.loc:=loc;
  338. paraloc^.size := paracgsize;
  339. paraloc^.register:=newreg(R_FPUREGISTER,nextfloatreg,R_SUBWHOLE);
  340. inc(nextfloatreg);
  341. dec(paralen,tcgsize2size[paraloc^.size]);
  342. end
  343. else { LOC_REFERENCE }
  344. *)
  345. begin
  346. {$ifdef DEBUG_CHARLIE}
  347. writeln('loc reference');
  348. {$endif DEBUG_CHARLIE}
  349. paraloc^.loc:=LOC_REFERENCE;
  350. paraloc^.size:=int_cgsize(paralen);
  351. if (side = callerside) then
  352. paraloc^.reference.index:=NR_STACK_POINTER_REG
  353. else
  354. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  355. paraloc^.reference.offset:=stack_offset;
  356. inc(stack_offset,align(paralen,4));
  357. paralen := 0;
  358. end;
  359. end;
  360. end;
  361. result:=stack_offset;
  362. // writeln('stack offset:',stack_offset);
  363. end;
  364. {
  365. if push_addr_param(hp.varspez,paradef,p.proccalloption) then
  366. paracgsize:=OS_ADDR
  367. else
  368. begin
  369. paracgsize:=def_cgsize(paradef);
  370. if paracgsize=OS_NO then
  371. paracgsize:=OS_ADDR;
  372. end;
  373. hp.paraloc[side].size:=paracgsize;
  374. hp.paraloc[side].Alignment:=std_param_align;
  375. paraloc:=hp.paraloc[side].add_location;
  376. paraloc^.size:=paracgsize;
  377. paraloc^.loc:=LOC_REFERENCE;
  378. if side=callerside then
  379. paraloc^.reference.index:=NR_STACK_POINTER_REG
  380. else
  381. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  382. paraloc^.reference.offset:=target_info.first_parm_offset+parasize;
  383. end;
  384. create_funcretloc_info(p,side);
  385. result:=parasize;
  386. end;
  387. }
  388. function tm68kparamanager.parsefuncretloc(p : tabstractprocdef; const s : string) : boolean;
  389. begin
  390. result:=false;
  391. case target_info.system of
  392. system_m68k_amiga:
  393. begin
  394. if s='D0' then
  395. p.exp_funcretloc:=NR_D0
  396. else if s='D1' then
  397. p.exp_funcretloc:=NR_D1
  398. else if s='D2' then
  399. p.exp_funcretloc:=NR_D2
  400. else if s='D3' then
  401. p.exp_funcretloc:=NR_D3
  402. else if s='D4' then
  403. p.exp_funcretloc:=NR_D4
  404. else if s='D5' then
  405. p.exp_funcretloc:=NR_D5
  406. else if s='D6' then
  407. p.exp_funcretloc:=NR_D6
  408. else if s='D7' then
  409. p.exp_funcretloc:=NR_D7
  410. else if s='A0' then
  411. p.exp_funcretloc:=NR_A0
  412. else if s='A1' then
  413. p.exp_funcretloc:=NR_A1
  414. else if s='A2' then
  415. p.exp_funcretloc:=NR_A2
  416. else if s='A3' then
  417. p.exp_funcretloc:=NR_A3
  418. else if s='A4' then
  419. p.exp_funcretloc:=NR_A4
  420. else if s='A5' then
  421. p.exp_funcretloc:=NR_A5
  422. { 'A6' is problematic, since it's the frame pointer in fpc,
  423. so it should be saved before a call! }
  424. else if s='A6' then
  425. p.exp_funcretloc:=NR_A6
  426. { 'A7' is the stack pointer on 68k, can't be overwritten by API calls }
  427. else
  428. p.exp_funcretloc:=NR_NO;
  429. if p.exp_funcretloc<>NR_NO then result:=true;
  430. end;
  431. else
  432. internalerror(2005121801);
  433. end;
  434. end;
  435. function tm68kparamanager.get_volatile_registers_int(calloption:tproccalloption):tcpuregisterset;
  436. begin
  437. { for now we set all int registers as volatile }
  438. Result:=[RS_D0..RS_D7];
  439. end;
  440. function tm68kparamanager.parseparaloc(p : tparavarsym;const s : string) : boolean;
  441. var
  442. paraloc : pcgparalocation;
  443. begin
  444. result:=false;
  445. case target_info.system of
  446. system_m68k_amiga:
  447. begin
  448. p.paraloc[callerside].alignment:=4;
  449. paraloc:=p.paraloc[callerside].add_location;
  450. paraloc^.loc:=LOC_REGISTER;
  451. paraloc^.size:=def_cgsize(p.vardef);
  452. { pattern is always uppercase'd }
  453. if s='D0' then
  454. paraloc^.register:=NR_D0
  455. else if s='D1' then
  456. paraloc^.register:=NR_D1
  457. else if s='D2' then
  458. paraloc^.register:=NR_D2
  459. else if s='D3' then
  460. paraloc^.register:=NR_D3
  461. else if s='D4' then
  462. paraloc^.register:=NR_D4
  463. else if s='D5' then
  464. paraloc^.register:=NR_D5
  465. else if s='D6' then
  466. paraloc^.register:=NR_D6
  467. else if s='D7' then
  468. paraloc^.register:=NR_D7
  469. else if s='A0' then
  470. paraloc^.register:=NR_A0
  471. else if s='A1' then
  472. paraloc^.register:=NR_A1
  473. else if s='A2' then
  474. paraloc^.register:=NR_A2
  475. else if s='A3' then
  476. paraloc^.register:=NR_A3
  477. else if s='A4' then
  478. paraloc^.register:=NR_A4
  479. else if s='A5' then
  480. paraloc^.register:=NR_A5
  481. { 'A6' is problematic, since it's the frame pointer in fpc,
  482. so it should be saved before a call! }
  483. else if s='A6' then
  484. paraloc^.register:=NR_A6
  485. { 'A7' is the stack pointer on 68k, can't be overwritten by API calls }
  486. else
  487. exit;
  488. { copy to callee side }
  489. p.paraloc[calleeside].add_location^:=paraloc^;
  490. end;
  491. else
  492. internalerror(200405092);
  493. end;
  494. result:=true;
  495. end;
  496. procedure tm68kparamanager.createtempparaloc(list: TAsmList;calloption : tproccalloption;parasym : tparavarsym;can_use_final_stack_loc : boolean;var cgpara:TCGPara);
  497. var
  498. paraloc : pcgparalocation;
  499. begin
  500. paraloc:=parasym.paraloc[callerside].location;
  501. { Never a need for temps when value is pushed (calls inside parameters
  502. will simply allocate even more stack space for their parameters) }
  503. if not(use_fixed_stack) then
  504. can_use_final_stack_loc:=true;
  505. inherited createtempparaloc(list,calloption,parasym,can_use_final_stack_loc,cgpara);
  506. end;
  507. function tm68kparamanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;
  508. var
  509. cur_stack_offset: aword;
  510. curintreg, curfloatreg: tsuperregister;
  511. begin
  512. init_values(curintreg,curfloatreg,cur_stack_offset);
  513. result:=create_paraloc_info_intern(p,callerside,p.paras,curintreg,curfloatreg,cur_stack_offset);
  514. if (p.proccalloption in cstylearrayofconst) then
  515. { just continue loading the parameters in the registers }
  516. result:=create_paraloc_info_intern(p,callerside,varargspara,curintreg,curfloatreg,cur_stack_offset)
  517. else
  518. internalerror(200410231);
  519. end;
  520. begin
  521. paramanager:=tm68kparamanager.create;
  522. end.