aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src/beam_ssa_opt.erl
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2018-09-17 06:31:50 +0200
committerBjörn Gustavsson <[email protected]>2018-09-17 06:41:01 +0200
commit25005204cca654920c7a751bb3ada4b9d9b0b285 (patch)
tree061fc92a7f2840f4adb504a251345749cf0f552b /lib/compiler/src/beam_ssa_opt.erl
parentf25448763f55d595e00fe570f2108f669f3f6b1a (diff)
downloadotp-25005204cca654920c7a751bb3ada4b9d9b0b285.tar.gz
otp-25005204cca654920c7a751bb3ada4b9d9b0b285.tar.bz2
otp-25005204cca654920c7a751bb3ada4b9d9b0b285.zip
beam_ssa_opt: Robustify float optimizations
The floating point optimization relies on heavily on the block order in the lineararized representation. A new optimization could easily break the optimization, for example so that no `fcheckerror` instructions were emitted. Rewrite the optimization to avoid dependencies on the linear block order.
Diffstat (limited to 'lib/compiler/src/beam_ssa_opt.erl')
-rw-r--r--lib/compiler/src/beam_ssa_opt.erl132
1 files changed, 51 insertions, 81 deletions
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index 7fbc090132..2174c6aa31 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -522,14 +522,15 @@ cse_suitable(#b_set{}) -> false.
{s=undefined :: 'undefined' | 'cleared',
regs=#{} :: #{beam_ssa:b_var():=beam_ssa:b_var()},
fail=none :: 'none' | beam_ssa:label(),
- ren=#{} :: #{beam_ssa:label():=beam_ssa:label()},
- non_guards :: gb_sets:set(beam_ssa:label())
+ non_guards :: gb_sets:set(beam_ssa:label()),
+ bs :: beam_ssa:block_map()
}).
ssa_opt_float(#st{ssa=Linear0,cnt=Count0}=St) ->
NonGuards0 = float_non_guards(Linear0),
NonGuards = gb_sets:from_list(NonGuards0),
- Fs = #fs{non_guards=NonGuards},
+ Blocks = maps:from_list(Linear0),
+ Fs = #fs{non_guards=NonGuards,bs=Blocks},
{Linear,Count} = float_opt(Linear0, Count0, Fs),
St#st{ssa=Linear,cnt=Count}.
@@ -542,42 +543,35 @@ float_non_guards([{L,#b_blk{is=Is}}|Bs]) ->
end;
float_non_guards([]) -> [?BADARG_BLOCK].
-float_opt([{L,Blk0}|Bs], Count, Fs) ->
- Blk = float_rename_phis(Blk0, Fs),
- case float_need_flush(Blk, Fs) of
- true ->
- float_flush(L, Blk, Bs, Count, Fs);
- false ->
- float_opt_1(L, Blk, Bs, Count, Fs)
- end;
-float_opt([], Count, _Fs) ->
- {[],Count}.
-
-float_opt_1(L, #b_blk{last=#b_br{fail=F}}=Blk, Bs0,
+float_opt([{L,#b_blk{last=#b_br{fail=F}}=Blk}|Bs0],
Count0, #fs{non_guards=NonGuards}=Fs) ->
case gb_sets:is_member(F, NonGuards) of
true ->
%% This block is not inside a guard.
%% We can do the optimization.
- float_opt_2(L, Blk, Bs0, Count0, Fs);
+ float_opt_1(L, Blk, Bs0, Count0, Fs);
false ->
%% This block is inside a guard. Don't do
%% any floating point optimizations.
{Bs,Count} = float_opt(Bs0, Count0, Fs),
{[{L,Blk}|Bs],Count}
end;
-float_opt_1(L, Blk, Bs, Count, Fs) ->
- float_opt_2(L, Blk, Bs, Count, Fs).
+float_opt([{L,Blk}|Bs], Count, Fs) ->
+ float_opt_1(L, Blk, Bs, Count, Fs);
+float_opt([], Count, _Fs) ->
+ {[],Count}.
-float_opt_2(L, #b_blk{is=Is0}=Blk0, Bs0, Count0, Fs0) ->
+float_opt_1(L, #b_blk{is=Is0}=Blk0, Bs0, Count0, Fs0) ->
case float_opt_is(Is0, Fs0, Count0, []) of
{Is1,Fs1,Count1} ->
- Fs = float_fail_label(Blk0, Fs1),
- Split = float_split_conv(Is1, Blk0),
- {Blks0,Count2} = float_number(Split, L, Count1),
- {Blks,Count3} = float_conv(Blks0, Fs#fs.fail, Count2),
- {Bs,Count} = float_opt(Bs0, Count3, Fs),
- {Blks++Bs,Count};
+ Fs2 = float_fail_label(Blk0, Fs1),
+ Fail = Fs2#fs.fail,
+ {Flush,Blk,Fs,Count2} = float_maybe_flush(Blk0, Fs2, Count1),
+ Split = float_split_conv(Is1, Blk),
+ {Blks0,Count3} = float_number(Split, L, Count2),
+ {Blks,Count4} = float_conv(Blks0, Fail, Count3),
+ {Bs,Count} = float_opt(Bs0, Count4, Fs),
+ {Blks++Flush++Bs,Count};
none ->
{Bs,Count} = float_opt(Bs0, Count0, Fs0),
{[{L,Blk0}|Bs],Count}
@@ -637,14 +631,42 @@ float_conv([{L,#b_blk{is=Is0}=Blk0}|Bs0], Fail, Count0) ->
end
end.
-float_need_flush(#b_blk{is=Is}, #fs{s=cleared}) ->
+float_maybe_flush(Blk0, #fs{s=cleared,fail=Fail,bs=Blocks}=Fs0, Count0) ->
+ #b_blk{last=#b_br{bool=#b_var{},succ=Succ}=Br} = Blk0,
+ #b_blk{is=Is} = maps:get(Succ, Blocks),
case Is of
[#b_set{anno=#{float_op:=_}}|_] ->
- false;
+ %% The next operation is also a floating point operation.
+ %% No flush needed.
+ {[],Blk0,Fs0,Count0};
_ ->
- true
+ %% Flush needed.
+ {Bool0,Count1} = new_reg('@ssa_bool', Count0),
+ Bool = #b_var{name=Bool0},
+
+ %% Allocate block numbers.
+ CheckL = Count1, %For checkerror.
+ FlushL = Count1 + 1, %For flushing of float regs.
+ Count = Count1 + 2,
+ Blk = Blk0#b_blk{last=Br#b_br{succ=CheckL}},
+
+ %% Build the block with the checkerror instruction.
+ CheckIs = [#b_set{op={float,checkerror},dst=Bool}],
+ CheckBr = #b_br{bool=Bool,succ=FlushL,fail=Fail},
+ CheckBlk = #b_blk{is=CheckIs,last=CheckBr},
+
+ %% Build the block that flushes all registers.
+ FlushIs = float_flush_regs(Fs0),
+ FlushBr = #b_br{bool=#b_literal{val=true},succ=Succ,fail=Succ},
+ FlushBlk = #b_blk{is=FlushIs,last=FlushBr},
+
+ %% Update state and blocks.
+ Fs = Fs0#fs{s=undefined,regs=#{},fail=none},
+ FlushBs = [{CheckL,CheckBlk},{FlushL,FlushBlk}],
+ {FlushBs,Blk,Fs,Count}
end;
-float_need_flush(_, _) -> false.
+float_maybe_flush(Blk, Fs, Count) ->
+ {[],Blk,Fs,Count}.
float_opt_is([#b_set{op=succeeded,args=[Src]}=I0],
#fs{regs=Rs}=Fs, Count, Acc) ->
@@ -669,27 +691,6 @@ float_opt_is([], Fs, _Count, _Acc) ->
#fs{s=undefined} = Fs, %Assertion.
none.
-float_rename_phis(#b_blk{is=Is}=Blk, #fs{ren=Ren}) ->
- if
- map_size(Ren) =:= 0 ->
- Blk;
- true ->
- Blk#b_blk{is=float_rename_phis_1(Is, Ren)}
- end.
-
-float_rename_phis_1([#b_set{op=phi,args=Args0}=I|Is], Ren) ->
- Args = [float_phi_arg(Arg, Ren) || Arg <- Args0],
- [I#b_set{args=Args}|float_rename_phis_1(Is, Ren)];
-float_rename_phis_1(Is, _) -> Is.
-
-float_phi_arg({Var,OldLbl}, Ren) ->
- case Ren of
- #{OldLbl:=NewLbl} ->
- {Var,NewLbl};
- #{} ->
- {Var,OldLbl}
- end.
-
float_make_op(#b_set{op={bif,Op},dst=Dst,args=As0}=I0,
Ts, #fs{s=S,regs=Rs0}=Fs, Count0) ->
{As1,Rs1,Count1} = float_load(As0, Ts, Rs0, Count0, []),
@@ -746,37 +747,6 @@ new_reg(Base, Count) ->
Fr = {Base,Count},
{Fr,Count+1}.
-float_flush(L, Blk, Bs0, Count0, #fs{s=cleared,fail=Fail,ren=Ren0}=Fs0) ->
- {Bool0,Count1} = new_reg('@ssa_bool', Count0),
- Bool = #b_var{name=Bool0},
-
- %% Insert two blocks before the current block. First allocate
- %% block numbers.
- FirstL = L, %For checkerror.
- MiddleL = Count1, %For flushed float regs.
- LastL = Count1 + 1, %For original block.
- Count2 = Count1 + 2,
-
- %% Build the block with the checkerror instruction.
- CheckIs = [#b_set{op={float,checkerror},dst=Bool}],
- FirstBlk = #b_blk{is=CheckIs,last=#b_br{bool=Bool,succ=MiddleL,fail=Fail}},
-
- %% Build the block that flushes all registers. Note that this must be a
- %% separate block in case the original block begins with a phi instruction,
- %% to avoid embedding a phi instruction in the middle of a block.
- FlushIs = float_flush_regs(Fs0),
- MiddleBlk = #b_blk{is=FlushIs,last=#b_br{bool=#b_literal{val=true},
- succ=LastL,fail=LastL}},
-
- %% The last block is the original unmodified block.
- LastBlk = Blk,
-
- %% Update state and blocks.
- Ren = Ren0#{L=>LastL},
- Fs = Fs0#fs{s=undefined,regs=#{},fail=none,ren=Ren},
- Bs1 = [{FirstL,FirstBlk},{MiddleL,MiddleBlk},{LastL,LastBlk}|Bs0],
- float_opt(Bs1, Count2, Fs).
-
float_fail_label(#b_blk{last=Last}, Fs) ->
case Last of
#b_br{bool=#b_var{},fail=Fail} ->