cpupara.pas 18 KB

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