+ transform for loop into equivalent while loop during pass 1 to simplify code generation

git-svn-id: trunk@34788 -
This commit is contained in:
florian 2016-11-05 23:05:43 +00:00
parent 82e6e1eb34
commit 0610187878
2 changed files with 195 additions and 471 deletions

View File

@ -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;

View File

@ -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;