%%% -*- erlang-indent-level: 2 -*-
%%%----------------------------------------------------------------------
%%% Author: Kostis Sagonas
%%%
%%% Contains code examples that exhibited bugs in the HiPE compiler.
%%%----------------------------------------------------------------------
-module(basic_bugs_hipe).
-export([test/0]).
test() ->
ok = test_ets_bifs(),
ok = test_bit_shift(),
ok = test_match_big_list(),
ok = test_unsafe_bsl(),
ok = test_unsafe_bsr(),
ok = test_R12B5_seg_fault(),
ok.
%%-----------------------------------------------------------------------
%% From: Bjorn Gustavsson
%%
%% This code, if HiPE compiled, crashed like this (on SPARC)
%%
%% (gdb) where
%% #0 fullsweep_heap (p=0x2c60dc, new_sz=610, objv=0xffbee8b4, nobj=3)
%% at beam/ggc.c:1060
%% #1 0x7ff24 in erts_garbage_collect (p=0x2c60dc, need=2, objv=0x1128fc, ...)
%% at beam/ggc.c:1648
%% #2 0xab6fc in hipe_mode_switch (p=0x2c60dc, cmd=704512, reg=0x1128fc)
%% at hipe/hipe_mode_switch.c:180
%% #3 0x8e27c in process_main () at beam/beam_emu.c:3314
%% #4 0x31338 in erl_start (argc=9, argv=0xffbeed5c) at beam/erl_init.c:936
%% #5 0x2d9f4 in main (argc=9, argv=0xffbeed5c) at sys/unix/erl_main.c:28
%%
%% A guess at what could be the problem: From R8, many ets BIFs trap
%% to other ets BIFs with a *different* arity (i.e. they have more or
%% less arguments). I have probably forgotten to mention that subtle
%% change.
%%-----------------------------------------------------------------------
test_ets_bifs() ->
Seed = {1032, 15890, 22716},
put(random_seed, Seed),
do_random_test().
do_random_test() ->
OrdSet = ets:new(xxx, [ordered_set]),
Set = ets:new(xxx, []),
do_n_times(fun() ->
Key = create_random_string(25),
Value = create_random_tuple(25),
ets:insert(OrdSet, {Key, Value}),
ets:insert(Set, {Key, Value})
end, 5000),
%% io:format("~nData inserted~n"),
do_n_times(fun() ->
I = random:uniform(25),
Key = create_random_string(I) ++ '_',
L1 = ets_match_object(OrdSet, {Key, '_'}),
L2 = lists:sort(ets_match_object(Set, {Key, '_'})),
case L1 == L2 of
false ->
%% io:format("~p != ~p~n", [L1, L2]),
exit({not_eq, L1, L2});
true ->
ok
end
end, 2000),
%% io:format("~nData matched~n"),
ets:match_delete(OrdSet, '_'),
ets:match_delete(Set, '_'),
ok.
create_random_string(0) ->
[];
create_random_string(OfLength) ->
C = case random:uniform(2) of
1 -> (random:uniform($Z - $A + 1) - 1) + $A;
_ -> (random:uniform($z - $a + 1) - 1) + $a
end,
[C | create_random_string(OfLength - 1)].
create_random_tuple(OfLength) ->
list_to_tuple([list_to_atom([X]) || X <- create_random_string(OfLength)]).
ets_match_object(Tab,Expr) ->
case random:uniform(2) of
1 -> ets:match_object(Tab,Expr);
_ -> match_object_chunked(Tab,Expr)
end.
match_object_chunked(Tab,Expr) ->
match_object_chunked_collect(ets:match_object(Tab, Expr,
random:uniform(1999) + 1)).
match_object_chunked_collect('$end_of_table') ->
[];
match_object_chunked_collect({Results, Continuation}) ->
Results ++ match_object_chunked_collect(ets:match_object(Continuation)).
do_n_times(_, 0) ->
ok;
do_n_times(Fun, N) ->
Fun(),
case N rem 1000 of
0 -> ok; %% WAS: io:format(".");
_ -> ok
end,
do_n_times(Fun, N - 1).
%%-----------------------------------------------------------------------
%% From: Niclas Pehrsson
%% Date: Apr 20, 2006
%%
%% We found something weird with the bit shifting in HiPE. It seems
%% that bsr in some cases shifts the bits in the wrong way...
%%
%% Fixed about 10 mins afterwards; was a bug in constant propagation.
%%-----------------------------------------------------------------------
test_bit_shift() ->
1 = plain_shift(), % 1
6 = length_list_plus(), % 6
0 = shift_length_list(), % 0
1 = shift_length_list_plus(), % 1
1 = shift_length_list_plus2(), % 1
24 = shift_length_list_plus_bsl(), % 24
1 = shift_fun(), % 1
%% {1, 6, 0, 1, 1, 24, 1} = {A, B, C, D, E, F, G},
ok.
plain_shift() ->
6 bsr 2.
length_list() ->
length([0,0]).
length_list_plus() ->
length([0,0]) + 4.
shift_length_list() ->
length([0,0]) bsr 2.
shift_length_list_plus() ->
(length([0,0]) + 4) bsr 2.
shift_length_list_plus_bsl() ->
(length([0,0]) + 4) bsl 2.
shift_length_list_plus2() ->
N = length([0,0]) + 4,
N bsr 2.
shift_fun() ->
(length_list() + 4) bsr 2.
%%-----------------------------------------------------------------------
%% From: Igor Goryachev
%% Date: June 15, 2006
%%
%% I have experienced a different behaviour and possibly a weird result
%% while playing with matching a big list on x86 and x86_64 machines.
%%-----------------------------------------------------------------------
-define(BIG_LIST,
["uid", "nickname", "n_family", "n_given", "email_pref",
"tel_home_number", "tel_cellular_number", "adr_home_country",
"adr_home_locality", "adr_home_region", "url", "gender", "bday",
"constitution", "height", "weight", "hair", "routine", "smoke",
"maritalstatus", "children", "independence", "school_number",
"school_locality", "school_title", "school_period", "org_orgname",
"title", "adr_work_locality", "photo_type", "photo_binval"]).
test_match_big_list() ->
case create_tuple_with_big_const_list() of
{selected, ?BIG_LIST, _} -> ok;
_ -> weird
end.
create_tuple_with_big_const_list() ->
{selected, ?BIG_LIST, [{"test"}]}.
%%-----------------------------------------------------------------------
%% In October 2006 the HiPE compiler acquired more type-driven
%% optimisations of arithmetic operations. One of these, the
%% transformation of bsl to a pure fixnum bsl fixnum -> fixnum version
%% (unsafe_bsl), was incorrectly performed even when the result
%% wouldn't be a fixnum. The error occurred for all backends, but the
%% only place known to break was hipe_arm:imm_to_am1/2. Some
%% immediates got broken on ARM, causing segmentation faults in
%% compiler_tests when HiPE recompiled itself.
%%-----------------------------------------------------------------------
test_unsafe_bsl() ->
ok = bsl_check(bsl_test_cases()).
bsl_test_cases() ->
[{16#FF, {16#FF, 0}},
{16#F000000F, {16#FF, 2}}].
bsl_check([]) -> ok;
bsl_check([{X, Y}|Rest]) ->
case imm_to_am1(X) of
Y -> bsl_check(Rest);
_ -> 'hipe_broke_bsl'
end.
imm_to_am1(Imm) ->
imm_to_am1(Imm band 16#FFFFFFFF, 16).
imm_to_am1(Imm, RotCnt) ->
if Imm >= 0, Imm =< 255 -> {Imm, RotCnt band 15};
true ->
NewRotCnt = RotCnt - 1,
if NewRotCnt =:= 0 -> []; % full circle, no joy
true ->
NewImm = (Imm bsr 2) bor ((Imm band 3) bsl 30),
imm_to_am1(NewImm, NewRotCnt)
end
end.
%%-----------------------------------------------------------------------
%% Another transformation, namely that of bsr to a pure fixnum bsr
%% fixnum -> fixnum version (unsafe_bsr), failed to check for shifts
%% larger than the number of bits in fixnums. Such shifts should
%% return zero, but instead they became plain machine-level shift
%% instructions. Machines often only consider the low-order bits of
%% the shift count, so machine-level shifts larger than the word size
%% do not match the Erlang semantics.
%%-----------------------------------------------------------------------
test_unsafe_bsr() ->
ok = bsr_check(bsr_test_cases()).
bsr_test_cases() ->
[{16#FF, 4, 16#0F},
{16#FF, 64, 0}].
bsr_check([]) -> ok;
bsr_check([{X, Y, Z}|Rest]) ->
case do_bsr(X, Y) of
Z -> bsr_check(Rest);
_ -> 'hipe_broke_bsr'
end.
do_bsr(X, Y) ->
(X band 16#FFFF) bsr (Y band 16#FFFF).
%%-----------------------------------------------------------------------
%% From: Sergey S, mid January 2009.
%%
%% While I was playing with +native option, I run into a bug in HiPE
%% which leads to segmentation fault using +native and Erlang R12B-5.
%%
%% Eshell V5.6.5
%% 1> crash:test().
%% # Some message to be printed here each loop iteration
%% Segmentation fault
%%
%% Diagnozed and fixed by Mikael Petterson (22 Jan 2009):
%%
%% I've analysed the recently posted HiPE bug report on erlang-bugs
%% <http://www.erlang.org/pipermail/erlang-bugs/2009-January/001162.html>.
%% The segfault is caused by memory corruption, which in turn is caused
%% by RTL removing an update of the HP (heap pointer) register due to
%% what looks like broken liveness information.
%%-----------------------------------------------------------------------
test_R12B5_seg_fault() ->
_ = spawn(fun() -> init() end),
ok.
init() ->
repeat(5, fun() -> void end),
receive after infinity -> ok end.
repeat(0, _) ->
ok;
repeat(N, Fun) ->
%% io:format("# Some message to be printed here each loop iteration\n"),
Fun(),
repeat(N - 1, Fun).