cpupara.pas 22 KB

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