cpupara.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. {
  2. $Id$
  3. Copyright (c) 2002 by Florian Klaempfl
  4. Generates the argument location information for i386
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published bymethodpointer
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit cpupara;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. globtype,
  23. aasmtai,cpubase,cgbase,
  24. symconst,symtype,symsym,symdef,
  25. parabase,paramgr;
  26. type
  27. ti386paramanager = class(tparamanager)
  28. function param_use_paraloc(const cgpara:tcgpara):boolean;override;
  29. function ret_in_param(def : tdef;calloption : tproccalloption) : boolean;override;
  30. function push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;override;
  31. function get_para_align(calloption : tproccalloption):byte;override;
  32. function get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;override;
  33. function get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;override;
  34. function get_volatile_registers_mm(calloption : tproccalloption):tcpuregisterset;override;
  35. { Returns the location for the nr-st 32 Bit int parameter
  36. if every parameter before is an 32 Bit int parameter as well
  37. and if the calling conventions for the helper routines of the
  38. rtl are used.
  39. }
  40. procedure getintparaloc(calloption : tproccalloption; nr : longint;var cgpara:TCGPara);override;
  41. function create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;override;
  42. function create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;override;
  43. procedure createtempparaloc(list: taasmoutput;calloption : tproccalloption;parasym : tparavarsym;var cgpara:TCGPara);override;
  44. private
  45. procedure create_funcretloc_info(p : tabstractprocdef; side: tcallercallee);
  46. procedure create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;var parasize:longint);
  47. procedure create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;var parareg,parasize:longint);
  48. end;
  49. implementation
  50. uses
  51. cutils,
  52. systems,verbose,
  53. defutil,
  54. cgutils;
  55. const
  56. parasupregs : array[0..2] of tsuperregister = (RS_EAX,RS_EDX,RS_ECX);
  57. {****************************************************************************
  58. TI386PARAMANAGER
  59. ****************************************************************************}
  60. function ti386paramanager.param_use_paraloc(const cgpara:tcgpara):boolean;
  61. var
  62. paraloc : pcgparalocation;
  63. begin
  64. if not assigned(cgpara.location) then
  65. internalerror(200410102);
  66. result:=true;
  67. { All locations are LOC_REFERENCE }
  68. paraloc:=cgpara.location;
  69. while assigned(paraloc) do
  70. begin
  71. if (paraloc^.loc<>LOC_REFERENCE) then
  72. begin
  73. result:=false;
  74. exit;
  75. end;
  76. paraloc:=paraloc^.next;
  77. end;
  78. end;
  79. function ti386paramanager.ret_in_param(def : tdef;calloption : tproccalloption) : boolean;
  80. begin
  81. case target_info.system of
  82. system_i386_win32 :
  83. begin
  84. case def.deftype of
  85. recorddef :
  86. begin
  87. { Win32 GCC returns small records in the FUNCTION_RETURN_REG.
  88. For stdcall we follow delphi instead of GCC }
  89. if (calloption in [pocall_cdecl,pocall_cppdecl]) 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. { Win32 stdcall passes small records on the stack for call by
  115. value }
  116. if (target_info.system=system_i386_win32) and
  117. (calloption=pocall_stdcall) and
  118. (varspez=vs_value) and
  119. (def.size<=16) then
  120. result:=false
  121. end;
  122. formaldef :
  123. result:=true;
  124. recorddef :
  125. begin
  126. { Win32 stdcall passes small records on the stack for call by
  127. value }
  128. if (target_info.system=system_i386_win32) and
  129. (calloption=pocall_stdcall) and
  130. (varspez=vs_value) and
  131. (def.size<=16) then
  132. result:=false
  133. else
  134. result:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and (def.size>sizeof(aint));
  135. end;
  136. arraydef :
  137. begin
  138. { Win32 stdcall passes arrays on the stack for call by
  139. value }
  140. if (target_info.system=system_i386_win32) and
  141. (calloption=pocall_stdcall) and
  142. (varspez=vs_value) and
  143. (tarraydef(def).highrange>=tarraydef(def).lowrange) then
  144. result:=false
  145. else
  146. { array of const values are pushed on the stack }
  147. if (calloption in [pocall_cdecl,pocall_cppdecl]) then
  148. result:=not is_array_of_const(def)
  149. else
  150. begin
  151. result:=(
  152. (tarraydef(def).highrange>=tarraydef(def).lowrange) and
  153. (def.size>sizeof(aint))
  154. ) or
  155. is_open_array(def) or
  156. is_array_of_const(def) or
  157. is_array_constructor(def);
  158. end;
  159. end;
  160. objectdef :
  161. result:=is_object(def);
  162. stringdef :
  163. result:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and (tstringdef(def).string_typ in [st_shortstring,st_longstring]);
  164. procvardef :
  165. result:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and (po_methodpointer in tprocvardef(def).procoptions);
  166. setdef :
  167. result:=not(calloption in [pocall_cdecl,pocall_cppdecl]) and (tsetdef(def).settype<>smallset);
  168. end;
  169. end;
  170. function ti386paramanager.get_para_align(calloption : tproccalloption):byte;
  171. begin
  172. if calloption=pocall_oldfpccall then
  173. begin
  174. if target_info.system in [system_i386_go32v2,system_i386_watcom] then
  175. result:=2
  176. else
  177. result:=4;
  178. end
  179. else
  180. result:=std_param_align;
  181. end;
  182. function ti386paramanager.get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;
  183. begin
  184. case calloption of
  185. pocall_internproc :
  186. result:=[];
  187. pocall_compilerproc :
  188. begin
  189. if pocall_default=pocall_oldfpccall then
  190. result:=[RS_EAX,RS_EDX,RS_ECX,RS_ESI,RS_EDI,RS_EBX]
  191. else
  192. result:=[RS_EAX,RS_EDX,RS_ECX];
  193. end;
  194. pocall_inline,
  195. pocall_register,
  196. pocall_safecall,
  197. pocall_stdcall,
  198. pocall_cdecl,
  199. pocall_cppdecl :
  200. result:=[RS_EAX,RS_EDX,RS_ECX];
  201. pocall_far16,
  202. pocall_pascal,
  203. pocall_oldfpccall :
  204. result:=[RS_EAX,RS_EDX,RS_ECX,RS_ESI,RS_EDI,RS_EBX];
  205. else
  206. internalerror(200309071);
  207. end;
  208. end;
  209. function ti386paramanager.get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;
  210. begin
  211. result:=[0..first_fpu_imreg-1];
  212. end;
  213. function ti386paramanager.get_volatile_registers_mm(calloption : tproccalloption):tcpuregisterset;
  214. begin
  215. result:=[0..first_mm_imreg-1];
  216. end;
  217. procedure ti386paramanager.getintparaloc(calloption : tproccalloption; nr : longint;var cgpara:TCGPara);
  218. var
  219. paraloc : pcgparalocation;
  220. begin
  221. cgpara.reset;
  222. cgpara.size:=OS_INT;
  223. cgpara.intsize:=tcgsize2size[OS_INT];
  224. cgpara.alignment:=get_para_align(calloption);
  225. paraloc:=cgpara.add_location;
  226. with paraloc^ do
  227. begin
  228. size:=OS_INT;
  229. if calloption=pocall_register then
  230. begin
  231. if (nr<=high(parasupregs)+1) then
  232. begin
  233. if nr=0 then
  234. internalerror(200309271);
  235. loc:=LOC_REGISTER;
  236. register:=newreg(R_INTREGISTER,parasupregs[nr-1],R_SUBWHOLE);
  237. end
  238. else
  239. begin
  240. loc:=LOC_REFERENCE;
  241. reference.index:=NR_STACK_POINTER_REG;
  242. reference.offset:=sizeof(aint)*nr;
  243. end;
  244. end
  245. else
  246. begin
  247. loc:=LOC_REFERENCE;
  248. reference.index:=NR_STACK_POINTER_REG;
  249. reference.offset:=sizeof(aint)*nr;
  250. end;
  251. end;
  252. end;
  253. procedure ti386paramanager.create_funcretloc_info(p : tabstractprocdef; side: tcallercallee);
  254. var
  255. retcgsize : tcgsize;
  256. begin
  257. { Constructors return self instead of a boolean }
  258. if (p.proctypeoption=potype_constructor) then
  259. retcgsize:=OS_ADDR
  260. else
  261. retcgsize:=def_cgsize(p.rettype.def);
  262. location_reset(p.funcretloc[side],LOC_INVALID,OS_NO);
  263. { void has no location }
  264. if is_void(p.rettype.def) then
  265. begin
  266. location_reset(p.funcretloc[side],LOC_VOID,OS_NO);
  267. exit;
  268. end;
  269. { Return in FPU register? }
  270. if p.rettype.def.deftype=floatdef then
  271. begin
  272. p.funcretloc[side].loc:=LOC_FPUREGISTER;
  273. p.funcretloc[side].register:=NR_FPU_RESULT_REG;
  274. p.funcretloc[side].size:=retcgsize;
  275. end
  276. else
  277. { Return in register? }
  278. if not ret_in_param(p.rettype.def,p.proccalloption) then
  279. begin
  280. if retcgsize in [OS_64,OS_S64] then
  281. begin
  282. { low 32bits }
  283. p.funcretloc[side].loc:=LOC_REGISTER;
  284. p.funcretloc[side].size:=OS_64;
  285. if side=callerside then
  286. p.funcretloc[side].register64.reglo:=NR_FUNCTION_RESULT64_LOW_REG
  287. else
  288. p.funcretloc[side].register64.reglo:=NR_FUNCTION_RETURN64_LOW_REG;
  289. { high 32bits }
  290. if side=callerside then
  291. p.funcretloc[side].register64.reghi:=NR_FUNCTION_RESULT64_HIGH_REG
  292. else
  293. p.funcretloc[side].register64.reghi:=NR_FUNCTION_RETURN64_HIGH_REG;
  294. end
  295. else
  296. begin
  297. p.funcretloc[side].loc:=LOC_REGISTER;
  298. p.funcretloc[side].size:=retcgsize;
  299. if side=callerside then
  300. p.funcretloc[side].register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(retcgsize))
  301. else
  302. p.funcretloc[side].register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(retcgsize));
  303. end;
  304. end
  305. else
  306. begin
  307. p.funcretloc[side].loc:=LOC_REFERENCE;
  308. p.funcretloc[side].size:=retcgsize;
  309. end;
  310. end;
  311. procedure ti386paramanager.create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;var parasize:longint);
  312. var
  313. i : integer;
  314. hp : tparavarsym;
  315. paraloc : pcgparalocation;
  316. l,
  317. paralen,
  318. varalign : longint;
  319. paraalign : shortint;
  320. pushaddr : boolean;
  321. paracgsize : tcgsize;
  322. begin
  323. paraalign:=get_para_align(p.proccalloption);
  324. { we push Flags and CS as long
  325. to cope with the IRETD
  326. and we save 6 register + 4 selectors }
  327. if po_interrupt in p.procoptions then
  328. inc(parasize,8+6*4+4*2);
  329. { Offset is calculated like:
  330. sub esp,12
  331. mov [esp+8],para3
  332. mov [esp+4],para2
  333. mov [esp],para1
  334. call function
  335. That means for pushes the para with the
  336. highest offset (see para3) needs to be pushed first
  337. }
  338. for i:=0 to paras.count-1 do
  339. begin
  340. hp:=tparavarsym(paras[i]);
  341. pushaddr:=push_addr_param(hp.varspez,hp.vartype.def,p.proccalloption);
  342. if pushaddr then
  343. begin
  344. paralen:=sizeof(aint);
  345. paracgsize:=OS_ADDR;
  346. end
  347. else
  348. begin
  349. paralen:=push_size(hp.varspez,hp.vartype.def,p.proccalloption);
  350. paracgsize:=def_cgsize(hp.vartype.def);
  351. end;
  352. hp.paraloc[side].reset;
  353. hp.paraloc[side].size:=paracgsize;
  354. hp.paraloc[side].intsize:=paralen;
  355. hp.paraloc[side].Alignment:=paraalign;
  356. { Copy to stack? }
  357. if paracgsize=OS_NO then
  358. begin
  359. paraloc:=hp.paraloc[side].add_location;
  360. paraloc^.loc:=LOC_REFERENCE;
  361. paraloc^.size:=paracgsize;
  362. if side=callerside then
  363. paraloc^.reference.index:=NR_STACK_POINTER_REG
  364. else
  365. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  366. varalign:=used_align(size_2_align(paralen),paraalign,paraalign);
  367. paraloc^.reference.offset:=parasize;
  368. if side=calleeside then
  369. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  370. parasize:=align(parasize+paralen,varalign);
  371. end
  372. else
  373. begin
  374. if paralen=0 then
  375. internalerror(200501163);
  376. while (paralen>0) do
  377. begin
  378. { We can allocate at maximum 32 bits per location }
  379. if paralen>sizeof(aint) then
  380. l:=sizeof(aint)
  381. else
  382. l:=paralen;
  383. paraloc:=hp.paraloc[side].add_location;
  384. paraloc^.loc:=LOC_REFERENCE;
  385. paraloc^.size:=int_cgsize(l);
  386. if side=callerside then
  387. paraloc^.reference.index:=NR_STACK_POINTER_REG
  388. else
  389. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  390. varalign:=used_align(size_2_align(l),paraalign,paraalign);
  391. paraloc^.reference.offset:=parasize;
  392. if side=calleeside then
  393. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  394. parasize:=align(parasize+l,varalign);
  395. dec(paralen,l);
  396. end;
  397. end;
  398. end;
  399. end;
  400. procedure ti386paramanager.create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee;paras:tparalist;
  401. var parareg,parasize:longint);
  402. var
  403. hp : tparavarsym;
  404. paraloc : pcgparalocation;
  405. paracgsize : tcgsize;
  406. i : integer;
  407. l,
  408. paralen,
  409. varalign : longint;
  410. pushaddr : boolean;
  411. paraalign : shortint;
  412. begin
  413. paraalign:=get_para_align(p.proccalloption);
  414. { Register parameters are assigned from left to right }
  415. for i:=0 to paras.count-1 do
  416. begin
  417. hp:=tparavarsym(paras[i]);
  418. pushaddr:=push_addr_param(hp.varspez,hp.vartype.def,p.proccalloption);
  419. if pushaddr then
  420. begin
  421. paralen:=sizeof(aint);
  422. paracgsize:=OS_ADDR;
  423. end
  424. else
  425. begin
  426. paralen:=push_size(hp.varspez,hp.vartype.def,p.proccalloption);
  427. paracgsize:=def_cgsize(hp.vartype.def);
  428. end;
  429. hp.paraloc[side].reset;
  430. hp.paraloc[side].size:=paracgsize;
  431. hp.paraloc[side].intsize:=paralen;
  432. hp.paraloc[side].Alignment:=paraalign;
  433. {
  434. EAX
  435. EDX
  436. ECX
  437. Stack
  438. Stack
  439. 64bit values,floats,arrays and records are always
  440. on the stack.
  441. }
  442. if (parareg<=high(parasupregs)) and
  443. (paralen<=sizeof(aint)) and
  444. (
  445. not(hp.vartype.def.deftype in [floatdef,recorddef,arraydef]) or
  446. pushaddr
  447. ) then
  448. begin
  449. paraloc:=hp.paraloc[side].add_location;
  450. paraloc^.size:=paracgsize;
  451. paraloc^.loc:=LOC_REGISTER;
  452. paraloc^.register:=newreg(R_INTREGISTER,parasupregs[parareg],cgsize2subreg(paracgsize));
  453. inc(parareg);
  454. end
  455. else
  456. begin
  457. { Copy to stack? }
  458. if paracgsize=OS_NO then
  459. begin
  460. paraloc:=hp.paraloc[side].add_location;
  461. paraloc^.loc:=LOC_REFERENCE;
  462. paraloc^.size:=paracgsize;
  463. if side=callerside then
  464. paraloc^.reference.index:=NR_STACK_POINTER_REG
  465. else
  466. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  467. varalign:=used_align(size_2_align(paralen),paraalign,paraalign);
  468. paraloc^.reference.offset:=parasize;
  469. if side=calleeside then
  470. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  471. parasize:=align(parasize+paralen,varalign);
  472. end
  473. else
  474. begin
  475. if paralen=0 then
  476. internalerror(200501163);
  477. while (paralen>0) do
  478. begin
  479. { We can allocate at maximum 32 bits per location }
  480. if paralen>sizeof(aint) then
  481. l:=sizeof(aint)
  482. else
  483. l:=paralen;
  484. paraloc:=hp.paraloc[side].add_location;
  485. paraloc^.loc:=LOC_REFERENCE;
  486. paraloc^.size:=int_cgsize(l);
  487. if side=callerside then
  488. paraloc^.reference.index:=NR_STACK_POINTER_REG
  489. else
  490. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  491. varalign:=used_align(size_2_align(l),paraalign,paraalign);
  492. paraloc^.reference.offset:=parasize;
  493. if side=calleeside then
  494. inc(paraloc^.reference.offset,target_info.first_parm_offset);
  495. parasize:=align(parasize+l,varalign);
  496. dec(paralen,l);
  497. end;
  498. end;
  499. end;
  500. end;
  501. end;
  502. function ti386paramanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  503. var
  504. parasize,
  505. parareg : longint;
  506. begin
  507. parasize:=0;
  508. parareg:=0;
  509. case p.proccalloption of
  510. pocall_register :
  511. create_register_paraloc_info(p,side,p.paras,parareg,parasize);
  512. pocall_inline,
  513. pocall_compilerproc,
  514. pocall_internproc :
  515. begin
  516. { Use default calling }
  517. if (pocall_default=pocall_register) then
  518. create_register_paraloc_info(p,side,p.paras,parareg,parasize)
  519. else
  520. create_stdcall_paraloc_info(p,side,p.paras,parasize);
  521. end;
  522. else
  523. create_stdcall_paraloc_info(p,side,p.paras,parasize);
  524. end;
  525. create_funcretloc_info(p,side);
  526. result:=parasize;
  527. end;
  528. function ti386paramanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargsparalist):longint;
  529. var
  530. parasize : longint;
  531. begin
  532. parasize:=0;
  533. { calculate the registers for the normal parameters }
  534. create_stdcall_paraloc_info(p,callerside,p.paras,parasize);
  535. { append the varargs }
  536. create_stdcall_paraloc_info(p,callerside,varargspara,parasize);
  537. result:=parasize;
  538. end;
  539. procedure ti386paramanager.createtempparaloc(list: taasmoutput;calloption : tproccalloption;parasym : tparavarsym;var cgpara:TCGPara);
  540. var
  541. paraloc : pcgparalocation;
  542. begin
  543. paraloc:=parasym.paraloc[callerside].location;
  544. { No need for temps when value is pushed }
  545. if assigned(paraloc) and
  546. (paraloc^.loc=LOC_REFERENCE) and
  547. (paraloc^.reference.index=NR_STACK_POINTER_REG) then
  548. duplicateparaloc(list,calloption,parasym,cgpara)
  549. else
  550. inherited createtempparaloc(list,calloption,parasym,cgpara);
  551. end;
  552. begin
  553. paramanager:=ti386paramanager.create;
  554. end.
  555. {
  556. $Log$
  557. Revision 1.68 2005-02-15 19:16:04 peter
  558. * fix passing of 64bit values when using -Or
  559. Revision 1.67 2005/02/14 19:42:02 peter
  560. win32 stdcall fixes needed for tw3650
  561. Revision 1.66 2005/02/14 17:13:09 peter
  562. * truncate log
  563. Revision 1.65 2005/02/03 20:04:49 peter
  564. * push_addr_param must be defined per target
  565. Revision 1.64 2005/01/30 11:03:22 peter
  566. * revert last commit
  567. Revision 1.62 2005/01/18 22:19:20 peter
  568. * multiple location support for i386 a_param_ref
  569. * remove a_param_copy_ref for i386
  570. Revision 1.61 2005/01/10 21:50:05 jonas
  571. + support for passing records in registers under darwin
  572. * tcgpara now also has an intsize field, which contains the size in
  573. bytes of the whole parameter
  574. }