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 /lib/compiler/src/beam_ssa_private_append.erl | |
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`
Diffstat (limited to 'lib/compiler/src/beam_ssa_private_append.erl')
-rw-r--r-- | lib/compiler/src/beam_ssa_private_append.erl | 21 |
1 files changed, 21 insertions, 0 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, |