n386set.pas 45 KB

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