cpupara.pas 22 KB

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