cpupara.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. {
  2. Copyright (c) 2002 by Florian Klaempfl
  3. Generates the argument location information for i386
  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 bymethodpointer
  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. unit cpupara;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,
  22. aasmtai,cpubase,cgbase,
  23. symconst,symtype,symsym,symdef,
  24. parabase,paramgr;
  25. type
  26. ti386paramanager = class(tparamanager)
  27. function param_use_paraloc(const cgpara:tcgpara):boolean;override;
  28. function ret_in_param(def : tdef;calloption : tproccalloption) : boolean;override;
  29. function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  30. function get_para_align(calloption : tproccalloption):byte;override;
  31. function get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;override;
  32. function get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;override;
  33. function get_volatile_registers_mm(calloption : tproccalloption):tcpuregisterset;override;
  34. { Returns the location for the nr-st 32 Bit int parameter
  35. if every parameter before is an 32 Bit int parameter as well
  36. and if the calling conventions for the helper routines of the
  37. rtl are used.
  38. }
  39. procedure getintparaloc(calloption : tproccalloption; nr : longint;var cgpara:TCGPara);override;
  40. function create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;override;
  41. function create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;override;
  42. procedure createtempparaloc(list: taasmoutput;calloption : tproccalloption;parasym : tparavarsym;var cgpara:TCGPara);override;
  43. private
  44. procedure create_funcretloc_info(p : tabstractprocdef; side: tcallercallee);
  45. procedure create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;var parasize:longint);
  46. procedure create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;var parareg,parasize:longint);
  47. end;
  48. implementation
  49. uses
  50. cutils,
  51. systems,verbose,
  52. defutil,
  53. cgutils;
  54. const
  55. parasupregs : array[0..2] of tsuperregister = (RS_EAX,RS_EDX,RS_ECX);
  56. {****************************************************************************
  57. TI386PARAMANAGER
  58. ****************************************************************************}
  59. function ti386paramanager.param_use_paraloc(const cgpara:tcgpara):boolean;
  60. var
  61. paraloc : pcgparalocation;
  62. begin
  63. if not assigned(cgpara.location) then
  64. internalerror(200410102);
  65. result:=true;
  66. { All locations are LOC_REFERENCE }
  67. paraloc:=cgpara.location;
  68. while assigned(paraloc) do
  69. begin
  70. if (paraloc^.loc<>LOC_REFERENCE) then
  71. begin
  72. result:=false;
  73. exit;
  74. end;
  75. paraloc:=paraloc^.next;
  76. end;
  77. end;
  78. function ti386paramanager.ret_in_param(def : tdef;calloption : tproccalloption) : boolean;
  79. begin
  80. case target_info.system of
  81. system_i386_win32 :
  82. begin
  83. case def.deftype of
  84. recorddef :
  85. begin
  86. { Win32 GCC returns small records in the FUNCTION_RETURN_REG.
  87. For stdcall we follow delphi instead of GCC }
  88. if (calloption in [pocall_cdecl,pocall_cppdecl]) and
  89. (def.size<=8) then
  90. begin
  91. result:=false;
  92. exit;
  93. end;
  94. end;
  95. end;
  96. end;
  97. system_i386_darwin :
  98. begin
  99. case def.deftype of
  100. recorddef :
  101. begin
  102. if (def.size <= 8) then
  103. begin
  104. result := false;
  105. exit;
  106. end;
  107. end;
  108. end;
  109. end;
  110. end;
  111. result:=inherited ret_in_param(def,calloption);
  112. end;
  113. function ti386paramanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  114. begin
  115. result:=false;
  116. { var,out always require address }
  117. if varspez in [vs_var,vs_out] then
  118. begin
  119. result:=true;
  120. exit;
  121. end;
  122. { Only vs_const, vs_value here }
  123. case def.deftype of
  124. variantdef :
  125. begin
  126. { variants are small enough to be passed by value except if
  127. required by the windows api
  128. }
  129. if (target_info.system=system_i386_win32) and
  130. (calloption=pocall_stdcall) and
  131. (varspez=vs_const) then
  132. result:=true
  133. else
  134. result:=false;
  135. end;
  136. formaldef :
  137. result:=true;
  138. recorddef :
  139. begin
  140. { Win32 stdcall passes small records on the stack for call by
  141. value }
  142. if (target_info.system=system_i386_win32) and
  143. (calloption=pocall_stdcall) and
  144. (varspez=vs_value) and
  145. (def.size<=16) then
  146. result:=false
  147. else
  148. result:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and (def.size>sizeof(aint));
  149. end;
  150. arraydef :
  151. begin
  152. { Win32 stdcall passes arrays on the stack for call by
  153. value }
  154. if (target_info.system=system_i386_win32) and
  155. (calloption=pocall_stdcall) and
  156. (varspez=vs_value) and
  157. (tarraydef(def).highrange>=tarraydef(def).lowrange) then
  158. result:=false
  159. else
  160. { array of const values are pushed on the stack as
  161. well as dyn. arrays }
  162. if (calloption in [pocall_cdecl,pocall_cppdecl]) then
  163. result:=not(is_array_of_const(def) or
  164. is_dynamic_array(def))
  165. else
  166. begin
  167. result:=(
  168. (tarraydef(def).highrange>=tarraydef(def).lowrange) and
  169. (def.size>sizeof(aint))
  170. ) or
  171. is_open_array(def) or
  172. is_array_of_const(def) or
  173. is_array_constructor(def);
  174. end;
  175. end;
  176. objectdef :
  177. result:=is_object(def);
  178. stringdef :
  179. result:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and (tstringdef(def).string_typ in [st_shortstring,st_longstring]);
  180. procvardef :
  181. result:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and (po_methodpointer in tprocvardef(def).procoptions);
  182. setdef :
  183. result:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and (tsetdef(def).settype<>smallset);
  184. end;
  185. end;
  186. function ti386paramanager.get_para_align(calloption : tproccalloption):byte;
  187. begin
  188. if calloption=pocall_oldfpccall then
  189. begin
  190. if target_info.system in [system_i386_go32v2,system_i386_watcom] then
  191. result:=2
  192. else
  193. result:=4;
  194. end
  195. else
  196. result:=std_param_align;
  197. end;
  198. function ti386paramanager.get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;
  199. begin
  200. case calloption of
  201. pocall_internproc :
  202. result:=[];
  203. pocall_register,
  204. pocall_safecall,
  205. pocall_stdcall,
  206. pocall_cdecl,
  207. pocall_cppdecl :
  208. result:=[RS_EAX,RS_EDX,RS_ECX];
  209. pocall_far16,
  210. pocall_pascal,
  211. pocall_oldfpccall :
  212. result:=[RS_EAX,RS_EDX,RS_ECX,RS_ESI,RS_EDI,RS_EBX];
  213. else
  214. internalerror(200309071);
  215. end;
  216. end;
  217. function ti386paramanager.get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;
  218. begin
  219. result:=[0..first_fpu_imreg-1];
  220. end;
  221. function ti386paramanager.get_volatile_registers_mm(calloption : tproccalloption):tcpuregisterset;
  222. begin
  223. result:=[0..first_mm_imreg-1];
  224. end;
  225. procedure ti386paramanager.getintparaloc(calloption : tproccalloption; nr : longint;var cgpara:TCGPara);
  226. var
  227. paraloc : pcgparalocation;
  228. begin
  229. cgpara.reset;
  230. cgpara.size:=OS_INT;
  231. cgpara.intsize:=tcgsize2size[OS_INT];
  232. cgpara.alignment:=get_para_align(calloption);
  233. paraloc:=cgpara.add_location;
  234. with paraloc^ do
  235. begin
  236. size:=OS_INT;
  237. if calloption=pocall_register then
  238. begin
  239. if (nr<=high(parasupregs)+1) then
  240. begin
  241. if nr=0 then
  242. internalerror(200309271);
  243. loc:=LOC_REGISTER;
  244. register:=newreg(R_INTREGISTER,parasupregs[nr-1],R_SUBWHOLE);
  245. end
  246. else
  247. begin
  248. loc:=LOC_REFERENCE;
  249. reference.index:=NR_STACK_POINTER_REG;
  250. reference.offset:=sizeof(aint)*nr;
  251. end;
  252. end
  253. else
  254. begin
  255. loc:=LOC_REFERENCE;
  256. reference.index:=NR_STACK_POINTER_REG;
  257. reference.offset:=sizeof(aint)*nr;
  258. end;
  259. end;
  260. end;
  261. procedure ti386paramanager.create_funcretloc_info(p : tabstractprocdef; side: tcallercallee);
  262. var
  263. retcgsize : tcgsize;
  264. begin
  265. { Constructors return self instead of a boolean }
  266. if (p.proctypeoption=potype_constructor) then
  267. retcgsize:=OS_ADDR
  268. else
  269. retcgsize:=def_cgsize(p.rettype.def);
  270. location_reset(p.funcretloc[side],LOC_INVALID,OS_NO);
  271. { void has no location }
  272. if is_void(p.rettype.def) then
  273. begin
  274. location_reset(p.funcretloc[side],LOC_VOID,OS_NO);
  275. exit;
  276. end;
  277. { Return in FPU register? }
  278. if p.rettype.def.deftype=floatdef then
  279. begin
  280. p.funcretloc[side].loc:=LOC_FPUREGISTER;
  281. p.funcretloc[side].register:=NR_FPU_RESULT_REG;
  282. p.funcretloc[side].size:=retcgsize;
  283. end
  284. else
  285. { Return in register? }
  286. if not ret_in_param(p.rettype.def,p.proccalloption) then
  287. begin
  288. if retcgsize in [OS_64,OS_S64] then
  289. begin
  290. { low 32bits }
  291. p.funcretloc[side].loc:=LOC_REGISTER;
  292. p.funcretloc[side].size:=OS_64;
  293. if side=callerside then
  294. p.funcretloc[side].register64.reglo:=NR_FUNCTION_RESULT64_LOW_REG
  295. else
  296. p.funcretloc[side].register64.reglo:=NR_FUNCTION_RETURN64_LOW_REG;
  297. { high 32bits }
  298. if side=callerside then
  299. p.funcretloc[side].register64.reghi:=NR_FUNCTION_RESULT64_HIGH_REG
  300. else
  301. p.funcretloc[side].register64.reghi:=NR_FUNCTION_RETURN64_HIGH_REG;
  302. end
  303. else
  304. begin
  305. p.funcretloc[side].loc:=LOC_REGISTER;
  306. p.funcretloc[side].size:=retcgsize;
  307. if side=callerside then
  308. p.funcretloc[side].register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(retcgsize))
  309. else
  310. p.funcretloc[side].register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(retcgsize));
  311. end;
  312. end
  313. else
  314. begin
  315. p.funcretloc[side].loc:=LOC_REFERENCE;
  316. p.funcretloc[side].size:=retcgsize;
  317. end;
  318. end;
  319. procedure ti386paramanager.create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;var parasize:longint);
  320. var
  321. i : integer;
  322. hp : tparavarsym;
  323. paraloc : pcgparalocation;
  324. l,
  325. paralen,
  326. varalign : longint;
  327. paraalign : shortint;
  328. pushaddr : boolean;
  329. paracgsize : tcgsize;
  330. begin
  331. paraalign:=get_para_align(p.proccalloption);
  332. { we push Flags and CS as long
  333. to cope with the IRETD
  334. and we save 6 register + 4 selectors }
  335. if po_interrupt in p.procoptions then
  336. inc(parasize,8+6*4+4*2);
  337. { Offset is calculated like:
  338. sub esp,12
  339. mov [esp+8],para3
  340. mov [esp+4],para2
  341. mov [esp],para1
  342. call function
  343. That means for pushes the para with the
  344. highest offset (see para3) needs to be pushed first
  345. }
  346. for i:=0 to paras.count-1 do
  347. begin
  348. hp:=tparavarsym(paras[i]);
  349. pushaddr:=push_addr_param(hp.varspez,hp.vartype.def,p.proccalloption);
  350. if pushaddr then
  351. begin
  352. paralen:=sizeof(aint);
  353. paracgsize:=OS_ADDR;
  354. end
  355. else
  356. begin
  357. paralen:=push_size(hp.varspez,hp.vartype.def,p.proccalloption);
  358. paracgsize:=def_cgsize(hp.vartype.def);
  359. end;
  360. hp.paraloc[side].reset;
  361. hp.paraloc[side].size:=paracgsize;
  362. hp.paraloc[side].intsize:=paralen;
  363. hp.paraloc[side].Alignment:=paraalign;
  364. { Copy to stack? }
  365. if (paracgsize=OS_NO) or
  366. (use_fixed_stack) then
  367. begin
  368. paraloc:=hp.paraloc[side].add_location;
  369. paraloc^.loc:=LOC_REFERENCE;
  370. paraloc^.size:=paracgsize;
  371. if side=callerside then
  372. paraloc^.reference.index:=NR_STACK_POINTER_REG
  373. else
  374. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  375. varalign:=used_align(size_2_align(paralen),paraalign,paraalign);
  376. { don't let push_size return 16, because then we can }
  377. { read past the end of the heap since the value is only }
  378. { 10 bytes long (JM) }
  379. if (paracgsize = OS_F80) and
  380. (target_info.system = system_i386_darwin) then
  381. paralen:=16;
  382. paraloc^.reference.offset:=parasize;
  383. if side=calleeside then
  384. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  385. parasize:=align(parasize+paralen,varalign);
  386. end
  387. else
  388. begin
  389. if paralen=0 then
  390. internalerror(200501163);
  391. while (paralen>0) do
  392. begin
  393. paraloc:=hp.paraloc[side].add_location;
  394. paraloc^.loc:=LOC_REFERENCE;
  395. { single and double need a single location }
  396. if (paracgsize in [OS_F64,OS_F32]) then
  397. begin
  398. paraloc^.size:=paracgsize;
  399. l:=paralen;
  400. end
  401. else
  402. begin
  403. { We can allocate at maximum 32 bits per location }
  404. if paralen>sizeof(aint) then
  405. l:=sizeof(aint)
  406. else
  407. l:=paralen;
  408. paraloc^.size:=int_cgsize(l);
  409. end;
  410. if side=callerside then
  411. paraloc^.reference.index:=NR_STACK_POINTER_REG
  412. else
  413. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  414. varalign:=used_align(size_2_align(l),paraalign,paraalign);
  415. paraloc^.reference.offset:=parasize;
  416. if side=calleeside then
  417. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  418. parasize:=align(parasize+l,varalign);
  419. dec(paralen,l);
  420. end;
  421. end;
  422. end;
  423. end;
  424. procedure ti386paramanager.create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;
  425. var parareg,parasize:longint);
  426. var
  427. hp : tparavarsym;
  428. paraloc : pcgparalocation;
  429. paracgsize : tcgsize;
  430. i : integer;
  431. l,
  432. paralen,
  433. varalign : longint;
  434. pushaddr : boolean;
  435. paraalign : shortint;
  436. begin
  437. paraalign:=get_para_align(p.proccalloption);
  438. { Register parameters are assigned from left to right }
  439. for i:=0 to paras.count-1 do
  440. begin
  441. hp:=tparavarsym(paras[i]);
  442. pushaddr:=push_addr_param(hp.varspez,hp.vartype.def,p.proccalloption);
  443. if pushaddr then
  444. begin
  445. paralen:=sizeof(aint);
  446. paracgsize:=OS_ADDR;
  447. end
  448. else
  449. begin
  450. paralen:=push_size(hp.varspez,hp.vartype.def,p.proccalloption);
  451. paracgsize:=def_cgsize(hp.vartype.def);
  452. end;
  453. hp.paraloc[side].reset;
  454. hp.paraloc[side].size:=paracgsize;
  455. hp.paraloc[side].intsize:=paralen;
  456. hp.paraloc[side].Alignment:=paraalign;
  457. {
  458. EAX
  459. EDX
  460. ECX
  461. Stack
  462. Stack
  463. 64bit values,floats,arrays and records are always
  464. on the stack.
  465. }
  466. if (parareg<=high(parasupregs)) and
  467. (paralen<=sizeof(aint)) and
  468. (
  469. not(hp.vartype.def.deftype in [floatdef,recorddef,arraydef]) or
  470. pushaddr
  471. ) then
  472. begin
  473. paraloc:=hp.paraloc[side].add_location;
  474. paraloc^.size:=paracgsize;
  475. paraloc^.loc:=LOC_REGISTER;
  476. paraloc^.register:=newreg(R_INTREGISTER,parasupregs[parareg],cgsize2subreg(paracgsize));
  477. inc(parareg);
  478. end
  479. else
  480. begin
  481. { Copy to stack? }
  482. if (use_fixed_stack) or
  483. (paracgsize=OS_NO) then
  484. begin
  485. paraloc:=hp.paraloc[side].add_location;
  486. paraloc^.loc:=LOC_REFERENCE;
  487. paraloc^.size:=paracgsize;
  488. if side=callerside then
  489. paraloc^.reference.index:=NR_STACK_POINTER_REG
  490. else
  491. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  492. varalign:=used_align(size_2_align(paralen),paraalign,paraalign);
  493. paraloc^.reference.offset:=parasize;
  494. if side=calleeside then
  495. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  496. parasize:=align(parasize+paralen,varalign);
  497. end
  498. else
  499. begin
  500. if paralen=0 then
  501. internalerror(200501163);
  502. while (paralen>0) do
  503. begin
  504. paraloc:=hp.paraloc[side].add_location;
  505. paraloc^.loc:=LOC_REFERENCE;
  506. { Extended and double need a single location }
  507. if (paracgsize in [OS_F64,OS_F32]) then
  508. begin
  509. paraloc^.size:=paracgsize;
  510. l:=paralen;
  511. end
  512. else
  513. begin
  514. { We can allocate at maximum 32 bits per location }
  515. if paralen>sizeof(aint) then
  516. l:=sizeof(aint)
  517. else
  518. l:=paralen;
  519. paraloc^.size:=int_cgsize(l);
  520. end;
  521. if side=callerside then
  522. paraloc^.reference.index:=NR_STACK_POINTER_REG
  523. else
  524. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  525. varalign:=used_align(size_2_align(l),paraalign,paraalign);
  526. paraloc^.reference.offset:=parasize;
  527. if side=calleeside then
  528. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  529. parasize:=align(parasize+l,varalign);
  530. dec(paralen,l);
  531. end;
  532. end;
  533. end;
  534. end;
  535. end;
  536. function ti386paramanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  537. var
  538. parasize,
  539. parareg : longint;
  540. begin
  541. parasize:=0;
  542. parareg:=0;
  543. case p.proccalloption of
  544. pocall_register :
  545. create_register_paraloc_info(p,side,p.paras,parareg,parasize);
  546. pocall_internproc :
  547. begin
  548. { Use default calling }
  549. if (pocall_default=pocall_register) then
  550. create_register_paraloc_info(p,side,p.paras,parareg,parasize)
  551. else
  552. create_stdcall_paraloc_info(p,side,p.paras,parasize);
  553. end;
  554. else
  555. create_stdcall_paraloc_info(p,side,p.paras,parasize);
  556. end;
  557. create_funcretloc_info(p,side);
  558. result:=parasize;
  559. end;
  560. function ti386paramanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;
  561. var
  562. parasize : longint;
  563. begin
  564. parasize:=0;
  565. { calculate the registers for the normal parameters }
  566. create_stdcall_paraloc_info(p,callerside,p.paras,parasize);
  567. { append the varargs }
  568. create_stdcall_paraloc_info(p,callerside,varargspara,parasize);
  569. result:=parasize;
  570. end;
  571. procedure ti386paramanager.createtempparaloc(list: taasmoutput;calloption : tproccalloption;parasym : tparavarsym;var cgpara:TCGPara);
  572. var
  573. paraloc : pcgparalocation;
  574. begin
  575. paraloc:=parasym.paraloc[callerside].location;
  576. { No need for temps when value is pushed }
  577. if not(use_fixed_stack) and
  578. assigned(paraloc) and
  579. (paraloc^.loc=LOC_REFERENCE) and
  580. (paraloc^.reference.index=NR_STACK_POINTER_REG) then
  581. duplicateparaloc(list,calloption,parasym,cgpara)
  582. else
  583. inherited createtempparaloc(list,calloption,parasym,cgpara);
  584. end;
  585. begin
  586. paramanager:=ti386paramanager.create;
  587. end.