cgcpu.pas 29 KB

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