From 5facb518a9ee2d756564eccd92a2c11f334d6282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 2 Jun 2016 09:58:46 +0200 Subject: Eliminate crash for map updates in guards beam_validator would complain that x(1) is uninitialized in a test_heap instruction when attempting to compile the following code with sys_core_fold turned off: foo(M) when not (M#{true := 0}); [M] -> ok. Simplified, the generated BEAM assembly code looked like this: test is_map BadMap x(0) put_map_exact Fail x(0) => x(1) ... jump BooleanStuff BadMap: move ok => x(1) jump Fail BooleanStuff: ... move Boolean => x(2) jump Build Fail: move false => x(2) Build: test_heap 2 3 %% x(0), x(1), x(2) must be live. ... That is, if put_map_exact failed, control would transfer to the label Fail without initializing x(1). Fix that by making sure that x(1) is initilized even if put_map_exact fails: test is_map BadMap x(0) put_map_exact BadLbl x(0) => x(1) ... jump OkLbl BadLbl: move ok => x(1) jump Fail OkLbl: jump BooleanStuff BadMap: move ok => x(1) jump Fail BooleanStuff: ... move Boolean => x(2) jump Build Fail: move false => x(2) Build: test_heap 2 3 %% x(0), x(1), x(2) must be live. ... Note that this situation is rare, and that other optimization passes (beam_dead and beam_jump in particular) will clean up this mess. --- lib/compiler/test/map_SUITE.erl | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'lib/compiler/test') diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index c3c4862794..36e82c1459 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -1287,6 +1287,7 @@ t_guard_update(Config) when is_list(Config) -> first = map_guard_update(#{}, #{x=>first}), second = map_guard_update(#{y=>old}, #{x=>second,y=>old}), third = map_guard_update(#{x=>old,y=>old}, #{x=>third,y=>old}), + bad_map_guard_update(), ok. t_guard_update_large(Config) when is_list(Config) -> @@ -1353,6 +1354,29 @@ map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second; map_guard_update(M1, M2) when M1#{x:=third} =:= M2 -> third; map_guard_update(_, _) -> error. +bad_map_guard_update() -> + do_bad_map_guard_update(fun burns/1), + do_bad_map_guard_update(fun turns/1), + ok. + +do_bad_map_guard_update(Fun) -> + do_bad_map_guard_update_1(Fun, #{}), + do_bad_map_guard_update_1(Fun, #{true=>1}), + ok. + +do_bad_map_guard_update_1(Fun, Value) -> + %% Note: The business with the seemingly redundant fun + %% disables inlining, which would otherwise change the + %% EXIT reason. + {'EXIT',{function_clause,_}} = (catch Fun(Value)), + ok. + +burns(Richmond) when not (Richmond#{true := 0}); [Richmond] -> + specification. + +turns(Richmond) when not (Richmond#{true => 0}); [Richmond] -> + specification. + t_guard_receive(Config) when is_list(Config) -> M0 = #{ id => 0 }, Pid = spawn_link(fun() -> guard_receive_loop() end), -- cgit v1.2.3