From cae485301cc0f7164f9f7f0ac50bcdb2d6abf199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 30 Aug 2024 16:43:38 +0200 Subject: [PATCH] wip --- lib/compiler/src/beam_ssa_codegen.erl | 429 ++++++++++---------- lib/compiler/test/beam_debug_info_SUITE.erl | 1 - 2 files changed, 207 insertions(+), 223 deletions(-) diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index e16e2835edac..51e6285ab67e 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -134,8 +134,7 @@ function(#b_function{anno=Anno,bs=Blocks,args=Args}, AtomMod, St0) -> Labels = (St4#cg.labels)#{0=>Entry,?EXCEPTION_BLOCK=>0}, St5 = St4#cg{labels=Labels,used_labels=gb_sets:singleton(Entry), ultimate_fail=Ult}, - Xregs = init_def_regs(Args, St5), - {Body,St} = cg_fun(Blocks, Xregs, NoBsMatch, St5#cg{fc_label=Fi}), + {Body,St} = cg_fun(Blocks, Args, NoBsMatch, St5#cg{fc_label=Fi}), Asm = [{label,Fi},line(Anno), {func_info,AtomMod,{atom,Name},Arity}] ++ add_parameter_annos(Body, Anno) ++ @@ -148,11 +147,6 @@ function(#b_function{anno=Anno,bs=Blocks,args=Args}, AtomMod, St0) -> erlang:raise(Class, Error, Stack) end. -init_def_regs(Args, #cg{debug_info=#{}}) -> - ordsets:from_list(Args); -init_def_regs(_, _) -> - none. - assert_exception_block(Blocks) -> %% Assertion: ?EXCEPTION_BLOCK must be a call erlang:error(badarg). case Blocks of @@ -179,7 +173,7 @@ add_parameter_annos([{label, _}=Entry | Body], Anno) -> [Entry | sort(Annos)] ++ Body. -cg_fun(Blocks, Xregs, NoBsMatch, St0) -> +cg_fun(Blocks, Args, NoBsMatch, St0) -> Linear0 = linearize(Blocks), St1 = collect_catch_labels(Linear0, St0), Linear1 = need_heap(Linear0), @@ -187,111 +181,14 @@ cg_fun(Blocks, Xregs, NoBsMatch, St0) -> Linear3 = liveness(Linear2, St1), Linear4 = defined(Linear3, St1), Linear5 = opt_allocate(Linear4, St1), - Linear6 = fix_wait_timeout(Linear5), - Linear = defined_regs(Linear6, Xregs, St1), - %% io:format("~p\n", [Linear]), - %% io:format("~p\n", [Xregs]), - %% io:format("~p\n", [St1]), - %% io:format("~p\n", [Xregs]), - %% io:nl(), - St2 = collect_debug_info(Linear, St1), + Linear = fix_wait_timeout(Linear5), + St2 = collect_debug_info(Linear, Args, St1), {Asm,St} = cg_linear(Linear, St2), case NoBsMatch of true -> {Asm,St}; false -> {bs_translate(Asm),St} end. -collect_debug_info(Linear, #cg{regs=Regs,debug_info=DebugInfo0}=St) - when is_map(DebugInfo0) -> - FrameSzMap = #{0 => none}, - VarMap = #{}, - DebugInfo = collect_debug_info_blk(Linear, Regs, FrameSzMap, - VarMap, DebugInfo0), - St#cg{debug_info=DebugInfo}; -collect_debug_info(_Linear, #cg{debug_info=none}=St) -> St. - -collect_debug_info_blk([{L,#cg_blk{is=Is,last=Last}}|Bs], - Regs, FrameSzMap0, VarMap0, Info0) -> - FrameSize0 = map_get(L, FrameSzMap0), - {VarMap,Info,FrameSize} = - collect_debug_info_is(Is, Regs, FrameSize0, VarMap0, Info0), - Successors = successors(Last), - FrameSzMap = foldl(fun(Succ, Acc) -> - Acc#{Succ => FrameSize} - end, FrameSzMap0, Successors), - collect_debug_info_blk(Bs, Regs, FrameSzMap, VarMap, Info); -collect_debug_info_blk([], _Regs, _FrameSzMap, _VarMap, Info) -> - Info. - -collect_debug_info_is([#cg_alloc{stack=FrameSize}|Is], - Regs, _FrameSize, VarMap, Info) - when is_integer(FrameSize) -> - collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); -collect_debug_info_is([#cg_set{anno=#{was_phi := true},op=copy}|Is], - Regs, FrameSize, VarMap, Info) -> - %% This copy operation originates from a phi node. The source and - %% destination are not equivalent and must not be added to VarMap. - collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); -collect_debug_info_is([#cg_set{anno=Anno,op=copy,dst=#b_var{name=Dst}, - args=[#b_var{name=Src}]}|Is], - Regs, FrameSize, VarMap0, Info) -> - VarMap = case Anno of - #{delayed_yreg_copy := true} -> - VarMap0#{Src => Dst}; - #{} -> - VarMap0#{Dst => Src} - end, - collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); -collect_debug_info_is([#cg_set{anno=Anno,op=executable_line, - args=[#b_literal{val=Index}]}|Is], - Regs, FrameSize, VarMap, Info0) -> - #{def_regs := DefRegs, - alias := Alias, - literals := Literals0} = Anno, - Literals1 = [{get_original_name(#b_var{name=Var}, VarMap, Alias),Val} || - {Val,Var} <- Literals0], - Literals = [{hd(Vars),[{literal,Val}]} || - {Vars,Val} <- Literals1, Vars =/= []], - LiveRegs = [{map_get(V, Regs),get_original_name(V, VarMap, Alias)} || - V <- DefRegs, - not is_beam_register(V)], - - %% io:format("~p:: ~p\n", [Index,Anno]), - - S0 = sofs:family(LiveRegs, [{reg,[variable]}]), - S1 = sofs:family_to_relation(S0), - S2 = sofs:converse(S1), - S3 = sofs:relation_to_family(S2), - S = Literals ++ sofs:to_external(S3), - Info = Info0#{Index => {FrameSize,S}}, - collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); -collect_debug_info_is([_|Is], Regs, FrameSize, VarMap, Info) -> - collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); -collect_debug_info_is([], _Regs, FrameSize, VarMap, Info) -> - {VarMap,Info,FrameSize}. - -get_original_name(#b_var{name=Name}, VarMap, Alias) -> - Vs = [Name|get_original_name_1(Name, VarMap, Alias)], - [V || V <- Vs, is_original_variable(V)]. - -get_original_name_1(Name, VarMap, Alias) -> - case VarMap of - #{Name := Var} -> - [Var|get_original_name_1(Var, VarMap, Alias)]; - #{} -> - [] - end ++ maps:get(Name, Alias, []). - -is_original_variable(Name) when is_atom(Name) -> - <> = atom_to_binary(Name), - if - C =:= $_ -> true; - $A =< C, C =< $Z -> true; - $À =< C, C =< $Þ, C =/= $× -> true; - true -> false - end; -is_original_variable(_Name) -> false. - %% collect_catch_labels(Linear, St) -> St. %% Collect all catch labels (labels for blocks that begin %% with 'landingpad' instructions) for later use. @@ -810,121 +707,6 @@ need_live_anno(Op) -> _ -> false end. -%%% -%%% If BEAM debug information should be included in the BEAM file, -%%% annotate `executable_line` instructions with all variables that -%%% have been defined and are still available in a BEAM register. -%%% - -defined_regs(Linear, none, #cg{}) -> - Linear; -defined_regs(Linear, Xregs, #cg{regs=Regs}) -> - def_regs(Linear, #{0 => Xregs}, Regs). - -def_regs([{L,#cg_blk{is=Is0,last=Last}=Blk0}|Bs], DefMap0, Regs) -> - Def0 = map_get(L, DefMap0), - %% io:format("~p: ~p\n", [L,Def0]), - {Is,Def,MaybeDef} = def_regs_is(Is0, Regs, Def0, []), - %% io:format("~p: ~p\n", [L,Def]), - %% io:nl(), - DefMap = def_successors(Last, Def, MaybeDef, DefMap0), - Blk = Blk0#cg_blk{is=Is}, - [{L,Blk}|def_regs(Bs, DefMap, Regs)]; -def_regs([], _, _) -> []. - -def_regs_is([#cg_alloc{live=Live}=I|Is], Regs, Def0, Acc) when is_integer(Live) -> - Def = trim_xregs(Def0, Live, Regs), - def_regs_is(Is, Regs, Def, [I|Acc]); -def_regs_is([#cg_set{op=succeeded,args=[Var]}=I], _Regs, Def, Acc) -> - %% Var will only be defined on the success branch of the `br` - %% for this block. - MaybeDef = [Var], - {reverse(Acc, [I]),Def,MaybeDef}; -def_regs_is([#cg_set{op=kill_try_tag,args=[#b_var{}=Tag]}=I|Is], Regs, Def0, Acc) -> - Def = ordsets:del_element(Tag, Def0), - def_regs_is(Is, Regs, Def, [I|Acc]); -def_regs_is([#cg_set{op=catch_end,dst=Dst,args=[#b_var{}=Tag|_]}=I|Is], Regs, Def0, Acc) -> - Def1 = trim_xregs(Def0, 0, Regs), - Def2 = ordsets:del_element(Tag, Def1), - Def = ordsets:add_element(Dst, Def2), - def_regs_is(Is, Regs, Def, [I|Acc]); -def_regs_is([#cg_set{anno=Anno0,op=executable_line}=I0|Is], Regs, Def, Acc) -> - Anno = Anno0#{def_regs => Def}, - I = I0#cg_set{anno=Anno}, - %% io:format("~p\n", [Def]), - %% io:format("~p\n", [I0]), - %% io:format("~p\n", [I]), - %% io:nl(), - def_regs_is(Is, Regs, Def, [I|Acc]); -def_regs_is([#cg_set{anno=Anno,dst=Dst,op={bif,Bif},args=Args}=I|Is], Regs, Def0, Acc) -> - Def1 = case is_gc_bif(Bif, Args) of - true -> - #{live := Live} = Anno, - trim_xregs(Def0, Live, Regs); - false -> - Def0 - end, - case Regs of - #{Dst := {Tag,_}=R} when Tag =:= x; Tag =:= y -> - Def2 = delete_xreg(Def1, R, Regs), - Def = ordsets:add_element(Dst, Def2), - def_regs_is(Is, Regs, Def, [I|Acc]); - #{} -> - def_regs_is(Is, Regs, Def1, [I|Acc]) - end; -def_regs_is([#cg_set{anno=Anno,dst=Dst}=I|Is], Regs, Def0, Acc) -> - Def1 = case Anno of - #{live := Live} -> trim_xregs(Def0, Live, Regs); - #{} -> Def0 - end, - Def2 = case Anno of - #{kill_yregs := KillYregs} -> - Def1 -- KillYregs; - #{} -> - Def1 - end, - case Anno of - #{clobbers := true} -> - Def3 = trim_xregs(Def2, 0, Regs), - Def = case Regs of - #{Dst := {Tag,_}=R} when Tag =:= x; Tag =:= y -> - Def4 = delete_xreg(Def3, R, Regs), - ordsets:add_element(Dst, Def4); - #{} -> - Def3 - end, - def_regs_is(Is, Regs, Def, [I|Acc]); - #{} -> - case Regs of - #{Dst := {Tag,_}=R} when Tag =:= x; Tag =:= y -> - Def3 = delete_xreg(Def2, R, Regs), - Def = ordsets:add_element(Dst, Def3), - def_regs_is(Is, Regs, Def, [I|Acc]); - #{} -> - def_regs_is(Is, Regs, Def1, [I|Acc]) - end - end; -def_regs_is([], _Regs, Def, Acc) -> - {reverse(Acc),Def,[]}. - -trim_xregs([V|Vs], Live, Regs) -> - case Regs of - #{V := {x,R}} when R < Live -> - [V|trim_xregs(Vs, Live, Regs)]; - #{V := {y,_}}-> - [V|trim_xregs(Vs, Live, Regs)]; - #{} -> - trim_xregs(Vs, Live, Regs) - end; -trim_xregs([], _, _) -> []. - -delete_xreg([V|Vs], R, Regs) -> - case Regs of - #{V := R} -> Vs; - #{} -> [V|delete_xreg(Vs, R, Regs)] - end; -delete_xreg([], _, _) -> []. - %%% %%% Add the following annotations for Y registers: %%% @@ -1186,6 +968,209 @@ fix_wait_timeout_is([I|Is], Acc) -> fix_wait_timeout_is(Is, [I|Acc]); fix_wait_timeout_is([], _Acc) -> no. +%%% +%%% Collect debug information; that is the mapping from variable names +%%% to registers. +%%% + +collect_debug_info(Linear0, Args, #cg{regs=Regs,debug_info=DebugInfo0}=St) + when is_map(DebugInfo0) -> + Def0 = ordsets:from_list(Args), + Linear = anno_defined_regs(Linear0, Def0, Regs), + FrameSzMap = #{0 => none}, + VarMap = #{}, + DebugInfo = collect_debug_info_blk(Linear, Regs, FrameSzMap, + VarMap, DebugInfo0), + St#cg{debug_info=DebugInfo}; +collect_debug_info(_Linear, _Args, #cg{debug_info=none}=St) -> St. + +collect_debug_info_blk([{L,#cg_blk{is=Is,last=Last}}|Bs], + Regs, FrameSzMap0, VarMap0, Info0) -> + FrameSize0 = map_get(L, FrameSzMap0), + {VarMap,Info,FrameSize} = + collect_debug_info_is(Is, Regs, FrameSize0, VarMap0, Info0), + Successors = successors(Last), + FrameSzMap = foldl(fun(Succ, Acc) -> + Acc#{Succ => FrameSize} + end, FrameSzMap0, Successors), + collect_debug_info_blk(Bs, Regs, FrameSzMap, VarMap, Info); +collect_debug_info_blk([], _Regs, _FrameSzMap, _VarMap, Info) -> + Info. + +collect_debug_info_is([#cg_alloc{stack=FrameSize}|Is], + Regs, _FrameSize, VarMap, Info) + when is_integer(FrameSize) -> + collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); +collect_debug_info_is([#cg_set{anno=#{was_phi := true},op=copy}|Is], + Regs, FrameSize, VarMap, Info) -> + %% This copy operation originates from a phi node. The source and + %% destination are not equivalent and must not be added to VarMap. + collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); +collect_debug_info_is([#cg_set{anno=Anno,op=copy,dst=#b_var{name=Dst}, + args=[#b_var{name=Src}]}|Is], + Regs, FrameSize, VarMap0, Info) -> + VarMap = case Anno of + #{delayed_yreg_copy := true} -> + VarMap0#{Src => Dst}; + #{} -> + VarMap0#{Dst => Src} + end, + collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); +collect_debug_info_is([#cg_set{anno=Anno,op=executable_line, + args=[#b_literal{val=Index}]}|Is], + Regs, FrameSize, VarMap, Info0) -> + #{def_regs := DefRegs, + alias := Alias, + literals := Literals0} = Anno, + Literals1 = [{get_original_name(#b_var{name=Var}, VarMap, Alias),Val} || + {Val,Var} <- Literals0], + Literals = [{hd(Vars),[{literal,Val}]} || + {Vars,Val} <- Literals1, Vars =/= []], + LiveRegs = [{map_get(V, Regs),get_original_name(V, VarMap, Alias)} || + V <- DefRegs, + not is_beam_register(V)], + S0 = sofs:family(LiveRegs, [{reg,[variable]}]), + S1 = sofs:family_to_relation(S0), + S2 = sofs:converse(S1), + S3 = sofs:relation_to_family(S2), + S = Literals ++ sofs:to_external(S3), + Info = Info0#{Index => {FrameSize,S}}, + collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); +collect_debug_info_is([_|Is], Regs, FrameSize, VarMap, Info) -> + collect_debug_info_is(Is, Regs, FrameSize, VarMap, Info); +collect_debug_info_is([], _Regs, FrameSize, VarMap, Info) -> + {VarMap,Info,FrameSize}. + +get_original_name(#b_var{name=Name}, VarMap, Alias) -> + Vs = [Name|get_original_name_1(Name, VarMap, Alias)], + [V || V <- Vs, is_original_variable(V)]. + +get_original_name_1(Name, VarMap, Alias) -> + case VarMap of + #{Name := Var} -> + [Var|get_original_name_1(Var, VarMap, Alias)]; + #{} -> + [] + end ++ maps:get(Name, Alias, []). + +is_original_variable(Name) when is_atom(Name) -> + <> = atom_to_binary(Name), + if + C =:= $_ -> true; + $A =< C, C =< $Z -> true; + $À =< C, C =< $Þ, C =/= $× -> true; + true -> false + end; +is_original_variable(_Name) -> false. + +%%% +%%% Annotate `executable_line` instructions with all variables that +%%% have been defined and are still available in a BEAM register. +%%% +%%% This pass is run when collection of BEAM debug information has +%%% been requested. +%%% + +anno_defined_regs(Linear, Def, Regs) -> + def_regs(Linear, #{0 => Def}, Regs). + +def_regs([{L,#cg_blk{is=Is0,last=Last}=Blk0}|Bs], DefMap0, Regs) -> + Def0 = map_get(L, DefMap0), + {Is,Def,MaybeDef} = def_regs_is(Is0, Regs, Def0, []), + DefMap = def_successors(Last, Def, MaybeDef, DefMap0), + Blk = Blk0#cg_blk{is=Is}, + [{L,Blk}|def_regs(Bs, DefMap, Regs)]; +def_regs([], _, _) -> []. + +def_regs_is([#cg_alloc{live=Live}=I|Is], Regs, Def0, Acc) when is_integer(Live) -> + Def = trim_xregs(Def0, Live, Regs), + def_regs_is(Is, Regs, Def, [I|Acc]); +def_regs_is([#cg_set{op=succeeded,args=[Var]}=I], _Regs, Def, Acc) -> + %% Var will only be defined on the success branch of the `br` + %% for this block. + MaybeDef = [Var], + {reverse(Acc, [I]),Def,MaybeDef}; +def_regs_is([#cg_set{op=kill_try_tag,args=[#b_var{}=Tag]}=I|Is], Regs, Def0, Acc) -> + Def = ordsets:del_element(Tag, Def0), + def_regs_is(Is, Regs, Def, [I|Acc]); +def_regs_is([#cg_set{op=catch_end,dst=Dst,args=[#b_var{}=Tag|_]}=I|Is], Regs, Def0, Acc) -> + Def1 = trim_xregs(Def0, 0, Regs), + Def2 = ordsets:del_element(Tag, Def1), + Def = ordsets:add_element(Dst, Def2), + def_regs_is(Is, Regs, Def, [I|Acc]); +def_regs_is([#cg_set{anno=Anno0,op=executable_line}=I0|Is], Regs, Def, Acc) -> + Anno = Anno0#{def_regs => Def}, + I = I0#cg_set{anno=Anno}, + def_regs_is(Is, Regs, Def, [I|Acc]); +def_regs_is([#cg_set{anno=Anno,dst=Dst,op={bif,Bif},args=Args}=I|Is], Regs, Def0, Acc) -> + Def1 = case is_gc_bif(Bif, Args) of + true -> + #{live := Live} = Anno, + trim_xregs(Def0, Live, Regs); + false -> + Def0 + end, + case Regs of + #{Dst := {Tag,_}=R} when Tag =:= x; Tag =:= y -> + Def2 = delete_xreg(Def1, R, Regs), + Def = ordsets:add_element(Dst, Def2), + def_regs_is(Is, Regs, Def, [I|Acc]); + #{} -> + def_regs_is(Is, Regs, Def1, [I|Acc]) + end; +def_regs_is([#cg_set{anno=Anno,dst=Dst}=I|Is], Regs, Def0, Acc) -> + Def1 = case Anno of + #{live := Live} -> trim_xregs(Def0, Live, Regs); + #{} -> Def0 + end, + Def2 = case Anno of + #{kill_yregs := KillYregs} -> + Def1 -- KillYregs; + #{} -> + Def1 + end, + case Anno of + #{clobbers := true} -> + Def3 = trim_xregs(Def2, 0, Regs), + Def = case Regs of + #{Dst := {Tag,_}=R} when Tag =:= x; Tag =:= y -> + Def4 = delete_xreg(Def3, R, Regs), + ordsets:add_element(Dst, Def4); + #{} -> + Def3 + end, + def_regs_is(Is, Regs, Def, [I|Acc]); + #{} -> + case Regs of + #{Dst := {Tag,_}=R} when Tag =:= x; Tag =:= y -> + Def3 = delete_xreg(Def2, R, Regs), + Def = ordsets:add_element(Dst, Def3), + def_regs_is(Is, Regs, Def, [I|Acc]); + #{} -> + def_regs_is(Is, Regs, Def1, [I|Acc]) + end + end; +def_regs_is([], _Regs, Def, Acc) -> + {reverse(Acc),Def,[]}. + +trim_xregs([V|Vs], Live, Regs) -> + case Regs of + #{V := {x,R}} when R < Live -> + [V|trim_xregs(Vs, Live, Regs)]; + #{V := {y,_}}-> + [V|trim_xregs(Vs, Live, Regs)]; + #{} -> + trim_xregs(Vs, Live, Regs) + end; +trim_xregs([], _, _) -> []. + +delete_xreg([V|Vs], R, Regs) -> + case Regs of + #{V := R} -> Vs; + #{} -> [V|delete_xreg(Vs, R, Regs)] + end; +delete_xreg([], _, _) -> []. + %%% %%% Here follows the main code generation functions. %%% diff --git a/lib/compiler/test/beam_debug_info_SUITE.erl b/lib/compiler/test/beam_debug_info_SUITE.erl index 41f0961a193d..1cf9dd75a29d 100644 --- a/lib/compiler/test/beam_debug_info_SUITE.erl +++ b/lib/compiler/test/beam_debug_info_SUITE.erl @@ -289,7 +289,6 @@ get_debug_info(Mod, Beam) -> {"LitT",Literals0}] = Chunks, Literals = decode_literal_table(Literals0), CallOp = beam_opcodes:opcode(call, 2), - %% io:format("~p: ~p\n", [Mod,DebugInfo0]), DebugInfo = decode_debug_info(DebugInfo0, Literals, CallOp), lists:zip(lists:seq(1, length(DebugInfo)), DebugInfo).