[m-rev.] for review: fix mantis bug 572

Julien Fischer jfischer at opturion.com
Tue Feb 20 11:08:09 AEDT 2024


On Tue, 20 Feb 2024, Zoltan Somogyi wrote:

> Fix a bug in liveness of if-then-elses that can't succeed.
> 
> This fixes Mantis bug #572.
> 
> compiler/liveness.m:
>     As above. The details are described in tests/valid/bug572.m.
> 
> compiler/hlds_out_goal.m:
>     Add a comment to direct readers to what they may be looking for.
> 
> tests/valid/bug572.m:
>     Add the test case for Mantis #572.
> 
> tests/valid/Mmakefile:
>     Enable the new test case.
> 
> diff --git a/compiler/hlds_out_goal.m b/compiler/hlds_out_goal.m
> index 3fe3668d3..52b5a2201 100644
> --- a/compiler/hlds_out_goal.m
> +++ b/compiler/hlds_out_goal.m
> @@ -47,6 +47,9 @@
>      % Print a goal in a way that is suitable for debugging the compiler
>      % (but necessarily for anything else).
>      %
> +    % If what you are after is a short, less-than-one-line description
> +    % of a goal, then look in hlds_des.m.

s/hlds_des/hlds_desc/

> +    %
>  :- pred dump_goal(io.text_output_stream::in, module_info::in,
>      var_name_source::in, tvarset::in, inst_varset::in, hlds_goal::in,
>      io::di, io::uo) is det.
> @@ -54,6 +57,9 @@
>      % Print a goal in a way that is suitable for debugging the compiler
>      % (but necessarily for anything else), followed by a newline.
>      %
> +    % If what you are after is a short, less-than-one-line description
> +    % of a goal, then look in hlds_des.m.

Again.

> +    %
>  :- pred dump_goal_nl(io.text_output_stream::in, module_info::in,
>      var_name_source::in, tvarset::in, inst_varset::in, hlds_goal::in,
>      io::di, io::uo) is det.

...

> diff --git a/compiler/liveness.m b/compiler/liveness.m
> index f6eab3564..dae075a08 100644
> --- a/compiler/liveness.m
> +++ b/compiler/liveness.m

...

> @@ -781,49 +814,69 @@ detect_deadness_in_goal(LiveInfo, !.Liveness, Goal0, Goal, !Deadness) :-

...

>          else
>              set_of_var.union(DeadnessCond, DeadnessElse, Deadness),
>              set_of_var.intersect(Deadness, CompletedNonLocals,
>                  CompletedNonLocalDeadness),
> -            InstmapReachable = no,
> +            % If the end of the if-then-else as a whole is not reachable,
> +            % then neither branch's end is reachable.
> +            BranchEndReachable = this_branch_end_is_not_reachable,
> +            % ZZZ

What's the ZZZ for?

>              add_branch_pre_deaths(DeadnessCond, Deadness0,
> -                CompletedNonLocalDeadness, InstmapReachable, Cond1, Cond),
> +                CompletedNonLocalDeadness, BranchEndReachable, _, Cond1, Cond),
>              add_branch_pre_deaths(DeadnessElse, Deadness0,
> -                CompletedNonLocalDeadness, InstmapReachable, Else1, Else)
> +                CompletedNonLocalDeadness, BranchEndReachable, _, Else1, Else)
>          ),
>          !:Deadness = Deadness,
>          GoalExpr = if_then_else(Vars, Cond, Then, Else),

...

> diff --git a/tests/valid/bug572.m b/tests/valid/bug572.m
> index e69de29bb..a316424c1 100644
> --- a/tests/valid/bug572.m
> +++ b/tests/valid/bug572.m
> @@ -0,0 +1,189 @@

...

> +% In this case, the sanity check that aborted the compiler was in the last
> +% pass, but the problem was caused by code in the second, which included
> +% TypeClassInfo_for_argument_21 in the post-death set of the goal that
> +% took out of it the type info variable for T, thus killing it before
> +% the end of the else part. It did this because this (compiler-generated)
> +% goal was the last reference it saw, and *due to all branches through
> +% the switch on TArgs throwing exceptions*, it knew that the end of the
> +% procedure body was not reachable. This reasoning was correct, but it
> +% disregarded the presence of the sanity checks in the last pass.
> +%
> +% I (zs) three possible approaches for fixing this bug.

*see* three possible approaches

> +% Approach one would simply disable all sanity checks that insist on
> +% agreement on the set of live vars at the ends of different branches
> +% in a branched control structure. This would include not just the sanity
> +% checks in the fourth pass in the liveness module, but all similar checks
> +% in all of the compiler's analysis and code generation modules.
> +% This would be discard a defense against bugs in quite a large part
> +% of the compiler, and hence would be a pretty bad idea.
> +%
> +% Approach two would be to make TypeClassInfo_for_argument_21 live at
> +% the end of the else part by adding it to the else part goal's post-birth set.
> +% The post-birth set is specifically designed for this use case; it is
> +% only ever used to tell the code generator "execution will never reach
> +% the end of this goal, but consider this variable to be born at the very end
> +% of it when comparing the set of live variables at the end of this branch
> +% with the livenesses at the ends of the other branches in this structure".
> +% This would almost certainly work, but it would mean that this variable
> +% first dies in the else part, and then gets born again. Besides this being
> +% inbelegant, the seeming resurrection of a previously-dead variable

s/inbelegant/inelegant/

> +% has also historically posed problems for the optional delay_death pass.
> +%
> +% The fix I (zs) chose uses approach three, which is to make
> +% TypeClassInfo_for_argument_21 live at the end of the else part
> +% by preventing it being killed in the first place. It was being killed
> +% by code that handled the arms of the branches control structures

s/branches/branched/

> +% (switches, disjunctions, or if-then-else) whose handling of arms that
> +% cannot succeed was set up to handle situations in which some *other* arm
> +% of the branched control structure *could* succeed. Its design seems to have
> +% considered the possibility that *none* of the branches could succeed, but
> +% the implementation did not :-( The fix is to make the implementation match
> +% the old design, which is: when we find that the current branch being
> +% considered cannot succeed but none of other branches can succeed either,
> +% then don't kill the variables that we used to kill.

That looks fine otherwise.

Julien.


More information about the reviews mailing list