cgcpu.pas 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. {
  2. Copyright (c) 1998-2002 by Florian Klaempfl
  3. This unit implements the code generator for the i386
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit cgcpu;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. globtype,
  22. cgbase,cgobj,cg64f32,cgx86,
  23. aasmbase,aasmtai,aasmdata,aasmcpu,
  24. cpubase,parabase,cgutils,
  25. symconst,symdef
  26. ;
  27. type
  28. tcg386 = class(tcgx86)
  29. procedure init_register_allocators;override;
  30. procedure do_register_allocation(list:TAsmList;headertai:tai);override;
  31. { passing parameter using push instead of mov }
  32. procedure a_load_reg_cgpara(list : TAsmList;size : tcgsize;r : tregister;const cgpara : tcgpara);override;
  33. procedure a_load_const_cgpara(list : TAsmList;size : tcgsize;a : tcgint;const cgpara : tcgpara);override;
  34. procedure a_load_ref_cgpara(list : TAsmList;size : tcgsize;const r : treference;const cgpara : tcgpara);override;
  35. procedure a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const cgpara : tcgpara);override;
  36. procedure g_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean);override;
  37. procedure g_copyvaluepara_openarray(list : TAsmList;const ref:treference;const lenloc:tlocation;elesize:tcgint;destreg:tregister);override;
  38. procedure g_releasevaluepara_openarray(list : TAsmList;const l:tlocation);override;
  39. procedure g_exception_reason_save(list : TAsmList; const href : treference);override;
  40. procedure g_exception_reason_save_const(list : TAsmList; const href : treference; a: tcgint);override;
  41. procedure g_exception_reason_load(list : TAsmList; const href : treference);override;
  42. procedure g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);override;
  43. procedure g_maybe_got_init(list: TAsmList); override;
  44. end;
  45. tcg64f386 = class(tcg64f32)
  46. procedure a_op64_ref_reg(list : TAsmList;op:TOpCG;size : tcgsize;const ref : treference;reg : tregister64);override;
  47. procedure a_op64_reg_reg(list : TAsmList;op:TOpCG;size : tcgsize;regsrc,regdst : tregister64);override;
  48. procedure a_op64_const_reg(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;reg : tregister64);override;
  49. procedure a_op64_const_ref(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;const ref : treference);override;
  50. private
  51. procedure get_64bit_ops(op:TOpCG;var op1,op2:TAsmOp);
  52. end;
  53. procedure create_codegen;
  54. implementation
  55. uses
  56. globals,verbose,systems,cutils,
  57. paramgr,procinfo,fmodule,
  58. rgcpu,rgx86,cpuinfo;
  59. function use_push(const cgpara:tcgpara):boolean;
  60. begin
  61. result:=(not paramanager.use_fixed_stack) and
  62. assigned(cgpara.location) and
  63. (cgpara.location^.loc=LOC_REFERENCE) and
  64. (cgpara.location^.reference.index=NR_STACK_POINTER_REG);
  65. end;
  66. procedure tcg386.init_register_allocators;
  67. begin
  68. inherited init_register_allocators;
  69. if not(target_info.system in [system_i386_darwin,system_i386_iphonesim]) and
  70. (cs_create_pic in current_settings.moduleswitches) then
  71. rg[R_INTREGISTER]:=trgcpu.create(R_INTREGISTER,R_SUBWHOLE,[RS_EAX,RS_EDX,RS_ECX,RS_ESI,RS_EDI],first_int_imreg,[RS_EBP])
  72. else
  73. rg[R_INTREGISTER]:=trgcpu.create(R_INTREGISTER,R_SUBWHOLE,[RS_EAX,RS_EDX,RS_ECX,RS_EBX,RS_ESI,RS_EDI],first_int_imreg,[RS_EBP]);
  74. rg[R_MMXREGISTER]:=trgcpu.create(R_MMXREGISTER,R_SUBNONE,[RS_XMM0,RS_XMM1,RS_XMM2,RS_XMM3,RS_XMM4,RS_XMM5,RS_XMM6,RS_XMM7],first_mm_imreg,[]);
  75. rg[R_MMREGISTER]:=trgcpu.create(R_MMREGISTER,R_SUBWHOLE,[RS_XMM0,RS_XMM1,RS_XMM2,RS_XMM3,RS_XMM4,RS_XMM5,RS_XMM6,RS_XMM7],first_mm_imreg,[]);
  76. rgfpu:=Trgx86fpu.create;
  77. end;
  78. procedure tcg386.do_register_allocation(list:TAsmList;headertai:tai);
  79. begin
  80. if (pi_needs_got in current_procinfo.flags) then
  81. begin
  82. if getsupreg(current_procinfo.got) < first_int_imreg then
  83. include(rg[R_INTREGISTER].used_in_proc,getsupreg(current_procinfo.got));
  84. end;
  85. inherited do_register_allocation(list,headertai);
  86. end;
  87. procedure tcg386.a_load_reg_cgpara(list : TAsmList;size : tcgsize;r : tregister;const cgpara : tcgpara);
  88. var
  89. pushsize : tcgsize;
  90. begin
  91. check_register_size(size,r);
  92. if use_push(cgpara) then
  93. begin
  94. cgpara.check_simple_location;
  95. if tcgsize2size[cgpara.location^.size]>cgpara.alignment then
  96. pushsize:=cgpara.location^.size
  97. else
  98. pushsize:=int_cgsize(cgpara.alignment);
  99. list.concat(taicpu.op_reg(A_PUSH,tcgsize2opsize[pushsize],makeregsize(list,r,pushsize)));
  100. end
  101. else
  102. inherited a_load_reg_cgpara(list,size,r,cgpara);
  103. end;
  104. procedure tcg386.a_load_const_cgpara(list : TAsmList;size : tcgsize;a : tcgint;const cgpara : tcgpara);
  105. var
  106. pushsize : tcgsize;
  107. begin
  108. if use_push(cgpara) then
  109. begin
  110. cgpara.check_simple_location;
  111. if tcgsize2size[cgpara.location^.size]>cgpara.alignment then
  112. pushsize:=cgpara.location^.size
  113. else
  114. pushsize:=int_cgsize(cgpara.alignment);
  115. list.concat(taicpu.op_const(A_PUSH,tcgsize2opsize[pushsize],a));
  116. end
  117. else
  118. inherited a_load_const_cgpara(list,size,a,cgpara);
  119. end;
  120. procedure tcg386.a_load_ref_cgpara(list : TAsmList;size : tcgsize;const r : treference;const cgpara : tcgpara);
  121. procedure pushdata(paraloc:pcgparalocation;ofs:tcgint);
  122. var
  123. pushsize : tcgsize;
  124. tmpreg : tregister;
  125. href : treference;
  126. begin
  127. if not assigned(paraloc) then
  128. exit;
  129. if (paraloc^.loc<>LOC_REFERENCE) or
  130. (paraloc^.reference.index<>NR_STACK_POINTER_REG) or
  131. (tcgsize2size[paraloc^.size]>sizeof(aint)) then
  132. internalerror(200501162);
  133. { Pushes are needed in reverse order, add the size of the
  134. current location to the offset where to load from. This
  135. prevents wrong calculations for the last location when
  136. the size is not a power of 2 }
  137. if assigned(paraloc^.next) then
  138. pushdata(paraloc^.next,ofs+tcgsize2size[paraloc^.size]);
  139. { Push the data starting at ofs }
  140. href:=r;
  141. inc(href.offset,ofs);
  142. if tcgsize2size[paraloc^.size]>cgpara.alignment then
  143. pushsize:=paraloc^.size
  144. else
  145. pushsize:=int_cgsize(cgpara.alignment);
  146. if tcgsize2size[paraloc^.size]<cgpara.alignment then
  147. begin
  148. tmpreg:=getintregister(list,pushsize);
  149. a_load_ref_reg(list,paraloc^.size,pushsize,href,tmpreg);
  150. list.concat(taicpu.op_reg(A_PUSH,TCgsize2opsize[pushsize],tmpreg));
  151. end
  152. else
  153. begin
  154. make_simple_ref(list,href);
  155. list.concat(taicpu.op_ref(A_PUSH,TCgsize2opsize[pushsize],href));
  156. end;
  157. end;
  158. var
  159. len : tcgint;
  160. href : treference;
  161. begin
  162. { cgpara.size=OS_NO requires a copy on the stack }
  163. if use_push(cgpara) then
  164. begin
  165. { Record copy? }
  166. if (cgpara.size in [OS_NO,OS_F64]) or (size=OS_NO) then
  167. begin
  168. cgpara.check_simple_location;
  169. len:=align(cgpara.intsize,cgpara.alignment);
  170. g_stackpointer_alloc(list,len);
  171. reference_reset_base(href,NR_STACK_POINTER_REG,0,4);
  172. g_concatcopy(list,r,href,len);
  173. end
  174. else
  175. begin
  176. if tcgsize2size[cgpara.size]<>tcgsize2size[size] then
  177. internalerror(200501161);
  178. { We need to push the data in reverse order,
  179. therefor we use a recursive algorithm }
  180. pushdata(cgpara.location,0);
  181. end
  182. end
  183. else
  184. inherited a_load_ref_cgpara(list,size,r,cgpara);
  185. end;
  186. procedure tcg386.a_loadaddr_ref_cgpara(list : TAsmList;const r : treference;const cgpara : tcgpara);
  187. var
  188. tmpreg : tregister;
  189. opsize : topsize;
  190. tmpref : treference;
  191. begin
  192. with r do
  193. begin
  194. if use_push(cgpara) then
  195. begin
  196. cgpara.check_simple_location;
  197. opsize:=tcgsize2opsize[OS_ADDR];
  198. if (segment=NR_NO) and (base=NR_NO) and (index=NR_NO) then
  199. begin
  200. if assigned(symbol) then
  201. begin
  202. if (target_info.system in [system_i386_darwin,system_i386_iphonesim]) and
  203. ((r.symbol.bind in [AB_EXTERNAL,AB_WEAK_EXTERNAL]) or
  204. (cs_create_pic in current_settings.moduleswitches)) then
  205. begin
  206. tmpreg:=getaddressregister(list);
  207. a_loadaddr_ref_reg(list,r,tmpreg);
  208. list.concat(taicpu.op_reg(A_PUSH,opsize,tmpreg));
  209. end
  210. else if cs_create_pic in current_settings.moduleswitches then
  211. begin
  212. if offset<>0 then
  213. begin
  214. tmpreg:=getaddressregister(list);
  215. a_loadaddr_ref_reg(list,r,tmpreg);
  216. list.concat(taicpu.op_reg(A_PUSH,opsize,tmpreg));
  217. end
  218. else
  219. begin
  220. reference_reset_symbol(tmpref,r.symbol,0,r.alignment);
  221. tmpref.refaddr:=addr_pic;
  222. tmpref.base:=current_procinfo.got;
  223. include(current_procinfo.flags,pi_needs_got);
  224. list.concat(taicpu.op_ref(A_PUSH,S_L,tmpref));
  225. end
  226. end
  227. else
  228. list.concat(Taicpu.Op_sym_ofs(A_PUSH,opsize,symbol,offset));
  229. end
  230. else
  231. list.concat(Taicpu.Op_const(A_PUSH,opsize,offset));
  232. end
  233. else if (segment=NR_NO) and (base=NR_NO) and (index<>NR_NO) and
  234. (offset=0) and (scalefactor=0) and (symbol=nil) then
  235. list.concat(Taicpu.Op_reg(A_PUSH,opsize,index))
  236. else if (segment=NR_NO) and (base<>NR_NO) and (index=NR_NO) and
  237. (offset=0) and (symbol=nil) then
  238. list.concat(Taicpu.Op_reg(A_PUSH,opsize,base))
  239. else
  240. begin
  241. tmpreg:=getaddressregister(list);
  242. a_loadaddr_ref_reg(list,r,tmpreg);
  243. list.concat(taicpu.op_reg(A_PUSH,opsize,tmpreg));
  244. end;
  245. end
  246. else
  247. inherited a_loadaddr_ref_cgpara(list,r,cgpara);
  248. end;
  249. end;
  250. procedure tcg386.g_proc_exit(list : TAsmList;parasize:longint;nostackframe:boolean);
  251. var
  252. stacksize : longint;
  253. begin
  254. { MMX needs to call EMMS }
  255. if assigned(rg[R_MMXREGISTER]) and
  256. (rg[R_MMXREGISTER].uses_registers) then
  257. list.concat(Taicpu.op_none(A_EMMS,S_NO));
  258. { remove stackframe }
  259. if not nostackframe then
  260. begin
  261. if (current_procinfo.framepointer=NR_STACK_POINTER_REG) then
  262. begin
  263. stacksize:=current_procinfo.calc_stackframe_size;
  264. if (target_info.system in [system_i386_darwin,system_i386_iphonesim]) and
  265. ((stacksize <> 0) or
  266. (pi_do_call in current_procinfo.flags) or
  267. { can't detect if a call in this case -> use nostackframe }
  268. { if you (think you) know what you are doing }
  269. (po_assembler in current_procinfo.procdef.procoptions)) then
  270. stacksize := align(stacksize+sizeof(aint),16) - sizeof(aint);
  271. if (stacksize<>0) then
  272. cg.a_op_const_reg(list,OP_ADD,OS_ADDR,stacksize,current_procinfo.framepointer);
  273. end
  274. else
  275. list.concat(Taicpu.op_none(A_LEAVE,S_NO));
  276. list.concat(tai_regalloc.dealloc(current_procinfo.framepointer,nil));
  277. end;
  278. { return from proc }
  279. if (po_interrupt in current_procinfo.procdef.procoptions) and
  280. { this messes up stack alignment }
  281. not(target_info.system in [system_i386_darwin,system_i386_iphonesim]) then
  282. begin
  283. if assigned(current_procinfo.procdef.funcretloc[calleeside].location) and
  284. (current_procinfo.procdef.funcretloc[calleeside].location^.loc=LOC_REGISTER) then
  285. begin
  286. if (getsupreg(current_procinfo.procdef.funcretloc[calleeside].location^.register)=RS_EAX) then
  287. list.concat(Taicpu.Op_const_reg(A_ADD,S_L,4,NR_ESP))
  288. else
  289. internalerror(2010053001);
  290. end
  291. else
  292. list.concat(Taicpu.Op_reg(A_POP,S_L,NR_EAX));
  293. list.concat(Taicpu.Op_reg(A_POP,S_L,NR_EBX));
  294. list.concat(Taicpu.Op_reg(A_POP,S_L,NR_ECX));
  295. if (current_procinfo.procdef.funcretloc[calleeside].size in [OS_64,OS_S64]) and
  296. assigned(current_procinfo.procdef.funcretloc[calleeside].location) and
  297. assigned(current_procinfo.procdef.funcretloc[calleeside].location^.next) and
  298. (current_procinfo.procdef.funcretloc[calleeside].location^.next^.loc=LOC_REGISTER) then
  299. begin
  300. if (getsupreg(current_procinfo.procdef.funcretloc[calleeside].location^.next^.register)=RS_EDX) then
  301. list.concat(Taicpu.Op_const_reg(A_ADD,S_L,4,NR_ESP))
  302. else
  303. internalerror(2010053002);
  304. end
  305. else
  306. list.concat(Taicpu.Op_reg(A_POP,S_L,NR_EDX));
  307. list.concat(Taicpu.Op_reg(A_POP,S_L,NR_ESI));
  308. list.concat(Taicpu.Op_reg(A_POP,S_L,NR_EDI));
  309. { .... also the segment registers }
  310. list.concat(Taicpu.Op_reg(A_POP,S_W,NR_DS));
  311. list.concat(Taicpu.Op_reg(A_POP,S_W,NR_ES));
  312. list.concat(Taicpu.Op_reg(A_POP,S_W,NR_FS));
  313. list.concat(Taicpu.Op_reg(A_POP,S_W,NR_GS));
  314. { this restores the flags }
  315. list.concat(Taicpu.Op_none(A_IRET,S_NO));
  316. end
  317. { Routines with the poclearstack flag set use only a ret }
  318. else if (current_procinfo.procdef.proccalloption in clearstack_pocalls) and
  319. (not paramanager.use_fixed_stack) then
  320. begin
  321. { complex return values are removed from stack in C code PM }
  322. { but not on win32 }
  323. { and not for safecall with hidden exceptions, because the result }
  324. { wich contains the exception is passed in EAX }
  325. if (target_info.system <> system_i386_win32) and
  326. not ((current_procinfo.procdef.proccalloption = pocall_safecall) and
  327. (tf_safecall_exceptions in target_info.flags)) and
  328. paramanager.ret_in_param(current_procinfo.procdef.returndef,
  329. current_procinfo.procdef.proccalloption) then
  330. list.concat(Taicpu.Op_const(A_RET,S_W,sizeof(aint)))
  331. else
  332. list.concat(Taicpu.Op_none(A_RET,S_NO));
  333. end
  334. { ... also routines with parasize=0 }
  335. else if (parasize=0) then
  336. list.concat(Taicpu.Op_none(A_RET,S_NO))
  337. else
  338. begin
  339. { parameters are limited to 65535 bytes because ret allows only imm16 }
  340. if (parasize>65535) then
  341. CGMessage(cg_e_parasize_too_big);
  342. list.concat(Taicpu.Op_const(A_RET,S_W,parasize));
  343. end;
  344. end;
  345. procedure tcg386.g_copyvaluepara_openarray(list : TAsmList;const ref:treference;const lenloc:tlocation;elesize:tcgint;destreg:tregister);
  346. var
  347. power,len : longint;
  348. opsize : topsize;
  349. {$ifndef __NOWINPECOFF__}
  350. again,ok : tasmlabel;
  351. {$endif}
  352. begin
  353. if paramanager.use_fixed_stack then
  354. begin
  355. inherited g_copyvaluepara_openarray(list,ref,lenloc,elesize,destreg);
  356. exit;
  357. end;
  358. { get stack space }
  359. getcpuregister(list,NR_EDI);
  360. a_load_loc_reg(list,OS_INT,lenloc,NR_EDI);
  361. list.concat(Taicpu.op_reg(A_INC,S_L,NR_EDI));
  362. { Now EDI contains (high+1). Copy it to ECX for later use. }
  363. getcpuregister(list,NR_ECX);
  364. list.concat(Taicpu.op_reg_reg(A_MOV,S_L,NR_EDI,NR_ECX));
  365. if (elesize<>1) then
  366. begin
  367. if ispowerof2(elesize, power) then
  368. list.concat(Taicpu.op_const_reg(A_SHL,S_L,power,NR_EDI))
  369. else
  370. list.concat(Taicpu.op_const_reg(A_IMUL,S_L,elesize,NR_EDI));
  371. end;
  372. {$ifndef __NOWINPECOFF__}
  373. { windows guards only a few pages for stack growing, }
  374. { so we have to access every page first }
  375. if target_info.system=system_i386_win32 then
  376. begin
  377. current_asmdata.getjumplabel(again);
  378. current_asmdata.getjumplabel(ok);
  379. a_label(list,again);
  380. list.concat(Taicpu.op_const_reg(A_CMP,S_L,winstackpagesize,NR_EDI));
  381. a_jmp_cond(list,OC_B,ok);
  382. list.concat(Taicpu.op_const_reg(A_SUB,S_L,winstackpagesize-4,NR_ESP));
  383. list.concat(Taicpu.op_reg(A_PUSH,S_L,NR_EDI));
  384. list.concat(Taicpu.op_const_reg(A_SUB,S_L,winstackpagesize,NR_EDI));
  385. a_jmp_always(list,again);
  386. a_label(list,ok);
  387. end;
  388. {$endif __NOWINPECOFF__}
  389. { If we were probing pages, EDI=(size mod pagesize) and ESP is decremented
  390. by (size div pagesize)*pagesize, otherwise EDI=size.
  391. Either way, subtracting EDI from ESP will set ESP to desired final value. }
  392. list.concat(Taicpu.op_reg_reg(A_SUB,S_L,NR_EDI,NR_ESP));
  393. { align stack on 4 bytes }
  394. list.concat(Taicpu.op_const_reg(A_AND,S_L,aint($fffffff4),NR_ESP));
  395. { load destination, don't use a_load_reg_reg, that will add a move instruction
  396. that can confuse the reg allocator }
  397. list.concat(Taicpu.Op_reg_reg(A_MOV,S_L,NR_ESP,NR_EDI));
  398. { Allocate ESI and load it with source }
  399. getcpuregister(list,NR_ESI);
  400. a_loadaddr_ref_reg(list,ref,NR_ESI);
  401. { calculate size }
  402. len:=elesize;
  403. opsize:=S_B;
  404. if (len and 3)=0 then
  405. begin
  406. opsize:=S_L;
  407. len:=len shr 2;
  408. end
  409. else
  410. if (len and 1)=0 then
  411. begin
  412. opsize:=S_W;
  413. len:=len shr 1;
  414. end;
  415. if len>1 then
  416. begin
  417. if ispowerof2(len, power) then
  418. list.concat(Taicpu.op_const_reg(A_SHL,S_L,power,NR_ECX))
  419. else
  420. list.concat(Taicpu.op_const_reg(A_IMUL,S_L,len,NR_ECX));
  421. end;
  422. list.concat(Taicpu.op_none(A_REP,S_NO));
  423. case opsize of
  424. S_B : list.concat(Taicpu.Op_none(A_MOVSB,S_NO));
  425. S_W : list.concat(Taicpu.Op_none(A_MOVSW,S_NO));
  426. S_L : list.concat(Taicpu.Op_none(A_MOVSD,S_NO));
  427. end;
  428. ungetcpuregister(list,NR_EDI);
  429. ungetcpuregister(list,NR_ECX);
  430. ungetcpuregister(list,NR_ESI);
  431. { patch the new address, but don't use a_load_reg_reg, that will add a move instruction
  432. that can confuse the reg allocator }
  433. list.concat(Taicpu.Op_reg_reg(A_MOV,S_L,NR_ESP,destreg));
  434. end;
  435. procedure tcg386.g_releasevaluepara_openarray(list : TAsmList;const l:tlocation);
  436. begin
  437. if paramanager.use_fixed_stack then
  438. begin
  439. inherited g_releasevaluepara_openarray(list,l);
  440. exit;
  441. end;
  442. { Nothing to release }
  443. end;
  444. procedure tcg386.g_exception_reason_save(list : TAsmList; const href : treference);
  445. begin
  446. if not paramanager.use_fixed_stack then
  447. list.concat(Taicpu.op_reg(A_PUSH,tcgsize2opsize[OS_INT],NR_FUNCTION_RESULT_REG))
  448. else
  449. inherited g_exception_reason_save(list,href);
  450. end;
  451. procedure tcg386.g_exception_reason_save_const(list : TAsmList;const href : treference; a: tcgint);
  452. begin
  453. if not paramanager.use_fixed_stack then
  454. list.concat(Taicpu.op_const(A_PUSH,tcgsize2opsize[OS_INT],a))
  455. else
  456. inherited g_exception_reason_save_const(list,href,a);
  457. end;
  458. procedure tcg386.g_exception_reason_load(list : TAsmList; const href : treference);
  459. begin
  460. if not paramanager.use_fixed_stack then
  461. begin
  462. cg.a_reg_alloc(list,NR_FUNCTION_RESULT_REG);
  463. list.concat(Taicpu.op_reg(A_POP,tcgsize2opsize[OS_INT],NR_FUNCTION_RESULT_REG))
  464. end
  465. else
  466. inherited g_exception_reason_load(list,href);
  467. end;
  468. procedure tcg386.g_maybe_got_init(list: TAsmList);
  469. var
  470. notdarwin: boolean;
  471. begin
  472. { allocate PIC register }
  473. if (cs_create_pic in current_settings.moduleswitches) and
  474. (tf_pic_uses_got in target_info.flags) and
  475. (pi_needs_got in current_procinfo.flags) then
  476. begin
  477. notdarwin:=not(target_info.system in [system_i386_darwin,system_i386_iphonesim]);
  478. { on darwin, the got register is virtual (and allocated earlier
  479. already) }
  480. if notdarwin then
  481. { ecx could be used in leaf procedures that don't use ecx to pass
  482. aparameter }
  483. current_procinfo.got:=NR_EBX;
  484. if notdarwin { needs testing before it can be enabled for non-darwin platforms
  485. and
  486. (current_settings.optimizecputype in [cpu_Pentium2,cpu_Pentium3,cpu_Pentium4]) } then
  487. begin
  488. current_module.requires_ebx_pic_helper:=true;
  489. cg.a_call_name_static(list,'fpc_geteipasebx');
  490. end
  491. else
  492. begin
  493. { call/pop is faster than call/ret/mov on Core Solo and later
  494. according to Apple's benchmarking -- and all Intel Macs
  495. have at least a Core Solo (furthermore, the i386 - Pentium 1
  496. don't have a return stack buffer) }
  497. a_call_name_static(list,current_procinfo.CurrGOTLabel.name);
  498. a_label(list,current_procinfo.CurrGotLabel);
  499. list.concat(taicpu.op_reg(A_POP,S_L,current_procinfo.got))
  500. end;
  501. if notdarwin then
  502. begin
  503. list.concat(taicpu.op_sym_ofs_reg(A_ADD,S_L,current_asmdata.RefAsmSymbol('_GLOBAL_OFFSET_TABLE_'),0,NR_PIC_OFFSET_REG));
  504. list.concat(tai_regalloc.alloc(NR_PIC_OFFSET_REG,nil));
  505. end;
  506. end;
  507. end;
  508. procedure tcg386.g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);
  509. {
  510. possible calling conventions:
  511. default stdcall cdecl pascal register
  512. default(0): OK OK OK OK OK
  513. virtual(1): OK OK OK OK OK(2)
  514. (0):
  515. set self parameter to correct value
  516. jmp mangledname
  517. (1): The wrapper code use %eax to reach the virtual method address
  518. set self to correct value
  519. move self,%eax
  520. mov 0(%eax),%eax ; load vmt
  521. jmp vmtoffs(%eax) ; method offs
  522. (2): Virtual use values pushed on stack to reach the method address
  523. so the following code be generated:
  524. set self to correct value
  525. push %ebx ; allocate space for function address
  526. push %eax
  527. mov self,%eax
  528. mov 0(%eax),%eax ; load vmt
  529. mov vmtoffs(%eax),eax ; method offs
  530. mov %eax,4(%esp)
  531. pop %eax
  532. ret 0; jmp the address
  533. }
  534. procedure getselftoeax(offs: longint);
  535. var
  536. href : treference;
  537. selfoffsetfromsp : longint;
  538. begin
  539. { mov offset(%esp),%eax }
  540. if (procdef.proccalloption<>pocall_register) then
  541. begin
  542. { framepointer is pushed for nested procs }
  543. if procdef.parast.symtablelevel>normal_function_level then
  544. selfoffsetfromsp:=2*sizeof(aint)
  545. else
  546. selfoffsetfromsp:=sizeof(aint);
  547. reference_reset_base(href,NR_ESP,selfoffsetfromsp+offs,4);
  548. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  549. end;
  550. end;
  551. procedure loadvmttoeax;
  552. var
  553. href : treference;
  554. begin
  555. { mov 0(%eax),%eax ; load vmt}
  556. reference_reset_base(href,NR_EAX,0,4);
  557. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  558. end;
  559. procedure op_oneaxmethodaddr(op: TAsmOp);
  560. var
  561. href : treference;
  562. begin
  563. if (procdef.extnumber=$ffff) then
  564. Internalerror(200006139);
  565. { call/jmp vmtoffs(%eax) ; method offs }
  566. reference_reset_base(href,NR_EAX,tobjectdef(procdef.struct).vmtmethodoffset(procdef.extnumber),4);
  567. list.concat(taicpu.op_ref(op,S_L,href));
  568. end;
  569. procedure loadmethodoffstoeax;
  570. var
  571. href : treference;
  572. begin
  573. if (procdef.extnumber=$ffff) then
  574. Internalerror(200006139);
  575. { mov vmtoffs(%eax),%eax ; method offs }
  576. reference_reset_base(href,NR_EAX,tobjectdef(procdef.struct).vmtmethodoffset(procdef.extnumber),4);
  577. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  578. end;
  579. var
  580. lab : tasmsymbol;
  581. make_global : boolean;
  582. href : treference;
  583. begin
  584. if not(procdef.proctypeoption in [potype_function,potype_procedure]) then
  585. Internalerror(200006137);
  586. if not assigned(procdef.struct) or
  587. (procdef.procoptions*[po_classmethod, po_staticmethod,
  588. po_methodpointer, po_interrupt, po_iocheck]<>[]) then
  589. Internalerror(200006138);
  590. if procdef.owner.symtabletype<>ObjectSymtable then
  591. Internalerror(200109191);
  592. make_global:=false;
  593. if (not current_module.is_unit) or
  594. create_smartlink or
  595. (procdef.owner.defowner.owner.symtabletype=globalsymtable) then
  596. make_global:=true;
  597. if make_global then
  598. List.concat(Tai_symbol.Createname_global(labelname,AT_FUNCTION,0))
  599. else
  600. List.concat(Tai_symbol.Createname(labelname,AT_FUNCTION,0));
  601. { set param1 interface to self }
  602. g_adjust_self_value(list,procdef,ioffset);
  603. if (po_virtualmethod in procdef.procoptions) and
  604. not is_objectpascal_helper(procdef.struct) then
  605. begin
  606. if (procdef.proccalloption=pocall_register) then
  607. begin
  608. { case 2 }
  609. list.concat(taicpu.op_reg(A_PUSH,S_L,NR_EBX)); { allocate space for address}
  610. list.concat(taicpu.op_reg(A_PUSH,S_L,NR_EAX));
  611. getselftoeax(8);
  612. loadvmttoeax;
  613. loadmethodoffstoeax;
  614. { mov %eax,4(%esp) }
  615. reference_reset_base(href,NR_ESP,4,4);
  616. list.concat(taicpu.op_reg_ref(A_MOV,S_L,NR_EAX,href));
  617. { pop %eax }
  618. list.concat(taicpu.op_reg(A_POP,S_L,NR_EAX));
  619. { ret ; jump to the address }
  620. list.concat(taicpu.op_none(A_RET,S_L));
  621. end
  622. else
  623. begin
  624. { case 1 }
  625. getselftoeax(0);
  626. loadvmttoeax;
  627. op_oneaxmethodaddr(A_JMP);
  628. end;
  629. end
  630. { case 0 }
  631. else
  632. begin
  633. if (target_info.system <> system_i386_darwin) then
  634. begin
  635. lab:=current_asmdata.RefAsmSymbol(procdef.mangledname);
  636. list.concat(taicpu.op_sym(A_JMP,S_NO,lab))
  637. end
  638. else
  639. list.concat(taicpu.op_sym(A_JMP,S_NO,get_darwin_call_stub(procdef.mangledname,false)))
  640. end;
  641. List.concat(Tai_symbol_end.Createname(labelname));
  642. end;
  643. { ************* 64bit operations ************ }
  644. procedure tcg64f386.get_64bit_ops(op:TOpCG;var op1,op2:TAsmOp);
  645. begin
  646. case op of
  647. OP_ADD :
  648. begin
  649. op1:=A_ADD;
  650. op2:=A_ADC;
  651. end;
  652. OP_SUB :
  653. begin
  654. op1:=A_SUB;
  655. op2:=A_SBB;
  656. end;
  657. OP_XOR :
  658. begin
  659. op1:=A_XOR;
  660. op2:=A_XOR;
  661. end;
  662. OP_OR :
  663. begin
  664. op1:=A_OR;
  665. op2:=A_OR;
  666. end;
  667. OP_AND :
  668. begin
  669. op1:=A_AND;
  670. op2:=A_AND;
  671. end;
  672. else
  673. internalerror(200203241);
  674. end;
  675. end;
  676. procedure tcg64f386.a_op64_ref_reg(list : TAsmList;op:TOpCG;size : tcgsize;const ref : treference;reg : tregister64);
  677. var
  678. op1,op2 : TAsmOp;
  679. tempref : treference;
  680. begin
  681. if not(op in [OP_NEG,OP_NOT]) then
  682. begin
  683. get_64bit_ops(op,op1,op2);
  684. tempref:=ref;
  685. tcgx86(cg).make_simple_ref(list,tempref);
  686. list.concat(taicpu.op_ref_reg(op1,S_L,tempref,reg.reglo));
  687. inc(tempref.offset,4);
  688. list.concat(taicpu.op_ref_reg(op2,S_L,tempref,reg.reghi));
  689. end
  690. else
  691. begin
  692. a_load64_ref_reg(list,ref,reg);
  693. a_op64_reg_reg(list,op,size,reg,reg);
  694. end;
  695. end;
  696. procedure tcg64f386.a_op64_reg_reg(list : TAsmList;op:TOpCG;size : tcgsize;regsrc,regdst : tregister64);
  697. var
  698. op1,op2 : TAsmOp;
  699. begin
  700. case op of
  701. OP_NEG :
  702. begin
  703. if (regsrc.reglo<>regdst.reglo) then
  704. a_load64_reg_reg(list,regsrc,regdst);
  705. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reghi));
  706. list.concat(taicpu.op_reg(A_NEG,S_L,regdst.reglo));
  707. list.concat(taicpu.op_const_reg(A_SBB,S_L,-1,regdst.reghi));
  708. exit;
  709. end;
  710. OP_NOT :
  711. begin
  712. if (regsrc.reglo<>regdst.reglo) then
  713. a_load64_reg_reg(list,regsrc,regdst);
  714. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reghi));
  715. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reglo));
  716. exit;
  717. end;
  718. end;
  719. get_64bit_ops(op,op1,op2);
  720. list.concat(taicpu.op_reg_reg(op1,S_L,regsrc.reglo,regdst.reglo));
  721. list.concat(taicpu.op_reg_reg(op2,S_L,regsrc.reghi,regdst.reghi));
  722. end;
  723. procedure tcg64f386.a_op64_const_reg(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;reg : tregister64);
  724. var
  725. op1,op2 : TAsmOp;
  726. begin
  727. case op of
  728. OP_AND,OP_OR,OP_XOR:
  729. begin
  730. cg.a_op_const_reg(list,op,OS_32,tcgint(lo(value)),reg.reglo);
  731. cg.a_op_const_reg(list,op,OS_32,tcgint(hi(value)),reg.reghi);
  732. end;
  733. OP_ADD, OP_SUB:
  734. begin
  735. // can't use a_op_const_ref because this may use dec/inc
  736. get_64bit_ops(op,op1,op2);
  737. list.concat(taicpu.op_const_reg(op1,S_L,aint(lo(value)),reg.reglo));
  738. list.concat(taicpu.op_const_reg(op2,S_L,aint(hi(value)),reg.reghi));
  739. end;
  740. else
  741. internalerror(200204021);
  742. end;
  743. end;
  744. procedure tcg64f386.a_op64_const_ref(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;const ref : treference);
  745. var
  746. op1,op2 : TAsmOp;
  747. tempref : treference;
  748. begin
  749. tempref:=ref;
  750. tcgx86(cg).make_simple_ref(list,tempref);
  751. case op of
  752. OP_AND,OP_OR,OP_XOR:
  753. begin
  754. cg.a_op_const_ref(list,op,OS_32,tcgint(lo(value)),tempref);
  755. inc(tempref.offset,4);
  756. cg.a_op_const_ref(list,op,OS_32,tcgint(hi(value)),tempref);
  757. end;
  758. OP_ADD, OP_SUB:
  759. begin
  760. get_64bit_ops(op,op1,op2);
  761. // can't use a_op_const_ref because this may use dec/inc
  762. list.concat(taicpu.op_const_ref(op1,S_L,aint(lo(value)),tempref));
  763. inc(tempref.offset,4);
  764. list.concat(taicpu.op_const_ref(op2,S_L,aint(hi(value)),tempref));
  765. end;
  766. else
  767. internalerror(200204022);
  768. end;
  769. end;
  770. procedure create_codegen;
  771. begin
  772. cg := tcg386.create;
  773. cg64 := tcg64f386.create;
  774. end;
  775. end.