cgcpu.pas 32 KB

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