anchor.diff:                     %% literal, which would trick 'succeeded' into thinking it
                      %% can't fail.
                      type(succeeded, [Arg], Anno, Ts0, Ds0);
  +               #{ Arg := _ } ->
  +                   Args = simplify_args([Arg], Ts0, Sub0),
  +                   type(succeeded, Args, Anno, Ts0, Ds0);
                  #{} ->
  -                   Args = simplify_args([Arg], Sub0, Ts0),
  -                   type(succeeded, Args, Anno, Ts0, Ds0)
  +                   %% The checked instruction has been removed and substituted,
  +                   %% so we can assume it always succeeds.
  +                   #{ Arg := _ } = Sub0,        %Assertion.
  +                   beam_types:make_atom(true)
              end,
       case beam_types:get_singleton_value(Type) of
           {ok, Lit} ->
               Sub = Sub0#{ Dst => #b_literal{val=Lit} },
  -            opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
  +            {Lit, Sub};
           error ->
               Ts = Ts0#{ Dst => Type },
               Ds = Ds0#{ Dst => I },
  -            opt_is([], Ts, Ds, Fdb, D, Sub0, [I | Acc])
  -    end;
  -opt_is([#b_set{args=Args0}=I0|Is],
  -       Ts, Ds, Fdb, D, Sub, Acc) ->
  -    Args = simplify_args(Args0, Sub, Ts),
  -    I = beam_ssa:normalize(I0#b_set{args=Args}),
  -    opt_simplify(I, Is, Ts, Ds, Fdb, D, Sub, Acc);
  -opt_is([], Ts, Ds, Fdb, _D, Sub, Acc) ->
  -    {reverse(Acc), Ts, Ds, Fdb, Sub}.
  -
  -opt_simplify(#b_set{dst=Dst}=I0, Is, Ts0, Ds0, Fdb, D, Sub0, Acc) ->
  -    case simplify(I0, Ts0) of
  +            {I, Ts, Ds, Sub0}
  +    end;
  +simplify(#b_set{dst=Dst,args=Args0}=I0, Ts0, Ds0, _Ls, Sub0) ->
  +    Args = simplify_args(Args0, Ts0, Sub0),
  +    I1 = beam_ssa:normalize(I0#b_set{args=Args}),
  +    case simplify(I1, Ts0) of
           #b_set{}=I2 ->
               I = beam_ssa:normalize(I2),
               Ts = update_types(I, Ts0, Ds0),
               Ds = Ds0#{ Dst => I },
  -            opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc]);
  +            {I, Ts, Ds, Sub0};
           #b_literal{}=Lit ->
               Sub = Sub0#{ Dst => Lit },
  -            opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc);
  +            {Lit, Sub};
           #b_var{}=Var ->
  -            case Is of
  -                [#b_set{op=succeeded,dst=SuccDst,args=[Dst]}] ->
  -                    %% We must remove this 'succeeded' instruction since the
  -                    %% variable it checks is gone.
  -                    Sub = Sub0#{ Dst => Var, SuccDst => #b_literal{val=true} },
  -                    opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
  -                _ ->
  -                    Sub = Sub0#{ Dst => Var},
  -                    opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc)
  -            end
  -    end.
  -
  -opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, Ts, Fdb0, D) ->
  -    I = opt_local_call_return(I0, Callee, Fdb0, Ts),
  -    case Fdb0 of
  -        #{ Callee := #func_info{exported=false,arg_types=ArgTypes0}=Info } ->
  -            Types = argument_types(Args, Ts),
  -
  -            %% Update the argument types of *this exact call*, the types
  -            %% will be joined later when the callee is optimized.
  -            CallId = {D#d.func_id, Dst},
  -            ArgTypes = update_arg_types(Types, ArgTypes0, CallId),
  -
  -            Fdb = Fdb0#{ Callee => Info#func_info{arg_types=ArgTypes} },
  -            {I, Fdb};
  -        #{} ->
  -            %% We can't narrow the argument types of exported functions as they
  -            %% can receive anything as part of an external call.
  -            {I, Fdb0}
  -    end;
  -opt_call(I, _Ts, Fdb, _D) ->
  -    {I, Fdb}.
  -
  -opt_local_call_return(#b_set{args=[_|Args]}=I, Callee, Fdb, Ts) ->
  -    case Fdb of
  -        #{ Callee := #func_info{succ_types=[_|_]=SuccTypes} } ->
  -            ArgTypes = argument_types(Args, Ts),
  -
  -            case return_type(SuccTypes, ArgTypes) of
  -                any -> I;
  -                Type -> beam_ssa:add_anno(result_type, Type, I)
  -            end;
  -        #{} ->
  -            I
  -    end.
  -
  -%% While we have no way to know which arguments a fun will be called with, we
  -%% do know its free variables and can update their types as if this were a
  -%% local call.
  -opt_make_fun(#b_set{op=make_fun,
  -                    dst=Dst,
  -                    args=[#b_local{}=Callee | FreeVars]}=I,
  -             D, Ts0, Ds0, Fdb0) ->
  -    Ts = update_types(I, Ts0, Ds0),
  -    Ds = Ds0#{ Dst => I },
  -    case Fdb0 of
  -        #{ Callee := #func_info{exported=false,arg_types=ArgTypes0}=Info } ->
  -            ArgCount = Callee#b_local.arity - length(FreeVars),
  -
  -            FVTypes = [raw_type(FreeVar, Ts) || FreeVar <- FreeVars],
  -            Types = duplicate(ArgCount, any) ++ FVTypes,
  -
  -            CallId = {D#d.func_id, Dst},
  -            ArgTypes = update_arg_types(Types, ArgTypes0, CallId),
  -
  -            Fdb = Fdb0#{ Callee => Info#func_info{arg_types=ArgTypes} },
  -            {Ts, Ds, Fdb, I};
  -        #{} ->
  -            %% We can't narrow the argument types of exported functions as they
  -            %% can receive anything as part of an external call.
  -            {Ts, Ds, Fdb0, I}
  -    end.
  -
  -update_arg_types([ArgType | ArgTypes], [TypeMap0 | TypeMaps], CallId) ->
  -    TypeMap = TypeMap0#{ CallId => ArgType },
  -    [TypeMap | update_arg_types(ArgTypes, TypeMaps, CallId)];
  -update_arg_types([], [], _CallId) ->
  -    [].
  -
  -%%%
  -%%% Optimization helpers
  -%%%
  -
  -simplify_terminator(#b_br{bool=Bool}=Br0, Sub, Ts, Ds) ->
  -    Br = beam_ssa:normalize(Br0#b_br{bool=simplify_arg(Bool, Sub, Ts)}),
  -    simplify_not(Br, Sub, Ts, Ds);
  -simplify_terminator(#b_switch{arg=Arg0}=Sw0, Sub, Ts, Ds) ->
  -    Arg = simplify_arg(Arg0, Sub, Ts),
  -    Sw = Sw0#b_switch{arg=Arg},
  -
  -    case beam_types:is_boolean_type(raw_type(Arg, Ts)) of
  -        true -> simplify_switch_bool(Sw, Sub, Ts, Ds);
  -        false -> beam_ssa:normalize(Sw)
  -    end;
  -simplify_terminator(#b_ret{arg=Arg}=Ret, Sub, Ts, Ds) ->
  -    %% Reducing the result of a call to a literal (fairly common for 'ok')
  -    %% breaks tail call optimization.
  -    case Ds of
  -        #{ Arg := #b_set{op=call}} -> Ret;
  -        #{} -> Ret#b_ret{arg=simplify_arg(Arg, Sub, Ts)}
  +            Sub = Sub0#{ Dst => Var },
  +            {Var, Sub}
       end.
   
   simplify(#b_set{op={bif,'and'},args=Args}=I, Ts) ->
  
positive.code:       FuncDb :: func_info_db(),
        Result :: {Linear, FuncDb}.
  opt_continue_1(Linear0, Args, Id, Ts, FuncDb0) ->
      UsedOnce = used_once(Linear0, Args),
      FakeCall = #b_set{op=call,args=[#b_remote{mod=#b_literal{val=unknown},
                                                name=#b_literal{val=unknown},
                                                arity=0}]},
      Defs = maps:from_list([{Var,FakeCall#b_set{dst=Var}} ||
                                #b_var{}=Var <- Args]),
  
      D = #d{ func_db=FuncDb0,
              func_id=Id,
              params=Args,
              ds=Defs,
              ls=#{0=>Ts,?EXCEPTION_BLOCK=>Ts},
              once=UsedOnce },
  
      {Linear, FuncDb, NewSucc} = opt(Linear0, D, []),
  
      case FuncDb of
          #{ Id := Entry0 } ->
  
negative.code:     #{func_info:={_Mod, Name, Arity}} = Anno,
      #b_local{name=#b_literal{val=Name}, arity=Arity}.
  
  opt([{L, #b_blk{is=Is0,last=Last0}=Blk0} | Bs],
      Ds0, Ls0, Fdb0, Sub0, SuccTypes0, Meta, Acc) when is_map_key(L, Ls0) ->
  
      #{ L := Ts0 } = Ls0,
  
      {Is, Ts, Ds, Fdb, Sub} = opt_is(Is0, Ts0, Ds0, Ls0, Fdb0, Sub0, Meta, []),
  
      Last1 = simplify_terminator(Last0, Ts, Ds, Sub),
      SuccTypes = update_success_types(Last1, Ts, Ds, Meta, SuccTypes0),
      {Last, Ls} = update_successors(Last1, Ts, Ds, Ls0, Meta#metadata.used_once),
  
      Blk = Blk0#b_blk{is=Is,last=Last},
      opt(Bs, Ds, Ls, Fdb, Sub, SuccTypes, Meta, [{L,Blk} | Acc]);
  opt([_Blk | Bs], Ds, Ls, Fdb, Sub, SuccTypes, Meta, Acc) ->
      %% This block is never reached. Discard it.
      opt(Bs, Ds, Ls, Fdb, Sub, SuccTypes, Meta, Acc);
  opt([], _Ds, _Ls, Fdb, _Sub, SuccTypes, _Meta, Acc) ->
      {reverse(Acc), Fdb, SuccTypes}.
  
  opt_is([#b_set{op=call,args=[#b_local{}|_]=Args0,dst=Dst}=I0|Is],
         Ts0, Ds0, Ls, Fdb0, Sub, Meta, Acc) ->
      Args = simplify_args(Args0, Ts0, Sub),
      I1 = beam_ssa:normalize(I0#b_set{args=Args}),
  
      {#b_set{}=I, Fdb} = opt_call(I1, Ts0, Fdb0, Meta),
  
      Ts = update_types(I, Ts0, Ds0),
      Ds = Ds0#{ Dst => I },
      opt_is(Is, Ts, Ds, Ls, Fdb, Sub, Meta, [I | Acc]);
  opt_is([#b_set{op=make_fun,args=Args0,dst=Dst}=I0|Is],
         Ts0, Ds0, Ls, Fdb0, Sub0, Meta, Acc) ->
      Args = simplify_args(Args0, Ts0, Sub0),
      I1 = beam_ssa:normalize(I0#b_set{args=Args}),
  
      {#b_set{}=I, Fdb} = opt_make_fun(I1, Ts0, Fdb0, Meta),
  
      Ts = update_types(I, Ts0, Ds0),
      Ds = Ds0#{ Dst => I },
      opt_is(Is, Ts, Ds, Ls, Fdb, Sub0, Meta, [I|Acc]);
  opt_is([I0 | Is], Ts0, Ds0, Ls, Fdb, Sub0, Meta, Acc) ->
      case simplify(I0, Ts0, Ds0, Ls, Sub0) of
          {#b_set{}=I, Ts, Ds, Sub} ->
              opt_is(Is, Ts, Ds, Ls, Fdb, Sub, Meta, [I | Acc]);
          {_Value, Sub} ->
              opt_is(Is, Ts0, Ds0, Ls, Fdb, Sub, Meta, Acc)
      end;
  opt_is([], Ts, Ds, _Ls, Fdb, Sub, _Meta, Acc) ->
      {reverse(Acc), Ts, Ds, Fdb, Sub}.
  
  opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, Ts, Fdb0, Meta) ->
      I = opt_local_call_return(I0, Callee, Fdb0, Ts),
      case Fdb0 of
          #{ Callee := #func_info{exported=false,arg_types=ArgTypes0}=Info } ->
              Types = argument_types(Args, Ts),
  
              %% Update the argument types of *this exact call*, the types
              %% will be joined later when the callee is optimized.
              CallId = {Meta#metadata.func_id, Dst},
              ArgTypes = update_arg_types(Types, ArgTypes0, CallId),
  
              Fdb = Fdb0#{ Callee => Info#func_info{arg_types=ArgTypes} },
              {I, Fdb};
          #{} ->
              %% We can't narrow the argument types of exported functions as they
              %% can receive anything as part of an external call. We can still
              %% rely on their return types however.
              {I, Fdb0}
      end;
  opt_call(I, _Ts, Fdb, _Meta) ->
      {I, Fdb}.
  
  opt_local_call_return(#b_set{args=[_|Args]}=I, Callee, Fdb, Ts) ->
      case Fdb of
          #{ Callee := #func_info{succ_types=[_|_]=SuccTypes} } ->
              ArgTypes = argument_types(Args, Ts),
  
              case return_type(SuccTypes, ArgTypes) of
                  any -> I;
                  Type -> beam_ssa:add_anno(result_type, Type, I)
              end;
          #{} ->
              I
      end.
  
  %% While we have no way to know which arguments a fun will be called with, we
  %% do know its free variables and can update their types as if this were a
  %% local call.
  opt_make_fun(#b_set{op=make_fun,
                      dst=Dst,
                      args=[#b_local{}=Callee | FreeVars]}=I,
               Ts, Fdb0, Meta) ->
      case Fdb0 of
          #{ Callee := #func_info{exported=false,arg_types=ArgTypes0}=Info } ->
              ArgCount = Callee#b_local.arity - length(FreeVars),
  
              FVTypes = [raw_type(FreeVar, Ts) || FreeVar <- FreeVars],
              Types = duplicate(ArgCount, any) ++ FVTypes,
  
              CallId = {Meta#metadata.func_id, Dst},
              ArgTypes = update_arg_types(Types, ArgTypes0, CallId),
  
              Fdb = Fdb0#{ Callee => Info#func_info{arg_types=ArgTypes} },
              {I, Fdb};
          #{} ->
              %% We can't narrow the argument types of exported functions as they
              %% can receive anything as part of an external call.
              {I, Fdb0}
      end.
  
  update_arg_types([ArgType | ArgTypes], [TypeMap0 | TypeMaps], CallId) ->
      TypeMap = TypeMap0#{ CallId => ArgType },
      [TypeMap | update_arg_types(ArgTypes, TypeMaps, CallId)];
  update_arg_types([], [], _CallId) ->
      [].
  
  %%%
  %%% Optimization helpers
  %%%
  
  simplify_terminator(#b_br{bool=Bool}=Br0, Ts, Ds, Sub) ->
      Br = beam_ssa:normalize(Br0#b_br{bool=simplify_arg(Bool, Ts, Sub)}),
      simplify_not(Br, Ts, Ds, Sub);
  simplify_terminator(#b_switch{arg=Arg0}=Sw0, Ts, Ds, Sub) ->
      Arg = simplify_arg(Arg0, Ts, Sub),
      Sw = Sw0#b_switch{arg=Arg},
  
      case beam_types:is_boolean_type(raw_type(Arg, Ts)) of
          true -> simplify_switch_bool(Sw, Ts, Ds, Sub);
          false -> beam_ssa:normalize(Sw)
      end;
  simplify_terminator(#b_ret{arg=Arg}=Ret, Ts, Ds, Sub) ->
      %% Reducing the result of a call to a literal (fairly common for 'ok')
      %% breaks tail call optimization.
      case Ds of
          #{ Arg := #b_set{op=call}} -> Ret;
          #{} -> Ret#b_ret{arg=simplify_arg(Arg, Ts, Sub)}
      end.
  
  simplify(#b_set{op=phi,dst=Dst,args=Args0}=I0, Ts0, Ds0, Ls, Sub0) ->
      %% Simplify the phi node by removing all predecessor blocks that no
      %% longer exists or no longer branches to this block.
      Args = [{simplify_arg(Arg, Ts0, Sub0), From} ||
                 {Arg,From} <- Args0, maps:is_key(From, Ls)],
      case all_same(Args) of
          true ->
              %% Eliminate the phi node if there is just one source
              %% value or if the values are identical.
              [{Val,_}|_] = Args,
              Sub = Sub0#{ Dst => Val },
              {Val, Sub};
          false ->
              I = I0#b_set{args=Args},
              Ts = update_types(I, Ts0, Ds0),
              Ds = Ds0#{Dst=>I},
              {I, Ts, Ds, Sub0}
      end;
  simplify(#b_set{op=succeeded,dst=Dst,args=[Arg],anno=Anno}=I,
           Ts0, Ds0, _Ls, Sub0) ->
      Type = case Ds0 of
                 #{ Arg := #b_set{op=call} } ->
                     %% Calls can always throw exceptions and their return types
  