n386cnv.pas 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. {
  2. $Id$
  3. Copyright (c) 1998-2000 by Florian Klaempfl
  4. Generate i386 assembler for type converting nodes
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program; if not, write to the Free Software
  15. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  16. ****************************************************************************
  17. }
  18. unit n386cnv;
  19. {$i defines.inc}
  20. interface
  21. uses
  22. node,ncnv,ncgcnv,types;
  23. type
  24. ti386typeconvnode = class(tcgtypeconvnode)
  25. protected
  26. procedure second_int_to_int;override;
  27. { procedure second_string_to_string;override; }
  28. { procedure second_cstring_to_pchar;override; }
  29. { procedure second_string_to_chararray;override; }
  30. { procedure second_array_to_pointer;override; }
  31. { procedure second_pointer_to_array;override; }
  32. { procedure second_chararray_to_string;override; }
  33. { procedure second_char_to_string;override; }
  34. procedure second_int_to_real;override;
  35. { procedure second_real_to_real;override; }
  36. { procedure second_cord_to_pointer;override; }
  37. { procedure second_proc_to_procvar;override; }
  38. { procedure second_bool_to_int;override; }
  39. procedure second_int_to_bool;override;
  40. { procedure second_load_smallset;override; }
  41. { procedure second_ansistring_to_pchar;override; }
  42. { procedure second_pchar_to_string;override; }
  43. { procedure second_class_to_intf;override; }
  44. { procedure second_char_to_char;override; }
  45. procedure pass_2;override;
  46. procedure second_call_helper(c : tconverttype);
  47. end;
  48. implementation
  49. uses
  50. verbose,systems,
  51. symconst,symdef,aasm,
  52. cgbase,temp_gen,pass_2,
  53. ncon,ncal,
  54. cpubase,
  55. cga,tgcpu,n386util;
  56. {*****************************************************************************
  57. SecondTypeConv
  58. *****************************************************************************}
  59. procedure ti386typeconvnode.second_int_to_int;
  60. var
  61. op : tasmop;
  62. opsize : topsize;
  63. hregister,
  64. hregister2 : tregister;
  65. begin
  66. { insert range check if not explicit conversion }
  67. if not(nf_explizit in flags) then
  68. emitrangecheck(left,resulttype.def);
  69. { is the result size smaller ? }
  70. if resulttype.def.size<left.resulttype.def.size then
  71. begin
  72. { only need to set the new size of a register }
  73. if (left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
  74. begin
  75. case resulttype.def.size of
  76. 1 : location.register:=makereg8(left.location.register);
  77. 2 : location.register:=makereg16(left.location.register);
  78. 4 : location.register:=makereg32(left.location.register);
  79. end;
  80. { we can release the upper register }
  81. if is_64bitint(left.resulttype.def) then
  82. ungetregister32(left.location.registerhigh);
  83. end;
  84. end
  85. { is the result size bigger ? }
  86. else if resulttype.def.size>left.resulttype.def.size then
  87. begin
  88. { remove reference }
  89. if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
  90. begin
  91. del_reference(left.location.reference);
  92. { we can do this here as we need no temp inside }
  93. ungetiftemp(left.location.reference);
  94. end;
  95. { get op and opsize, handle separate for constants, because
  96. movz doesn't support constant values }
  97. if (left.location.loc=LOC_MEM) and (left.location.reference.is_immediate) then
  98. begin
  99. if is_64bitint(resulttype.def) then
  100. opsize:=S_L
  101. else
  102. opsize:=def_opsize(resulttype.def);
  103. op:=A_MOV;
  104. end
  105. else
  106. begin
  107. opsize:=def2def_opsize(left.resulttype.def,resulttype.def);
  108. if opsize in [S_B,S_W,S_L] then
  109. op:=A_MOV
  110. else
  111. if is_signed(left.resulttype.def) then
  112. op:=A_MOVSX
  113. else
  114. op:=A_MOVZX;
  115. end;
  116. { load the register we need }
  117. if left.location.loc<>LOC_REGISTER then
  118. hregister:=getregister32
  119. else
  120. hregister:=left.location.register;
  121. { set the correct register size and location }
  122. clear_location(location);
  123. location.loc:=LOC_REGISTER;
  124. { do we need a second register for a 64 bit type ? }
  125. if is_64bitint(resulttype.def) then
  126. begin
  127. hregister2:=getregister32;
  128. location.registerhigh:=hregister2;
  129. end;
  130. case resulttype.def.size of
  131. 1:
  132. location.register:=makereg8(hregister);
  133. 2:
  134. location.register:=makereg16(hregister);
  135. 4,8:
  136. location.register:=makereg32(hregister);
  137. end;
  138. { insert the assembler code }
  139. if left.location.loc in [LOC_CREGISTER,LOC_REGISTER] then
  140. emit_reg_reg(op,opsize,left.location.register,location.register)
  141. else
  142. emit_ref_reg(op,opsize,
  143. newreference(left.location.reference),location.register);
  144. { do we need a sign extension for int64? }
  145. if is_64bitint(resulttype.def) then
  146. { special case for constants (JM) }
  147. if is_constintnode(left) then
  148. begin
  149. if tordconstnode(left).value >= 0 then
  150. emit_reg_reg(A_XOR,S_L,
  151. hregister2,hregister2)
  152. else
  153. emit_const_reg(A_MOV,S_L,longint($ffffffff),hregister2);
  154. end
  155. else
  156. begin
  157. if (torddef(resulttype.def).typ=s64bit) and
  158. is_signed(left.resulttype.def) then
  159. begin
  160. emit_reg_reg(A_MOV,S_L,location.register,hregister2);
  161. emit_const_reg(A_SAR,S_L,31,hregister2);
  162. end
  163. else
  164. emit_reg_reg(A_XOR,S_L,hregister2,hregister2);
  165. end;
  166. end;
  167. end;
  168. procedure ti386typeconvnode.second_int_to_real;
  169. var
  170. r : preference;
  171. hregister : tregister;
  172. l1,l2 : tasmlabel;
  173. begin
  174. { for u32bit a solution is to push $0 and to load a comp }
  175. { does this first, it destroys maybe EDI }
  176. hregister:=R_EDI;
  177. if torddef(left.resulttype.def).typ=u32bit then
  178. push_int(0);
  179. if (left.location.loc=LOC_REGISTER) or
  180. (left.location.loc=LOC_CREGISTER) then
  181. begin
  182. if not (torddef(left.resulttype.def).typ in [u32bit,s32bit,u64bit,s64bit]) then
  183. getexplicitregister32(R_EDI);
  184. case torddef(left.resulttype.def).typ of
  185. s8bit : emit_reg_reg(A_MOVSX,S_BL,left.location.register,R_EDI);
  186. u8bit : emit_reg_reg(A_MOVZX,S_BL,left.location.register,R_EDI);
  187. s16bit : emit_reg_reg(A_MOVSX,S_WL,left.location.register,R_EDI);
  188. u16bit : emit_reg_reg(A_MOVZX,S_WL,left.location.register,R_EDI);
  189. u32bit,s32bit:
  190. hregister:=left.location.register;
  191. u64bit,s64bit:
  192. begin
  193. emit_reg(A_PUSH,S_L,left.location.registerhigh);
  194. hregister:=left.location.registerlow;
  195. end;
  196. end;
  197. ungetregister(left.location.register);
  198. end
  199. else
  200. begin
  201. r:=newreference(left.location.reference);
  202. getexplicitregister32(R_EDI);
  203. case torddef(left.resulttype.def).typ of
  204. s8bit:
  205. emit_ref_reg(A_MOVSX,S_BL,r,R_EDI);
  206. u8bit:
  207. emit_ref_reg(A_MOVZX,S_BL,r,R_EDI);
  208. s16bit:
  209. emit_ref_reg(A_MOVSX,S_WL,r,R_EDI);
  210. u16bit:
  211. emit_ref_reg(A_MOVZX,S_WL,r,R_EDI);
  212. u32bit,s32bit:
  213. emit_ref_reg(A_MOV,S_L,r,R_EDI);
  214. u64bit,s64bit:
  215. begin
  216. inc(r^.offset,4);
  217. emit_ref_reg(A_MOV,S_L,r,R_EDI);
  218. emit_reg(A_PUSH,S_L,R_EDI);
  219. r:=newreference(left.location.reference);
  220. emit_ref_reg(A_MOV,S_L,r,R_EDI);
  221. end;
  222. end;
  223. del_reference(left.location.reference);
  224. ungetiftemp(left.location.reference);
  225. end;
  226. { for 64 bit integers, the high dword is already pushed }
  227. emit_reg(A_PUSH,S_L,hregister);
  228. if hregister = R_EDI then
  229. ungetregister32(R_EDI);
  230. r:=new_reference(R_ESP,0);
  231. case torddef(left.resulttype.def).typ of
  232. u32bit:
  233. begin
  234. emit_ref(A_FILD,S_IQ,r);
  235. emit_const_reg(A_ADD,S_L,8,R_ESP);
  236. end;
  237. s64bit:
  238. begin
  239. emit_ref(A_FILD,S_IQ,r);
  240. emit_const_reg(A_ADD,S_L,8,R_ESP);
  241. end;
  242. u64bit:
  243. begin
  244. { unsigned 64 bit ints are harder to handle: }
  245. { we load bits 0..62 and then check bit 63: }
  246. { if it is 1 then we add $80000000 000000000 }
  247. { as double }
  248. inc(r^.offset,4);
  249. getexplicitregister32(R_EDI);
  250. emit_ref_reg(A_MOV,S_L,r,R_EDI);
  251. r:=new_reference(R_ESP,4);
  252. emit_const_ref(A_AND,S_L,$7fffffff,r);
  253. emit_const_reg(A_TEST,S_L,longint($80000000),R_EDI);
  254. ungetregister32(R_EDI);
  255. r:=new_reference(R_ESP,0);
  256. emit_ref(A_FILD,S_IQ,r);
  257. getdatalabel(l1);
  258. getlabel(l2);
  259. emitjmp(C_Z,l2);
  260. Consts.concat(Tai_label.Create(l1));
  261. { I got this constant from a test progtram (FK) }
  262. Consts.concat(Tai_const.Create_32bit(0));
  263. Consts.concat(Tai_const.Create_32bit(1138753536));
  264. r:=new_reference(R_NO,0);
  265. r^.symbol:=l1;
  266. emit_ref(A_FADD,S_FL,r);
  267. emitlab(l2);
  268. emit_const_reg(A_ADD,S_L,8,R_ESP);
  269. end
  270. else
  271. begin
  272. emit_ref(A_FILD,S_IL,r);
  273. getexplicitregister32(R_EDI);
  274. emit_reg(A_POP,S_L,R_EDI);
  275. ungetregister32(R_EDI);
  276. end;
  277. end;
  278. inc(fpuvaroffset);
  279. clear_location(location);
  280. location.loc:=LOC_FPU;
  281. end;
  282. procedure ti386typeconvnode.second_int_to_bool;
  283. var
  284. hregister : tregister;
  285. resflags : tresflags;
  286. opsize : topsize;
  287. pref : preference;
  288. begin
  289. clear_location(location);
  290. { byte(boolean) or word(wordbool) or longint(longbool) must }
  291. { be accepted for var parameters }
  292. if (nf_explizit in flags) and
  293. (left.resulttype.def.size=resulttype.def.size) and
  294. (left.location.loc in [LOC_REFERENCE,LOC_MEM,LOC_CREGISTER]) then
  295. begin
  296. set_location(location,left.location);
  297. exit;
  298. end;
  299. location.loc:=LOC_REGISTER;
  300. del_location(left.location);
  301. opsize:=def_opsize(left.resulttype.def);
  302. case left.location.loc of
  303. LOC_MEM,LOC_REFERENCE :
  304. begin
  305. if is_64bitint(left.resulttype.def) then
  306. begin
  307. hregister:=getregister32;
  308. emit_ref_reg(A_MOV,opsize,
  309. newreference(left.location.reference),hregister);
  310. pref:=newreference(left.location.reference);
  311. inc(pref^.offset,4);
  312. emit_reg_ref(A_OR,opsize,
  313. hregister,pref);
  314. end
  315. else
  316. begin
  317. hregister:=def_getreg(left.resulttype.def);
  318. emit_ref_reg(A_MOV,opsize,
  319. newreference(left.location.reference),hregister);
  320. emit_reg_reg(A_OR,opsize,hregister,hregister);
  321. end;
  322. resflags:=F_NE;
  323. end;
  324. LOC_FLAGS :
  325. begin
  326. hregister:=getregister32;
  327. resflags:=left.location.resflags;
  328. end;
  329. LOC_REGISTER,LOC_CREGISTER :
  330. begin
  331. hregister:=left.location.register;
  332. emit_reg_reg(A_OR,opsize,hregister,hregister);
  333. resflags:=F_NE;
  334. end;
  335. else
  336. internalerror(10062);
  337. end;
  338. case resulttype.def.size of
  339. 1 : location.register:=makereg8(hregister);
  340. 2 : location.register:=makereg16(hregister);
  341. 4 : location.register:=makereg32(hregister);
  342. else
  343. internalerror(10064);
  344. end;
  345. emit_flag2reg(resflags,location.register);
  346. end;
  347. {****************************************************************************
  348. TI386TYPECONVNODE
  349. ****************************************************************************}
  350. procedure ti386typeconvnode.second_call_helper(c : tconverttype);
  351. const
  352. secondconvert : array[tconverttype] of pointer = (
  353. @second_nothing, {equal}
  354. @second_nothing, {not_possible}
  355. @second_nothing, {second_string_to_string, handled in resulttype pass }
  356. @second_char_to_string,
  357. @second_nothing, { pchar_to_string, handled in resulttype pass }
  358. @second_nothing, {cchar_to_pchar}
  359. @second_cstring_to_pchar,
  360. @second_ansistring_to_pchar,
  361. @second_string_to_chararray,
  362. @second_nothing, { chararray_to_string, handled in resulttype pass }
  363. @second_array_to_pointer,
  364. @second_pointer_to_array,
  365. @second_int_to_int,
  366. @second_int_to_bool,
  367. @second_bool_to_int, { bool_to_bool }
  368. @second_bool_to_int,
  369. @second_real_to_real,
  370. @second_int_to_real,
  371. @second_proc_to_procvar,
  372. @second_nothing, { arrayconstructor_to_set }
  373. @second_nothing, { second_load_smallset, handled in first pass }
  374. @second_cord_to_pointer,
  375. @second_nothing, { interface 2 string }
  376. @second_nothing, { interface 2 guid }
  377. @second_class_to_intf,
  378. @second_char_to_char,
  379. @second_nothing { normal_2_smallset }
  380. );
  381. type
  382. tprocedureofobject = procedure of object;
  383. var
  384. r : packed record
  385. proc : pointer;
  386. obj : pointer;
  387. end;
  388. begin
  389. { this is a little bit dirty but it works }
  390. { and should be quite portable too }
  391. r.proc:=secondconvert[c];
  392. r.obj:=self;
  393. tprocedureofobject(r){$ifdef FPC}();{$endif FPC}
  394. end;
  395. procedure ti386typeconvnode.pass_2;
  396. {$ifdef TESTOBJEXT2}
  397. var
  398. r : preference;
  399. nillabel : plabel;
  400. {$endif TESTOBJEXT2}
  401. begin
  402. { this isn't good coding, I think tc_bool_2_int, shouldn't be }
  403. { type conversion (FK) }
  404. if not(convtype in [tc_bool_2_int,tc_bool_2_bool]) then
  405. begin
  406. secondpass(left);
  407. set_location(location,left.location);
  408. if codegenerror then
  409. exit;
  410. end;
  411. second_call_helper(convtype);
  412. {$ifdef TESTOBJEXT2}
  413. { Check explicit conversions to objects pointers !! }
  414. if p^.explizit and
  415. (p^.resulttype.def.deftype=pointerdef) and
  416. (tpointerdef(p^.resulttype.def).definition.deftype=objectdef) and not
  417. (tobjectdef(tpointerdef(p^.resulttype.def).definition).isclass) and
  418. ((tobjectdef(tpointerdef(p^.resulttype.def).definition).options and oo_hasvmt)<>0) and
  419. (cs_check_range in aktlocalswitches) then
  420. begin
  421. new(r);
  422. reset_reference(r^);
  423. if p^.location.loc in [LOC_REGISTER,LOC_CREGISTER] then
  424. r^.base:=p^.location.register
  425. else
  426. begin
  427. getexplicitregister32(R_EDI);
  428. emit_mov_loc_reg(p^.location,R_EDI);
  429. r^.base:=R_EDI;
  430. end;
  431. { NIL must be accepted !! }
  432. emit_reg_reg(A_OR,S_L,r^.base,r^.base);
  433. ungetregister32(R_EDI);
  434. getlabel(nillabel);
  435. emitjmp(C_E,nillabel);
  436. { this is one point where we need vmt_offset (PM) }
  437. r^.offset:= tobjectdef(tpointerdef(p^.resulttype.def).definition).vmt_offset;
  438. getexplicitregister32(R_EDI);
  439. emit_ref_reg(A_MOV,S_L,r,R_EDI);
  440. emit_sym(A_PUSH,S_L,
  441. newasmsymbol(tobjectdef(tpointerdef(p^.resulttype.def).definition).vmt_mangledname));
  442. emit_reg(A_PUSH,S_L,R_EDI);
  443. ungetregister32(R_EDI);
  444. emitcall('FPC_CHECK_OBJECT_EXT');
  445. emitlab(nillabel);
  446. end;
  447. {$endif TESTOBJEXT2}
  448. end;
  449. begin
  450. ctypeconvnode:=ti386typeconvnode;
  451. end.
  452. {
  453. $Log$
  454. Revision 1.26 2001-09-30 21:28:34 peter
  455. * int64->boolean fixed
  456. Revision 1.25 2001/09/30 16:12:47 jonas
  457. - removed unnecessary i386 pass_2 of as- and isnode and added dummy generic ones
  458. Revision 1.24 2001/09/29 21:32:47 jonas
  459. * almost all second pass typeconvnode helpers are now processor independent
  460. * fixed converting boolean to int64/qword
  461. * fixed register allocation bugs which could cause internalerror 10
  462. * isnode and asnode are completely processor indepent now as well
  463. * fpc_do_as now returns its class argument (necessary to be able to use it
  464. properly with compilerproc)
  465. Revision 1.23 2001/09/03 13:27:42 jonas
  466. * compilerproc implementation of set addition/substraction/...
  467. * changed the declaration of some set helpers somewhat to accomodate the
  468. above change
  469. * i386 still uses the old code for comparisons of sets, because its
  470. helpers return the results in the flags
  471. * dummy tc_normal_2_small_set type conversion because I need the original
  472. resulttype of the set add nodes
  473. NOTE: you have to start a cycle with 1.0.5!
  474. Revision 1.22 2001/08/29 19:49:03 jonas
  475. * some fixes in compilerprocs for chararray to string conversions
  476. * conversion from string to chararray is now also done via compilerprocs
  477. Revision 1.21 2001/08/28 13:24:47 jonas
  478. + compilerproc implementation of most string-related type conversions
  479. - removed all code from the compiler which has been replaced by
  480. compilerproc implementations (using {$ifdef hascompilerproc} is not
  481. necessary in the compiler)
  482. Revision 1.20 2001/08/26 13:36:57 florian
  483. * some cg reorganisation
  484. * some PPC updates
  485. Revision 1.19 2001/08/01 21:44:59 peter
  486. * fixed empty pwidechar register allocation
  487. Revision 1.18 2001/07/30 20:59:29 peter
  488. * m68k updates from v10 merged
  489. Revision 1.17 2001/07/16 13:19:08 jonas
  490. * fixed allocation of register before release in second_cstring_to_pchar
  491. Revision 1.16 2001/07/08 21:00:17 peter
  492. * various widestring updates, it works now mostly without charset
  493. mapping supported
  494. Revision 1.15 2001/05/08 21:06:33 florian
  495. * some more support for widechars commited especially
  496. regarding type casting and constants
  497. Revision 1.14 2001/04/13 01:22:18 peter
  498. * symtable change to classes
  499. * range check generation and errors fixed, make cycle DEBUG=1 works
  500. * memory leaks fixed
  501. Revision 1.13 2001/04/02 21:20:36 peter
  502. * resulttype rewrite
  503. Revision 1.12 2001/01/08 21:45:11 peter
  504. * internalerror for string to chararray
  505. Revision 1.11 2000/12/25 00:07:32 peter
  506. + new tlinkedlist class (merge of old tstringqueue,tcontainer and
  507. tlinkedlist objects)
  508. Revision 1.10 2000/12/07 17:19:46 jonas
  509. * new constant handling: from now on, hex constants >$7fffffff are
  510. parsed as unsigned constants (otherwise, $80000000 got sign extended
  511. and became $ffffffff80000000), all constants in the longint range
  512. become longints, all constants >$7fffffff and <=cardinal($ffffffff)
  513. are cardinals and the rest are int64's.
  514. * added lots of longint typecast to prevent range check errors in the
  515. compiler and rtl
  516. * type casts of symbolic ordinal constants are now preserved
  517. * fixed bug where the original resulttype.def wasn't restored correctly
  518. after doing a 64bit rangecheck
  519. Revision 1.9 2000/12/05 11:44:33 jonas
  520. + new integer regvar handling, should be much more efficient
  521. Revision 1.8 2000/11/29 00:30:46 florian
  522. * unused units removed from uses clause
  523. * some changes for widestrings
  524. Revision 1.7 2000/11/16 15:27:48 jonas
  525. * fixed web bug 1242
  526. Revision 1.6 2000/11/13 11:30:56 florian
  527. * some bugs with interfaces and NIL fixed
  528. Revision 1.5 2000/11/12 23:24:14 florian
  529. * interfaces are basically running
  530. Revision 1.4 2000/11/11 16:00:10 jonas
  531. * optimize converting of 8/16/32 bit constants to 64bit ones
  532. Revision 1.3 2000/11/04 14:25:23 florian
  533. + merged Attila's changes for interfaces, not tested yet
  534. Revision 1.2 2000/10/31 22:02:56 peter
  535. * symtable splitted, no real code changes
  536. Revision 1.1 2000/10/15 09:33:31 peter
  537. * moved n386*.pas to i386/ cpu_target dir
  538. Revision 1.1 2000/10/14 10:14:48 peter
  539. * moehrendorf oct 2000 rewrite
  540. }