cpupara.pas 23 KB

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