anchor.diff:      #{func_info:={_Mod, Name, Arity}} = Anno,
       #b_local{name=#b_literal{val=Name}, arity=Arity}.
   
  -opt([{L,Blk}|Bs], #d{ls=Ls}=D, Acc) ->
  -    case Ls of
  -        #{L:=Ts} ->
  -            opt_1(L, Blk, Bs, Ts, D, Acc);
  +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};
           #{} ->
  -            %% This block is never reached. Discard it.
  -            opt(Bs, D, Acc)
  -    end;
  -opt([], D, Acc) ->
  -    #d{func_db=FuncDb,succ_types=NewSucc} = D,
  -    {reverse(Acc), FuncDb, NewSucc}.
  -
  -opt_1(L, #b_blk{is=Is0,last=Last0}=Blk0, Bs, Ts0,
  -      #d{ds=Ds0,sub=Sub0,func_db=Fdb0}=D0, Acc) ->
  -    {Is,Ts,Ds,Fdb,Sub} = opt_is(Is0, Ts0, Ds0, Fdb0, D0, Sub0, []),
  -
  -    D1 = D0#d{ds=Ds,sub=Sub,func_db=Fdb},
  -
  -    Last1 = simplify_terminator(Last0, Sub, Ts, Ds),
  -    {Last, D} = update_successors(Last1, Ts, D1),
  -
  -    Blk = Blk0#b_blk{is=Is,last=Last},
  -    opt(Bs, D, [{L,Blk}|Acc]).
  -
  -opt_is([#b_set{op=phi,dst=Dst,args=Args0}=I0|Is],
  -       Ts0, Ds0, Fdb, #d{ls=Ls}=D, Sub0, Acc) ->
  +            %% 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, Sub0, Ts0),From} ||
  +    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},
  -            opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc);
  +            Sub = Sub0#{ Dst => Val },
  +            {Val, Sub};
           false ->
               I = I0#b_set{args=Args},
               Ts = update_types(I, Ts0, Ds0),
               Ds = Ds0#{Dst=>I},
  -            opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc])
  -    end;
  -opt_is([#b_set{op=call,args=Args0}=I0|Is],
  -       Ts, Ds, Fdb0, D, Sub, Acc) ->
  -    Args = simplify_args(Args0, Sub, Ts),
  -    I1 = beam_ssa:normalize(I0#b_set{args=Args}),
  -    {I, Fdb} = opt_call(I1, Ts, Fdb0, D),
  -    opt_simplify(I, Is, Ts, Ds, Fdb, D, Sub, Acc);
  -opt_is([#b_set{op=make_fun,args=Args0}=I0|Is],
  -       Ts0, Ds0, Fdb0, D, Sub0, Acc) ->
  -    Args = simplify_args(Args0, Sub0, Ts0),
  -    I1 = beam_ssa:normalize(I0#b_set{args=Args}),
  -    {Ts,Ds,Fdb,I} = opt_make_fun(I1, D, Ts0, Ds0, Fdb0),
  -    opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc]);
  -opt_is([#b_set{op=succeeded,args=[Arg],dst=Dst,anno=Anno}=I],
  -       Ts0, Ds0, Fdb, D, Sub0, Acc) ->
  +            {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
  
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: 
  -define(UNICODE_MAX, (16#10FFFF)).
  
  %% Constant metadata used by both subpasses.
  -record(metadata,
          { func_id :: func_id(),
            params :: [beam_ssa:b_var()],
            used_once :: cerl_sets:set(beam_ssa:b_var()) }).
  
  -type type_db() :: #{ beam_ssa:var_name() := type() }.
  
  -spec opt_start(Linear, Args, Anno, FuncDb) -> {Linear, FuncDb} when
        Linear :: [{non_neg_integer(), beam_ssa:b_blk()}],
  