diff options
author | Björn Gustavsson <bjorn@erlang.org> | 2021-09-20 09:21:09 +0200 |
---|---|---|
committer | Björn Gustavsson <bjorn@erlang.org> | 2021-10-15 08:13:13 +0200 |
commit | d424e1dffbe6303475656f0fa847c00fb356add4 (patch) | |
tree | cfe399a57944421b740e5af8752c51ccfddf4a05 /lib/compiler/src/beam_ssa_opt.erl | |
parent | 16c5728f442b51caca42bcd827a2058051ef8733 (diff) | |
download | erlang-d424e1dffbe6303475656f0fa847c00fb356add4.tar.gz |
compiler: Add a new instruction for creating binaries
Binary construction using the binary syntax currently uses multiple
instructions. That makes it hard to provide good information in the
exception if construction fails. It also makes it harder to optimize
construction in the JIT.
Therefore, introduce the instruction `bs_create_bin` that constructs
a binary in one go.
To be able to test the new instruction before implementing it in the
runtime system, force translation back to the old-style instructions
in the `beam_clean` pass.
Diffstat (limited to 'lib/compiler/src/beam_ssa_opt.erl')
-rw-r--r-- | lib/compiler/src/beam_ssa_opt.erl | 185 |
1 files changed, 72 insertions, 113 deletions
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl index 72e320749e..91db5c6400 100644 --- a/lib/compiler/src/beam_ssa_opt.erl +++ b/lib/compiler/src/beam_ssa_opt.erl @@ -277,7 +277,7 @@ module_passes(Opts) -> repeated_passes(Opts) -> Ps = [?PASS(ssa_opt_live), ?PASS(ssa_opt_ne), - ?PASS(ssa_opt_bs_puts), + ?PASS(ssa_opt_bs_create_bin), ?PASS(ssa_opt_dead), ?PASS(ssa_opt_cse), ?PASS(ssa_opt_tail_phis), @@ -1788,105 +1788,73 @@ bsm_shortcut([], _PosMap) -> []. %%% If an integer segment or a float segment has a literal size and %%% a literal value, convert to a binary segment. Coalesce adjacent %%% literal binary segments. Literal binary segments will be converted -%%% to bs_put_string instructions in later pass. +%%% to bs_put_string instructions in a later pass. %%% -ssa_opt_bs_puts({#opt_st{ssa=Linear0,cnt=Count0}=St, FuncDb}) -> - {Linear,Count} = opt_bs_puts(Linear0, Count0, []), - {St#opt_st{ssa=Linear,cnt=Count}, FuncDb}. - -opt_bs_puts([{L,#b_blk{is=Is}=Blk0}|Bs], Count0, Acc0) -> - case Is of - [#b_set{op=bs_put},#b_set{op={succeeded,_}}]=Is -> - case opt_bs_put(L, Is, Blk0, Count0, Acc0) of - not_possible -> - opt_bs_puts(Bs, Count0, [{L,Blk0}|Acc0]); - {Count,Acc1} -> - Acc = opt_bs_puts_merge(Acc1), - opt_bs_puts(Bs, Count, Acc) - end; - _ -> - opt_bs_puts(Bs, Count0, [{L,Blk0}|Acc0]) - end; -opt_bs_puts([], Count, Acc) -> - {reverse(Acc),Count}. - -opt_bs_puts_merge([{L1,#b_blk{is=Is}=Blk0},{L2,#b_blk{is=AccIs}}=BAcc|Acc]) -> - case {AccIs,Is} of - {[#b_set{op=bs_put, - args=[#b_literal{val=binary}, - #b_literal{}, - #b_literal{val=Bin0}, - #b_literal{val=all}, - #b_literal{val=1}]}, - #b_set{op={succeeded,_}}], - [#b_set{op=bs_put, - args=[#b_literal{val=binary}, - #b_literal{}, - #b_literal{val=Bin1}, - #b_literal{val=all}, - #b_literal{val=1}]}=I0, - #b_set{op={succeeded,_}}=Succeeded]} -> - %% Coalesce the two segments to one. - Bin = <<Bin0/bitstring,Bin1/bitstring>>, - I = I0#b_set{args=bs_put_args(binary, Bin, all)}, - Blk = Blk0#b_blk{is=[I,Succeeded]}, - [{L2,Blk}|Acc]; - {_,_} -> - [{L1,Blk0},BAcc|Acc] - end. +ssa_opt_bs_create_bin({#opt_st{ssa=Linear0}=St, FuncDb}) -> + Linear = opt_create_bin_fs(Linear0), + {St#opt_st{ssa=Linear}, FuncDb}. -opt_bs_put(L, [I0,Succeeded], #b_blk{last=Br0}=Blk0, Count0, Acc) -> - case opt_bs_put(I0) of - [Bin] when is_bitstring(Bin) -> - Args = bs_put_args(binary, Bin, all), - I = I0#b_set{args=Args}, - Blk = Blk0#b_blk{is=[I,Succeeded]}, - {Count0,[{L,Blk}|Acc]}; - [{int,Int,Size},Bin] when is_bitstring(Bin) -> - %% Construct a bs_put_integer instruction following - %% by a bs_put_binary instruction. - IntArgs = bs_put_args(integer, Int, Size), - BinArgs = bs_put_args(binary, Bin, all), - - {BinL,BinVarNum,BinBoolNum} = {Count0,Count0+1,Count0+2}, - Count = Count0 + 3, - BinVar = #b_var{name={'@ssa_bs_put',BinVarNum}}, - BinBool = #b_var{name={'@ssa_bool',BinBoolNum}}, - - BinI = I0#b_set{dst=BinVar,args=BinArgs}, - BinSucceeded = Succeeded#b_set{dst=BinBool,args=[BinVar]}, - BinBlk = Blk0#b_blk{is=[BinI,BinSucceeded], - last=Br0#b_br{bool=BinBool}}, - - IntI = I0#b_set{args=IntArgs}, - IntBlk = Blk0#b_blk{is=[IntI,Succeeded],last=Br0#b_br{succ=BinL}}, - - {Count,[{BinL,BinBlk},{L,IntBlk}|Acc]}; +opt_create_bin_fs([{L,#b_blk{is=Is0}=Blk0}|Bs]) -> + Is = opt_create_bin_is(Is0), + Blk = Blk0#b_blk{is=Is}, + [{L,Blk}|opt_create_bin_fs(Bs)]; +opt_create_bin_fs([]) -> []. + +opt_create_bin_is([#b_set{op=bs_create_bin,args=Args0}=I0|Is]) -> + Args = opt_create_bin_args(Args0), + I = I0#b_set{args=Args}, + [I|opt_create_bin_is(Is)]; +opt_create_bin_is([I|Is]) -> + [I|opt_create_bin_is(Is)]; +opt_create_bin_is([]) -> []. + +opt_create_bin_args([#b_literal{val=binary},#b_literal{val=[1|_]}, + #b_literal{val=Bin0},#b_literal{val=all}, + #b_literal{val=binary},#b_literal{val=[1|_]}, + #b_literal{val=Bin1},#b_literal{val=all}|Args0]) -> + %% Coalesce two litary binary segments to one. + Bin = <<Bin0/bitstring,Bin1/bitstring>>, + Args = [#b_literal{val=binary},#b_literal{val=[1]}, + #b_literal{val=Bin},#b_literal{val=all}|Args0], + opt_create_bin_args(Args); +opt_create_bin_args([#b_literal{val=Type}=Type0,#b_literal{val=UFs}=UFs0,Val,Size|Args0]) -> + [Unit|Flags] = UFs, + case opt_create_bin_arg(Type, Unit, UFs, Val, Size) of not_possible -> - not_possible - end. - -opt_bs_put(#b_set{args=[#b_literal{val=binary},_,#b_literal{val=Val}, - #b_literal{val=all},#b_literal{val=Unit}]}) - when is_bitstring(Val) -> - if - bit_size(Val) rem Unit =:= 0 -> - [Val]; - true -> - not_possible - end; -opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, - #b_literal{val=Val},#b_literal{val=Size}, - #b_literal{val=Unit}]}=I0) when is_integer(Size) -> + [Type0,UFs0,Val,Size|opt_create_bin_args(Args0)]; + [Bin] when is_bitstring(Bin) -> + Args = [#b_literal{val=binary},#b_literal{val=[1]}, + #b_literal{val=Bin},#b_literal{val=all}|Args0], + opt_create_bin_args(Args); + [{int,Int,IntSize},Bin] when is_bitstring(Bin) -> + Args = [#b_literal{val=integer},#b_literal{val=[1|Flags]}, + #b_literal{val=Int},#b_literal{val=IntSize}, + #b_literal{val=binary},#b_literal{val=[1]}, + #b_literal{val=Bin},#b_literal{val=all}|Args0], + opt_create_bin_args(Args) + end; +opt_create_bin_args([]) -> []. + +opt_create_bin_arg(binary, Unit, _Flags, #b_literal{val=Val}, #b_literal{val=all}) + when Unit =/= 1, bit_size(Val) rem Unit =:= 0 -> + [Val]; +opt_create_bin_arg(Type, Unit, Flags, #b_literal{val=Val}, #b_literal{val=Size}) + when is_integer(Size), is_integer(Unit) -> EffectiveSize = Size * Unit, if EffectiveSize > 0 -> - case {Type,opt_bs_put_endian(Flags)} of + case {Type,opt_create_bin_endian(Flags)} of {integer,big} when is_integer(Val) -> if EffectiveSize < 64 -> [<<Val:EffectiveSize>>]; + EffectiveSize > 1 bsl 24 -> + %% The binary construction could fail with a + %% system_limit. Don't optimize to ensure that + %% the extended error information will be + %% accurate. + not_possible; true -> opt_bs_put_split_int(Val, EffectiveSize) end; @@ -1894,9 +1862,8 @@ opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, %% To avoid an explosion in code size, we only try %% to optimize relatively small fields. <<Int:EffectiveSize>> = <<Val:EffectiveSize/little>>, - Args = bs_put_args(Type, Int, EffectiveSize), - I = I0#b_set{args=Args}, - opt_bs_put(I); + opt_create_bin_arg(Type, 1, [], #b_literal{val=Int}, + #b_literal{val=EffectiveSize}); {binary,_} when is_bitstring(Val) -> case Val of <<Bitstring:EffectiveSize/bits,_/bits>> -> @@ -1907,8 +1874,14 @@ opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, end; {float,Endian} -> try - [opt_bs_put_float(Val, EffectiveSize, Endian)] - catch error:_ -> + case Endian of + big -> + [<<Val:EffectiveSize/big-float-unit:1>>]; + little -> + [<<Val:EffectiveSize/little-float-unit:1>>] + end + catch + error:_ -> not_possible end; {_,_} -> @@ -1917,25 +1890,12 @@ opt_bs_put(#b_set{args=[#b_literal{val=Type},#b_literal{val=Flags}, true -> not_possible end; -opt_bs_put(#b_set{}) -> not_possible. - -opt_bs_put_float(N, Sz, Endian) -> - case Endian of - big -> <<N:Sz/big-float-unit:1>>; - little -> <<N:Sz/little-float-unit:1>> - end. - -bs_put_args(Type, Val, Size) -> - [#b_literal{val=Type}, - #b_literal{val=[unsigned,big]}, - #b_literal{val=Val}, - #b_literal{val=Size}, - #b_literal{val=1}]. +opt_create_bin_arg(_, _, _, _, _) -> not_possible. -opt_bs_put_endian([big=E|_]) -> E; -opt_bs_put_endian([little=E|_]) -> E; -opt_bs_put_endian([native=E|_]) -> E; -opt_bs_put_endian([_|Fs]) -> opt_bs_put_endian(Fs). +opt_create_bin_endian([little=E|_]) -> E; +opt_create_bin_endian([native=E|_]) -> E; +opt_create_bin_endian([_|Fs]) -> opt_create_bin_endian(Fs); +opt_create_bin_endian([]) -> big. opt_bs_put_split_int(Int, Size) -> Pos = opt_bs_put_split_int_1(Int, 0, Size - 1), @@ -2512,7 +2472,6 @@ unsuitable(Linear, Blocks) -> unsuitable_1([{L,#b_blk{is=[#b_set{op=Op}=I|_]}}|Bs]) -> Unsuitable = case Op of bs_extract -> true; - bs_put -> true; {float,_} -> true; landingpad -> true; _ -> beam_ssa:is_loop_header(I) |