n386set.pas 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108
  1. {
  2. $Id$
  3. Copyright (c) 1998-2002 by Florian Klaempfl
  4. Generate i386 assembler for in set/case 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 n386set;
  19. {$i fpcdefs.inc}
  20. interface
  21. uses
  22. node,nset;
  23. type
  24. ti386setelementnode = class(tsetelementnode)
  25. procedure pass_2;override;
  26. end;
  27. ti386innode = class(tinnode)
  28. procedure pass_2;override;
  29. end;
  30. ti386casenode = class(tcasenode)
  31. procedure pass_2;override;
  32. end;
  33. implementation
  34. uses
  35. globtype,systems,
  36. verbose,globals,
  37. symconst,symdef,types,
  38. aasmbase,aasmtai,aasmcpu,
  39. cginfo,cgbase,pass_2,
  40. ncon,
  41. cpubase,
  42. cga,cgobj,tgobj,ncgutil,regvars,rgobj;
  43. const
  44. bytes2Sxx:array[1..8] of Topsize=(S_B,S_W,S_NO,S_L,S_NO,S_NO,S_NO,S_Q);
  45. {*****************************************************************************
  46. TI386SETELEMENTNODE
  47. *****************************************************************************}
  48. procedure ti386setelementnode.pass_2;
  49. var
  50. pushedregs : tmaybesave;
  51. begin
  52. { load first value in 32bit register }
  53. secondpass(left);
  54. if left.location.loc in [LOC_REGISTER,LOC_CREGISTER] then
  55. location_force_reg(exprasmlist,left.location,OS_32,false);
  56. { also a second value ? }
  57. if assigned(right) then
  58. begin
  59. maybe_save(exprasmlist,right.registers32,left.location,pushedregs);
  60. secondpass(right);
  61. if codegenerror then
  62. exit;
  63. maybe_restore(exprasmlist,left.location,pushedregs);
  64. if right.location.loc in [LOC_REGISTER,LOC_CREGISTER] then
  65. location_force_reg(exprasmlist,right.location,OS_32,false);
  66. end;
  67. { we doesn't modify the left side, we check only the type }
  68. location_copy(location,left.location);
  69. end;
  70. {*****************************************************************************
  71. TI386INNODE
  72. *****************************************************************************}
  73. procedure ti386innode.pass_2;
  74. type
  75. Tsetpart=record
  76. range : boolean; {Part is a range.}
  77. start,stop : byte; {Start/stop when range; Stop=element when an element.}
  78. end;
  79. var
  80. genjumps,
  81. use_small,
  82. ranges : boolean;
  83. hr,hr2,
  84. pleftreg : tregister;
  85. href : treference;
  86. opsize : topsize;
  87. setparts : array[1..8] of Tsetpart;
  88. i,numparts : byte;
  89. adjustment : longint;
  90. pushedregs : tmaybesave;
  91. l,l2 : tasmlabel;
  92. {$ifdef CORRECT_SET_IN_FPC}
  93. AM : tasmop;
  94. {$endif CORRECT_SET_IN_FPC}
  95. function analizeset(Aset:pconstset;is_small:boolean):boolean;
  96. type
  97. byteset=set of byte;
  98. var
  99. compares,maxcompares:word;
  100. i:byte;
  101. begin
  102. analizeset:=false;
  103. ranges:=false;
  104. numparts:=0;
  105. compares:=0;
  106. { Lots of comparisions take a lot of time, so do not allow
  107. too much comparisions. 8 comparisions are, however, still
  108. smalller than emitting the set }
  109. if cs_littlesize in aktglobalswitches then
  110. maxcompares:=8
  111. else
  112. maxcompares:=5;
  113. { when smallset is possible allow only 3 compares the smallset
  114. code is for littlesize also smaller when more compares are used }
  115. if is_small then
  116. maxcompares:=3;
  117. for i:=0 to 255 do
  118. if i in byteset(Aset^) then
  119. begin
  120. if (numparts=0) or (i<>setparts[numparts].stop+1) then
  121. begin
  122. {Set element is a separate element.}
  123. inc(compares);
  124. if compares>maxcompares then
  125. exit;
  126. inc(numparts);
  127. setparts[numparts].range:=false;
  128. setparts[numparts].stop:=i;
  129. end
  130. else
  131. {Set element is part of a range.}
  132. if not setparts[numparts].range then
  133. begin
  134. {Transform an element into a range.}
  135. setparts[numparts].range:=true;
  136. setparts[numparts].start:=setparts[numparts].stop;
  137. setparts[numparts].stop:=i;
  138. ranges := true;
  139. { there's only one compare per range anymore. Only a }
  140. { sub is added, but that's much faster than a }
  141. { cmp/jcc combo so neglect its effect }
  142. { inc(compares);
  143. if compares>maxcompares then
  144. exit; }
  145. end
  146. else
  147. begin
  148. {Extend a range.}
  149. setparts[numparts].stop:=i;
  150. end;
  151. end;
  152. analizeset:=true;
  153. end;
  154. begin
  155. { We check first if we can generate jumps, this can be done
  156. because the resulttype.def is already set in firstpass }
  157. { check if we can use smallset operation using btl which is limited
  158. to 32 bits, the left side may also not contain higher values !! }
  159. use_small:=(tsetdef(right.resulttype.def).settype=smallset) and
  160. ((left.resulttype.def.deftype=orddef) and (torddef(left.resulttype.def).high<=32) or
  161. (left.resulttype.def.deftype=enumdef) and (tenumdef(left.resulttype.def).max<=32));
  162. { Can we generate jumps? Possible for all types of sets }
  163. genjumps:=(right.nodetype=setconstn) and
  164. analizeset(tsetconstnode(right).value_set,use_small);
  165. { calculate both operators }
  166. { the complex one first }
  167. firstcomplex(self);
  168. secondpass(left);
  169. { Only process the right if we are not generating jumps }
  170. if not genjumps then
  171. begin
  172. maybe_save(exprasmlist,right.registers32,left.location,pushedregs);
  173. secondpass(right);
  174. maybe_restore(exprasmlist,left.location,pushedregs);
  175. end;
  176. if codegenerror then
  177. exit;
  178. { ofcourse not commutative }
  179. if nf_swaped in flags then
  180. swapleftright;
  181. if genjumps then
  182. begin
  183. { It gives us advantage to check for the set elements
  184. separately instead of using the SET_IN_BYTE procedure.
  185. To do: Build in support for LOC_JUMP }
  186. opsize := def_opsize(left.resulttype.def);
  187. { If register is used, use only lower 8 bits }
  188. if left.location.loc in [LOC_REGISTER,LOC_CREGISTER] then
  189. begin
  190. { for ranges we always need a 32bit register, because then we }
  191. { use the register as base in a reference (JM) }
  192. if ranges then
  193. begin
  194. pleftreg:=rg.makeregsize(left.location.register,OS_INT);
  195. cg.a_load_reg_reg(exprasmlist,left.location.size,left.location.register,pleftreg);
  196. if opsize <> S_L then
  197. emit_const_reg(A_AND,S_L,255,pleftreg);
  198. opsize := S_L;
  199. end
  200. else
  201. { otherwise simply use the lower 8 bits (no "and" }
  202. { necessary this way) (JM) }
  203. begin
  204. pleftreg:=rg.makeregsize(left.location.register,OS_8);
  205. opsize := S_B;
  206. end;
  207. end
  208. else
  209. begin
  210. { load the value in a register }
  211. pleftreg := rg.getexplicitregisterint(exprasmlist,R_EDI);
  212. opsize := S_L;
  213. emit_ref_reg(A_MOVZX,S_BL,left.location.reference,pleftreg);
  214. end;
  215. { Get a label to jump to the end }
  216. location_reset(location,LOC_FLAGS,OS_NO);
  217. { It's better to use the zero flag when there are
  218. no ranges }
  219. if ranges then
  220. location.resflags:=F_C
  221. else
  222. location.resflags:=F_E;
  223. getlabel(l);
  224. { how much have we already substracted from the x in the }
  225. { "x in [y..z]" expression }
  226. adjustment := 0;
  227. for i:=1 to numparts do
  228. if setparts[i].range then
  229. { use fact that a <= x <= b <=> cardinal(x-a) <= cardinal(b-a) }
  230. begin
  231. { is the range different from all legal values? }
  232. if (setparts[i].stop-setparts[i].start <> 255) then
  233. begin
  234. { yes, is the lower bound <> 0? }
  235. if (setparts[i].start <> 0) then
  236. { we're going to substract from the left register, }
  237. { so in case of a LOC_CREGISTER first move the value }
  238. { to edi (not done before because now we can do the }
  239. { move and substract in one instruction with LEA) }
  240. if (pleftreg <> R_EDI) and
  241. (left.location.loc = LOC_CREGISTER) then
  242. begin
  243. rg.ungetregister(exprasmlist,pleftreg);
  244. rg.getexplicitregisterint(exprasmlist,R_EDI);
  245. reference_reset_base(href,pleftreg,-setparts[i].start);
  246. emit_ref_reg(A_LEA,S_L,href,R_EDI);
  247. { only now change pleftreg since previous value is }
  248. { still used in previous instruction }
  249. pleftreg := R_EDI;
  250. opsize := S_L;
  251. end
  252. else
  253. begin
  254. { otherwise, the value is already in a register }
  255. { that can be modified }
  256. if setparts[i].start-adjustment <> 1 then
  257. emit_const_reg(A_SUB,opsize,
  258. setparts[i].start-adjustment,pleftreg)
  259. else emit_reg(A_DEC,opsize,pleftreg);
  260. end;
  261. { new total value substracted from x: }
  262. { adjustment + (setparts[i].start - adjustment) }
  263. adjustment := setparts[i].start;
  264. { check if result < b-a+1 (not "result <= b-a", since }
  265. { we need a carry in case the element is in the range }
  266. { (this will never overflow since we check at the }
  267. { beginning whether stop-start <> 255) }
  268. emit_const_reg(A_CMP,opsize,
  269. setparts[i].stop-setparts[i].start+1,pleftreg);
  270. { use C_C instead of C_B: the meaning is the same, but }
  271. { then the optimizer can easier trace the jump to its }
  272. { final destination since the resultflag of this node }
  273. { is set to the carryflag }
  274. emitjmp(C_C,l);
  275. end
  276. else
  277. { if setparts[i].start = 0 and setparts[i].stop = 255, }
  278. { it's always true since "in" is only allowed for bytes }
  279. begin
  280. emit_none(A_STC,S_NO);
  281. cg.a_jmp_always(exprasmlist,l);
  282. end;
  283. end
  284. else
  285. begin
  286. { Emit code to check if left is an element }
  287. emit_const_reg(A_CMP,opsize,setparts[i].stop-adjustment,
  288. pleftreg);
  289. { Result should be in carry flag when ranges are used }
  290. if ranges then
  291. emit_none(A_STC,S_NO);
  292. { If found, jump to end }
  293. emitjmp(C_E,l);
  294. end;
  295. if ranges and
  296. { if the last one was a range, the carry flag is already }
  297. { set appropriately }
  298. not(setparts[numparts].range) then
  299. emit_none(A_CLC,S_NO);
  300. { To compensate for not doing a second pass }
  301. right.location.reference.symbol:=nil;
  302. { Now place the end label }
  303. cg.a_label(exprasmlist,l);
  304. case left.location.loc of
  305. LOC_REGISTER,
  306. LOC_CREGISTER :
  307. rg.ungetregister(exprasmlist,pleftreg);
  308. else
  309. begin
  310. reference_release(exprasmlist,left.location.reference);
  311. rg.ungetregister(exprasmlist,R_EDI);
  312. end;
  313. end;
  314. end
  315. else
  316. begin
  317. location_reset(location,LOC_FLAGS,OS_NO);
  318. { We will now generated code to check the set itself, no jmps,
  319. handle smallsets separate, because it allows faster checks }
  320. if use_small then
  321. begin
  322. if left.nodetype=ordconstn then
  323. begin
  324. location.resflags:=F_NE;
  325. case right.location.loc of
  326. LOC_REGISTER,
  327. LOC_CREGISTER:
  328. begin
  329. emit_const_reg(A_TEST,S_L,
  330. 1 shl (tordconstnode(left).value and 31),right.location.register);
  331. end;
  332. LOC_REFERENCE,
  333. LOC_CREFERENCE :
  334. begin
  335. emit_const_ref(A_TEST,S_L,1 shl (tordconstnode(left).value and 31),
  336. right.location.reference);
  337. end;
  338. else
  339. internalerror(200203312);
  340. end;
  341. location_release(exprasmlist,right.location);
  342. end
  343. else
  344. begin
  345. case left.location.loc of
  346. LOC_REGISTER,
  347. LOC_CREGISTER:
  348. begin
  349. hr:=rg.makeregsize(left.location.register,OS_INT);
  350. cg.a_load_reg_reg(exprasmlist,left.location.size,left.location.register,hr);
  351. end;
  352. else
  353. begin
  354. { the set element isn't never samller than a byte }
  355. { and because it's a small set we need only 5 bits }
  356. { but 8 bits are easier to load }
  357. rg.getexplicitregisterint(exprasmlist,R_EDI);
  358. emit_ref_reg(A_MOVZX,S_BL,left.location.reference,R_EDI);
  359. hr:=R_EDI;
  360. location_release(exprasmlist,left.location);
  361. end;
  362. end;
  363. case right.location.loc of
  364. LOC_REGISTER,
  365. LOC_CREGISTER :
  366. begin
  367. emit_reg_reg(A_BT,S_L,hr,
  368. right.location.register);
  369. rg.ungetregisterint(exprasmlist,right.location.register);
  370. end;
  371. LOC_CONSTANT :
  372. begin
  373. { We have to load the value into a register because
  374. btl does not accept values only refs or regs (PFV) }
  375. hr2:=rg.getregisterint(exprasmlist);
  376. emit_const_reg(A_MOV,S_L,
  377. right.location.value,hr2);
  378. emit_reg_reg(A_BT,S_L,hr,hr2);
  379. rg.ungetregisterint(exprasmlist,hr2);
  380. end;
  381. LOC_CREFERENCE,
  382. LOC_REFERENCE :
  383. begin
  384. location_release(exprasmlist,right.location);
  385. emit_reg_ref(A_BT,S_L,hr,right.location.reference);
  386. end;
  387. else
  388. internalerror(2002032210);
  389. end;
  390. { simply to indicate EDI is deallocated here too (JM) }
  391. rg.ungetregisterint(exprasmlist,hr);
  392. location.loc:=LOC_FLAGS;
  393. location.resflags:=F_C;
  394. end;
  395. end
  396. else
  397. begin
  398. if right.location.loc=LOC_CONSTANT then
  399. begin
  400. location.resflags:=F_C;
  401. getlabel(l);
  402. getlabel(l2);
  403. { Is this treated in firstpass ?? }
  404. if left.nodetype=ordconstn then
  405. begin
  406. hr:=rg.getregisterint(exprasmlist);
  407. left.location.loc:=LOC_REGISTER;
  408. left.location.register:=hr;
  409. emit_const_reg(A_MOV,S_L,
  410. tordconstnode(left).value,hr);
  411. end;
  412. case left.location.loc of
  413. LOC_REGISTER,
  414. LOC_CREGISTER:
  415. begin
  416. hr:=rg.makeregsize(left.location.register,OS_INT);
  417. cg.a_load_reg_reg(exprasmlist,left.location.size,left.location.register,hr);
  418. emit_const_reg(A_CMP,S_L,31,hr);
  419. emitjmp(C_NA,l);
  420. { reset carry flag }
  421. emit_none(A_CLC,S_NO);
  422. cg.a_jmp_always(exprasmlist,l2);
  423. cg.a_label(exprasmlist,l);
  424. { We have to load the value into a register because
  425. btl does not accept values only refs or regs (PFV) }
  426. hr2:=rg.getregisterint(exprasmlist);
  427. emit_const_reg(A_MOV,S_L,right.location.value,hr2);
  428. emit_reg_reg(A_BT,S_L,hr,hr2);
  429. rg.ungetregisterint(exprasmlist,hr2);
  430. end;
  431. else
  432. begin
  433. {$ifdef CORRECT_SET_IN_FPC}
  434. if m_tp in aktmodeswitches then
  435. begin
  436. {***WARNING only correct if
  437. reference is 32 bits (PM) *****}
  438. emit_const_ref(A_CMP,S_L,31,reference_copy(left.location.reference));
  439. end
  440. else
  441. {$endif CORRECT_SET_IN_FPC}
  442. begin
  443. emit_const_ref(A_CMP,S_B,31,left.location.reference);
  444. end;
  445. emitjmp(C_NA,l);
  446. { reset carry flag }
  447. emit_none(A_CLC,S_NO);
  448. cg.a_jmp_always(exprasmlist,l2);
  449. cg.a_label(exprasmlist,l);
  450. location_release(exprasmlist,left.location);
  451. hr:=rg.getregisterint(exprasmlist);
  452. emit_ref_reg(A_MOV,S_L,left.location.reference,hr);
  453. { We have to load the value into a register because
  454. btl does not accept values only refs or regs (PFV) }
  455. hr2:=rg.getregisterint(exprasmlist);
  456. emit_const_reg(A_MOV,S_L,
  457. right.location.value,hr2);
  458. emit_reg_reg(A_BT,S_L,hr,hr2);
  459. rg.ungetregisterint(exprasmlist,hr2);
  460. end;
  461. end;
  462. cg.a_label(exprasmlist,l2);
  463. end { of right.location.loc=LOC_CONSTANT }
  464. { do search in a normal set which could have >32 elementsm
  465. but also used if the left side contains higher values > 32 }
  466. else if left.nodetype=ordconstn then
  467. begin
  468. location.resflags:=F_NE;
  469. inc(right.location.reference.offset,tordconstnode(left).value shr 3);
  470. emit_const_ref(A_TEST,S_B,1 shl (tordconstnode(left).value and 7),right.location.reference);
  471. location_release(exprasmlist,right.location);
  472. end
  473. else
  474. begin
  475. if (left.location.loc in [LOC_REGISTER,LOC_CREGISTER]) then
  476. pleftreg:=rg.makeregsize(left.location.register,OS_INT)
  477. else
  478. pleftreg:=rg.getexplicitregisterint(exprasmlist,R_EDI);
  479. cg.a_load_loc_reg(exprasmlist,left.location,pleftreg);
  480. location_freetemp(exprasmlist,left.location);
  481. location_release(exprasmlist,left.location);
  482. emit_reg_ref(A_BT,S_L,pleftreg,right.location.reference);
  483. rg.ungetregister(exprasmlist,pleftreg);
  484. location_release(exprasmlist,right.location);
  485. { tg.ungetiftemp(exprasmlist,right.location.reference) happens below }
  486. location.resflags:=F_C;
  487. end;
  488. end;
  489. end;
  490. location_freetemp(exprasmlist,right.location);
  491. end;
  492. {*****************************************************************************
  493. TI386CASENODE
  494. *****************************************************************************}
  495. procedure ti386casenode.pass_2;
  496. var
  497. with_sign : boolean;
  498. opsize : topsize;
  499. jmp_gt,jmp_le,jmp_lee : tasmcond;
  500. hp : tnode;
  501. { register with case expression }
  502. hregister,hregister2 : tregister;
  503. endlabel,elselabel : tasmlabel;
  504. { true, if we can omit the range check of the jump table }
  505. jumptable_no_range : boolean;
  506. { where to put the jump table }
  507. jumpsegment : TAAsmoutput;
  508. min_label : TConstExprInt;
  509. procedure gentreejmp(p : pcaserecord);
  510. var
  511. lesslabel,greaterlabel : tasmlabel;
  512. begin
  513. cg.a_label(exprasmlist,p^._at);
  514. { calculate labels for left and right }
  515. if (p^.less=nil) then
  516. lesslabel:=elselabel
  517. else
  518. lesslabel:=p^.less^._at;
  519. if (p^.greater=nil) then
  520. greaterlabel:=elselabel
  521. else
  522. greaterlabel:=p^.greater^._at;
  523. { calculate labels for left and right }
  524. { no range label: }
  525. if p^._low=p^._high then
  526. begin
  527. emit_const_reg(A_CMP,opsize,p^._low,hregister);
  528. if greaterlabel=lesslabel then
  529. emitjmp(C_NE,lesslabel)
  530. else
  531. begin
  532. emitjmp(jmp_le,lesslabel);
  533. emitjmp(jmp_gt,greaterlabel);
  534. end;
  535. cg.a_jmp_always(exprasmlist,p^.statement);
  536. end
  537. else
  538. begin
  539. emit_const_reg(A_CMP,opsize,p^._low,hregister);
  540. emitjmp(jmp_le,lesslabel);
  541. emit_const_reg(A_CMP,opsize,p^._high,hregister);
  542. emitjmp(jmp_gt,greaterlabel);
  543. cg.a_jmp_always(exprasmlist,p^.statement);
  544. end;
  545. if assigned(p^.less) then
  546. gentreejmp(p^.less);
  547. if assigned(p^.greater) then
  548. gentreejmp(p^.greater);
  549. end;
  550. procedure genlinearcmplist(hp : pcaserecord);
  551. var
  552. first : boolean;
  553. last : TConstExprInt;
  554. procedure genitem(t : pcaserecord);
  555. var
  556. l1 : tasmlabel;
  557. begin
  558. if assigned(t^.less) then
  559. genitem(t^.less);
  560. if t^._low=t^._high then
  561. begin
  562. if opsize=S_Q then
  563. begin
  564. getlabel(l1);
  565. emit_const_reg(A_CMP,S_L,longint(hi(int64(t^._low))),hregister2);
  566. emitjmp(C_NZ,l1);
  567. emit_const_reg(A_CMP,S_L,longint(lo(int64(t^._low))),hregister);
  568. emitjmp(C_Z,t^.statement);
  569. cg.a_label(exprasmlist,l1);
  570. end
  571. else
  572. begin
  573. emit_const_reg(A_CMP,opsize,longint(t^._low),hregister);
  574. emitjmp(C_Z,t^.statement);
  575. last:=t^._low;
  576. end;
  577. end
  578. else
  579. begin
  580. { if there is no unused label between the last and the }
  581. { present label then the lower limit can be checked }
  582. { immediately. else check the range in between: }
  583. if first or (t^._low-last>1) then
  584. begin
  585. if opsize=S_Q then
  586. begin
  587. getlabel(l1);
  588. emit_const_reg(A_CMP,S_L,longint(hi(int64(t^._low))),hregister2);
  589. emitjmp(jmp_le,elselabel);
  590. emitjmp(jmp_gt,l1);
  591. emit_const_reg(A_CMP,S_L,longint(lo(int64(t^._low))),hregister);
  592. { the comparisation of the low dword must be always unsigned! }
  593. emitjmp(C_B,elselabel);
  594. cg.a_label(exprasmlist,l1);
  595. end
  596. else
  597. begin
  598. emit_const_reg(A_CMP,opsize,longint(t^._low),hregister);
  599. emitjmp(jmp_le,elselabel);
  600. end;
  601. end;
  602. if opsize=S_Q then
  603. begin
  604. getlabel(l1);
  605. emit_const_reg(A_CMP,S_L,longint(hi(int64(t^._high))),hregister2);
  606. emitjmp(jmp_le,t^.statement);
  607. emitjmp(jmp_gt,l1);
  608. emit_const_reg(A_CMP,S_L,longint(lo(int64(t^._high))),hregister);
  609. { the comparisation of the low dword must be always unsigned! }
  610. emitjmp(C_BE,t^.statement);
  611. cg.a_label(exprasmlist,l1);
  612. end
  613. else
  614. begin
  615. emit_const_reg(A_CMP,opsize,longint(t^._high),hregister);
  616. emitjmp(jmp_lee,t^.statement);
  617. end;
  618. last:=t^._high;
  619. end;
  620. first:=false;
  621. if assigned(t^.greater) then
  622. genitem(t^.greater);
  623. end;
  624. begin
  625. last:=0;
  626. first:=true;
  627. genitem(hp);
  628. cg.a_jmp_always(exprasmlist,elselabel);
  629. end;
  630. procedure genlinearlist(hp : pcaserecord);
  631. var
  632. first : boolean;
  633. last : TConstExprInt;
  634. {helplabel : longint;}
  635. procedure genitem(t : pcaserecord);
  636. procedure gensub(value:longint);
  637. begin
  638. if value=1 then
  639. emit_reg(A_DEC,opsize,hregister)
  640. else
  641. emit_const_reg(A_SUB,opsize,value,hregister);
  642. end;
  643. begin
  644. if assigned(t^.less) then
  645. genitem(t^.less);
  646. { need we to test the first value }
  647. if first and (t^._low>get_min_value(left.resulttype.def)) then
  648. begin
  649. emit_const_reg(A_CMP,opsize,longint(t^._low),hregister);
  650. emitjmp(jmp_le,elselabel);
  651. end;
  652. if t^._low=t^._high then
  653. begin
  654. if t^._low-last=0 then
  655. emit_reg_reg(A_OR,opsize,hregister,hregister)
  656. else
  657. gensub(longint(t^._low-last));
  658. last:=t^._low;
  659. emitjmp(C_Z,t^.statement);
  660. end
  661. else
  662. begin
  663. { it begins with the smallest label, if the value }
  664. { is even smaller then jump immediately to the }
  665. { ELSE-label }
  666. if first then
  667. begin
  668. { have we to ajust the first value ? }
  669. if (t^._low>get_min_value(left.resulttype.def)) then
  670. gensub(longint(t^._low));
  671. end
  672. else
  673. begin
  674. { if there is no unused label between the last and the }
  675. { present label then the lower limit can be checked }
  676. { immediately. else check the range in between: }
  677. { note: you can't use gensub() here because dec doesn't }
  678. { change the carry flag (needed for jmp_lxx) (JM) }
  679. emit_const_reg(A_SUB,opsize,longint(t^._low-last),hregister);
  680. emitjmp(jmp_le,elselabel);
  681. end;
  682. emit_const_reg(A_SUB,opsize,longint(t^._high-t^._low),hregister);
  683. emitjmp(jmp_lee,t^.statement);
  684. last:=t^._high;
  685. end;
  686. first:=false;
  687. if assigned(t^.greater) then
  688. genitem(t^.greater);
  689. end;
  690. begin
  691. { do we need to generate cmps? }
  692. if (with_sign and (min_label<0)) then
  693. genlinearcmplist(hp)
  694. else
  695. begin
  696. last:=0;
  697. first:=true;
  698. genitem(hp);
  699. cg.a_jmp_always(exprasmlist,elselabel);
  700. end;
  701. end;
  702. procedure genjumptable(hp : pcaserecord;min_,max_ : longint);
  703. var
  704. table : tasmlabel;
  705. last : TConstExprInt;
  706. href : treference;
  707. procedure genitem(t : pcaserecord);
  708. var
  709. i : longint;
  710. begin
  711. if assigned(t^.less) then
  712. genitem(t^.less);
  713. { fill possible hole }
  714. for i:=last+1 to t^._low-1 do
  715. jumpSegment.concat(Tai_const_symbol.Create(elselabel));
  716. for i:=t^._low to t^._high do
  717. jumpSegment.concat(Tai_const_symbol.Create(t^.statement));
  718. last:=t^._high;
  719. if assigned(t^.greater) then
  720. genitem(t^.greater);
  721. end;
  722. begin
  723. if not(jumptable_no_range) then
  724. begin
  725. emit_const_reg(A_CMP,opsize,longint(min_),hregister);
  726. { case expr less than min_ => goto elselabel }
  727. emitjmp(jmp_le,elselabel);
  728. emit_const_reg(A_CMP,opsize,longint(max_),hregister);
  729. emitjmp(jmp_gt,elselabel);
  730. end;
  731. getlabel(table);
  732. { extend with sign }
  733. if opsize=S_W then
  734. begin
  735. if with_sign then
  736. emit_reg_reg(A_MOVSX,S_WL,hregister,
  737. rg.makeregsize(hregister,OS_INT))
  738. else
  739. emit_reg_reg(A_MOVZX,S_WL,hregister,
  740. rg.makeregsize(hregister,OS_INT));
  741. hregister:=rg.makeregsize(hregister,OS_INT);
  742. end
  743. else if opsize=S_B then
  744. begin
  745. if with_sign then
  746. emit_reg_reg(A_MOVSX,S_BL,hregister,
  747. rg.makeregsize(hregister,OS_INT))
  748. else
  749. emit_reg_reg(A_MOVZX,S_BL,hregister,
  750. rg.makeregsize(hregister,OS_INT));
  751. hregister:=rg.makeregsize(hregister,OS_INT);
  752. end;
  753. reference_reset_symbol(href,table,0);
  754. href.offset:=(-longint(min_))*4;
  755. href.index:=hregister;
  756. href.scalefactor:=4;
  757. emit_ref(A_JMP,S_NO,href);
  758. { !!!!! generate tables
  759. if not(cs_littlesize in aktlocalswitches) then
  760. jumpSegment.concat(Taicpu.Op_const(A_ALIGN,S_NO,4));
  761. }
  762. jumpSegment.concat(Tai_label.Create(table));
  763. last:=min_;
  764. genitem(hp);
  765. { !!!!!!!
  766. if not(cs_littlesize in aktlocalswitches) then
  767. emit_const(A_ALIGN,S_NO,4);
  768. }
  769. end;
  770. var
  771. lv,hv,
  772. max_label: tconstexprint;
  773. labels : longint;
  774. max_linear_list : longint;
  775. otl, ofl: tasmlabel;
  776. isjump : boolean;
  777. {$ifdef Delphi}
  778. dist : cardinal;
  779. {$else Delphi}
  780. dist : dword;
  781. {$endif Delphi}
  782. begin
  783. getlabel(endlabel);
  784. getlabel(elselabel);
  785. if (cs_create_smart in aktmoduleswitches) then
  786. jumpsegment:=procinfo^.aktlocaldata
  787. else
  788. jumpsegment:=datasegment;
  789. with_sign:=is_signed(left.resulttype.def);
  790. if with_sign then
  791. begin
  792. jmp_gt:=C_G;
  793. jmp_le:=C_L;
  794. jmp_lee:=C_LE;
  795. end
  796. else
  797. begin
  798. jmp_gt:=C_A;
  799. jmp_le:=C_B;
  800. jmp_lee:=C_BE;
  801. end;
  802. rg.cleartempgen;
  803. { save current truelabel and falselabel }
  804. isjump:=false;
  805. if left.location.loc=LOC_JUMP then
  806. begin
  807. otl:=truelabel;
  808. getlabel(truelabel);
  809. ofl:=falselabel;
  810. getlabel(falselabel);
  811. isjump:=true;
  812. end;
  813. secondpass(left);
  814. { determines the size of the operand }
  815. opsize:=bytes2Sxx[left.resulttype.def.size];
  816. { copy the case expression to a register }
  817. location_force_reg(exprasmlist,left.location,def_cgsize(left.resulttype.def),false);
  818. if opsize=S_Q then
  819. begin
  820. hregister:=left.location.registerlow;
  821. hregister2:=left.location.registerhigh;
  822. end
  823. else
  824. hregister:=left.location.register;
  825. if isjump then
  826. begin
  827. truelabel:=otl;
  828. falselabel:=ofl;
  829. end;
  830. { we need the min_label always to choose between }
  831. { cmps and subs/decs }
  832. min_label:=case_get_min(nodes);
  833. load_all_regvars(exprasmlist);
  834. { now generate the jumps }
  835. if opsize=S_Q then
  836. genlinearcmplist(nodes)
  837. else
  838. begin
  839. if cs_optimize in aktglobalswitches then
  840. begin
  841. { procedures are empirically passed on }
  842. { consumption can also be calculated }
  843. { but does it pay on the different }
  844. { processors? }
  845. { moreover can the size only be appro- }
  846. { ximated as it is not known if rel8, }
  847. { rel16 or rel32 jumps are used }
  848. max_label:=case_get_max(nodes);
  849. labels:=case_count_labels(nodes);
  850. { can we omit the range check of the jump table ? }
  851. getrange(left.resulttype.def,lv,hv);
  852. jumptable_no_range:=(lv=min_label) and (hv=max_label);
  853. { hack a little bit, because the range can be greater }
  854. { than the positive range of a longint }
  855. if (min_label<0) and (max_label>0) then
  856. begin
  857. {$ifdef Delphi}
  858. if min_label=longint($80000000) then
  859. dist:=Cardinal(max_label)+Cardinal($80000000)
  860. else
  861. dist:=Cardinal(max_label)+Cardinal(-min_label)
  862. {$else Delphi}
  863. if min_label=$80000000 then
  864. dist:=dword(max_label)+dword($80000000)
  865. else
  866. dist:=dword(max_label)+dword(-min_label)
  867. {$endif Delphi}
  868. end
  869. else
  870. dist:=max_label-min_label;
  871. { optimize for size ? }
  872. if cs_littlesize in aktglobalswitches then
  873. begin
  874. if (labels<=2) or
  875. ((max_label-min_label)<0) or
  876. ((max_label-min_label)>3*labels) then
  877. { a linear list is always smaller than a jump tree }
  878. genlinearlist(nodes)
  879. else
  880. { if the labels less or more a continuum then }
  881. genjumptable(nodes,min_label,max_label);
  882. end
  883. else
  884. begin
  885. if jumptable_no_range then
  886. max_linear_list:=4
  887. else
  888. max_linear_list:=2;
  889. { a jump table crashes the pipeline! }
  890. if aktoptprocessor=Class386 then
  891. inc(max_linear_list,3);
  892. if aktoptprocessor=ClassP5 then
  893. inc(max_linear_list,6);
  894. if aktoptprocessor>=ClassP6 then
  895. inc(max_linear_list,9);
  896. if (labels<=max_linear_list) then
  897. genlinearlist(nodes)
  898. else
  899. begin
  900. if (dist>4*cardinal(labels)) then
  901. begin
  902. if labels>16 then
  903. gentreejmp(nodes)
  904. else
  905. genlinearlist(nodes);
  906. end
  907. else
  908. genjumptable(nodes,min_label,max_label);
  909. end;
  910. end;
  911. end
  912. else
  913. { it's always not bad }
  914. genlinearlist(nodes);
  915. end;
  916. rg.ungetregister(exprasmlist,hregister);
  917. { now generate the instructions }
  918. hp:=right;
  919. while assigned(hp) do
  920. begin
  921. rg.cleartempgen;
  922. secondpass(tbinarynode(hp).right);
  923. { don't come back to case line }
  924. aktfilepos:=exprasmList.getlasttaifilepos^;
  925. load_all_regvars(exprasmlist);
  926. cg.a_jmp_always(exprasmlist,endlabel);
  927. hp:=tbinarynode(hp).left;
  928. end;
  929. cg.a_label(exprasmlist,elselabel);
  930. { ...and the else block }
  931. if assigned(elseblock) then
  932. begin
  933. rg.cleartempgen;
  934. secondpass(elseblock);
  935. load_all_regvars(exprasmlist);
  936. end;
  937. cg.a_label(exprasmlist,endlabel);
  938. end;
  939. begin
  940. csetelementnode:=ti386setelementnode;
  941. cinnode:=ti386innode;
  942. ccasenode:=ti386casenode;
  943. end.
  944. {
  945. $Log$
  946. Revision 1.32 2002-07-01 18:46:33 peter
  947. * internal linker
  948. * reorganized aasm layer
  949. Revision 1.31 2002/05/18 13:34:25 peter
  950. * readded missing revisions
  951. Revision 1.30 2002/05/16 19:46:52 carl
  952. + defines.inc -> fpcdefs.inc to avoid conflicts if compiling by hand
  953. + try to fix temp allocation (still in ifdef)
  954. + generic constructor calls
  955. + start of tassembler / tmodulebase class cleanup
  956. Revision 1.28 2002/05/13 19:54:38 peter
  957. * removed n386ld and n386util units
  958. * maybe_save/maybe_restore added instead of the old maybe_push
  959. Revision 1.27 2002/05/12 16:53:17 peter
  960. * moved entry and exitcode to ncgutil and cgobj
  961. * foreach gets extra argument for passing local data to the
  962. iterator function
  963. * -CR checks also class typecasts at runtime by changing them
  964. into as
  965. * fixed compiler to cycle with the -CR option
  966. * fixed stabs with elf writer, finally the global variables can
  967. be watched
  968. * removed a lot of routines from cga unit and replaced them by
  969. calls to cgobj
  970. * u32bit-s32bit updates for and,or,xor nodes. When one element is
  971. u32bit then the other is typecasted also to u32bit without giving
  972. a rangecheck warning/error.
  973. * fixed pascal calling method with reversing also the high tree in
  974. the parast, detected by tcalcst3 test
  975. Revision 1.26 2002/04/25 20:16:40 peter
  976. * moved more routines from cga/n386util
  977. Revision 1.25 2002/04/21 19:02:07 peter
  978. * removed newn and disposen nodes, the code is now directly
  979. inlined from pexpr
  980. * -an option that will write the secondpass nodes to the .s file, this
  981. requires EXTDEBUG define to actually write the info
  982. * fixed various internal errors and crashes due recent code changes
  983. Revision 1.24 2002/04/21 15:37:26 carl
  984. * changeregsize -> rg.makeregsize
  985. Revision 1.23 2002/04/19 15:39:35 peter
  986. * removed some more routines from cga
  987. * moved location_force_reg/mem to ncgutil
  988. * moved arrayconstructnode secondpass to ncgld
  989. Revision 1.22 2002/04/15 19:44:21 peter
  990. * fixed stackcheck that would be called recursively when a stack
  991. error was found
  992. * generic changeregsize(reg,size) for i386 register resizing
  993. * removed some more routines from cga unit
  994. * fixed returnvalue handling
  995. * fixed default stacksize of linux and go32v2, 8kb was a bit small :-)
  996. Revision 1.21 2002/04/02 17:11:36 peter
  997. * tlocation,treference update
  998. * LOC_CONSTANT added for better constant handling
  999. * secondadd splitted in multiple routines
  1000. * location_force_reg added for loading a location to a register
  1001. of a specified size
  1002. * secondassignment parses now first the right and then the left node
  1003. (this is compatible with Kylix). This saves a lot of push/pop especially
  1004. with string operations
  1005. * adapted some routines to use the new cg methods
  1006. Revision 1.20 2002/03/31 20:26:39 jonas
  1007. + a_loadfpu_* and a_loadmm_* methods in tcg
  1008. * register allocation is now handled by a class and is mostly processor
  1009. independent (+rgobj.pas and i386/rgcpu.pas)
  1010. * temp allocation is now handled by a class (+tgobj.pas, -i386\tgcpu.pas)
  1011. * some small improvements and fixes to the optimizer
  1012. * some register allocation fixes
  1013. * some fpuvaroffset fixes in the unary minus node
  1014. * push/popusedregisters is now called rg.save/restoreusedregisters and
  1015. (for i386) uses temps instead of push/pop's when using -Op3 (that code is
  1016. also better optimizable)
  1017. * fixed and optimized register saving/restoring for new/dispose nodes
  1018. * LOC_FPU locations now also require their "register" field to be set to
  1019. R_ST, not R_ST0 (the latter is used for LOC_CFPUREGISTER locations only)
  1020. - list field removed of the tnode class because it's not used currently
  1021. and can cause hard-to-find bugs
  1022. }