cgcpu.pas 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802
  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. { but not on win32 }
  278. if (target_info.system <> system_i386_win32) and
  279. paramanager.ret_in_param(current_procinfo.procdef.returndef,
  280. current_procinfo.procdef.proccalloption) then
  281. list.concat(Taicpu.Op_const(A_RET,S_W,sizeof(aint)))
  282. else
  283. list.concat(Taicpu.Op_none(A_RET,S_NO));
  284. end
  285. { ... also routines with parasize=0 }
  286. else if (parasize=0) then
  287. list.concat(Taicpu.Op_none(A_RET,S_NO))
  288. else
  289. begin
  290. { parameters are limited to 65535 bytes because ret allows only imm16 }
  291. if (parasize>65535) then
  292. CGMessage(cg_e_parasize_too_big);
  293. list.concat(Taicpu.Op_const(A_RET,S_W,parasize));
  294. end;
  295. end;
  296. procedure tcg386.g_copyvaluepara_openarray(list : TAsmList;const ref:treference;const lenloc:tlocation;elesize:aint;destreg:tregister);
  297. var
  298. power,len : longint;
  299. opsize : topsize;
  300. {$ifndef __NOWINPECOFF__}
  301. again,ok : tasmlabel;
  302. {$endif}
  303. begin
  304. if use_fixed_stack then
  305. begin
  306. inherited g_copyvaluepara_openarray(list,ref,lenloc,elesize,destreg);
  307. exit;
  308. end;
  309. { get stack space }
  310. getcpuregister(list,NR_EDI);
  311. a_load_loc_reg(list,OS_INT,lenloc,NR_EDI);
  312. list.concat(Taicpu.op_reg(A_INC,S_L,NR_EDI));
  313. if (elesize<>1) then
  314. begin
  315. if ispowerof2(elesize, power) then
  316. list.concat(Taicpu.op_const_reg(A_SHL,S_L,power,NR_EDI))
  317. else
  318. list.concat(Taicpu.op_const_reg(A_IMUL,S_L,elesize,NR_EDI));
  319. end;
  320. {$ifndef __NOWINPECOFF__}
  321. { windows guards only a few pages for stack growing, }
  322. { so we have to access every page first }
  323. if target_info.system=system_i386_win32 then
  324. begin
  325. current_asmdata.getjumplabel(again);
  326. current_asmdata.getjumplabel(ok);
  327. a_label(list,again);
  328. list.concat(Taicpu.op_const_reg(A_CMP,S_L,winstackpagesize,NR_EDI));
  329. a_jmp_cond(list,OC_B,ok);
  330. list.concat(Taicpu.op_const_reg(A_SUB,S_L,winstackpagesize-4,NR_ESP));
  331. list.concat(Taicpu.op_reg(A_PUSH,S_L,NR_EDI));
  332. list.concat(Taicpu.op_const_reg(A_SUB,S_L,winstackpagesize,NR_EDI));
  333. a_jmp_always(list,again);
  334. a_label(list,ok);
  335. list.concat(Taicpu.op_reg_reg(A_SUB,S_L,NR_EDI,NR_ESP));
  336. ungetcpuregister(list,NR_EDI);
  337. { now reload EDI }
  338. getcpuregister(list,NR_EDI);
  339. a_load_loc_reg(list,OS_INT,lenloc,NR_EDI);
  340. list.concat(Taicpu.op_reg(A_INC,S_L,NR_EDI));
  341. if (elesize<>1) then
  342. begin
  343. if ispowerof2(elesize, power) then
  344. list.concat(Taicpu.op_const_reg(A_SHL,S_L,power,NR_EDI))
  345. else
  346. list.concat(Taicpu.op_const_reg(A_IMUL,S_L,elesize,NR_EDI));
  347. end;
  348. end
  349. else
  350. {$endif __NOWINPECOFF__}
  351. list.concat(Taicpu.op_reg_reg(A_SUB,S_L,NR_EDI,NR_ESP));
  352. { align stack on 4 bytes }
  353. list.concat(Taicpu.op_const_reg(A_AND,S_L,aint($fffffff4),NR_ESP));
  354. { load destination, don't use a_load_reg_reg, that will add a move instruction
  355. that can confuse the reg allocator }
  356. list.concat(Taicpu.Op_reg_reg(A_MOV,S_L,NR_ESP,NR_EDI));
  357. { Allocate other registers }
  358. getcpuregister(list,NR_ECX);
  359. getcpuregister(list,NR_ESI);
  360. { load count }
  361. a_load_loc_reg(list,OS_INT,lenloc,NR_ECX);
  362. { load source }
  363. a_loadaddr_ref_reg(list,ref,NR_ESI);
  364. { scheduled .... }
  365. list.concat(Taicpu.op_reg(A_INC,S_L,NR_ECX));
  366. { calculate size }
  367. len:=elesize;
  368. opsize:=S_B;
  369. if (len and 3)=0 then
  370. begin
  371. opsize:=S_L;
  372. len:=len shr 2;
  373. end
  374. else
  375. if (len and 1)=0 then
  376. begin
  377. opsize:=S_W;
  378. len:=len shr 1;
  379. end;
  380. if len<>0 then
  381. begin
  382. if ispowerof2(len, power) then
  383. list.concat(Taicpu.op_const_reg(A_SHL,S_L,power,NR_ECX))
  384. else
  385. list.concat(Taicpu.op_const_reg(A_IMUL,S_L,len,NR_ECX));
  386. end;
  387. list.concat(Taicpu.op_none(A_REP,S_NO));
  388. case opsize of
  389. S_B : list.concat(Taicpu.Op_none(A_MOVSB,S_NO));
  390. S_W : list.concat(Taicpu.Op_none(A_MOVSW,S_NO));
  391. S_L : list.concat(Taicpu.Op_none(A_MOVSD,S_NO));
  392. end;
  393. ungetcpuregister(list,NR_EDI);
  394. ungetcpuregister(list,NR_ECX);
  395. ungetcpuregister(list,NR_ESI);
  396. { patch the new address, but don't use a_load_reg_reg, that will add a move instruction
  397. that can confuse the reg allocator }
  398. list.concat(Taicpu.Op_reg_reg(A_MOV,S_L,NR_ESP,destreg));
  399. end;
  400. procedure tcg386.g_releasevaluepara_openarray(list : TAsmList;const l:tlocation);
  401. begin
  402. if use_fixed_stack then
  403. begin
  404. inherited g_releasevaluepara_openarray(list,l);
  405. exit;
  406. end;
  407. { Nothing to release }
  408. end;
  409. procedure tcg386.g_exception_reason_save(list : TAsmList; const href : treference);
  410. begin
  411. if not use_fixed_stack then
  412. list.concat(Taicpu.op_reg(A_PUSH,tcgsize2opsize[OS_INT],NR_FUNCTION_RESULT_REG))
  413. else
  414. inherited g_exception_reason_save(list,href);
  415. end;
  416. procedure tcg386.g_exception_reason_save_const(list : TAsmList;const href : treference; a: aint);
  417. begin
  418. if not use_fixed_stack then
  419. list.concat(Taicpu.op_const(A_PUSH,tcgsize2opsize[OS_INT],a))
  420. else
  421. inherited g_exception_reason_save_const(list,href,a);
  422. end;
  423. procedure tcg386.g_exception_reason_load(list : TAsmList; const href : treference);
  424. begin
  425. if not use_fixed_stack then
  426. list.concat(Taicpu.op_reg(A_POP,tcgsize2opsize[OS_INT],NR_FUNCTION_RESULT_REG))
  427. else
  428. inherited g_exception_reason_load(list,href);
  429. end;
  430. procedure tcg386.g_intf_wrapper(list: TAsmList; procdef: tprocdef; const labelname: string; ioffset: longint);
  431. {
  432. possible calling conventions:
  433. default stdcall cdecl pascal register
  434. default(0): OK OK OK(1) OK OK
  435. virtual(2): OK OK OK(3) OK OK
  436. (0):
  437. set self parameter to correct value
  438. jmp mangledname
  439. (1): The code is the following
  440. set self parameter to correct value
  441. call mangledname
  442. set self parameter to interface value
  443. (2): The wrapper code use %eax to reach the virtual method address
  444. set self to correct value
  445. move self,%eax
  446. mov 0(%eax),%eax ; load vmt
  447. jmp vmtoffs(%eax) ; method offs
  448. (3): The wrapper code use %eax to reach the virtual method address
  449. set self to correct value
  450. move self,%eax
  451. mov 0(%eax),%eax ; load vmt
  452. jmp vmtoffs(%eax) ; method offs
  453. set self parameter to interface value
  454. (4): Virtual use values pushed on stack to reach the method address
  455. so the following code be generated:
  456. set self to correct value
  457. push %ebx ; allocate space for function address
  458. push %eax
  459. mov self,%eax
  460. mov 0(%eax),%eax ; load vmt
  461. mov vmtoffs(%eax),eax ; method offs
  462. mov %eax,4(%esp)
  463. pop %eax
  464. ret 0; jmp the address
  465. }
  466. procedure getselftoeax(offs: longint);
  467. var
  468. href : treference;
  469. selfoffsetfromsp : longint;
  470. begin
  471. { mov offset(%esp),%eax }
  472. if (procdef.proccalloption<>pocall_register) then
  473. begin
  474. { framepointer is pushed for nested procs }
  475. if procdef.parast.symtablelevel>normal_function_level then
  476. selfoffsetfromsp:=2*sizeof(aint)
  477. else
  478. selfoffsetfromsp:=sizeof(aint);
  479. reference_reset_base(href,NR_ESP,selfoffsetfromsp+offs);
  480. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  481. end;
  482. end;
  483. procedure loadvmttoeax;
  484. var
  485. href : treference;
  486. begin
  487. { mov 0(%eax),%eax ; load vmt}
  488. reference_reset_base(href,NR_EAX,0);
  489. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  490. end;
  491. procedure op_oneaxmethodaddr(op: TAsmOp);
  492. var
  493. href : treference;
  494. begin
  495. if (procdef.extnumber=$ffff) then
  496. Internalerror(200006139);
  497. { call/jmp vmtoffs(%eax) ; method offs }
  498. reference_reset_base(href,NR_EAX,procdef._class.vmtmethodoffset(procdef.extnumber));
  499. list.concat(taicpu.op_ref(op,S_L,href));
  500. end;
  501. procedure loadmethodoffstoeax;
  502. var
  503. href : treference;
  504. begin
  505. if (procdef.extnumber=$ffff) then
  506. Internalerror(200006139);
  507. { mov vmtoffs(%eax),%eax ; method offs }
  508. reference_reset_base(href,NR_EAX,procdef._class.vmtmethodoffset(procdef.extnumber));
  509. cg.a_load_ref_reg(list,OS_ADDR,OS_ADDR,href,NR_EAX);
  510. end;
  511. var
  512. lab : tasmsymbol;
  513. make_global : boolean;
  514. href : treference;
  515. begin
  516. if not(procdef.proctypeoption in [potype_function,potype_procedure]) then
  517. Internalerror(200006137);
  518. if not assigned(procdef._class) or
  519. (procdef.procoptions*[po_classmethod, po_staticmethod,
  520. po_methodpointer, po_interrupt, po_iocheck]<>[]) then
  521. Internalerror(200006138);
  522. if procdef.owner.symtabletype<>ObjectSymtable then
  523. Internalerror(200109191);
  524. make_global:=false;
  525. if (not current_module.is_unit) or
  526. (cs_create_smart in current_settings.moduleswitches) or
  527. (af_smartlink_sections in target_asm.flags) or
  528. (procdef.owner.defowner.owner.symtabletype=globalsymtable) then
  529. make_global:=true;
  530. if make_global then
  531. List.concat(Tai_symbol.Createname_global(labelname,AT_FUNCTION,0))
  532. else
  533. List.concat(Tai_symbol.Createname(labelname,AT_FUNCTION,0));
  534. { set param1 interface to self }
  535. g_adjust_self_value(list,procdef,ioffset);
  536. { case 1 or 2 }
  537. if (procdef.proccalloption in clearstack_pocalls) then
  538. begin
  539. if po_virtualmethod in procdef.procoptions then
  540. begin
  541. { case 2 }
  542. getselftoeax(0);
  543. loadvmttoeax;
  544. op_oneaxmethodaddr(A_CALL);
  545. end
  546. else
  547. begin
  548. { case 1 }
  549. cg.a_call_name(list,procdef.mangledname);
  550. end;
  551. { restore param1 value self to interface }
  552. g_adjust_self_value(list,procdef,-ioffset);
  553. end
  554. else if po_virtualmethod in procdef.procoptions then
  555. begin
  556. if (procdef.proccalloption=pocall_register) then
  557. begin
  558. { case 4 }
  559. list.concat(taicpu.op_reg(A_PUSH,S_L,NR_EBX)); { allocate space for address}
  560. list.concat(taicpu.op_reg(A_PUSH,S_L,NR_EAX));
  561. getselftoeax(8);
  562. loadvmttoeax;
  563. loadmethodoffstoeax;
  564. { mov %eax,4(%esp) }
  565. reference_reset_base(href,NR_ESP,4);
  566. list.concat(taicpu.op_reg_ref(A_MOV,S_L,NR_EAX,href));
  567. { pop %eax }
  568. list.concat(taicpu.op_reg(A_POP,S_L,NR_EAX));
  569. { ret ; jump to the address }
  570. list.concat(taicpu.op_none(A_RET,S_L));
  571. end
  572. else
  573. begin
  574. { case 3 }
  575. getselftoeax(0);
  576. loadvmttoeax;
  577. op_oneaxmethodaddr(A_JMP);
  578. end;
  579. end
  580. { case 0 }
  581. else
  582. begin
  583. if (target_info.system <> system_i386_darwin) then
  584. begin
  585. lab:=current_asmdata.RefAsmSymbol(procdef.mangledname);
  586. list.concat(taicpu.op_sym(A_JMP,S_NO,lab))
  587. end
  588. else
  589. list.concat(taicpu.op_sym(A_JMP,S_NO,get_darwin_call_stub(procdef.mangledname)))
  590. end;
  591. List.concat(Tai_symbol_end.Createname(labelname));
  592. end;
  593. { ************* 64bit operations ************ }
  594. procedure tcg64f386.get_64bit_ops(op:TOpCG;var op1,op2:TAsmOp);
  595. begin
  596. case op of
  597. OP_ADD :
  598. begin
  599. op1:=A_ADD;
  600. op2:=A_ADC;
  601. end;
  602. OP_SUB :
  603. begin
  604. op1:=A_SUB;
  605. op2:=A_SBB;
  606. end;
  607. OP_XOR :
  608. begin
  609. op1:=A_XOR;
  610. op2:=A_XOR;
  611. end;
  612. OP_OR :
  613. begin
  614. op1:=A_OR;
  615. op2:=A_OR;
  616. end;
  617. OP_AND :
  618. begin
  619. op1:=A_AND;
  620. op2:=A_AND;
  621. end;
  622. else
  623. internalerror(200203241);
  624. end;
  625. end;
  626. procedure tcg64f386.a_op64_ref_reg(list : TAsmList;op:TOpCG;size : tcgsize;const ref : treference;reg : tregister64);
  627. var
  628. op1,op2 : TAsmOp;
  629. tempref : treference;
  630. begin
  631. get_64bit_ops(op,op1,op2);
  632. list.concat(taicpu.op_ref_reg(op1,S_L,ref,reg.reglo));
  633. tempref:=ref;
  634. inc(tempref.offset,4);
  635. list.concat(taicpu.op_ref_reg(op2,S_L,tempref,reg.reghi));
  636. end;
  637. procedure tcg64f386.a_op64_reg_reg(list : TAsmList;op:TOpCG;size : tcgsize;regsrc,regdst : tregister64);
  638. var
  639. op1,op2 : TAsmOp;
  640. begin
  641. case op of
  642. OP_NEG :
  643. begin
  644. if (regsrc.reglo<>regdst.reglo) then
  645. a_load64_reg_reg(list,regsrc,regdst);
  646. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reghi));
  647. list.concat(taicpu.op_reg(A_NEG,S_L,regdst.reglo));
  648. list.concat(taicpu.op_const_reg(A_SBB,S_L,-1,regdst.reghi));
  649. exit;
  650. end;
  651. OP_NOT :
  652. begin
  653. if (regsrc.reglo<>regdst.reglo) then
  654. a_load64_reg_reg(list,regsrc,regdst);
  655. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reghi));
  656. list.concat(taicpu.op_reg(A_NOT,S_L,regdst.reglo));
  657. exit;
  658. end;
  659. end;
  660. get_64bit_ops(op,op1,op2);
  661. list.concat(taicpu.op_reg_reg(op1,S_L,regsrc.reglo,regdst.reglo));
  662. list.concat(taicpu.op_reg_reg(op2,S_L,regsrc.reghi,regdst.reghi));
  663. end;
  664. procedure tcg64f386.a_op64_const_reg(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;reg : tregister64);
  665. var
  666. op1,op2 : TAsmOp;
  667. begin
  668. case op of
  669. OP_AND,OP_OR,OP_XOR:
  670. begin
  671. cg.a_op_const_reg(list,op,OS_32,aint(lo(value)),reg.reglo);
  672. cg.a_op_const_reg(list,op,OS_32,aint(hi(value)),reg.reghi);
  673. end;
  674. OP_ADD, OP_SUB:
  675. begin
  676. // can't use a_op_const_ref because this may use dec/inc
  677. get_64bit_ops(op,op1,op2);
  678. list.concat(taicpu.op_const_reg(op1,S_L,aint(lo(value)),reg.reglo));
  679. list.concat(taicpu.op_const_reg(op2,S_L,aint(hi(value)),reg.reghi));
  680. end;
  681. else
  682. internalerror(200204021);
  683. end;
  684. end;
  685. procedure tcg64f386.a_op64_const_ref(list : TAsmList;op:TOpCG;size : tcgsize;value : int64;const ref : treference);
  686. var
  687. op1,op2 : TAsmOp;
  688. tempref : treference;
  689. begin
  690. case op of
  691. OP_AND,OP_OR,OP_XOR:
  692. begin
  693. cg.a_op_const_ref(list,op,OS_32,aint(lo(value)),ref);
  694. tempref:=ref;
  695. inc(tempref.offset,4);
  696. cg.a_op_const_ref(list,op,OS_32,aint(hi(value)),tempref);
  697. end;
  698. OP_ADD, OP_SUB:
  699. begin
  700. get_64bit_ops(op,op1,op2);
  701. // can't use a_op_const_ref because this may use dec/inc
  702. list.concat(taicpu.op_const_ref(op1,S_L,aint(lo(value)),ref));
  703. tempref:=ref;
  704. inc(tempref.offset,4);
  705. list.concat(taicpu.op_const_ref(op2,S_L,aint(hi(value)),tempref));
  706. end;
  707. else
  708. internalerror(200204022);
  709. end;
  710. end;
  711. begin
  712. cg := tcg386.create;
  713. cg64 := tcg64f386.create;
  714. end.