cgcpu.pas 32 KB

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