cpupara.pas 20 KB

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