diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index aaa82274453..840847d9cd7 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -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}; diff --git a/lib/compiler/src/beam_call_types.erl b/lib/compiler/src/beam_call_types.erl index 1784efccb5a..b5d692b1518 100644 --- a/lib/compiler/src/beam_call_types.erl +++ b/lib/compiler/src/beam_call_types.erl @@ -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, diff --git a/lib/compiler/src/beam_flatten.erl b/lib/compiler/src/beam_flatten.erl index e32425fbdbd..19779a6e712 100644 --- a/lib/compiler/src/beam_flatten.erl +++ b/lib/compiler/src/beam_flatten.erl @@ -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}}}) -> diff --git a/lib/compiler/src/beam_ssa.erl b/lib/compiler/src/beam_ssa.erl index 3959c3b9bfd..cf4251aa14d 100644 --- a/lib/compiler/src/beam_ssa.erl +++ b/lib/compiler/src/beam_ssa.erl @@ -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]). diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index e9f03fa7b18..187126ac547 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -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. @@ -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) -> @@ -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) -> @@ -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]}; diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl index 415346070f4..1df772f62d8 100644 --- a/lib/compiler/src/beam_ssa_pre_codegen.erl +++ b/lib/compiler/src/beam_ssa_pre_codegen.erl @@ -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) -> @@ -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. @@ -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 diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index daa8a954328..7831e47ac90 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -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, @@ -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. diff --git a/lib/compiler/test/native_record_SUITE.erl b/lib/compiler/test/native_record_SUITE.erl index 7271bef0290..ff5d6bfd618 100644 --- a/lib/compiler/test/native_record_SUITE.erl +++ b/lib/compiler/test/native_record_SUITE.erl @@ -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), @@ -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}.