cgcpu.pas 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  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);
  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. (2): The wrapper code use %eax to reach the virtual method address
  477. set self to correct value
  478. move self,%eax
  479. mov 0(%eax),%eax ; load vmt
  480. jmp vmtoffs(%eax) ; method offs
  481. (3): The wrapper code use %eax to reach the virtual method address
  482. set self to correct value
  483. move self,%eax
  484. mov 0(%eax),%eax ; load vmt
  485. jmp vmtoffs(%eax) ; method offs
  486. set self parameter to interface value
  487. (4): Virtual use values pushed on stack to reach the method address
  488. so the following code be generated:
  489. set self to correct value
  490. push %ebx ; allocate space for function address
  491. push %eax
  492. mov self,%eax
  493. mov 0(%eax),%eax ; load vmt
  494. mov vmtoffs(%eax),eax ; method offs
  495. mov %eax,4(%esp)
  496. pop %eax
  497. ret 0; jmp the address
  498. }
  499. procedure getselftoeax(offs: longint);
  500. var
  501. href : treference;
  502. selfoffsetfromsp : longint;
  503. begin
  504. { mov offset(%esp),%eax }
  505. if (procdef.proccalloption<>pocall_register) then
  506. begin
  507. { framepointer is pushed for nested procs }
  508. if procdef.parast.symtablelevel>normal_function_level then
  509. selfoffsetfromsp:=2*sizeof(aint)
  510. else
  511. selfoffsetfromsp:=sizeof(aint);
  512. reference_reset_base(href,NR_ESP,selfoffsetfromsp+offs);
  513. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  514. end;
  515. end;
  516. procedure loadvmttoeax;
  517. var
  518. href : treference;
  519. begin
  520. { mov 0(%eax),%eax ; load vmt}
  521. reference_reset_base(href,NR_EAX,0);
  522. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  523. end;
  524. procedure op_oneaxmethodaddr(op: TAsmOp);
  525. var
  526. href : treference;
  527. begin
  528. if (procdef.extnumber=$ffff) then
  529. Internalerror(200006139);
  530. { call/jmp vmtoffs(%eax) ; method offs }
  531. reference_reset_base(href,NR_EAX,procdef._class.vmtmethodoffset(procdef.extnumber));
  532. list.concat(taicpu.op_ref(op,S_L,href));
  533. end;
  534. procedure loadmethodoffstoeax;
  535. var
  536. href : treference;
  537. begin
  538. if (procdef.extnumber=$ffff) then
  539. Internalerror(200006139);
  540. { mov vmtoffs(%eax),%eax ; method offs }
  541. reference_reset_base(href,NR_EAX,procdef._class.vmtmethodoffset(procdef.extnumber));
  542. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  543. end;
  544. var
  545. lab : tasmsymbol;
  546. make_global : boolean;
  547. href : treference;
  548. begin
  549. if not(procdef.proctypeoption in [potype_function,potype_procedure]) then
  550. Internalerror(200006137);
  551. if not assigned(procdef._class) or
  552. (procdef.procoptions*[po_classmethod, po_staticmethod,
  553. po_methodpointer, po_interrupt, po_iocheck]<>[]) then
  554. Internalerror(200006138);
  555. if procdef.owner.symtabletype<>ObjectSymtable then
  556. Internalerror(200109191);
  557. make_global:=false;
  558. if (not current_module.is_unit) or
  559. create_smartlink or
  560. (procdef.owner.defowner.owner.symtabletype=globalsymtable) then
  561. make_global:=true;
  562. if make_global then
  563. List.concat(Tai_symbol.Createname_global(labelname,AT_FUNCTION,0))
  564. else
  565. List.concat(Tai_symbol.Createname(labelname,AT_FUNCTION,0));
  566. { set param1 interface to self }
  567. g_adjust_self_value(list,procdef,ioffset);
  568. { case 1 or 2 }
  569. if (procdef.proccalloption in clearstack_pocalls) then
  570. begin
  571. if po_virtualmethod in procdef.procoptions then
  572. begin
  573. { case 2 }
  574. getselftoeax(0);
  575. loadvmttoeax;
  576. op_oneaxmethodaddr(A_CALL);
  577. end
  578. else
  579. begin
  580. { case 1 }
  581. cg.a_call_name(list,procdef.mangledname);
  582. end;
  583. { restore param1 value self to interface }
  584. g_adjust_self_value(list,procdef,-ioffset);
  585. end
  586. else if po_virtualmethod in procdef.procoptions then
  587. begin
  588. if (procdef.proccalloption=pocall_register) then
  589. begin
  590. { case 4 }
  591. list.concat(taicpu.op_reg(A_PUSH,S_L,NR_EBX)); { allocate space for address}
  592. list.concat(taicpu.op_reg(A_PUSH,S_L,NR_EAX));
  593. getselftoeax(8);
  594. loadvmttoeax;
  595. loadmethodoffstoeax;
  596. { mov %eax,4(%esp) }
  597. reference_reset_base(href,NR_ESP,4);
  598. list.concat(taicpu.op_reg_ref(A_MOV,S_L,NR_EAX,href));
  599. { pop %eax }
  600. list.concat(taicpu.op_reg(A_POP,S_L,NR_EAX));
  601. { ret ; jump to the address }
  602. list.concat(taicpu.op_none(A_RET,S_L));
  603. end
  604. else
  605. begin
  606. { case 3 }
  607. getselftoeax(0);
  608. loadvmttoeax;
  609. op_oneaxmethodaddr(A_JMP);
  610. end;
  611. end
  612. { case 0 }
  613. else
  614. begin
  615. if (target_info.system <> system_i386_darwin) then
  616. begin
  617. lab:=current_asmdata.RefAsmSymbol(procdef.mangledname);
  618. list.concat(taicpu.op_sym(A_JMP,S_NO,lab))
  619. end
  620. else
  621. list.concat(taicpu.op_sym(A_JMP,S_NO,get_darwin_call_stub(procdef.mangledname)))
  622. end;
  623. List.concat(Tai_symbol_end.Createname(labelname));
  624. end;
  625. { ************* 64bit operations ************ }
  626. procedure tcg64f386.get_64bit_ops(op:TOpCG;var op1,op2:TAsmOp);
  627. begin
  628. case op of
  629. OP_ADD :
  630. begin
  631. op1:=A_ADD;
  632. op2:=A_ADC;
  633. end;
  634. OP_SUB :
  635. begin
  636. op1:=A_SUB;
  637. op2:=A_SBB;
  638. end;
  639. OP_XOR :
  640. begin
  641. op1:=A_XOR;
  642. op2:=A_XOR;
  643. end;
  644. OP_OR :
  645. begin
  646. op1:=A_OR;
  647. op2:=A_OR;
  648. end;
  649. OP_AND :
  650. begin
  651. op1:=A_AND;
  652. op2:=A_AND;
  653. end;
  654. else
  655. internalerror(200203241);
  656. end;
  657. end;
  658. procedure tcg64f386.a_op64_ref_reg(list : TAsmList;op:TOpCG;size : tcgsize;const ref : treference;reg : tregister64);
  659. var
  660. op1,op2 : TAsmOp;
  661. tempref : treference;
  662. begin
  663. if not(op in [OP_NEG,OP_NOT]) then
  664. begin
  665. get_64bit_ops(op,op1,op2);
  666. tempref:=ref;
  667. tcgx86(cg).make_simple_ref(list,tempref);
  668. list.concat(taicpu.op_ref_reg(op1,S_L,tempref,reg.reglo));
  669. inc(tempref.offset,4);
  670. list.concat(taicpu.op_ref_reg(op2,S_L,tempref,reg.reghi));
  671. end
  672. else
  673. begin
  674. a_load64_ref_reg(list,ref,reg);
  675. a_op64_reg_reg(list,op,size,reg,reg);
  676. end;
  677. end;
  678. procedure tcg64f386.a_op64_reg_reg(list : TAsmList;op:TOpCG;size : tcgsize;regsrc,regdst : tregister64);
  679. var
  680. op1,op2 : TAsmOp;
  681. begin
  682. case op of
  683. OP_NEG :
  684. begin
  685. if (regsrc.reglo<>regdst.reglo) then
  686. a_load64_reg_reg(list,regsrc,regdst);
  687. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reghi));
  688. list.concat(taicpu.op_reg(A_NEG,S_L,regdst.reglo));
  689. list.concat(taicpu.op_const_reg(A_SBB,S_L,-1,regdst.reghi));
  690. exit;
  691. end;
  692. OP_NOT :
  693. begin
  694. if (regsrc.reglo<>regdst.reglo) then
  695. a_load64_reg_reg(list,regsrc,regdst);
  696. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reghi));
  697. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reglo));
  698. exit;
  699. end;
  700. end;
  701. get_64bit_ops(op,op1,op2);
  702. list.concat(taicpu.op_reg_reg(op1,S_L,regsrc.reglo,regdst.reglo));
  703. list.concat(taicpu.op_reg_reg(op2,S_L,regsrc.reghi,regdst.reghi));
  704. end;
  705. procedure tcg64f386.a_op64_const_reg(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;reg : tregister64);
  706. var
  707. op1,op2 : TAsmOp;
  708. begin
  709. case op of
  710. OP_AND,OP_OR,OP_XOR:
  711. begin
  712. cg.a_op_const_reg(list,op,OS_32,aint(lo(value)),reg.reglo);
  713. cg.a_op_const_reg(list,op,OS_32,aint(hi(value)),reg.reghi);
  714. end;
  715. OP_ADD, OP_SUB:
  716. begin
  717. // can't use a_op_const_ref because this may use dec/inc
  718. get_64bit_ops(op,op1,op2);
  719. list.concat(taicpu.op_const_reg(op1,S_L,aint(lo(value)),reg.reglo));
  720. list.concat(taicpu.op_const_reg(op2,S_L,aint(hi(value)),reg.reghi));
  721. end;
  722. else
  723. internalerror(200204021);
  724. end;
  725. end;
  726. procedure tcg64f386.a_op64_const_ref(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;const ref : treference);
  727. var
  728. op1,op2 : TAsmOp;
  729. tempref : treference;
  730. begin
  731. tempref:=ref;
  732. tcgx86(cg).make_simple_ref(list,tempref);
  733. case op of
  734. OP_AND,OP_OR,OP_XOR:
  735. begin
  736. cg.a_op_const_ref(list,op,OS_32,aint(lo(value)),tempref);
  737. inc(tempref.offset,4);
  738. cg.a_op_const_ref(list,op,OS_32,aint(hi(value)),tempref);
  739. end;
  740. OP_ADD, OP_SUB:
  741. begin
  742. get_64bit_ops(op,op1,op2);
  743. // can't use a_op_const_ref because this may use dec/inc
  744. list.concat(taicpu.op_const_ref(op1,S_L,aint(lo(value)),tempref));
  745. inc(tempref.offset,4);
  746. list.concat(taicpu.op_const_ref(op2,S_L,aint(hi(value)),tempref));
  747. end;
  748. else
  749. internalerror(200204022);
  750. end;
  751. end;
  752. begin
  753. cg := tcg386.create;
  754. cg64 := tcg64f386.create;
  755. end.