diff --git a/compiler/ncgflw.pas b/compiler/ncgflw.pas index 29c0eb1d29..3b0073355b 100644 --- a/compiler/ncgflw.pas +++ b/compiler/ncgflw.pas @@ -42,10 +42,7 @@ interface end; tcgfornode = class(tfornode) - usedregvars: tusedregvars; - procedure pass_generate_code;override; - procedure sync_regvars(checkusedregvars: boolean); end; tcgexitnode = class(texitnode) @@ -186,15 +183,15 @@ implementation hlcg.a_label(current_asmdata.CurrAsmList,lcont); if lnf_checknegate in loopflags then - begin + begin truelabel:=lbreak; falselabel:=lloop; - end + end else - begin + begin truelabel:=lloop; falselabel:=lbreak; - end; + end; secondpass(left); hlcg.maketojumpboollabels(current_asmdata.CurrAsmList,left,truelabel,falselabel); @@ -379,435 +376,10 @@ implementation SecondFor *****************************************************************************} - procedure tcgfornode.sync_regvars(checkusedregvars: boolean); - begin - if (cs_opt_regvar in current_settings.optimizerswitches) and - not(pi_has_label in current_procinfo.flags) then - begin - if checkusedregvars then - begin - usedregvars.intregvars.init; - usedregvars.addrregvars.init; - usedregvars.fpuregvars.init; - usedregvars.mmregvars.init; - - { We have to synchronise the loop variable and loop body. } - { The loop end is not necessary, unless it's a register } - { variable. The start value also doesn't matter. } - - { loop var } - get_used_regvars(left,usedregvars); - { loop body } - get_used_regvars(t2,usedregvars); - { end value can't be a regvar, but may be a temp in register } - get_used_regvars(t1,usedregvars); - - gen_sync_regvars(current_asmdata.CurrAsmList,usedregvars); - end - else - begin - gen_sync_regvars(current_asmdata.CurrAsmList,usedregvars); - usedregvars.intregvars.done; - usedregvars.addrregvars.done; - usedregvars.fpuregvars.done; - usedregvars.mmregvars.done; - end; - end; - end; - - procedure tcgfornode.pass_generate_code; - var - l3,oldclabel,oldblabel : tasmlabel; - temptovalue : boolean; - hop : topcg; - hcond : topcmp; - opsize : tcgsize; - count_var_is_signed,do_loopvar_at_end : boolean; - cmp_const:Tconstexprint; - oldflowcontrol : tflowcontrol; - oldexecutionweight : longint; begin - location_reset(location,LOC_VOID,OS_NO); - - oldclabel:=current_procinfo.CurrContinueLabel; - oldblabel:=current_procinfo.CurrBreakLabel; - current_asmdata.getjumplabel(current_procinfo.CurrContinueLabel); - current_asmdata.getjumplabel(current_procinfo.CurrBreakLabel); - current_asmdata.getjumplabel(l3); - - { only calculate reference } - opsize := def_cgsize(left.resultdef); - count_var_is_signed:=is_signed(left.resultdef); - - { first set the to value - because the count var can be in the expression ! } - do_loopvar_at_end:=(lnf_dont_mind_loopvar_on_exit in loopflags) - { if the loop is unrolled and there is a jump into the loop, - then we can't do the trick with incrementing the loop var only at the - end - } - and not(assigned(entrylabel)); - - secondpass(t1); - if t1.location.loc in [LOC_FLAGS,LOC_JUMP] then - hlcg.location_force_reg(current_asmdata.CurrAsmList,t1.location,t1.resultdef,t1.resultdef,false); - { calculate pointer value and check if changeable and if so } - { load into temporary variable } - if t1.nodetype<>ordconstn then - begin - do_loopvar_at_end:=false; - temptovalue:=true; - end - else - temptovalue:=false; - - { load loopvar, prefer loopvar being a register variable } - oldexecutionweight:=cg.executionweight; - inc(cg.executionweight,8); - secondpass(left); - cg.executionweight:=oldexecutionweight; - - { load from value } - secondpass(right); - if right.location.loc in [LOC_FLAGS,LOC_JUMP] then - hlcg.location_force_reg(current_asmdata.CurrAsmList,right.location,right.resultdef,right.resultdef,false); - - hlcg.maybe_change_load_node_reg(current_asmdata.CurrAsmList,left,false); - oldflowcontrol:=flowcontrol; - include(flowcontrol,fc_inflowcontrol); - exclude(flowcontrol,fc_unwind_loop); - { produce start assignment } - case left.location.loc of - LOC_REFERENCE, - LOC_CREFERENCE : - hlcg.a_load_loc_ref(current_asmdata.CurrAsmList,right.resultdef,left.resultdef,right.location,left.location.reference); - LOC_REGISTER, - LOC_CREGISTER: - hlcg.a_load_loc_reg(current_asmdata.CurrAsmList,right.resultdef,left.resultdef,right.location,left.location.register); - LOC_SUBSETREG, - LOC_CSUBSETREG : - hlcg.a_load_loc_subsetreg(current_asmdata.CurrAsmList,right.resultdef,left.resultdef,right.location,left.location.sreg); - else - internalerror(200501311); - end; - - if lnf_backward in loopflags then - if count_var_is_signed then - hcond:=OC_LT - else - hcond:=OC_B - else - if count_var_is_signed then - hcond:=OC_GT - else - hcond:=OC_A; - - sync_regvars(true); -{$ifdef OLDREGVARS} - load_all_regvars(current_asmdata.CurrAsmList); -{$endif OLDREGVARS} - - if temptovalue then - begin - case t1.location.loc of - LOC_REGISTER,LOC_CREGISTER: - hlcg.a_cmp_reg_loc_label(current_asmdata.CurrAsmList,left.resultdef,hcond, - t1.location.register,left.location,current_procinfo.CurrBreakLabel); - LOC_REFERENCE,LOC_CREFERENCE: - hlcg.a_cmp_ref_loc_label(current_asmdata.CurrAsmList,left.resultdef,hcond, - t1.location.reference,left.location,current_procinfo.CurrBreakLabel); - else - InternalError(2013051601); - end; - end - else - begin - if lnf_testatbegin in loopflags then - begin - hlcg.a_cmp_const_loc_label(current_asmdata.CurrAsmList,left.resultdef,hcond, - tordconstnode(t1).value.svalue, - left.location,current_procinfo.CurrBreakLabel); - end; - end; - - {If the loopvar doesn't mind on exit, we avoid this ugly - dec instruction and do the loopvar inc/dec after the loop - body.} - if not do_loopvar_at_end then - begin - if lnf_backward in loopflags then - hop:=OP_ADD - else - hop:=OP_SUB; - hlcg.a_op_const_loc(current_asmdata.CurrAsmList,hop,left.resultdef,1,left.location); - end; - - if assigned(entrylabel) then - hlcg.a_jmp_always(current_asmdata.CurrAsmList,tcglabelnode(entrylabel).getasmlabel); - - { align loop target } - if not(cs_opt_size in current_settings.optimizerswitches) then - current_asmdata.CurrAsmList.concat(Tai_align.Create(current_settings.alignment.loopalign)); - hlcg.a_label(current_asmdata.CurrAsmList,l3); - - {If the loopvar doesn't mind on exit, we avoid the loopvar inc/dec - after the loop body instead of here.} - if not do_loopvar_at_end then - begin - { according to count direction DEC or INC... } - if lnf_backward in loopflags then - hop:=OP_SUB - else - hop:=OP_ADD; - hlcg.a_op_const_loc(current_asmdata.CurrAsmList,hop,left.resultdef,1,left.location); - end; - - if assigned(t2) then - begin - { Calc register weight } - oldexecutionweight:=cg.executionweight; - cg.executionweight:=cg.executionweight*8; - secondpass(t2); - cg.executionweight:=oldexecutionweight; -{$ifdef OLDREGVARS} - load_all_regvars(current_asmdata.CurrAsmList); -{$endif OLDREGVARS} - end; - - {If the loopvar doesn't mind on exit, we do the loopvar inc/dec - after the loop body instead of here.} - if do_loopvar_at_end then - begin - { according to count direction DEC or INC... } - if lnf_backward in loopflags then - hop:=OP_SUB - else - hop:=OP_ADD; - hlcg.a_op_const_loc(current_asmdata.CurrAsmList,hop,left.resultdef,1,left.location); - end; - - hlcg.a_label(current_asmdata.CurrAsmList,current_procinfo.CurrContinueLabel); - - if do_loopvar_at_end then - if lnf_backward in loopflags then - if count_var_is_signed then - hcond:=OC_GTE - else - hcond:=OC_AE - else - if count_var_is_signed then - hcond:=OC_LTE - else - hcond:=OC_BE - else - if lnf_backward in loopflags then - if count_var_is_signed then - hcond:=OC_GT - else - hcond:=OC_A - else - if count_var_is_signed then - hcond:=OC_LT - else - hcond:=OC_B; -{$ifdef OLDREGVARS} - load_all_regvars(current_asmdata.CurrAsmList); -{$endif OLDREGVARS} - - { produce comparison and the corresponding } - { jump } - if temptovalue then - begin - case t1.location.loc of - LOC_REGISTER,LOC_CREGISTER: - hlcg.a_cmp_reg_loc_label(current_asmdata.CurrAsmList,left.resultdef,hcond,t1.location.register, - left.location,l3); - LOC_REFERENCE,LOC_CREFERENCE: - hlcg.a_cmp_ref_loc_label(current_asmdata.CurrAsmList,left.resultdef,hcond,t1.location.reference, - left.location,l3); - else - InternalError(2013051602); - end; - end - else - begin - cmp_const:=Tordconstnode(t1).value; - if do_loopvar_at_end then - begin - {Watch out for wrap around 255 -> 0.} - {Ugly: This code is way to long... Use tables?} - case opsize of - OS_8: - begin - if lnf_backward in loopflags then - begin - if byte(cmp_const.svalue)=low(byte) then - begin - hcond:=OC_NE; - cmp_const:=high(byte); - end - end - else - begin - if byte(cmp_const.svalue)=high(byte) then - begin - hcond:=OC_NE; - cmp_const:=low(byte); - end - end - end; - OS_16: - begin - if lnf_backward in loopflags then - begin - if word(cmp_const.svalue)=high(word) then - begin - hcond:=OC_NE; - cmp_const:=low(word); - end - end - else - begin - if word(cmp_const.svalue)=low(word) then - begin - hcond:=OC_NE; - cmp_const:=high(word); - end - end - end; - OS_32: - begin - if lnf_backward in loopflags then - begin - if cardinal(cmp_const.svalue)=high(cardinal) then - begin - hcond:=OC_NE; - cmp_const:=low(cardinal); - end - end - else - begin - if cardinal(cmp_const.svalue)=low(cardinal) then - begin - hcond:=OC_NE; - cmp_const:=high(cardinal); - end - end - end; - OS_64: - begin - if lnf_backward in loopflags then - begin - if qword(cmp_const.uvalue)=high(qword) then - begin - hcond:=OC_NE; - cmp_const:=low(qword); - end - end - else - begin - if qword(cmp_const.uvalue)=low(qword) then - begin - hcond:=OC_NE; - cmp_const:=high(qword); - end - end - end; - OS_S8: - begin - if lnf_backward in loopflags then - begin - if shortint(cmp_const.svalue)=low(shortint) then - begin - hcond:=OC_NE; - cmp_const:=high(shortint); - end - end - else - begin - if shortint(cmp_const.svalue)=high(shortint) then - begin - hcond:=OC_NE; - cmp_const:=int64(low(shortint)); - end - end - end; - OS_S16: - begin - if lnf_backward in loopflags then - begin - if integer(cmp_const.svalue)=high(smallint) then - begin - hcond:=OC_NE; - cmp_const:=int64(low(smallint)); - end - end - else - begin - if integer(cmp_const.svalue)=low(smallint) then - begin - hcond:=OC_NE; - cmp_const:=int64(high(smallint)); - end - end - end; - OS_S32: - begin - if lnf_backward in loopflags then - begin - if longint(cmp_const.svalue)=high(longint) then - begin - hcond:=OC_NE; - cmp_const:=int64(low(longint)); - end - end - else - begin - if longint(cmp_const.svalue)=low(longint) then - begin - hcond:=OC_NE; - cmp_const:=int64(high(longint)); - end - end - end; - OS_S64: - begin - if lnf_backward in loopflags then - begin - if int64(cmp_const.svalue)=high(int64) then - begin - hcond:=OC_NE; - cmp_const:=low(int64); - end - end - else - begin - if int64(cmp_const.svalue)=low(int64) then - begin - hcond:=OC_NE; - cmp_const:=high(int64); - end - end - end; - else - internalerror(200201021); - end; - end; - - hlcg.a_cmp_const_loc_label(current_asmdata.CurrAsmList,left.resultdef,hcond, - tcgint(cmp_const.svalue),left.location,l3); - end; - - { this is the break label: } - hlcg.a_label(current_asmdata.CurrAsmList,current_procinfo.CurrBreakLabel); - - sync_regvars(false); - - current_procinfo.CurrContinueLabel:=oldclabel; - current_procinfo.CurrBreakLabel:=oldblabel; - { a break/continue in a while/repeat block can't be seen outside } - flowcontrol:=oldflowcontrol+(flowcontrol-[fc_break,fc_continue,fc_inflowcontrol]); + { for nodes are converted in pass_1 in a while loop } + internalerror(2015082501); end; diff --git a/compiler/nflw.pas b/compiler/nflw.pas index 1237a401e1..5ccc66b491 100644 --- a/compiler/nflw.pas +++ b/compiler/nflw.pas @@ -72,6 +72,9 @@ interface end; twhilerepeatnode = class(tloopnode) + { l: condition; r: body; tab: test at begin; cn: negate condition + x,y,true,false: while loop + x,y,false,true: repeat until loop } constructor create(l,r:Tnode;tab,cn:boolean);virtual;reintroduce; function pass_typecheck:tnode;override; function pass_1 : tnode;override; @@ -101,7 +104,6 @@ interface loopiteration : tnode; loopvar_notid:cardinal; constructor create(l,r,_t1,_t2 : tnode;back : boolean);virtual;reintroduce; - function wrap_to_value:tnode; function pass_typecheck:tnode;override; function pass_1 : tnode;override; function simplify(forinline : boolean) : tnode;override; @@ -1475,29 +1477,6 @@ implementation end; - function tfornode.wrap_to_value:tnode; - var - statements: tstatementnode; - temp: ttempcreatenode; - begin - result:=internalstatements(statements); - temp:=ctempcreatenode.create(t1.resultdef,t1.resultdef.size,tt_persistent,true); - addstatement(statements,temp); - addstatement(statements,cassignmentnode.create( - ctemprefnode.create(temp), - t1)); - { create a new for node, it is cheaper than cloning entire loop body } - addstatement(statements,cfornode.create( - left,right,ctemprefnode.create(temp),t2,lnf_backward in loopflags)); - addstatement(statements,ctempdeletenode.create(temp)); - { all child nodes are reused } - left:=nil; - right:=nil; - t1:=nil; - t2:=nil; - end; - - function tfornode.pass_typecheck:tnode; var res : tnode; @@ -1545,23 +1524,196 @@ implementation function tfornode.pass_1 : tnode; + var + ifblock,whileblock,loopblock : tblocknode; + ifstatements,statements,loopstatements : tstatementnode; + fromtemp,totemp : ttempcreatenode; + do_loopvar_at_end : Boolean; + { if the lower and/or upper bound are variable, we need a surrounding if } + needsifblock : Boolean; + cond : tnodetype; + fromexpr : tnode; + toexpr : tnode; + { if the upper bound is not constant, it must be store in a temp initially } + usetotemp : boolean; + { if the lower bound is not constant, it must be store in a temp before calculating the upper bound } + usefromtemp : boolean; + + procedure iterate_counter(var s : tstatementnode;fw : boolean); + begin + if fw then + addstatement(s, + cassignmentnode.create_internal(left.getcopy,cinlinenode.createintern(in_succ_x,false,left.getcopy))) + else + addstatement(s, + cassignmentnode.create_internal(left.getcopy,cinlinenode.createintern(in_pred_x,false,left.getcopy))); + end; + + function iterate_counter_func(arg : tnode;fw : boolean) : tnode; + begin + if fw then + result:=cinlinenode.createintern(in_succ_x,false,arg) + else + result:=cinlinenode.createintern(in_pred_x,false,arg); + end; + begin - result:=nil; - expectloc:=LOC_VOID; + result:=nil; + expectloc:=LOC_VOID; + fromtemp:=nil; + totemp:=nil; - firstpass(left); - firstpass(right); - firstpass(t1); + firstpass(left); + firstpass(right); + firstpass(t1); - if assigned(t2) then - firstpass(t2); - if codegenerror then - exit; + if assigned(t2) then + begin + firstpass(t2); + if codegenerror then + exit; + end; - { 'to' value must be evaluated once before loop, so its possible modifications - inside loop body do not affect the number of iterations (see webtbs/tw8883). } - if not (t1.nodetype in [ordconstn,temprefn]) then - result:=wrap_to_value; + { first set the to value + because the count var can be in the expression ! } + do_loopvar_at_end:=(lnf_dont_mind_loopvar_on_exit in loopflags) + { if the loop is unrolled and there is a jump into the loop, + then we can't do the trick with incrementing the loop var only at the + end + } + and not(assigned(entrylabel)); + + { calculate pointer value and check if changeable and if so + load into temporary variable } + if (right.nodetype<>ordconstn) or (t1.nodetype<>ordconstn) then + begin + do_loopvar_at_end:=false; + needsifblock:=true; + end + else + needsifblock:=false; + + { convert the for loop into a while loop } + result:=internalstatements(statements); + ifblock:=internalstatements(ifstatements); + loopblock:=internalstatements(loopstatements); + + usefromtemp:=(might_have_sideeffects(t1) and not(is_const(right))) or (node_complexity(right)>1); + usetotemp:=not(is_const(t1)); + + if needsifblock then + begin + { do not generate a temp. for the from node, if it is a const, it can be copied directly since + no side effect might change it } + if usefromtemp then + begin + fromtemp:=ctempcreatenode.create(right.resultdef,right.resultdef.size,tt_persistent,true); + { the if block might be optimized out, so we put the deletetempnode after the if-block, however, + this causes a long life time of the fromtemp. If the final regsync is left away, the reg. allocator + figures out the needed life time. As their are no loops involved between the uses of the fromtemp, + this does no hurt } + fromtemp.includetempflag(ti_no_final_regsync); + addstatement(statements,fromtemp); + { while it would be beneficial to fold the initial reverse succ/pred into this assignment, this is + not possible because it might wrap around and the if check later on goes wrong } + addstatement(statements,cassignmentnode.create_internal(ctemprefnode.create(fromtemp),right.getcopy)); + end; + + if usetotemp then + begin + totemp:=ctempcreatenode.create(t1.resultdef,t1.resultdef.size,tt_persistent,true); + addstatement(statements,totemp); + addstatement(statements,cassignmentnode.create_internal(ctemprefnode.create(totemp),t1.getcopy)); + end; + + if usefromtemp then + begin + addstatement(ifstatements,cassignmentnode.create_internal(left.getcopy,ctemprefnode.create(fromtemp))); + if not(do_loopvar_at_end) then + iterate_counter(ifstatements,lnf_backward in loopflags); + end + else + begin + if not(do_loopvar_at_end) then + addstatement(ifstatements,cassignmentnode.create_internal(left.getcopy, + iterate_counter_func(right.getcopy,lnf_backward in loopflags))) + else + addstatement(ifstatements,cassignmentnode.create_internal(left.getcopy,right.getcopy)); + end; + end + else + begin + if not(do_loopvar_at_end) then + addstatement(ifstatements,cassignmentnode.create_internal(left.getcopy, + iterate_counter_func(right.getcopy,lnf_backward in loopflags))) + else + addstatement(ifstatements,cassignmentnode.create_internal(left.getcopy,right.getcopy)); + end; + + if assigned(entrylabel) then + addstatement(ifstatements,cgotonode.create(tlabelnode(entrylabel).labsym)); + + if not(do_loopvar_at_end) then + iterate_counter(loopstatements,not(lnf_backward in loopflags)); + + { avoid copying t2, it is used only once and it might be big } + addstatement(loopstatements,t2); + t2:=nil; + + if do_loopvar_at_end then + iterate_counter(loopstatements,not(lnf_backward in loopflags)); + + if do_loopvar_at_end then + begin + if lnf_backward in loopflags then + cond:=ltn + else + cond:=gtn; + end + else + begin + if lnf_backward in loopflags then + cond:=lten + else + cond:=gten; + end; + + if needsifblock then + begin + if usetotemp then + toexpr:=ctemprefnode.create(totemp) + else + toexpr:=t1.getcopy; + + addstatement(ifstatements,cwhilerepeatnode.create(caddnode.create(cond,left.getcopy,toexpr),loopblock,false,true)); + + if usefromtemp then + fromexpr:=ctemprefnode.create(fromtemp) + else + fromexpr:=right.getcopy; + + if usetotemp then + toexpr:=ctemprefnode.create(totemp) + else + toexpr:=t1.getcopy; + + if lnf_backward in loopflags then + addstatement(statements,cifnode.create(caddnode.create(gten, + fromexpr,toexpr),ifblock,nil)) + else + addstatement(statements,cifnode.create(caddnode.create(lten, + fromexpr,toexpr),ifblock,nil)); + + if usetotemp then + addstatement(statements,ctempdeletenode.create(totemp)); + if usefromtemp then + addstatement(statements,ctempdeletenode.create(fromtemp)); + end + else + begin + addstatement(ifstatements,cwhilerepeatnode.create(caddnode.create(cond,left.getcopy,t1.getcopy),loopblock,false,true)); + addstatement(statements,ifblock); + end; end;