ncgmat.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. {
  2. Copyright (c) 1998-2002 by Florian Klaempfl
  3. Generate generic mathematical nodes
  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 ncgmat;
  18. {$i fpcdefs.inc}
  19. interface
  20. uses
  21. node,nmat,cpubase,cgbase;
  22. type
  23. tcgunaryminusnode = class(tunaryminusnode)
  24. protected
  25. { This routine is called to change the sign of the
  26. floating point value in the floating point
  27. register r.
  28. This routine should be overridden, since
  29. the generic version is not optimal at all. The
  30. generic version assumes that floating
  31. point values are stored in the register
  32. in IEEE-754 format.
  33. }
  34. procedure emit_float_sign_change(r: tregister; _size : tcgsize);virtual;
  35. {$ifdef SUPPORT_MMX}
  36. procedure second_mmx;virtual;abstract;
  37. {$endif SUPPORT_MMX}
  38. {$ifndef cpu64bitalu}
  39. procedure second_64bit;virtual;
  40. {$endif not cpu64bitalu}
  41. procedure second_integer;virtual;
  42. procedure second_float;virtual;
  43. public
  44. procedure pass_generate_code;override;
  45. end;
  46. tcgmoddivnode = class(tmoddivnode)
  47. procedure pass_generate_code;override;
  48. protected
  49. { This routine must do an actual 32-bit division, be it
  50. signed or unsigned. The result must set into the the
  51. @var(num) register.
  52. @param(signed Indicates if the division must be signed)
  53. @param(denum Register containing the denominator
  54. @param(num Register containing the numerator, will also receive result)
  55. The actual optimizations regarding shifts have already
  56. been done and emitted, so this should really a do a divide.
  57. }
  58. procedure emit_div_reg_reg(signed: boolean;denum,num : tregister);virtual;abstract;
  59. { This routine must do an actual 32-bit modulo, be it
  60. signed or unsigned. The result must set into the the
  61. @var(num) register.
  62. @param(signed Indicates if the modulo must be signed)
  63. @param(denum Register containing the denominator
  64. @param(num Register containing the numerator, will also receive result)
  65. The actual optimizations regarding shifts have already
  66. been done and emitted, so this should really a do a modulo.
  67. }
  68. procedure emit_mod_reg_reg(signed: boolean;denum,num : tregister);virtual;abstract;
  69. {$ifndef cpu64bitalu}
  70. { This routine must do an actual 64-bit division, be it
  71. signed or unsigned. The result must set into the the
  72. @var(num) register.
  73. @param(signed Indicates if the division must be signed)
  74. @param(denum Register containing the denominator
  75. @param(num Register containing the numerator, will also receive result)
  76. The actual optimizations regarding shifts have already
  77. been done and emitted, so this should really a do a divide.
  78. Currently, this routine should only be implemented on
  79. 64-bit systems, otherwise a helper is called in 1st pass.
  80. }
  81. procedure emit64_div_reg_reg(signed: boolean;denum,num : tregister64);virtual;
  82. {$endif not cpu64bitalu}
  83. end;
  84. tcgshlshrnode = class(tshlshrnode)
  85. {$ifndef cpu64bitalu}
  86. procedure second_64bit;virtual;
  87. {$endif not cpu64bitalu}
  88. procedure second_integer;virtual;
  89. procedure pass_generate_code;override;
  90. end;
  91. tcgnotnode = class(tnotnode)
  92. protected
  93. procedure second_boolean;virtual;abstract;
  94. {$ifdef SUPPORT_MMX}
  95. procedure second_mmx;virtual;abstract;
  96. {$endif SUPPORT_MMX}
  97. {$ifndef cpu64bitalu}
  98. procedure second_64bit;virtual;
  99. {$endif not cpu64bitalu}
  100. procedure second_integer;virtual;
  101. public
  102. procedure pass_generate_code;override;
  103. end;
  104. implementation
  105. uses
  106. globtype,systems,
  107. cutils,verbose,globals,
  108. symtable,symconst,symtype,symdef,aasmbase,aasmtai,aasmdata,aasmcpu,defutil,
  109. parabase,
  110. pass_2,
  111. ncon,
  112. tgobj,ncgutil,cgobj,cgutils,paramgr,hlcgobj
  113. {$ifndef cpu64bitalu}
  114. ,cg64f32
  115. {$endif not cpu64bitalu}
  116. ;
  117. {*****************************************************************************
  118. TCGUNARYMINUSNODE
  119. *****************************************************************************}
  120. procedure tcgunaryminusnode.emit_float_sign_change(r: tregister; _size : tcgsize);
  121. var
  122. href,
  123. href2 : treference;
  124. begin
  125. { get a temporary memory reference to store the floating
  126. point value
  127. }
  128. tg.gettemp(current_asmdata.CurrAsmList,tcgsize2size[_size],tcgsize2size[_size],tt_normal,href);
  129. { store the floating point value in the temporary memory area }
  130. cg.a_loadfpu_reg_ref(current_asmdata.CurrAsmList,_size,_size,r,href);
  131. { only single and double ieee are supported, for little endian
  132. the signed bit is in the second dword }
  133. href2:=href;
  134. case _size of
  135. OS_F64 :
  136. if target_info.endian = endian_little then
  137. inc(href2.offset,4);
  138. OS_F32 :
  139. ;
  140. else
  141. internalerror(200406021);
  142. end;
  143. { flip sign-bit (bit 31/63) of single/double }
  144. cg.a_op_const_ref(current_asmdata.CurrAsmList,OP_XOR,OS_32,aint($80000000),href2);
  145. cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,_size,_size,href,r);
  146. tg.ungetiftemp(current_asmdata.CurrAsmList,href);
  147. end;
  148. {$ifndef cpu64bitalu}
  149. procedure tcgunaryminusnode.second_64bit;
  150. var
  151. tr: tregister;
  152. hl: tasmlabel;
  153. begin
  154. secondpass(left);
  155. location_reset(location,LOC_REGISTER,left.location.size);
  156. location.register64.reglo:=cg.getintregister(current_asmdata.CurrAsmList,OS_32);
  157. location.register64.reghi:=cg.getintregister(current_asmdata.CurrAsmList,OS_32);
  158. cg64.a_op64_loc_reg(current_asmdata.CurrAsmList,OP_NEG,OS_S64,
  159. left.location,joinreg64(location.register64.reglo,location.register64.reghi));
  160. { there's only overflow in case left was low(int64) -> -left = left }
  161. if (cs_check_overflow in current_settings.localswitches) then
  162. begin
  163. tr:=cg.getintregister(current_asmdata.CurrAsmList,OS_32);
  164. cg.a_op_const_reg_reg(current_asmdata.CurrAsmList,OP_XOR,OS_32,
  165. aint($80000000),location.register64.reghi,tr);
  166. cg.a_op_reg_reg(current_asmdata.CurrAsmList,OP_OR,OS_32,
  167. location.register64.reglo,tr);
  168. current_asmdata.getjumplabel(hl);
  169. cg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,OS_32,OC_NE,0,tr,hl);
  170. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_OVERFLOW',false);
  171. cg.a_label(current_asmdata.CurrAsmList,hl);
  172. end;
  173. end;
  174. {$endif not cpu64bitalu}
  175. procedure tcgunaryminusnode.second_float;
  176. begin
  177. secondpass(left);
  178. location_reset(location,LOC_FPUREGISTER,def_cgsize(resultdef));
  179. case left.location.loc of
  180. LOC_REFERENCE,
  181. LOC_CREFERENCE :
  182. begin
  183. location.register:=cg.getfpuregister(current_asmdata.CurrAsmList,location.size);
  184. cg.a_loadfpu_ref_reg(current_asmdata.CurrAsmList,
  185. left.location.size,location.size,
  186. left.location.reference,location.register);
  187. emit_float_sign_change(location.register,def_cgsize(left.resultdef));
  188. end;
  189. LOC_FPUREGISTER:
  190. begin
  191. location.register:=left.location.register;
  192. emit_float_sign_change(location.register,def_cgsize(left.resultdef));
  193. end;
  194. LOC_CFPUREGISTER:
  195. begin
  196. location.register:=cg.getfpuregister(current_asmdata.CurrAsmList,location.size);
  197. cg.a_loadfpu_reg_reg(current_asmdata.CurrAsmList,left.location.size,location.size,left.location.register,location.register);
  198. emit_float_sign_change(location.register,def_cgsize(left.resultdef));
  199. end;
  200. else
  201. internalerror(200306021);
  202. end;
  203. end;
  204. procedure tcgunaryminusnode.second_integer;
  205. var
  206. hl: tasmlabel;
  207. opsize: tdef;
  208. begin
  209. secondpass(left);
  210. {$ifdef cpunodefaultint}
  211. opsize:=left.resultdef;
  212. {$else cpunodefaultint}
  213. { in case of a 32 bit system that can natively execute 64 bit operations }
  214. if (left.resultdef.size<=sinttype.size) then
  215. opsize:=sinttype
  216. else
  217. opsize:={$ifdef cpu16bitalu}s32inttype{$else}s64inttype{$endif};
  218. {$endif cpunodefaultint}
  219. if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
  220. hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,opsize,false);
  221. location_reset(location,LOC_REGISTER,def_cgsize(opsize));
  222. location.register:=cg.getintregister(current_asmdata.CurrAsmList,location.size);
  223. hlcg.a_op_reg_reg(current_asmdata.CurrAsmList,OP_NEG,opsize,left.location.register,location.register);
  224. if (cs_check_overflow in current_settings.localswitches) then
  225. begin
  226. current_asmdata.getjumplabel(hl);
  227. hlcg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,opsize,OC_NE,torddef(opsize).low.svalue,location.register,hl);
  228. hlcg.g_call_system_proc(current_asmdata.CurrAsmList,'fpc_overflow',nil);
  229. hlcg.a_label(current_asmdata.CurrAsmList,hl);
  230. end;
  231. end;
  232. procedure tcgunaryminusnode.pass_generate_code;
  233. begin
  234. {$ifndef cpu64bitalu}
  235. if is_64bit(left.resultdef) then
  236. second_64bit
  237. else
  238. {$endif not cpu64bitalu}
  239. {$ifdef SUPPORT_MMX}
  240. if (cs_mmx in current_settings.localswitches) and is_mmx_able_array(left.resultdef) then
  241. second_mmx
  242. else
  243. {$endif SUPPORT_MMX}
  244. if (left.resultdef.typ=floatdef) then
  245. second_float
  246. else
  247. second_integer;
  248. end;
  249. {*****************************************************************************
  250. TCGMODDIVNODE
  251. *****************************************************************************}
  252. {$ifndef cpu64bitalu}
  253. procedure tcgmoddivnode.emit64_div_reg_reg(signed: boolean; denum,num:tregister64);
  254. begin
  255. { handled in pass_1 already, unless pass_1 is
  256. overridden
  257. }
  258. { should be handled in pass_1 (JM) }
  259. internalerror(200109052);
  260. end;
  261. {$endif not cpu64bitalu}
  262. procedure tcgmoddivnode.pass_generate_code;
  263. var
  264. hreg1 : tregister;
  265. hdenom : tregister;
  266. power : longint;
  267. hl : tasmlabel;
  268. paraloc1 : tcgpara;
  269. opsize : tcgsize;
  270. opdef : tdef;
  271. pd: tprocdef;
  272. begin
  273. secondpass(left);
  274. if codegenerror then
  275. exit;
  276. secondpass(right);
  277. if codegenerror then
  278. exit;
  279. location_copy(location,left.location);
  280. {$ifndef cpu64bitalu}
  281. if is_64bit(resultdef) then
  282. begin
  283. if is_signed(left.resultdef) then
  284. opdef:=s64inttype
  285. else
  286. opdef:=u64inttype;
  287. { this code valid for 64-bit cpu's only ,
  288. otherwise helpers are called in pass_1
  289. }
  290. hlcg.location_force_reg(current_asmdata.CurrAsmList,location,left.resultdef,opdef,false);
  291. location_copy(location,left.location);
  292. hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,opdef,false);
  293. emit64_div_reg_reg(is_signed(left.resultdef),
  294. joinreg64(right.location.register64.reglo,right.location.register64.reghi),
  295. joinreg64(location.register64.reglo,location.register64.reghi));
  296. end
  297. else
  298. {$endif not cpu64bitalu}
  299. begin
  300. if is_signed(left.resultdef) then
  301. begin
  302. opsize:=OS_SINT;
  303. opdef:=ossinttype;
  304. end
  305. else
  306. begin
  307. opsize:=OS_INT;
  308. opdef:=osuinttype;
  309. end;
  310. { put numerator in register }
  311. hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,opdef,false);
  312. hreg1:=left.location.register;
  313. if (nodetype=divn) and
  314. (right.nodetype=ordconstn) and
  315. ispowerof2(tordconstnode(right).value.svalue,power) then
  316. Begin
  317. { for signed numbers, the numerator must be adjusted before the
  318. shift instruction, but not wih unsigned numbers! Otherwise,
  319. "Cardinal($ffffffff) div 16" overflows! (JM) }
  320. If is_signed(left.resultdef) Then
  321. Begin
  322. current_asmdata.getjumplabel(hl);
  323. cg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_GT,0,hreg1,hl);
  324. if power=1 then
  325. cg.a_op_const_reg(current_asmdata.CurrAsmList,OP_ADD,OS_INT,1,hreg1)
  326. else
  327. cg.a_op_const_reg(current_asmdata.CurrAsmList,OP_ADD,OS_INT,Tordconstnode(right).value.svalue-1,hreg1);
  328. cg.a_label(current_asmdata.CurrAsmList,hl);
  329. cg.a_op_const_reg(current_asmdata.CurrAsmList,OP_SAR,OS_INT,power,hreg1);
  330. End
  331. Else { not signed }
  332. cg.a_op_const_reg(current_asmdata.CurrAsmList,OP_SHR,OS_INT,power,hreg1);
  333. End
  334. else
  335. begin
  336. { bring denominator to hdenom }
  337. { hdenom is always free, it's }
  338. { only used for temporary }
  339. { purposes }
  340. hdenom := cg.getintregister(current_asmdata.CurrAsmList,OS_INT);
  341. hlcg.a_load_loc_reg(current_asmdata.CurrAsmList,right.resultdef,osuinttype,right.location,hdenom);
  342. { verify if the divisor is zero, if so return an error
  343. immediately
  344. }
  345. current_asmdata.getjumplabel(hl);
  346. cg.a_cmp_const_reg_label(current_asmdata.CurrAsmList,OS_INT,OC_NE,0,hdenom,hl);
  347. paraloc1.init;
  348. pd:=search_system_proc('fpc_handleerror');
  349. paramanager.getintparaloc(pd,1,paraloc1);
  350. cg.a_load_const_cgpara(current_asmdata.CurrAsmList,OS_S32,aint(200),paraloc1);
  351. paramanager.freecgpara(current_asmdata.CurrAsmList,paraloc1);
  352. cg.a_call_name(current_asmdata.CurrAsmList,'FPC_HANDLEERROR',false);
  353. paraloc1.done;
  354. cg.a_label(current_asmdata.CurrAsmList,hl);
  355. if nodetype = modn then
  356. emit_mod_reg_reg(is_signed(left.resultdef),hdenom,hreg1)
  357. else
  358. emit_div_reg_reg(is_signed(left.resultdef),hdenom,hreg1);
  359. end;
  360. location_reset(location,LOC_REGISTER,opsize);
  361. location.register:=hreg1;
  362. end;
  363. cg.g_overflowcheck(current_asmdata.CurrAsmList,location,resultdef);
  364. end;
  365. {*****************************************************************************
  366. TCGSHLRSHRNODE
  367. *****************************************************************************}
  368. {$ifndef cpu64bitalu}
  369. procedure tcgshlshrnode.second_64bit;
  370. begin
  371. { already hanled in 1st pass }
  372. internalerror(2002081501);
  373. end;
  374. {$endif not cpu64bitalu}
  375. procedure tcgshlshrnode.second_integer;
  376. var
  377. op : topcg;
  378. opdef : tdef;
  379. hcountreg : tregister;
  380. opsize : tcgsize;
  381. begin
  382. { determine operator }
  383. case nodetype of
  384. shln: op:=OP_SHL;
  385. shrn: op:=OP_SHR;
  386. else
  387. internalerror(2013120102);
  388. end;
  389. {$ifdef cpunodefaultint}
  390. opsize:=left.location.size;
  391. opdef:=left.resultdef;
  392. {$else cpunodefaultint}
  393. { load left operators in a register }
  394. if is_signed(left.resultdef) then
  395. begin
  396. {$ifdef cpu16bitalu}
  397. if left.resultdef.size > 2 then
  398. begin
  399. opsize:=OS_S32;
  400. opdef:=s32inttype;
  401. end
  402. else
  403. {$endif cpu16bitalu}
  404. begin
  405. opsize:=OS_SINT;
  406. opdef:=ossinttype
  407. end;
  408. end
  409. else
  410. begin
  411. {$ifdef cpu16bitalu}
  412. if left.resultdef.size > 2 then
  413. begin
  414. opsize:=OS_32;
  415. opdef:=u32inttype;
  416. end
  417. else
  418. {$endif cpu16bitalu}
  419. begin
  420. opsize:=OS_INT;
  421. opdef:=osuinttype;
  422. end;
  423. end;
  424. {$endif cpunodefaultint}
  425. hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,opdef,true);
  426. location_reset(location,LOC_REGISTER,opsize);
  427. location.register:=cg.getintregister(current_asmdata.CurrAsmList,opsize);
  428. { shifting by a constant directly coded: }
  429. if (right.nodetype=ordconstn) then
  430. begin
  431. { l shl 32 should 0 imho, but neither TP nor Delphi do it in this way (FK)
  432. if right.value<=31 then
  433. }
  434. cg.a_op_const_reg_reg(current_asmdata.CurrAsmList,op,location.size,
  435. tordconstnode(right).value.uvalue and 31,left.location.register,location.register);
  436. {
  437. else
  438. emit_reg_reg(A_XOR,S_L,hregister1,
  439. hregister1);
  440. }
  441. end
  442. else
  443. begin
  444. { load right operators in a register - this
  445. is done since most target cpu which will use this
  446. node do not support a shift count in a mem. location (cec)
  447. }
  448. if not(right.location.loc in [LOC_CREGISTER,LOC_REGISTER]) then
  449. begin
  450. hcountreg:=cg.getintregister(current_asmdata.CurrAsmList,opsize);
  451. hlcg.a_load_loc_reg(current_asmdata.CurrAsmList,right.resultdef,opdef,right.location,hcountreg);
  452. end
  453. else
  454. hcountreg:=right.location.register;
  455. cg.a_op_reg_reg_reg(current_asmdata.CurrAsmList,op,opsize,hcountreg,left.location.register,location.register);
  456. end;
  457. end;
  458. procedure tcgshlshrnode.pass_generate_code;
  459. begin
  460. secondpass(left);
  461. secondpass(right);
  462. {$ifndef cpu64bitalu}
  463. if is_64bit(left.resultdef) then
  464. second_64bit
  465. else
  466. {$endif not cpu64bitalu}
  467. second_integer;
  468. end;
  469. {*****************************************************************************
  470. TCGNOTNODE
  471. *****************************************************************************}
  472. {$ifndef cpu64bitalu}
  473. procedure tcgnotnode.second_64bit;
  474. begin
  475. secondpass(left);
  476. if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
  477. hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
  478. location_reset(location,LOC_REGISTER,left.location.size);
  479. location.register64.reglo:=cg.getintregister(current_asmdata.CurrAsmList,OS_32);
  480. location.register64.reghi:=cg.getintregister(current_asmdata.CurrAsmList,OS_32);
  481. { perform the NOT operation }
  482. cg64.a_op64_reg_reg(current_asmdata.CurrAsmList,OP_NOT,location.size,left.location.register64,location.register64);
  483. end;
  484. {$endif not cpu64bitalu}
  485. procedure tcgnotnode.second_integer;
  486. begin
  487. secondpass(left);
  488. if not(left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
  489. hlcg.location_force_reg(current_asmdata.CurrAsmList,left.location,left.resultdef,left.resultdef,false);
  490. location_reset(location,LOC_REGISTER,left.location.size);
  491. location.register:=cg.getintregister(current_asmdata.CurrAsmList,location.size);
  492. { perform the NOT operation }
  493. hlcg.a_op_reg_reg(current_asmdata.CurrAsmList,OP_NOT,left.resultdef,left.location.register,location.register);
  494. end;
  495. procedure tcgnotnode.pass_generate_code;
  496. begin
  497. if is_boolean(resultdef) then
  498. second_boolean
  499. {$ifdef SUPPORT_MMX}
  500. else if (cs_mmx in current_settings.localswitches) and is_mmx_able_array(left.resultdef) then
  501. second_mmx
  502. {$endif SUPPORT_MMX}
  503. {$ifndef cpu64bitalu}
  504. else if is_64bit(left.resultdef) then
  505. second_64bit
  506. {$endif not cpu64bitalu}
  507. else
  508. second_integer;
  509. end;
  510. begin
  511. cmoddivnode:=tcgmoddivnode;
  512. cunaryminusnode:=tcgunaryminusnode;
  513. cshlshrnode:=tcgshlshrnode;
  514. cnotnode:=tcgnotnode;
  515. end.