cgcpu.pas 31 KB

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