cpupara.pas 20 KB

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