Skip to content

Commit 51eea9a

Browse files
committed
compiler: Change some bifs to guard bifs in try/catch
When put in a try, calling a guard bif with no side-effects is already optimized to remove the try/catch. However, there are bifs with side-effects that can be safely optimized in the same way in order to gain performance. Example code: try binary_to_atom(A, utf8) catch _:_ -> [] end. Before, SSA for the bif call after optimizations: _6 = call (`erlang`:`binary_to_atom`/2), _0, `utf8` _14 = succeeded:body _6 Now, SSA for the bif call after optimizations: _6 = bif:binary_to_atom _0, `utf8` _14 = succeeded:guard _6 Bifs that are optimized for try/catch in this change: `binary_to_atom/1`, `binary_to_atom/2`, `binary_to_existing_atom/1`, `list_to_atom/1`, `list_to_existing_atom/1`.
1 parent 115761f commit 51eea9a

File tree

6 files changed

+109
-7
lines changed

6 files changed

+109
-7
lines changed

erts/emulator/beam/bif.tab

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ ubif erlang:hd/1
8484
bif erlang:integer_to_list/1
8585
ubif erlang:length/1
8686
bif erlang:link/1
87-
bif erlang:list_to_atom/1
87+
ubif erlang:list_to_atom/1
8888
bif erlang:list_to_binary/1
8989
bif erlang:list_to_float/1
9090
bif erlang:list_to_pid/1
@@ -478,7 +478,7 @@ bif string:list_to_float/1
478478
bif erlang:make_fun/3
479479
bif erlang:iolist_size/1
480480
bif erlang:iolist_to_binary/1
481-
bif erlang:list_to_existing_atom/1
481+
ubif erlang:list_to_existing_atom/1
482482

483483
#
484484
# New Bifs in R12B-0
@@ -510,8 +510,8 @@ bif unicode:bin_is_7bit/1
510510
# New Bifs in R13A.
511511
#
512512
bif erlang:atom_to_binary/2
513-
bif erlang:binary_to_atom/2
514-
bif erlang:binary_to_existing_atom/2
513+
ubif erlang:binary_to_atom/2
514+
ubif erlang:binary_to_existing_atom/2
515515
bif net_kernel:dflag_unicode_io/1
516516
#
517517
# New Bifs in R13B-1

lib/compiler/src/beam_ssa.erl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
merge_blocks/2,
3939
normalize/1,
4040
no_side_effect/1,
41+
can_be_guard_bif/3,
4142
predecessors/1,
4243
rename_vars/3,
4344
rpo/1,rpo/2,
@@ -234,6 +235,19 @@ no_side_effect(#b_set{op=Op}) ->
234235
_ -> false
235236
end.
236237

238+
-spec can_be_guard_bif(atom(), atom(), integer()) -> boolean().
239+
240+
can_be_guard_bif(M, F, A) ->
241+
case {M,F,A} of
242+
{erlang, binary_to_atom, 2} -> true;
243+
{erlang, binary_to_existing_atom, 2} -> true;
244+
{erlang, list_to_atom, 1} -> true;
245+
{erlang, list_to_existing_atom, 1} -> true;
246+
{_,_,_} -> false
247+
end.
248+
249+
250+
237251
%% insert_on_edges(Insertions, BlockMap, Count) -> {BlockMap, Count}.
238252
%% Inserts instructions on the specified normal edges. It will not work on
239253
%% exception edges.

lib/compiler/src/beam_ssa_opt.erl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,6 +1893,23 @@ reduce_try_is([#b_set{op={succeeded,body}}=I0|Is], Acc) ->
18931893
%% succeeded to the `guard`, since the try/catch will be removed.
18941894
I = I0#b_set{op={succeeded,guard}},
18951895
reduce_try_is(Is, [I|Acc]);
1896+
reduce_try_is([#b_set{op=call,args=[#b_remote{mod=#b_literal{val=M},
1897+
name=#b_literal{val=F},
1898+
arity=A}=R0|Args0]}=I0|Is],
1899+
Acc) ->
1900+
%% Rewrite binary_to_atom/1 call to binary_to_atom/2.
1901+
{I1, Args1} = if {M, F, A} =:= {erlang, binary_to_atom, 1} ->
1902+
Args = Args0++[#b_literal{val=utf8}],
1903+
{I0#b_set{args=[R0#b_remote{arity=2}|Args]},Args};
1904+
true -> {I0, Args0}
1905+
end,
1906+
%% Remove try-catch for bifs that can be written as guards.
1907+
case beam_ssa:can_be_guard_bif(M, F, A) of
1908+
true ->
1909+
I = I1#b_set{op={bif,F},args=Args1},
1910+
reduce_try_is(Is, [I|Acc]);
1911+
false -> unsafe
1912+
end;
18961913
reduce_try_is([#b_set{op=Op}=I|Is], Acc) ->
18971914
IsSafe = case Op of
18981915
phi -> true;

lib/compiler/src/beam_ssa_pre_codegen.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,8 @@ sanitize_instr({bif,Bif}, [#b_literal{val=Lit}], _I) ->
929929
end
930930
end;
931931
sanitize_instr({bif,Bif}, [#b_literal{val=Lit1},#b_literal{val=Lit2}], _I) ->
932-
true = erl_bifs:is_pure(erlang, Bif, 2), %Assertion.
932+
true = erl_bifs:is_pure(erlang, Bif, 2) orelse
933+
beam_ssa:can_be_guard_bif(erlang, Bif, 2), %Assertion.
933934
try
934935
{subst,#b_literal{val=erlang:Bif(Lit1, Lit2)}}
935936
catch
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
%% %CopyrightBegin%
2+
%%
3+
%% Copyright Ericsson AB 2024. All Rights Reserved.
4+
%%
5+
%% Licensed under the Apache License, Version 2.0 (the "License");
6+
%% you may not use this file except in compliance with the License.
7+
%% You may obtain a copy of the License at
8+
%%
9+
%% http://www.apache.org/licenses/LICENSE-2.0
10+
%%
11+
%% Unless required by applicable law or agreed to in writing, software
12+
%% distributed under the License is distributed on an "AS IS" BASIS,
13+
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
%% See the License for the specific language governing permissions and
15+
%% limitations under the License.
16+
%%
17+
%% %CopyrightEnd%
18+
%%
19+
%% This module tests functions which have previously crashed the
20+
%% compiler when the `no_type_opt` option was used.
21+
%%
22+
23+
-module(no_type_info).
24+
-export([try_bif1/1, try_bif2/2, try_bif3/1]).
25+
26+
27+
28+
try_bif1(B) ->
29+
%ssa% () when post_ssa_opt ->
30+
%ssa% X = bif:binary_to_atom(B),
31+
%ssa% _ = succeeded:guard(X).
32+
try binary_to_atom(B)
33+
catch _:_ -> []
34+
end.
35+
36+
try_bif2(A, B) ->
37+
%ssa% () when post_ssa_opt ->
38+
%ssa% X = bif:binary_to_atom(A, B),
39+
%ssa% _ = succeeded:guard(X).
40+
try binary_to_atom(A, B)
41+
catch _:_ -> []
42+
end.
43+
44+
try_bif3(A) ->
45+
%ssa% () when post_ssa_opt ->
46+
%ssa% X = erlang:float_to_list(A),
47+
%ssa% _ = succeeded:body(X).
48+
try float_to_list(A)
49+
catch _:_ -> []
50+
end.

lib/compiler/test/bif_SUITE.erl

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
beam_validator/1,trunc_and_friends/1,cover_safe_and_pure_bifs/1,
2828
cover_trim/1,
2929
head_tail/1,
30-
min_max/1]).
30+
min_max/1,
31+
non_throwing/1]).
3132

3233
suite() ->
3334
[{ct_hooks,[ts_install_cth]}].
@@ -43,7 +44,8 @@ groups() ->
4344
cover_safe_and_pure_bifs,
4445
cover_trim,
4546
head_tail,
46-
min_max
47+
min_max,
48+
non_throwing
4749
]}].
4850

4951
init_per_suite(Config) ->
@@ -292,6 +294,24 @@ int_clamped_add(A) when is_integer(A) ->
292294
num_clamped_add(A) ->
293295
min(max(A, 0), 10) + 100.
294296

297+
non_throwing(_Config) ->
298+
a = with_try(fun binary_to_atom/1, <<"a">>),
299+
l = with_try(fun list_to_existing_atom/1, [108]),
300+
[] = with_try(fun list_to_atom/1, [a]),
301+
'Erlang' = with_try_2(fun binary_to_atom/2, <<"Erlang">>, unicode),
302+
[] = with_try_2(fun binary_to_existing_atom/2, a, unicode),
303+
ok.
304+
305+
with_try(Fun, A) ->
306+
try Fun(A)
307+
catch _:_ -> []
308+
end.
309+
310+
with_try_2(Fun, A, B) ->
311+
try Fun(A, B)
312+
catch _:_ -> []
313+
end.
314+
295315
%%%
296316
%%% Common utilities.
297317
%%%

0 commit comments

Comments
 (0)