cpupara.pas 18 KB

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