cpupara.pas 20 KB

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