cpupara.pas 23 KB

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