cgcpu.pas 32 KB

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