cgcpu.pas 32 KB

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