cpupara.pas 23 KB

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