diff options
author | Frej Drejhammar <frej.drejhammar@gmail.com> | 2023-05-04 14:57:56 +0200 |
---|---|---|
committer | Frej Drejhammar <frej.drejhammar@gmail.com> | 2023-05-04 15:13:05 +0200 |
commit | 5277d99c337d85766dd8c087413ae7598390e743 (patch) | |
tree | 6bf85045575e9063018fe4d6432f63d3e79cd151 | |
parent | c9919949e055dea8f86d50ea7fef70eefd835e00 (diff) | |
download | erlang-5277d99c337d85766dd8c087413ae7598390e743.tar.gz |
compiler: Avoid invalid code for bs_create_bin with initial literal
This change stops the compiler from generating invalid code when
bs_create_bin is given a literal <<>> as its first fragment. As the
type analyzer considers a literal <<>> an appendable bitstring, code
sequences such as:
_6 = bs_create_bin `append`, `[8,{segment,1}]`, `<<>>`, `all`
...
_14 = bs_create_bin `append`, `[8,{segment,1}]`, _6, `all`
would be rewritten to:
_6 = bs_create_bin `private_append`, `[8,{segment,1}]`, `<<>>`, `all`
...
_14 = bs_create_bin `private_append`, `[8,{segment,1}]`, _6, `all`
which is not legal, as private_append on a literal will crash the
runtime system.
By inserting a bs_init_writable in front of bs_create_bin instructions
with a literal <<>> as the first fragment, and then using the freshly
created writable binary instead of the literal, the code sequence
becomes valid:
_1 = bs_init_writable `256`
_6 = bs_create_bin `private_append`, `[8,{segment,1}]`, _1, `all`
...
_14 = bs_create_bin `private_append`, `[8,{segment,1}]`, _6, `all`
-rw-r--r-- | lib/compiler/src/beam_ssa_private_append.erl | 21 | ||||
-rw-r--r-- | lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl | 27 |
2 files changed, 47 insertions, 1 deletions
diff --git a/lib/compiler/src/beam_ssa_private_append.erl b/lib/compiler/src/beam_ssa_private_append.erl index bf51e8b81a..c295492762 100644 --- a/lib/compiler/src/beam_ssa_private_append.erl +++ b/lib/compiler/src/beam_ssa_private_append.erl @@ -86,6 +86,17 @@ find_appends_blk([], _, Found) -> Found. find_appends_is([#b_set{dst=Dst, op=bs_create_bin, + args=[#b_literal{val=append}, + _, + Lit=#b_literal{val= <<>>}|_]}|Is], + Fun, Found0) -> + %% Special case for when the first fragment is a literal <<>> as + %% it won't be annotated as unique nor will it die with the + %% instruction. + AlreadyFound = maps:get(Fun, Found0, []), + Found = Found0#{Fun => [{append,Dst,Lit}|AlreadyFound]}, + find_appends_is(Is, Fun, Found); +find_appends_is([#b_set{dst=Dst, op=bs_create_bin, args=[#b_literal{val=append},SegmentInfo,Var|_], anno=#{first_fragment_dies:=Dies}=Anno}|Is], Fun, Found0) -> @@ -468,6 +479,16 @@ patch_appends_is([I0=#b_set{dst=Dst}|Rest], PD0, Cnt0, Acc, BlockAdditions0) Ps = keysort(1, map(ExtractOpargs, Patches)), {Is, Cnt} = patch_opargs(I0, Ps, Cnt0), patch_appends_is(Rest, PD, Cnt, Is++Acc, BlockAdditions0); + [{append,Dst,#b_literal{val= <<>>}=Lit}] -> + %% Special case for when the first fragment is a literal + %% <<>> and it has to be replaced with a bs_init_writable. + #b_set{op=bs_create_bin,dst=Dst,args=Args0}=I0, + [#b_literal{val=append},SegInfo,Lit|OtherArgs] = Args0, + {V,Cnt} = new_var(Cnt0), + Init = #b_set{op=bs_init_writable,dst=V,args=[#b_literal{val=256}]}, + I = I0#b_set{args=[#b_literal{val=private_append}, + SegInfo,V|OtherArgs]}, + patch_appends_is(Rest, PD, Cnt, [I,Init|Acc], BlockAdditions0); [{append,Dst,_}] -> #b_set{op=bs_create_bin,dst=Dst,args=Args0}=I0, [#b_literal{val=append}|OtherArgs] = Args0, diff --git a/lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl b/lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl index 4d4b0f1bbd..c1edc54460 100644 --- a/lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl +++ b/lib/compiler/test/beam_ssa_check_SUITE_data/private_append.erl @@ -22,6 +22,8 @@ %% -module(private_append). +-feature(maybe_expr, enable). + -export([transformable0/1, transformable1/1, transformable1b/1, @@ -76,7 +78,9 @@ not_transformable14/0, not_transformable15/2, - id/1]). + id/1, + + bs_create_bin_on_literal/0]). %% Trivial smoke test transformable0(L) -> @@ -977,3 +981,24 @@ not_transformable15(_, V) -> id(I) -> I. + +%% Check that we don't try to private_append to something created by +%% bs_create_bin `append`, _, `<<>>`, ... +bs_create_bin_on_literal() -> +%ssa% () when post_ssa_opt -> +%ssa% X = bs_init_writable(_), +%ssa% Y = bs_create_bin(private_append, _, X, ...), +%ssa% Z = bs_create_bin(private_append, _, Y, ...), +%ssa% ret(Z). + << + << + (maybe + 2147483647 ?= ok + else + <<_>> -> + ok; + _ -> + <<>> + end)/bytes + >>/binary + >>. |