diff --git a/lib/compiler/src/beam_ssa_destructive_update.erl b/lib/compiler/src/beam_ssa_destructive_update.erl index 9cf9d814c97c..668da13a2ffe 100644 --- a/lib/compiler/src/beam_ssa_destructive_update.erl +++ b/lib/compiler/src/beam_ssa_destructive_update.erl @@ -891,13 +891,28 @@ patch_literal_term(<<>>, {self,init_writable}, Cnt0) -> {V,Cnt} = new_var(Cnt0), I = #b_set{op=bs_init_writable,dst=V,args=[#b_literal{val=256}]}, {V,[I],Cnt}; -patch_literal_term([H0|T0], {hd,Element,_}, Cnt0) -> - {H,Extra,Cnt1} = patch_literal_term(H0, Element, Cnt0), - {T,[],Cnt1} = patch_literal_term(T0, [], Cnt1), - {Dst,Cnt} = new_var(Cnt1), +patch_literal_term(Lst, {hd,_,_}=E, Cnt0) -> + patch_literal_list(Lst, E, Cnt0); +patch_literal_term(Lit, [], Cnt) -> + {#b_literal{val=Lit}, [], Cnt}. + +%% +%% The initial value tracker is unable to easily follow list tails, to +%% compensate for this a patch for the head of a literal list is +%% applied to all elements of the list. Sometimes this is unnecessary, +%% but as it appears to be infrequent and mostly harmless, this avoids +%% extra complexity in the tracker. +%% +patch_literal_list([H0|T0], {hd,Element,_}=E, Cnt0) -> + {H,Extra2,Cnt1} = patch_literal_term(H0, Element, Cnt0), + {T,Extra1,Cnt2} = patch_literal_term(T0, E, Cnt1), + Extra = Extra2 ++ Extra1, + {Dst,Cnt} = new_var(Cnt2), I = #b_set{op=put_list,dst=Dst,args=[H,T]}, {Dst, [I|Extra], Cnt}; -patch_literal_term(Lit, [], Cnt) -> +patch_literal_list(Lit, {hd,_,_}, Cnt) -> + %% Lit is normally [], but if it is not, we know that it is not a + %% cons that needs to end up on the heap, so it can be left as is. {#b_literal{val=Lit}, [], Cnt}. patch_literal_tuple(Tuple, Elements0, Cnt) -> diff --git a/lib/compiler/test/beam_ssa_check_SUITE_data/tuple_inplace_checks.erl b/lib/compiler/test/beam_ssa_check_SUITE_data/tuple_inplace_checks.erl index 70c28fd9bde4..640da51d64ea 100644 --- a/lib/compiler/test/beam_ssa_check_SUITE_data/tuple_inplace_checks.erl +++ b/lib/compiler/test/beam_ssa_check_SUITE_data/tuple_inplace_checks.erl @@ -22,7 +22,8 @@ -export([do0a/0, do0b/2, different_sizes/2, ambiguous_inits/1, update_record0/0, fc/0, track_update_record/1, - gh8124_a/0, gh8124_b/0]). + gh8124_a/0, gh8124_b/0, + failure_to_patch_list/0]). -record(r, {a=0,b=0,c=0,tot=0}). -record(r1, {a}). @@ -236,3 +237,26 @@ gh8124_b() -> [R] = gh8124_b_inner(), R#r{a = <<"value 2">>}. + +%% Check that the list of tuples is built on the heap. + +failure_to_patch_list() -> +%ssa% () when post_ssa_opt -> +%ssa% T0 = put_tuple(...), +%ssa% L0 = put_list(T0, []), +%ssa% T1 = put_tuple(...), +%ssa% L1 = put_list(T1, L0), +%ssa% _ = call(_, L1). + _ = [ + ftpl(ClassDef) || + ClassDef <- [#r{a={}}, + #r{} + ] + ], + ok. + +ftpl(Ts0) -> +%ssa% (X) when post_ssa_opt -> +%ssa% _ = update_record(inplace, 5, X,...). + A = erlang:timestamp(), + Ts0#r{a=A}.