summaryrefslogtreecommitdiff
path: root/lib/compiler
diff options
context:
space:
mode:
authorJohn Högberg <john@erlang.org>2021-01-28 13:34:16 +0100
committerJohn Högberg <john@erlang.org>2021-01-28 13:38:57 +0100
commit424167b36657b0546074b3d88c8511b370fa84ea (patch)
tree0189455327dda6153f0e0531cb4b5a4f03b4252f /lib/compiler
parent7d5fa20a463395045fef400445514e59db0a71ae (diff)
parent1dcc1a3c571b6927ccbac17189b86f3b2b3bf018 (diff)
downloaderlang-424167b36657b0546074b3d88c8511b370fa84ea.tar.gz
Merge branch 'maint'
* maint: beam_validator: Ignore 'fcheckerror' / 'fclearerror'
Diffstat (limited to 'lib/compiler')
-rw-r--r--lib/compiler/src/beam_validator.erl145
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl26
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/freg_state.S59
-rw-r--r--lib/compiler/test/float_SUITE.erl22
4 files changed, 69 insertions, 183 deletions
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 2ab751f6e9..02eecea29c 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -193,8 +193,6 @@ validate_0([{function, Name, Arity, Entry, Code} | Fs], Module, Level, Ft) ->
hl=0,
%%Available heap size for floats.
hf=0,
- %% Floating point state.
- fls=undefined,
%% List of hot catch/try tags
ct=[],
%% Previous instruction was setelement/3.
@@ -307,8 +305,9 @@ init_function_args(-1, Vst) ->
init_function_args(X, Vst) ->
init_function_args(X - 1, create_term(any, argument, [], {x,X}, Vst)).
-kill_heap_allocation(St) ->
- St#st{h=0,hl=0,hf=0}.
+kill_heap_allocation(#vst{current=St0}=Vst) ->
+ St = St0#st{h=0,hl=0,hf=0},
+ Vst#vst{current=St}.
validate_branches(MFA, Vst) ->
#vst{ branched=Targets0, labels=Labels0 } = Vst,
@@ -397,7 +396,6 @@ vi({fmove,Src,{fr,_}=Dst}, Vst) ->
set_freg(Dst, Vst);
vi({fmove,{fr,_}=Src,Dst}, Vst0) ->
assert_freg_set(Src, Vst0),
- assert_fls(checked, Vst0),
Vst = eat_heap_float(Vst0),
create_term(#t_float{}, fmove, [], Dst, Vst);
vi({kill,Reg}, Vst) ->
@@ -704,9 +702,8 @@ vi({gc_bif,Op,{f,Fail},Live,Ss,Dst}, Vst0) ->
%% Heap allocations and X registers are killed regardless of whether we
%% fail or not, as we may fail after GC.
- #vst{current=St0} = Vst0,
- St = kill_heap_allocation(St0),
- Vst = prune_x_regs(Live, Vst0#vst{current=St}),
+ Vst1 = kill_heap_allocation(Vst0),
+ Vst = prune_x_regs(Live, Vst1),
validate_bif(gc_bif, Op, Fail, Ss, Dst, Vst0, Vst);
@@ -762,13 +759,10 @@ vi({wait,{f,Lbl}}, Vst) ->
vi({wait_timeout,{f,Lbl},Src}, Vst0) ->
assert_no_exception(Lbl),
- %% Note that the receive state is not cleared since we may re-enter the
- %% loop while waiting. If we time out we'll be transferred to a timeout
- %% instruction that clears the state.
assert_term(Src, Vst0),
verify_y_init(Vst0),
- Vst = branch(Lbl, prune_x_regs(0, Vst0)),
+ Vst = branch(Lbl, schedule_out(0, Vst0)),
branch(?EXCEPTION_LABEL, Vst);
%%
@@ -799,9 +793,11 @@ vi({try_end,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst) ->
vi({try_case,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst0) ->
case get_tag_type(Reg, Vst0) of
{trytag,_Fail}=Tag ->
- %% Kill the catch tag and all x registers.
+ %% Kill the catch tag and all other state (as if we've been
+ %% scheduled out with no live registers). Only previously allocated
+ %% Y registers are alive at this point.
Vst1 = kill_catch_tag(Reg, Vst0),
- Vst2 = prune_x_regs(0, Vst1),
+ Vst2 = schedule_out(0, Vst1),
%% Class:Error:Stacktrace
ClassType = #t_atom{elements=[error,exit,throw]},
@@ -812,7 +808,6 @@ vi({try_case,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst0) ->
error({wrong_tag_type,Type})
end;
vi(build_stacktrace, Vst0) ->
- assert_float_checked(Vst0),
verify_y_init(Vst0),
verify_live(1, Vst0),
@@ -971,26 +966,13 @@ vi({fconv,Src,{fr,_}=Dst}, Vst) ->
assert_term(Src, Vst),
branch(?EXCEPTION_LABEL, Vst,
- fun(FailVst) ->
- %% This is a hack to supress assert_float_checked/1 in
- %% fork_state/2, since this instruction is legal even when
- %% the state is unchecked.
- set_fls(checked, FailVst)
- end,
fun(SuccVst0) ->
SuccVst = update_type(fun meet/2, number, Src, SuccVst0),
set_freg(Dst, SuccVst)
end);
vi(fclearerror, Vst) ->
- case get_fls(Vst) of
- undefined -> ok;
- checked -> ok;
- Fls -> error({bad_floating_point_state,Fls})
- end,
- set_fls(cleared, Vst);
-vi({fcheckerror,_}, Vst0) ->
- assert_fls(cleared, Vst0),
- Vst = set_fls(checked, Vst0),
+ Vst;
+vi({fcheckerror, _}, Vst) ->
branch(?EXCEPTION_LABEL, Vst);
%%
@@ -1162,8 +1144,6 @@ validate_var_info([], _Reg, Vst) ->
%% The stackframe must have a known size and be initialized.
%% Does not return to the instruction following the call.
validate_tail_call(Deallocate, Func, Live, #vst{current=#st{numy=NumY}}=Vst0) ->
- assert_float_checked(Vst0),
-
verify_y_init(Vst0),
verify_live(Live, Vst0),
verify_call_args(Func, Live, Vst0),
@@ -1190,18 +1170,15 @@ validate_tail_call(Deallocate, Func, Live, #vst{current=#st{numy=NumY}}=Vst0) ->
%% The instruction will return to the instruction following the call.
validate_body_call(Func, Live,
#vst{current=#st{numy=NumY}}=Vst) when is_integer(NumY)->
- assert_float_checked(Vst),
-
verify_y_init(Vst),
verify_live(Live, Vst),
verify_call_args(Func, Live, Vst),
- SuccFun = fun(#vst{current=St0}=SuccVst0) ->
+ SuccFun = fun(SuccVst0) ->
{RetType, _, _} = call_types(Func, Live, SuccVst0),
true = RetType =/= none, %Assertion.
- St = St0#st{f=init_fregs()},
- SuccVst = prune_x_regs(0, SuccVst0#vst{current=St}),
+ SuccVst = schedule_out(0, SuccVst0),
create_term(RetType, call, [], {x,0}, SuccVst)
end,
@@ -1217,13 +1194,6 @@ validate_body_call(Func, Live,
validate_body_call(_, _, #vst{current=#st{numy=NumY}}) ->
error({allocated, NumY}).
-assert_float_checked(Vst) ->
- case get_fls(Vst) of
- undefined -> ok;
- checked -> ok;
- Fls -> error({unsafe_instruction,{float_error_state,Fls}})
- end.
-
init_try_catch_branch(Kind, Dst, Fail, Vst0) ->
assert_no_exception(Fail),
@@ -1360,7 +1330,6 @@ verify_return(#vst{current=#st{recv_state=State}}) when State =/= none ->
%% Returning in the middle of a receive loop will ruin the next receive.
error({return_in_receive,State});
verify_return(Vst) ->
- assert_float_checked(Vst),
verify_no_ct(Vst),
kill_state(Vst).
@@ -1373,7 +1342,6 @@ verify_return(Vst) ->
%%
validate_bif(Kind, Op, Fail, Ss, Dst, OrigVst, Vst) ->
- assert_float_checked(Vst),
case will_bif_succeed(Op, Ss, Vst) of
yes ->
%% This BIF cannot fail (neither throw nor branch), make sure it's
@@ -1761,25 +1729,31 @@ test_heap(Heap, Live, Vst0) ->
heap_alloc(Heap, Vst).
heap_alloc(Heap, #vst{current=St0}=Vst) ->
- St1 = kill_heap_allocation(St0),
- St = heap_alloc_1(Heap, St1),
+ {HeapWords, Floats, Funs} = heap_alloc_1(Heap),
+
+ St = St0#st{h=HeapWords,hf=Floats,hl=Funs},
+
Vst#vst{current=St}.
-heap_alloc_1({alloc,Alloc}, St) ->
- heap_alloc_2(Alloc, St);
-heap_alloc_1(HeapWords, St) when is_integer(HeapWords) ->
- St#st{h=HeapWords}.
-
-heap_alloc_2([{words,HeapWords}|T], St0) ->
- St = St0#st{h=HeapWords},
- heap_alloc_2(T, St);
-heap_alloc_2([{funs,Funs}|T], St0) ->
- St = St0#st{hl=Funs},
- heap_alloc_2(T, St);
-heap_alloc_2([{floats,Floats}|T], St0) ->
- St = St0#st{hf=Floats},
- heap_alloc_2(T, St);
-heap_alloc_2([], St) -> St.
+heap_alloc_1({alloc, Alloc}) ->
+ heap_alloc_2(Alloc, 0, 0, 0);
+heap_alloc_1(HeapWords) when is_integer(HeapWords) ->
+ {HeapWords, 0, 0}.
+
+heap_alloc_2([{words, HeapWords} | T], 0, Floats, Funs) ->
+ heap_alloc_2(T, HeapWords, Floats, Funs);
+heap_alloc_2([{floats, Floats} | T], HeapWords, 0, Funs) ->
+ heap_alloc_2(T, HeapWords, Floats, Funs);
+heap_alloc_2([{funs, Funs} | T], HeapWords, Floats, 0) ->
+ heap_alloc_2(T, HeapWords, Floats, Funs);
+heap_alloc_2([], HeapWords, Floats, Funs) ->
+ {HeapWords, Floats, Funs}.
+
+schedule_out(Live, Vst0) when is_integer(Live) ->
+ Vst1 = prune_x_regs(Live, Vst0),
+ Vst2 = kill_heap_allocation(Vst1),
+ Vst = kill_fregs(Vst2),
+ update_receive_state(none, Vst).
prune_x_regs(Live, #vst{current=St0}=Vst) when is_integer(Live) ->
#st{fragile=Fragile0,xs=Xs0} = St0,
@@ -1818,22 +1792,10 @@ assert_arities(_) -> error(bad_tuple_arity_list).
%%%
-%%% Floating point checking.
-%%%
-%%% Possible values for the fls field (=floating point error state).
-%%%
-%%% undefined - Undefined (initial state). No float operations allowed.
-%%%
-%%% cleared - fclearerror/0 has been executed. Float operations
-%%% are allowed (such as fadd).
+%%% Floating point helpers.
%%%
-%%% checked - fcheckerror/1 has been executed. It is allowed to
-%%% move values out of floating point registers.
-%%%
-%%% The following instructions may be executed in any state:
-%%%
-%%% fconv Src {fr,_}
-%%% fmove Src {fr,_} %% Move INTO floating point register.
+%%% fconv Src {fr,_}
+%%% fmove Src {fr,_} %% Move known float INTO floating point register.
%%%
is_float_arith_bif(fadd, [_, _]) -> true;
@@ -1843,25 +1805,16 @@ is_float_arith_bif(fnegate, [_]) -> true;
is_float_arith_bif(fsub, [_, _]) -> true;
is_float_arith_bif(_, _) -> false.
-validate_float_arith_bif(Ss, Dst, Vst0) ->
- _ = [assert_freg_set(S, Vst0) || S <- Ss],
- assert_fls(cleared, Vst0),
- Vst = set_fls(cleared, Vst0),
+validate_float_arith_bif(Ss, Dst, Vst) ->
+ _ = [assert_freg_set(S, Vst) || S <- Ss],
set_freg(Dst, Vst).
-assert_fls(Fls, Vst) ->
- case get_fls(Vst) of
- Fls -> ok;
- OtherFls -> error({bad_floating_point_state,OtherFls})
- end.
-
-set_fls(Fls, #vst{current=#st{}=St}=Vst) when is_atom(Fls) ->
- Vst#vst{current=St#st{fls=Fls}}.
-
-get_fls(#vst{current=#st{fls=Fls}}) when is_atom(Fls) -> Fls.
-
init_fregs() -> 0.
+kill_fregs(#vst{current=St0}=Vst) ->
+ St = St0#st{f=init_fregs()},
+ Vst#vst{current=St}.
+
set_freg({fr,Fr}=Freg, #vst{current=#st{f=Fregs0}=St}=Vst) ->
check_limit(Freg),
Bit = 1 bsl Fr,
@@ -2306,7 +2259,7 @@ new_value(Type, Op, Ss, #vst{current=#st{vs=Vs0}=St,ref_ctr=Counter}=Vst) ->
{Ref, Vst#vst{current=St#st{vs=Vs},ref_ctr=Counter+1}}.
kill_catch_tag(Reg, #vst{current=#st{ct=[Tag|Tags]}=St}=Vst0) ->
- Vst = Vst0#vst{current=St#st{ct=Tags,fls=undefined}},
+ Vst = Vst0#vst{current=St#st{ct=Tags}},
Tag = get_tag_type(Reg, Vst), %Assertion.
kill_tag(Reg, Vst).
@@ -2568,10 +2521,6 @@ branch(Fail, Vst) ->
fork_state(?EXCEPTION_LABEL, Vst0) ->
#vst{current=#st{ct=CatchTags,numy=NumY}} = Vst0,
- %% Floating-point exceptions must be checked before any other kind of
- %% exception can be raised.
- assert_float_checked(Vst0),
-
%% The stack will be scanned looking for a catch tag, so all Y registers
%% must be initialized.
verify_y_init(Vst0),
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 0aa7b246fe..8295e09d36 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -28,7 +28,7 @@
dead_code/1,
overwrite_catchtag/1,overwrite_trytag/1,accessing_tags/1,bad_catch_try/1,
cons_guard/1,
- freg_range/1,freg_uninit/1,freg_state/1,
+ freg_range/1,freg_uninit/1,
bad_bin_match/1,bad_dsetel/1,
state_after_fault_in_catch/1,no_exception_in_catch/1,
undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1,
@@ -63,7 +63,7 @@ groups() ->
unsafe_catch,dead_code,
overwrite_catchtag,overwrite_trytag,accessing_tags,
bad_catch_try,cons_guard,freg_range,freg_uninit,
- freg_state,bad_bin_match,bad_dsetel,
+ bad_bin_match,bad_dsetel,
state_after_fault_in_catch,no_exception_in_catch,
undef_label,illegal_instruction,failing_gc_guard_bif,
map_field_lists,cover_bin_opt,val_dsetel,
@@ -290,28 +290,6 @@ freg_uninit(Config) when is_list(Config) ->
{uninitialized_reg,{fr,0}}}}] = Errors,
ok.
-freg_state(Config) when is_list(Config) ->
- Errors = do_val(freg_state, Config),
- [{{t,sum_1,2},
- {{bif,fmul,{f,0},[{fr,0},{fr,1}],{fr,0}},
- 6,
- {bad_floating_point_state,undefined}}},
- {{t,sum_2,2},
- {{fmove,{fr,0},{x,0}},
- 8,
- {bad_floating_point_state,cleared}}},
- {{t,sum_3,2},
- {{bif,'-',{f,0},[{x,1},{x,0}],{x,1}},
- 8,
- {unsafe_instruction,{float_error_state,cleared}}}},
- {{t,sum_4,2},
- {{fcheckerror,{f,0}},
- 4,
- {bad_floating_point_state,undefined}}},
- {{t,sum_5,2},
- {fclearerror,5,{bad_floating_point_state,cleared}}}] = Errors,
- ok.
-
bad_bin_match(Config) when is_list(Config) ->
[{{t,t,1},{return,5,{match_context,{x,0}}}}] =
do_val(bad_bin_match, Config),
diff --git a/lib/compiler/test/beam_validator_SUITE_data/freg_state.S b/lib/compiler/test/beam_validator_SUITE_data/freg_state.S
deleted file mode 100644
index 7466763482..0000000000
--- a/lib/compiler/test/beam_validator_SUITE_data/freg_state.S
+++ /dev/null
@@ -1,59 +0,0 @@
-{module, freg_state}. %% version = 0
-
-{exports, [{sum_1,2},{sum_2,2},{sum_3,2},{sum_4,2},{sum_5,2}]}.
-
-{attributes, []}.
-
-
-{function, sum_1, 2, 2}.
- {label,1}.
- {func_info,{atom,t},{atom,sum_1},2}.
- {label,2}.
- {fconv,{x,0},{fr,0}}.
- {fconv,{x,1},{fr,1}}.
- {bif,fmul,{f,0},[{fr,0},{fr,1}],{fr,0}}.
- {'%live',1}.
- return.
-
-{function, sum_2, 2, 4}.
- {label,3}.
- {func_info,{atom,t},{atom,sum_2},2}.
- {label,4}.
- {fconv,{x,0},{fr,0}}.
- {fconv,{x,1},{fr,1}}.
- fclearerror.
- {bif,fmul,{f,0},[{fr,0},{fr,1}],{fr,0}}.
- {fmove,{fr,0},{x,0}}.
- {'%live',1}.
- return.
-
-{function, sum_3, 2, 6}.
- {label,5}.
- {func_info,{atom,t},{atom,sum_3},2}.
- {label,6}.
- {fconv,{x,0},{fr,0}}.
- {fconv,{x,1},{fr,1}}.
- fclearerror.
- {bif,fmul,{f,0},[{fr,0},{fr,1}],{fr,0}}.
- {bif,'-',{f,0},[{x,1},{x,0}],{x,1}}.
- {fcheckerror,{f,0}}.
- {fmove,{fr,0},{x,0}}.
- {'%live',1}.
- return.
-
-{function, sum_4, 2, 8}.
- {label,6}.
- {func_info,{atom,t},{atom,sum_4},2}.
- {label,8}.
- {fcheckerror,{f,0}}.
- {fmove,{fr,0},{x,0}}.
- {'%live',1}.
- return.
-
-{function, sum_5, 2, 10}.
- {label,9}.
- {func_info,{atom,t},{atom,sum_5},2}.
- {label,10}.
- fclearerror.
- fclearerror.
- return.
diff --git a/lib/compiler/test/float_SUITE.erl b/lib/compiler/test/float_SUITE.erl
index c9c0beb70f..44d54de84c 100644
--- a/lib/compiler/test/float_SUITE.erl
+++ b/lib/compiler/test/float_SUITE.erl
@@ -22,7 +22,7 @@
init_per_group/2,end_per_group/2,
pending/1,bif_calls/1,math_functions/1,mixed_float_and_int/1,
subtract_number_type/1,float_followed_by_guard/1,
- fconv_line_numbers/1,float_zero/1]).
+ fconv_line_numbers/1,float_zero/1,exception_signals/1]).
-include_lib("common_test/include/ct.hrl").
@@ -31,7 +31,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[pending, bif_calls, math_functions, float_zero,
mixed_float_and_int, subtract_number_type,
- float_followed_by_guard,fconv_line_numbers].
+ float_followed_by_guard,fconv_line_numbers,
+ exception_signals].
groups() ->
[].
@@ -226,5 +227,22 @@ fconv_line_numbers_1(A) ->
false
end, Stacktrace).
+%% ERL-1471: compiler generated invalid 'fclearerror' / 'fcheckerror'
+%% sequences.
+exception_signals(Config) when is_list(Config) ->
+ 2.0 = exception_signals_1(id(25), id(true), []),
+ 2.0 = exception_signals_1(id(25), id(false), []),
+ 2.0 = exception_signals_1(id(25.0), id(true), []),
+ 2.0 = exception_signals_1(id(25.0), id(false), []),
+ ok.
+
+exception_signals_1(Width, Value, _Opts) ->
+ Height = Width / 25.0,
+ _Middle = case Value of
+ true -> Width / 2.0;
+ false -> 0
+ end,
+ _More = Height + 1.
+
id(I) -> I.