n386set.pas 45 KB

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