{ Copyright (c) 2000-2002 by Florian Klaempfl Type checking and register allocation for set/case nodes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************** } unit nset; {$i fpcdefs.inc} interface uses cclasses, node,globtype,globals, aasmbase,aasmtai,symtype; type pcaselabel = ^tcaselabel; tcaselabel = record { range } _low, _high : TConstExprInt; { unique blockid } blockid : longint; { left and right tree node } less, greater : pcaselabel; end; pcaseblock = ^tcaseblock; tcaseblock = record { label (only used in pass_2) } blocklabel : tasmlabel; { instructions } statement : tnode; end; tsetelementnode = class(tbinarynode) constructor create(l,r : tnode);virtual; function det_resulttype:tnode;override; function pass_1 : tnode;override; end; tsetelementnodeclass = class of tsetelementnode; tinnode = class(tbinopnode) constructor create(l,r : tnode);virtual; function det_resulttype:tnode;override; function pass_1 : tnode;override; end; tinnodeclass = class of tinnode; trangenode = class(tbinarynode) constructor create(l,r : tnode);virtual; function det_resulttype:tnode;override; function pass_1 : tnode;override; end; trangenodeclass = class of trangenode; tcasenode = class(tunarynode) labels : pcaselabel; blocks : tlist; elseblock : tnode; constructor create(l:tnode);virtual; destructor destroy;override; constructor ppuload(t:tnodetype;ppufile:tcompilerppufile);override; procedure ppuwrite(ppufile:tcompilerppufile);override; procedure buildderefimpl;override; procedure derefimpl;override; function _getcopy : tnode;override; procedure insertintolist(l : tnodelist);override; function det_resulttype:tnode;override; function pass_1 : tnode;override; function docompare(p: tnode): boolean; override; procedure addlabel(blockid:longint;l,h : TConstExprInt); procedure addblock(blockid:longint;instr:tnode); procedure addelseblock(instr:tnode); end; tcasenodeclass = class of tcasenode; var csetelementnode : tsetelementnodeclass; cinnode : tinnodeclass; crangenode : trangenodeclass; ccasenode : tcasenodeclass; { counts the labels } function case_count_labels(root : pcaselabel) : longint; { searches the highest label } {$ifdef int64funcresok} function case_get_max(root : pcaselabel) : tconstexprint; {$else int64funcresok} function case_get_max(root : pcaselabel) : longint; {$endif int64funcresok} { searches the lowest label } {$ifdef int64funcresok} function case_get_min(root : pcaselabel) : tconstexprint; {$else int64funcresok} function case_get_min(root : pcaselabel) : longint; {$endif int64funcresok} implementation uses systems, verbose, symconst,symdef,symsym,symtable,defutil,defcmp, htypechk,pass_1, nbas,ncnv,ncon,nld,cgobj,cgbase; {***************************************************************************** TSETELEMENTNODE *****************************************************************************} constructor tsetelementnode.create(l,r : tnode); begin inherited create(setelementn,l,r); end; function tsetelementnode.det_resulttype:tnode; begin result:=nil; resulttypepass(left); if assigned(right) then resulttypepass(right); set_varstate(left,vs_read,[vsf_must_be_valid]); if codegenerror then exit; resulttype:=left.resulttype; end; function tsetelementnode.pass_1 : tnode; begin result:=nil; firstpass(left); if assigned(right) then firstpass(right); if codegenerror then exit; expectloc:=left.expectloc; calcregisters(self,0,0,0); end; {***************************************************************************** TINNODE *****************************************************************************} constructor tinnode.create(l,r : tnode); begin inherited create(inn,l,r); end; function tinnode.det_resulttype:tnode; var t : tnode; pst : pconstset; function createsetconst(psd : tsetdef) : pconstset; var pcs : pconstset; pes : tenumsym; i : longint; begin new(pcs); case psd.elementtype.def.deftype of enumdef : begin pes:=tenumsym(tenumdef(psd.elementtype.def).firstenum); while assigned(pes) do begin include(pcs^,pes.value); pes:=pes.nextenum; end; end; orddef : begin for i:=torddef(psd.elementtype.def).low to torddef(psd.elementtype.def).high do include(pcs^,i); end; end; createsetconst:=pcs; end; begin result:=nil; resulttype:=booltype; resulttypepass(right); set_varstate(right,vs_read,[vsf_must_be_valid]); if codegenerror then exit; { Convert array constructor first to set } if is_array_constructor(right.resulttype.def) then begin arrayconstructor_to_set(right); firstpass(right); if codegenerror then exit; end; if right.resulttype.def.deftype<>setdef then CGMessage(sym_e_set_expected); if (right.nodetype=typen) then begin { we need to create a setconstn } pst:=createsetconst(tsetdef(ttypenode(right).resulttype.def)); t:=csetconstnode.create(pst,ttypenode(right).resulttype); dispose(pst); right.free; right:=t; end; resulttypepass(left); set_varstate(left,vs_read,[vsf_must_be_valid]); if codegenerror then exit; if not assigned(left.resulttype.def) then internalerror(20021126); if (m_fpc in aktmodeswitches) then begin { insert a hint that a range check error might occur on non-byte elements with the in operator. } if ( (left.resulttype.def.deftype = orddef) and not (torddef(left.resulttype.def).typ in [s8bit,u8bit,uchar,bool8bit]) ) or ( (left.resulttype.def.deftype = enumdef) and (tenumdef(left.resulttype.def).maxval > 255) ) then CGMessage(type_h_in_range_check); { type conversion/check } if assigned(tsetdef(right.resulttype.def).elementtype.def) then inserttypeconv(left,tsetdef(right.resulttype.def).elementtype); end else begin { insert explicit type conversion/check } if assigned(tsetdef(right.resulttype.def).elementtype.def) then inserttypeconv_internal(left,tsetdef(right.resulttype.def).elementtype); end; { empty set then return false } if not assigned(tsetdef(right.resulttype.def).elementtype.def) or ((right.nodetype = setconstn) and (tnormalset(tsetconstnode(right).value_set^) = [])) then begin t:=cordconstnode.create(0,booltype,false); resulttypepass(t); result:=t; exit; end; { constant evaluation } if (left.nodetype=ordconstn) and (right.nodetype=setconstn) then begin t:=cordconstnode.create(byte(tordconstnode(left).value in Tsetconstnode(right).value_set^), booltype,true); resulttypepass(t); result:=t; exit; end; end; { Warning : This is the first pass for the generic version } { the only difference is mainly the result location which } { is changed, compared to the i386 version. } { ALSO REGISTER ALLOC IS WRONG? } function tinnode.pass_1 : tnode; begin result:=nil; expectloc:=LOC_REGISTER; firstpass(right); firstpass(left); if codegenerror then exit; left_right_max; if tsetdef(right.resulttype.def).settype<>smallset then begin if registersint < 3 then registersint := 3; end else begin { a smallset needs maybe an misc. register } if (left.nodetype<>ordconstn) and not(right.expectloc in [LOC_CREGISTER,LOC_REGISTER]) and (right.registersint<1) then inc(registersint); end; end; {***************************************************************************** TRANGENODE *****************************************************************************} constructor trangenode.create(l,r : tnode); begin inherited create(rangen,l,r); end; function trangenode.det_resulttype : tnode; begin result:=nil; resulttypepass(left); resulttypepass(right); set_varstate(left,vs_read,[vsf_must_be_valid]); set_varstate(right,vs_read,[vsf_must_be_valid]); if codegenerror then exit; { both types must be compatible } if compare_defs(left.resulttype.def,right.resulttype.def,left.nodetype)=te_incompatible then IncompatibleTypes(left.resulttype.def,right.resulttype.def); { Check if only when its a constant set } if (left.nodetype=ordconstn) and (right.nodetype=ordconstn) then begin { upper limit must be greater or equal than lower limit } if (tordconstnode(left).value>tordconstnode(right).value) and ((tordconstnode(left).value<0) or (tordconstnode(right).value>=0)) then CGMessage(parser_e_upper_lower_than_lower); end; resulttype:=left.resulttype; end; function trangenode.pass_1 : tnode; begin result:=nil; firstpass(left); firstpass(right); if codegenerror then exit; left_right_max; expectloc:=left.expectloc; end; {***************************************************************************** Case Helpers *****************************************************************************} function case_count_labels(root : pcaselabel) : longint; var _l : longint; procedure count(p : pcaselabel); begin inc(_l); if assigned(p^.less) then count(p^.less); if assigned(p^.greater) then count(p^.greater); end; begin _l:=0; count(root); case_count_labels:=_l; end; {$ifdef int64funcresok} function case_get_max(root : pcaselabel) : tconstexprint; {$else int64funcresok} function case_get_max(root : pcaselabel) : longint; {$endif int64funcresok} var hp : pcaselabel; begin hp:=root; while assigned(hp^.greater) do hp:=hp^.greater; case_get_max:=hp^._high; end; {$ifdef int64funcresok} function case_get_min(root : pcaselabel) : tconstexprint; {$else int64funcresok} function case_get_min(root : pcaselabel) : longint; {$endif int64funcresok} var hp : pcaselabel; begin hp:=root; while assigned(hp^.less) do hp:=hp^.less; case_get_min:=hp^._low; end; procedure deletecaselabels(p : pcaselabel); begin if assigned(p^.greater) then deletecaselabels(p^.greater); if assigned(p^.less) then deletecaselabels(p^.less); dispose(p); end; function copycaselabel(p : pcaselabel) : pcaselabel; var n : pcaselabel; begin new(n); n^:=p^; if assigned(p^.greater) then n^.greater:=copycaselabel(p^.greater); if assigned(p^.less) then n^.less:=copycaselabel(p^.less); copycaselabel:=n; end; procedure ppuwritecaselabel(ppufile:tcompilerppufile;p : pcaselabel); var b : byte; begin ppufile.putexprint(p^._low); ppufile.putexprint(p^._high); ppufile.putlongint(p^.blockid); b:=0; if assigned(p^.greater) then b:=b or 1; if assigned(p^.less) then b:=b or 2; ppufile.putbyte(b); if assigned(p^.greater) then ppuwritecaselabel(ppufile,p^.greater); if assigned(p^.less) then ppuwritecaselabel(ppufile,p^.less); end; function ppuloadcaselabel(ppufile:tcompilerppufile):pcaselabel; var b : byte; p : pcaselabel; begin new(p); p^._low:=ppufile.getexprint; p^._high:=ppufile.getexprint; p^.blockid:=ppufile.getlongint; b:=ppufile.getbyte; if (b and 1)=1 then p^.greater:=ppuloadcaselabel(ppufile) else p^.greater:=nil; if (b and 2)=2 then p^.less:=ppuloadcaselabel(ppufile) else p^.less:=nil; ppuloadcaselabel:=p; end; {***************************************************************************** TCASENODE *****************************************************************************} constructor tcasenode.create(l:tnode); begin inherited create(casen,l); labels:=nil; blocks:=tlist.create; elseblock:=nil; end; destructor tcasenode.destroy; var i : longint; hp : pcaseblock; begin elseblock.free; deletecaselabels(labels); for i:=0 to blocks.count-1 do begin pcaseblock(blocks[i])^.statement.free; hp:=pcaseblock(blocks[i]); dispose(hp); end; inherited destroy; end; constructor tcasenode.ppuload(t:tnodetype;ppufile:tcompilerppufile); var cnt,i : longint; begin inherited ppuload(t,ppufile); elseblock:=ppuloadnode(ppufile); cnt:=ppufile.getlongint(); blocks:=tlist.create; for i:=0 to cnt-1 do addblock(i,ppuloadnode(ppufile)); labels:=ppuloadcaselabel(ppufile); end; procedure tcasenode.ppuwrite(ppufile:tcompilerppufile); var i : longint; begin inherited ppuwrite(ppufile); ppuwritenode(ppufile,elseblock); ppufile.putlongint(blocks.count); for i:=0 to blocks.count-1 do ppuwritenode(ppufile,pcaseblock(blocks[i])^.statement); ppuwritecaselabel(ppufile,labels); end; procedure tcasenode.buildderefimpl; var i : integer; begin inherited buildderefimpl; if assigned(elseblock) then elseblock.buildderefimpl; for i:=0 to blocks.count-1 do pcaseblock(blocks[i])^.statement.buildderefimpl; end; procedure tcasenode.derefimpl; var i : integer; begin inherited derefimpl; if assigned(elseblock) then elseblock.derefimpl; for i:=0 to blocks.count-1 do pcaseblock(blocks[i])^.statement.derefimpl; end; function tcasenode.det_resulttype : tnode; begin result:=nil; resulttype:=voidtype; end; function tcasenode.pass_1 : tnode; var old_t_times : longint; hp : tnode; i : integer; begin result:=nil; expectloc:=LOC_VOID; { evalutes the case expression } firstpass(left); set_varstate(left,vs_read,[vsf_must_be_valid]); if codegenerror then exit; registersint:=left.registersint; registersfpu:=left.registersfpu; {$ifdef SUPPORT_MMX} registersmmx:=left.registersmmx; {$endif SUPPORT_MMX} { walk through all instructions } { estimates the repeat of each instruction } old_t_times:=cg.t_times; if not(cs_littlesize in aktglobalswitches) then begin cg.t_times:=cg.t_times div case_count_labels(labels); if cg.t_times<1 then cg.t_times:=1; end; { first case } for i:=0 to blocks.count-1 do begin firstpass(pcaseblock(blocks[i])^.statement); { searchs max registers } hp:=pcaseblock(blocks[i])^.statement; if hp.registersint>registersint then registersint:=hp.registersint; if hp.registersfpu>registersfpu then registersfpu:=hp.registersfpu; {$ifdef SUPPORT_MMX} if hp.registersmmx>registersmmx then registersmmx:=hp.registersmmx; {$endif SUPPORT_MMX} end; { may be handle else tree } if assigned(elseblock) then begin firstpass(elseblock); if registersintb2.count then exit; for i:=0 to b1.count-1 do begin if not pcaseblock(b1[i])^.statement.isequal(pcaseblock(b2[i])^.statement) then exit; end; result:=true; end; function tcasenode.docompare(p: tnode): boolean; begin result := inherited docompare(p) and caselabelsequal(labels,tcasenode(p).labels) and caseblocksequal(blocks,tcasenode(p).blocks) and elseblock.isequal(tcasenode(p).elseblock); end; procedure tcasenode.addblock(blockid:longint;instr:tnode); var hcaseblock : pcaseblock; begin new(hcaseblock); fillchar(hcaseblock^,sizeof(hcaseblock^),0); hcaseblock^.statement:=instr; if blockid>=blocks.count then blocks.count:=blockid+1; blocks[blockid]:=hcaseblock; end; procedure tcasenode.addelseblock(instr:tnode); begin elseblock:=instr; end; procedure tcasenode.addlabel(blockid:longint;l,h : TConstExprInt); var hcaselabel : pcaselabel; function insertlabel(var p : pcaselabel):pcaselabel; begin if p=nil then begin p:=hcaselabel; result:=p; end else if (p^._low>hcaselabel^._low) and (p^._low>hcaselabel^._high) then begin if (hcaselabel^.blockid = p^.blockid) and (p^._low = hcaselabel^._high + 1) then begin p^._low := hcaselabel^._low; dispose(hcaselabel); result:=p; end else result:=insertlabel(p^.less) end else if (p^._high