diff options
author | Björn-Egil Dahlberg <[email protected]> | 2015-08-31 16:07:46 +0200 |
---|---|---|
committer | Björn-Egil Dahlberg <[email protected]> | 2015-09-04 17:58:03 +0200 |
commit | d0784035abb22f4f385c8a8737a7b15c3741bbca (patch) | |
tree | 93713fcdc1aa0728b3a65ba6a7fef2ee82f641e9 | |
parent | c9bbba0db169ece606b02162057e4681b8fb1ce4 (diff) | |
download | otp-d0784035abb22f4f385c8a8737a7b15c3741bbca.tar.gz otp-d0784035abb22f4f385c8a8737a7b15c3741bbca.tar.bz2 otp-d0784035abb22f4f385c8a8737a7b15c3741bbca.zip |
compiler: Fix get_map_elements register corruption
Instruction get_map_elements might destroy target registers when the fail-label is taken.
Only seen for patterns with two, and only two, target registers.
Specifically: we copy one register, and then jump.
foo(A,#{a := V1, b := V2}) -> ...
foo(A,#{b := V}) -> ...
call foo(value, #{a=>whops, c=>42}).
corresponding assembler:
{test,is_map,{f,5},[{x,1}]}.
{get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}.
%% if 'a' exists but not 'b' {x,1} is overwritten, jump {f,7}
{move,{integer,1},{x,0}}.
{call_only,3,{f,10}}.
{label,7}.
{get_map_elements,{f,8},{x,1},{list,[{atom,b},{x,2}]}}.
%% {x,1} (src) is read with a corrupt value
{move,{x,0},{x,1}}.
{move,{integer,2},{x,0}}.
{call_only,3,{f,10}}.
The fix is to remove 'opt_moves' pass for get_map_elements instruction
in the case of two or more destinations.
Reported-by: Valery Tikhonov
-rw-r--r-- | lib/compiler/src/beam_block.erl | 4 |
1 files changed, 3 insertions, 1 deletions
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 2def3de7f3..0321b1c07b 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -251,7 +251,9 @@ opt([{set,_,_,{line,_}}=Line1, {set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is]) when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg -> opt([Line2,I2,Line1,I1|Is]); -opt([{set,Ds0,Ss,Op}|Is0]) -> +opt([{set,[_|_],_Ss,{get_map_elements,_F}}=I|Is]) -> + [I|opt(Is)]; +opt([{set,Ds0,Ss,Op}|Is0]) -> {Ds,Is} = opt_moves(Ds0, Is0), [{set,Ds,Ss,Op}|opt(Is)]; opt([{'%live',_,_}=I|Is]) -> |