Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/compiler/src/beam_block.erl
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ collect({move,S,D}) -> {set,[D],[S],move};
collect({put_list,S1,S2,D}) -> {set,[D],[S1,S2],put_list};
collect({put_tuple2,D,{list,Els}}) -> {set,[D],Els,put_tuple2};
collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}};
collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}};
collect({get_hd,S,D}) -> {set,[D],[S],get_hd};
collect({get_tl,S,D}) -> {set,[D],[S],get_tl};
collect(remove_message) -> {set,[],[],remove_message};
Expand Down
50 changes: 22 additions & 28 deletions lib/compiler/src/beam_call_types.erl
Original file line number Diff line number Diff line change
Expand Up @@ -426,36 +426,30 @@ types(erlang, is_record, [Type,Mod0,Name0]=Args) ->
case {Mod0,Name0} of
{#t_atom{elements=[Mod]},
#t_atom{elements=[Name]}} ->
%% We KNOW that this is_record/3 test is always preceeded
%% by an is_record/1 test. Therefore, `Type` is always a
%% record or a record set.
RetType =
case meet(Type, #t_record{}) of
#t_record{name={Mod,Name}} ->
#t_atom{elements=[true]};
#t_record{name={_,_}} ->
%% Wrong name.
#t_atom{elements=[false]};
none ->
#t_atom{elements=[false]};
#t_record{name=nil} ->
beam_types:make_boolean();
Other ->
case normalize(Other) of
#t_record{name=nil} ->
%% This is always a native record.
Recs = Other#t_union.native_record_set,
maybe
false ?= any(fun(#t_record{name=RecName}) ->
case RecName of
nil -> true;
{Mod,Name} -> true;
_ -> false
end
end, Recs),
#t_atom{elements=[false]}
else
_ ->
beam_types:make_boolean()
end;
_ ->
#t_record{name=Id} ->
case Id of
{Mod,Name} ->
#t_atom{elements=[true]};
{_,_} ->
#t_atom{elements=[false]};
nil ->
beam_types:make_boolean()
end;
#t_union{native_record_set=Recs} ->
case any(fun(#t_record{name=Id}) ->
case Id of
{Mod,Name} -> true;
{_,_} -> false
end
end, Recs) of
false ->
#t_atom{elements=[false]};
true ->
beam_types:make_boolean()
end
end,
Expand Down
1 change: 0 additions & 1 deletion lib/compiler/src/beam_flatten.erl
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ norm({set,[D],[S],fconv}) -> {fconv,S,D};
norm({set,[D],[S1,S2],put_list}) -> {put_list,S1,S2,D};
norm({set,[D],Els,put_tuple2}) -> {put_tuple2,D,{list,Els}};
norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D};
norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
norm({set,[D],[S],get_hd}) -> {get_hd,S,D};
norm({set,[D],[S],get_tl}) -> {get_tl,S,D};
norm({set,[D],[S|Puts],{alloc,R,{put_map,Op,F}}}) ->
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler/src/beam_ssa.erl
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
'bs_get' | 'bs_get_position' | 'bs_match_string' |
'bs_restore' | 'bs_save' | 'bs_set_position' | 'bs_skip' |
'copy' | 'match_fail' | 'put_tuple_arity' |
'set_tuple_element' | 'succeeded' |
'succeeded' |
'update_record'.

-import(lists, [foldl/3,mapfoldl/3,reverse/1,reverse/2,sort/1]).
Expand Down
15 changes: 1 addition & 14 deletions lib/compiler/src/beam_ssa_codegen.erl
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,6 @@ classify_heap_need(recv_next) -> gc;
classify_heap_need(remove_message) -> neutral;
classify_heap_need(require_stack) -> neutral;
classify_heap_need(resume) -> gc;
classify_heap_need(set_tuple_element) -> gc;
classify_heap_need(succeeded) -> neutral;
classify_heap_need(wait_timeout) -> gc.

Expand Down Expand Up @@ -2372,8 +2371,6 @@ cg_instr(bs_set_position, [Ctx,Pos], _Dst) ->
[{bs_set_position,Ctx,Pos}];
cg_instr(build_stacktrace, Args, Dst) ->
setup_args(Args) ++ [build_stacktrace|copy({x,0}, Dst)];
cg_instr(set_tuple_element=Op, [New,Tuple,{integer,Index}], _Dst) ->
[{Op,New,Tuple,Index}];
cg_instr({float,get}, [Src], Dst) ->
[{fmove,Src,Dst}];
cg_instr({float,put}, [Src], Dst) ->
Expand Down Expand Up @@ -2427,9 +2424,6 @@ cg_test(put_record, Fail, [{atom,empty},Id|Ss], Dst, #cg_set{anno=Anno}=Set) ->
cg_test(put_record, Fail, [Arg,Id|Ss], Dst, #cg_set{anno=Anno}=Set) ->
Live = get_live(Set),
[line(Anno),{put_record,Fail,Id,Arg,Dst,Live,{list,Ss}}];
cg_test(set_tuple_element=Op, Fail, Args, Dst, Set) ->
{f,0} = Fail, %Assertion.
cg_instr(Op, Args, Dst, Set);
cg_test(raw_raise, _Fail, Args, Dst, _I) ->
cg_instr(raw_raise, Args, Dst);
cg_test(resume, _Fail, [_,_]=Args, Dst, _I) ->
Expand Down Expand Up @@ -2483,14 +2477,7 @@ cg_bs_skip(Fail, [{atom,Type}|Ss0], Set) ->
%% Utf8/16/32.
[Ctx,Live,field_flags(Flags, Set)]
end,
case {Type,Ss} of
{binary,[_,{atom,all},1,_]} ->
[];
{binary,[R,{atom,all},U,_]} ->
[{test,bs_test_unit,Fail,[R,U]}];
{_,_} ->
[{test,Op,Fail,Ss}]
end.
[{test,Op,Fail,Ss}].

field_flags(Flags, #cg_set{anno=#{location:={File,Line}}}) ->
{field_flags,[{anno,[Line,{file,File}]}|Flags]};
Expand Down
62 changes: 14 additions & 48 deletions lib/compiler/src/beam_ssa_pre_codegen.erl
Original file line number Diff line number Diff line change
Expand Up @@ -404,63 +404,37 @@ bs_restores_is([#b_set{anno=#{ensured := _},
%% instruction, so there will never be a restore to this
%% position.
Start = bs_subst_ctx(NewPos, CtxChain),
case Args of
[#b_literal{val=skip},_FromPos,_Type,_Flags,#b_literal{val=all},_] ->
case is_skip_all(Args) of
true ->
%% This instruction will be optimized away. (The unit test
%% part of it has been take care of by the preceding
%% bs_ensure instruction.) All positions will be
%% unchanged.
SPos = FPos = SPos0,
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
[_,FromPos|_] ->
false ->
[_,FromPos|_] = Args,
SPos = SPos0#{Start := NewPos},
FPos = SPos0#{Start := FromPos},
bs_restores_is(Is, CtxChain, SPos, FPos, Rs)
end;
bs_restores_is([#b_set{op=bs_match,dst=NewPos,args=Args}=I|Is],
bs_restores_is([#b_set{op=bs_match,dst=NewPos,args=Args}|Is],
CtxChain, SPos0, _FPos, Rs0) ->
false = is_skip_all(Args), %Assertion.
Start = bs_subst_ctx(NewPos, CtxChain),
[_,FromPos|_] = Args,
case SPos0 of
#{Start:=FromPos} ->
%% Same position, no restore needed.
SPos = case bs_match_type(I) of
plain ->
%% Update position to new position.
SPos0#{Start:=NewPos};
_ ->
%% Position will not change (test_unit
%% instruction or no instruction at
%% all).
SPos0
end,
SPos = SPos0#{Start:=NewPos},
FPos = SPos0,
bs_restores_is(Is, CtxChain, SPos, FPos, Rs0);
#{Start:=_} ->
%% Different positions, might need a restore instruction.
case bs_match_type(I) of
none ->
%% This is a tail test that will be optimized away.
%% There's no need to do a restore, and all
%% positions are unchanged.
FPos = SPos0,
bs_restores_is(Is, CtxChain, SPos0, FPos, Rs0);
test_unit ->
%% This match instruction will be replaced by
%% a test_unit instruction. We will need a
%% restore. The new position will be the position
%% restored to (NOT NewPos).
SPos = SPos0#{Start:=FromPos},
FPos = SPos,
Rs = Rs0#{NewPos=>{Start,FromPos}},
bs_restores_is(Is, CtxChain, SPos, FPos, Rs);
plain ->
%% Match or skip. Position will be changed.
SPos = SPos0#{Start:=NewPos},
FPos = SPos0#{Start:=FromPos},
Rs = Rs0#{NewPos=>{Start,FromPos}},
bs_restores_is(Is, CtxChain, SPos, FPos, Rs)
end
%% Match or skip. Position will be changed.
SPos = SPos0#{Start := NewPos},
FPos = SPos0#{Start := FromPos},
Rs = Rs0#{NewPos => {Start,FromPos}},
bs_restores_is(Is, CtxChain, SPos, FPos, Rs)
end;
bs_restores_is([#b_set{op=bs_extract,args=[FromPos|_]}|Is],
CtxChain, SPos, _FPos, Rs) ->
Expand Down Expand Up @@ -511,15 +485,8 @@ bs_restores_is([], _CtxChain, SPos, _FPos, Rs) ->
FPos = SPos,
{SPos, FPos, Rs}.

bs_match_type(#b_set{args=[#b_literal{val=skip},_Ctx,
#b_literal{val=binary},_Flags,
#b_literal{val=all},#b_literal{val=U}]}) ->
case U of
1 -> none;
_ -> test_unit
end;
bs_match_type(_) ->
plain.
is_skip_all([#b_literal{val=skip},_,_,_,#b_literal{val=all},_]) -> true;
is_skip_all(_) -> false.

%% Call instructions leave the match position in an undefined state,
%% requiring us to invalidate each affected argument.
Expand Down Expand Up @@ -2946,7 +2913,6 @@ use_zreg(recv_marker_bind) -> yes;
use_zreg(recv_marker_clear) -> yes;
use_zreg(remove_message) -> yes;
use_zreg(require_stack) -> yes;
use_zreg(set_tuple_element) -> yes;
use_zreg(succeeded) -> yes;
use_zreg(wait_timeout) -> yes;
%% There's no way we can combine these into a test instruction, so we must
Expand Down
9 changes: 8 additions & 1 deletion lib/compiler/test/bs_match_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2374,7 +2374,9 @@ throw_after_byte(<<_,_/binary>>) ->
matches_on_parameter(Config) when is_list(Config) ->
%% This improves coverage for matching on "naked" parameters.
{<<"urka">>, <<"a">>} = matches_on_parameter_1(<<"gurka">>),
ok = (catch matches_on_parameter_2(<<"10001110101">>, 0)).
?assertThrow(ok, matches_on_parameter_2(<<"10001110101">>, 0)),
<<"urka">> = matches_on_parameter_3(<<"gurka">>),
ok.

matches_on_parameter_1(Bin) ->
<<"g", A/binary>> = Bin,
Expand All @@ -2388,6 +2390,11 @@ matches_on_parameter_2(Bin, Offset) ->
_ -> [Bit | matches_on_parameter_2(Bin, Offset + 1)]
end.

matches_on_parameter_3(Bin) ->
<<"g", A/binary>> = Bin,
<<_,_,"rk", _/binary>> = Bin,
A.

big_positions(Config) when is_list(Config) ->
%% This provides coverage for when match context positions no longer fit
%% into an immediate on 32-bit platforms.
Expand Down
25 changes: 25 additions & 0 deletions lib/compiler/test/native_record_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,24 @@ is_record_bif(Config) ->
false = is_record(Config, a),
false = is_record(Config, ?MODULE, a),

case is_list(Config) of
true ->
false = is_record(Config, empty),
false = is_record(Config, ?MODULE, empty),
false = is_record(Config, a),
false = is_record(Config, ?MODULE, a)
end,

false = is_record(not_a_record(id(a)), ?MODULE, empty),
false = is_record(not_a_record(id(0)), ?MODULE, empty),

true = is_record(some_record(#a{x=0,y=1}), ?MODULE, a),
false = is_record(some_record(#a{x=0,y=1}), ?MODULE, empty),
true = is_record(some_record(#b{}), ?MODULE, b),
false = is_record(some_record(#b{}), ?MODULE, empty),
true = is_record(some_record(#empty{}), ?MODULE, empty),
false = is_record(some_record(#empty{}), ?MODULE, 'div'),

BR = id(#b{}),
true = is_record(BR),
true = is_record(BR, b),
Expand Down Expand Up @@ -497,6 +515,13 @@ is_record_bif(Config) ->

ok.

not_a_record(A) when is_atom(A) -> a;
not_a_record(I) when is_integer(I) -> 42.

some_record(#a{}=R) -> R;
some_record(#b{}=R) -> R;
some_record(#_{}=R) -> R.

-record #r_two{r=1,s=2}.
-record #r_three{r=1,s=2,t=3}.

Expand Down
Loading