cpupara.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  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. cclasses,globtype,
  23. aasmtai,cpubase,cgbase,
  24. symconst,symtype,symdef,
  25. parabase,paramgr;
  26. type
  27. ti386paramanager = class(tparamanager)
  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:tvarargspara):longint;override;
  42. procedure createtempparaloc(list: taasmoutput;calloption : tproccalloption;paraitem : tparaitem;var cgpara:TCGPara);override;
  43. private
  44. procedure create_funcret_paraloc_info(p : tabstractprocdef; side: tcallercallee);
  45. procedure create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee;firstpara:tparaitem;
  46. var parasize:longint);
  47. procedure create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee;firstpara:tparaitem;
  48. var parareg,parasize:longint);
  49. end;
  50. implementation
  51. uses
  52. cutils,
  53. systems,verbose,
  54. defutil;
  55. const
  56. parasupregs : array[0..2] of tsuperregister = (RS_EAX,RS_EDX,RS_ECX);
  57. {****************************************************************************
  58. TI386PARAMANAGER
  59. ****************************************************************************}
  60. function ti386paramanager.ret_in_param(def : tdef;calloption : tproccalloption) : boolean;
  61. begin
  62. case target_info.system of
  63. system_i386_win32 :
  64. begin
  65. case def.deftype of
  66. recorddef :
  67. begin
  68. { Win32 GCC returns small records in the FUNCTION_RETURN_REG.
  69. For stdcall we follow delphi instead of GCC }
  70. if (calloption in [pocall_cdecl,pocall_cppdecl]) and
  71. (def.size<=8) then
  72. begin
  73. result:=false;
  74. exit;
  75. end;
  76. end;
  77. end;
  78. end;
  79. end;
  80. result:=inherited ret_in_param(def,calloption);
  81. end;
  82. function ti386paramanager.push_addr_param(varspez:tvarspez;def : tdef;calloption : tproccalloption) : boolean;
  83. begin
  84. case target_info.system of
  85. system_i386_win32 :
  86. begin
  87. case def.deftype of
  88. recorddef :
  89. begin
  90. { Win32 passes small records on the stack for call by
  91. value }
  92. if (calloption in [pocall_stdcall,pocall_cdecl,pocall_cppdecl]) and
  93. (varspez=vs_value) and
  94. (def.size<=8) then
  95. begin
  96. result:=false;
  97. exit;
  98. end;
  99. end;
  100. arraydef :
  101. begin
  102. { Win32 passes arrays on the stack for call by
  103. value }
  104. if (calloption in [pocall_stdcall,pocall_cdecl,pocall_cppdecl]) and
  105. (varspez=vs_value) and
  106. (tarraydef(def).highrange>=tarraydef(def).lowrange) then
  107. begin
  108. result:=true;
  109. exit;
  110. end;
  111. end;
  112. end;
  113. end;
  114. end;
  115. result:=inherited push_addr_param(varspez,def,calloption);
  116. end;
  117. function ti386paramanager.get_para_align(calloption : tproccalloption):byte;
  118. begin
  119. if calloption=pocall_oldfpccall then
  120. begin
  121. if target_info.system in [system_i386_go32v2,system_i386_watcom] then
  122. result:=2
  123. else
  124. result:=4;
  125. end
  126. else
  127. result:=std_param_align;
  128. end;
  129. function ti386paramanager.get_volatile_registers_int(calloption : tproccalloption):tcpuregisterset;
  130. begin
  131. case calloption of
  132. pocall_internproc :
  133. result:=[];
  134. pocall_compilerproc :
  135. begin
  136. if pocall_default=pocall_oldfpccall then
  137. result:=[RS_EAX,RS_EDX,RS_ECX,RS_ESI,RS_EDI,RS_EBX]
  138. else
  139. result:=[RS_EAX,RS_EDX,RS_ECX];
  140. end;
  141. pocall_inline,
  142. pocall_register,
  143. pocall_safecall,
  144. pocall_stdcall,
  145. pocall_cdecl,
  146. pocall_cppdecl :
  147. result:=[RS_EAX,RS_EDX,RS_ECX];
  148. pocall_far16,
  149. pocall_pascal,
  150. pocall_oldfpccall :
  151. result:=[RS_EAX,RS_EDX,RS_ECX,RS_ESI,RS_EDI,RS_EBX];
  152. else
  153. internalerror(200309071);
  154. end;
  155. end;
  156. function ti386paramanager.get_volatile_registers_fpu(calloption : tproccalloption):tcpuregisterset;
  157. begin
  158. result:=[0..first_fpu_imreg-1];
  159. end;
  160. function ti386paramanager.get_volatile_registers_mm(calloption : tproccalloption):tcpuregisterset;
  161. begin
  162. result:=[0..first_mm_imreg-1];
  163. end;
  164. procedure ti386paramanager.getintparaloc(calloption : tproccalloption; nr : longint;var cgpara:TCGPara);
  165. var
  166. paraloc : pcgparalocation;
  167. begin
  168. cgpara.reset;
  169. cgpara.size:=OS_INT;
  170. cgpara.alignment:=get_para_align(calloption);
  171. paraloc:=cgpara.add_location;
  172. with paraloc^ do
  173. begin
  174. size:=OS_INT;
  175. if calloption=pocall_register then
  176. begin
  177. if (nr<=high(parasupregs)+1) then
  178. begin
  179. if nr=0 then
  180. internalerror(200309271);
  181. loc:=LOC_REGISTER;
  182. register:=newreg(R_INTREGISTER,parasupregs[nr-1],R_SUBWHOLE);
  183. end
  184. else
  185. begin
  186. loc:=LOC_REFERENCE;
  187. reference.index:=NR_STACK_POINTER_REG;
  188. reference.offset:=sizeof(aint)*nr;
  189. end;
  190. end
  191. else
  192. begin
  193. loc:=LOC_REFERENCE;
  194. reference.index:=NR_STACK_POINTER_REG;
  195. reference.offset:=sizeof(aint)*nr;
  196. end;
  197. end;
  198. end;
  199. procedure ti386paramanager.create_funcret_paraloc_info(p : tabstractprocdef; side: tcallercallee);
  200. var
  201. hiparaloc,
  202. paraloc : pcgparalocation;
  203. retcgsize : tcgsize;
  204. begin
  205. { Constructors return self instead of a boolean }
  206. if (p.proctypeoption=potype_constructor) then
  207. retcgsize:=OS_ADDR
  208. else
  209. retcgsize:=def_cgsize(p.rettype.def);
  210. p.funcret_paraloc[side].reset;
  211. p.funcret_paraloc[side].Alignment:=std_param_align;
  212. p.funcret_paraloc[side].size:=retcgsize;
  213. { void has no location }
  214. if is_void(p.rettype.def) then
  215. exit;
  216. paraloc:=p.funcret_paraloc[side].add_location;
  217. { Return in FPU register? }
  218. if p.rettype.def.deftype=floatdef then
  219. begin
  220. paraloc^.loc:=LOC_FPUREGISTER;
  221. paraloc^.register:=NR_FPU_RESULT_REG;
  222. paraloc^.size:=retcgsize;
  223. end
  224. else
  225. { Return in register? }
  226. if not ret_in_param(p.rettype.def,p.proccalloption) then
  227. begin
  228. if retcgsize in [OS_64,OS_S64] then
  229. begin
  230. { low 32bits }
  231. paraloc^.loc:=LOC_REGISTER;
  232. paraloc^.size:=OS_32;
  233. if side=callerside then
  234. paraloc^.register:=NR_FUNCTION_RESULT64_LOW_REG
  235. else
  236. paraloc^.register:=NR_FUNCTION_RETURN64_LOW_REG;
  237. { high 32bits }
  238. hiparaloc:=p.funcret_paraloc[side].add_location;
  239. hiparaloc^.loc:=LOC_REGISTER;
  240. hiparaloc^.size:=OS_32;
  241. if side=callerside then
  242. hiparaloc^.register:=NR_FUNCTION_RESULT64_HIGH_REG
  243. else
  244. hiparaloc^.register:=NR_FUNCTION_RETURN64_HIGH_REG;
  245. end
  246. else
  247. begin
  248. paraloc^.loc:=LOC_REGISTER;
  249. paraloc^.size:=retcgsize;
  250. if side=callerside then
  251. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RESULT_REG,cgsize2subreg(retcgsize))
  252. else
  253. paraloc^.register:=newreg(R_INTREGISTER,RS_FUNCTION_RETURN_REG,cgsize2subreg(retcgsize));
  254. end;
  255. end
  256. else
  257. begin
  258. paraloc^.loc:=LOC_REFERENCE;
  259. paraloc^.size:=retcgsize;
  260. end;
  261. end;
  262. procedure ti386paramanager.create_stdcall_paraloc_info(p : tabstractprocdef; side: tcallercallee;firstpara:tparaitem;
  263. var parasize:longint);
  264. var
  265. hp : tparaitem;
  266. paraloc : pcgparalocation;
  267. l,
  268. varalign,
  269. paraalign : longint;
  270. paracgsize : tcgsize;
  271. begin
  272. paraalign:=get_para_align(p.proccalloption);
  273. { we push Flags and CS as long
  274. to cope with the IRETD
  275. and we save 6 register + 4 selectors }
  276. if po_interrupt in p.procoptions then
  277. inc(parasize,8+6*4+4*2);
  278. { Offset is calculated like:
  279. sub esp,12
  280. mov [esp+8],para3
  281. mov [esp+4],para2
  282. mov [esp],para1
  283. call function
  284. That means for pushes the para with the
  285. highest offset (see para3) needs to be pushed first
  286. }
  287. hp:=firstpara;
  288. while assigned(hp) do
  289. begin
  290. if push_addr_param(hp.paratyp,hp.paratype.def,p.proccalloption) then
  291. paracgsize:=OS_ADDR
  292. else
  293. begin
  294. paracgsize:=def_cgSize(hp.paratype.def);
  295. if paracgsize=OS_NO then
  296. paracgsize:=OS_ADDR;
  297. end;
  298. hp.paraloc[side].reset;
  299. hp.paraloc[side].size:=paracgsize;
  300. hp.paraloc[side].Alignment:=paraalign;
  301. paraloc:=hp.paraloc[side].add_location;
  302. paraloc^.loc:=LOC_REFERENCE;
  303. paraloc^.size:=paracgsize;
  304. if side=callerside then
  305. paraloc^.reference.index:=NR_STACK_POINTER_REG
  306. else
  307. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  308. l:=push_size(hp.paratyp,hp.paratype.def,p.proccalloption);
  309. varalign:=used_align(size_2_align(l),paraalign,paraalign);
  310. paraloc^.reference.offset:=parasize;
  311. parasize:=align(parasize+l,varalign);
  312. hp:=tparaitem(hp.next);
  313. end;
  314. { Adapt offsets for left-to-right calling }
  315. if p.proccalloption in pushleftright_pocalls then
  316. begin
  317. hp:=tparaitem(p.para.first);
  318. while assigned(hp) do
  319. begin
  320. l:=push_size(hp.paratyp,hp.paratype.def,p.proccalloption);
  321. varalign:=used_align(size_2_align(l),paraalign,paraalign);
  322. l:=align(l,varalign);
  323. with hp.paraloc[side].location^ do
  324. begin
  325. reference.offset:=parasize-reference.offset-l;
  326. if side=calleeside then
  327. inc(reference.offset,target_info.first_parm_offset);
  328. end;
  329. hp:=tparaitem(hp.next);
  330. end;
  331. end
  332. else
  333. begin
  334. { Only need to adapt the callee side to include the
  335. standard stackframe size }
  336. if side=calleeside then
  337. begin
  338. hp:=tparaitem(p.para.first);
  339. while assigned(hp) do
  340. begin
  341. inc(hp.paraloc[side].location^.reference.offset,target_info.first_parm_offset);
  342. hp:=tparaitem(hp.next);
  343. end;
  344. end;
  345. end;
  346. end;
  347. procedure ti386paramanager.create_register_paraloc_info(p : tabstractprocdef; side: tcallercallee;firstpara:tparaitem;
  348. var parareg,parasize:longint);
  349. var
  350. hp : tparaitem;
  351. paraloc : pcgparalocation;
  352. pushaddr,
  353. is_64bit : boolean;
  354. paracgsize : tcgsize;
  355. l,
  356. varalign,
  357. paraalign : longint;
  358. begin
  359. paraalign:=get_para_align(p.proccalloption);
  360. { Register parameters are assigned from left to right }
  361. hp:=firstpara;
  362. while assigned(hp) do
  363. begin
  364. pushaddr:=push_addr_param(hp.paratyp,hp.paratype.def,p.proccalloption);
  365. if pushaddr then
  366. paracgsize:=OS_ADDR
  367. else
  368. paracgsize:=def_cgsize(hp.paratype.def);
  369. is_64bit:=(paracgsize in [OS_64,OS_S64,OS_F64]);
  370. hp.paraloc[side].reset;
  371. hp.paraloc[side].size:=paracgsize;
  372. hp.paraloc[side].Alignment:=paraalign;
  373. {
  374. EAX
  375. EDX
  376. ECX
  377. Stack
  378. Stack
  379. 64bit values,floats,arrays and records are always
  380. on the stack.
  381. }
  382. paraloc:=hp.paraloc[side].add_location;
  383. paraloc^.size:=paracgsize;
  384. if (parareg<=high(parasupregs)) and
  385. not(
  386. is_64bit or
  387. ((hp.paratype.def.deftype in [floatdef,recorddef,arraydef]) and
  388. (not pushaddr))
  389. ) then
  390. begin
  391. paraloc^.loc:=LOC_REGISTER;
  392. paraloc^.register:=newreg(R_INTREGISTER,parasupregs[parareg],cgsize2subreg(paracgsize));
  393. inc(parareg);
  394. end
  395. else
  396. begin
  397. paraloc^.loc:=LOC_REFERENCE;
  398. if side=callerside then
  399. paraloc^.reference.index:=NR_STACK_POINTER_REG
  400. else
  401. paraloc^.reference.index:=NR_FRAME_POINTER_REG;
  402. l:=push_size(hp.paratyp,hp.paratype.def,p.proccalloption);
  403. varalign:=size_2_align(l);
  404. paraloc^.reference.offset:=parasize;
  405. varalign:=used_align(varalign,paraalign,paraalign);
  406. parasize:=align(parasize+l,varalign);
  407. end;
  408. hp:=tparaitem(hp.next);
  409. end;
  410. { Register parameters are assigned from left-to-right, adapt offset
  411. for calleeside to be reversed }
  412. hp:=tparaitem(p.para.first);
  413. while assigned(hp) do
  414. begin
  415. with hp.paraloc[side].location^ do
  416. begin
  417. if (loc=LOC_REFERENCE) then
  418. begin
  419. l:=push_size(hp.paratyp,hp.paratype.def,p.proccalloption);
  420. varalign:=used_align(size_2_align(l),paraalign,paraalign);
  421. l:=align(l,varalign);
  422. reference.offset:=parasize-reference.offset-l;
  423. if side=calleeside then
  424. inc(reference.offset,target_info.first_parm_offset);
  425. end;
  426. end;
  427. hp:=tparaitem(hp.next);
  428. end;
  429. end;
  430. function ti386paramanager.create_paraloc_info(p : tabstractprocdef; side: tcallercallee):longint;
  431. var
  432. parasize,
  433. parareg : longint;
  434. begin
  435. parasize:=0;
  436. parareg:=0;
  437. case p.proccalloption of
  438. pocall_register :
  439. create_register_paraloc_info(p,side,tparaitem(p.para.first),parareg,parasize);
  440. pocall_inline,
  441. pocall_compilerproc,
  442. pocall_internproc :
  443. begin
  444. { Use default calling }
  445. if (pocall_default=pocall_register) then
  446. create_register_paraloc_info(p,side,tparaitem(p.para.first),parareg,parasize)
  447. else
  448. create_stdcall_paraloc_info(p,side,tparaitem(p.para.first),parasize);
  449. end;
  450. else
  451. create_stdcall_paraloc_info(p,side,tparaitem(p.para.first),parasize);
  452. end;
  453. create_funcret_paraloc_info(p,side);
  454. result:=parasize;
  455. end;
  456. function ti386paramanager.create_varargs_paraloc_info(p : tabstractprocdef; varargspara:tvarargspara):longint;
  457. var
  458. parasize : longint;
  459. begin
  460. parasize:=0;
  461. { calculate the registers for the normal parameters }
  462. create_stdcall_paraloc_info(p,callerside,tparaitem(p.para.first),parasize);
  463. { append the varargs }
  464. create_stdcall_paraloc_info(p,callerside,tparaitem(varargspara.first),parasize);
  465. result:=parasize;
  466. end;
  467. procedure ti386paramanager.createtempparaloc(list: taasmoutput;calloption : tproccalloption;paraitem : tparaitem;var cgpara:TCGPara);
  468. var
  469. paraloc : pcgparalocation;
  470. begin
  471. paraloc:=paraitem.paraloc[callerside].location;
  472. { No need for temps when value is pushed }
  473. if assigned(paraloc) and
  474. (paraloc^.loc=LOC_REFERENCE) and
  475. (paraloc^.reference.index=NR_STACK_POINTER_REG) then
  476. duplicateparaloc(list,calloption,paraitem,cgpara)
  477. else
  478. inherited createtempparaloc(list,calloption,paraitem,cgpara);
  479. end;
  480. begin
  481. paramanager:=ti386paramanager.create;
  482. end.
  483. {
  484. $Log$
  485. Revision 1.55 2004-09-21 17:25:12 peter
  486. * paraloc branch merged
  487. Revision 1.54.4.1 2004/08/31 20:43:06 peter
  488. * paraloc patch
  489. Revision 1.54 2004/07/09 23:30:13 jonas
  490. * changed first_sse_imreg to first_mm_imreg
  491. Revision 1.53 2004/07/09 23:09:02 peter
  492. * varargs calculation fixed, it's now the same as the other
  493. targets
  494. Revision 1.52 2004/06/20 08:55:31 florian
  495. * logs truncated
  496. Revision 1.51 2004/06/16 20:07:10 florian
  497. * dwarf branch merged
  498. Revision 1.50.2.3 2004/05/02 21:37:35 florian
  499. * setting of func. ret. for i386 fixed
  500. Revision 1.50.2.2 2004/05/02 12:45:32 peter
  501. * enabled cpuhasfixedstack for x86-64 again
  502. * fixed size of temp allocation for parameters
  503. Revision 1.50.2.1 2004/05/01 16:02:10 peter
  504. * POINTER_SIZE replaced with sizeof(aint)
  505. * aint,aword,tconst*int moved to globtype
  506. Revision 1.50 2004/02/09 22:14:17 peter
  507. * more x86_64 parameter fixes
  508. * tparalocation.lochigh is now used to indicate if registerhigh
  509. is used and what the type is
  510. }