File 3974-beam_ssa_opt-Optimize-building-of-the-function-datab.patch of Package erlang
From 4760620b431f7ba1c643b156b40916942a6fade6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org>
Date: Fri, 26 Sep 2025 09:27:16 +0200
Subject: [PATCH 4/4] beam_ssa_opt: Optimize building of the function database
The function database is used by the type optimization pass. Building
the database was unnecessary slow because it depended on adding one
element at a time to an ordset.
After changing the building to use ordsets:from_list/1 and sofs,
the database was built always four times as fast for
`NBAP-PDU-Contents.erl` (214016 lines).
`NBAP-PDU-Contents.erl` was generated by compiling
`lib/asn1/test/asn1_SUITE_data/nbapsystem/NBAP-PDU-Contents.asn`
with the ASN.1 compiler.
---
lib/compiler/src/beam_ssa_opt.erl | 63 ++++++++++++++++---------------
1 file changed, 32 insertions(+), 31 deletions(-)
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index e2de01639e..68d9767236 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -357,48 +357,49 @@ fdb_fs([#b_function{ args=Args,bs=Bs }=F | Fs], Exports, FuncDb0) ->
Exported = gb_sets:is_element({Name, Arity}, Exports),
ArgTypes = duplicate(length(Args), #{}),
- FuncDb1 = case FuncDb0 of
- %% We may have an entry already if someone's called us.
- #{ Id := Info } ->
- FuncDb0#{ Id := Info#func_info{ exported=Exported,
- arg_types=ArgTypes }};
- #{} ->
- FuncDb0#{ Id => #func_info{ exported=Exported,
- arg_types=ArgTypes }}
- end,
-
RPO = beam_ssa:rpo(Bs),
- FuncDb = beam_ssa:fold_blocks(fun(_L, #b_blk{is=Is}, FuncDb) ->
- fdb_is(Is, Id, FuncDb)
- end, RPO, FuncDb1, Bs),
+ Callees0 = beam_ssa:fold_blocks(fun(_L, #b_blk{is=Is}, Acc) ->
+ fdb_is(Is, Acc)
+ end, RPO, [], Bs),
+ Callees = ordsets:from_list(Callees0),
+
+ CallerVertex0 = maps:get(Id, FuncDb0, #func_info{}),
+ CallerVertex = CallerVertex0#func_info{exported=Exported,
+ arg_types=ArgTypes,
+ out=Callees},
+ FuncDb = FuncDb0#{Id => CallerVertex},
fdb_fs(Fs, Exports, FuncDb);
-fdb_fs([], _Exports, FuncDb) ->
- FuncDb.
+fdb_fs([], _Exports, FuncDb0) ->
+ S0 = lists:map(fun({Caller,#func_info{out=Callees}}) -> {Caller,Callees} end, maps:to_list(FuncDb0)),
+ S1 = sofs:family(S0),
+ S2 = sofs:family_to_relation(S1),
+ S3 = sofs:converse(S2),
+ S4 = sofs:relation_to_family(S3),
+ S = sofs:to_external(S4),
+ fdb_update_callees(S, FuncDb0).
fdb_is([#b_set{op=call,
args=[#b_local{}=Callee | _]} | Is],
- Caller, FuncDb) ->
- fdb_is(Is, Caller, fdb_update(Caller, Callee, FuncDb));
+ Acc) ->
+ fdb_is(Is, [Callee|Acc]);
fdb_is([#b_set{op=make_fun,args=[#b_local{}=Callee | _]} | Is],
- Caller, FuncDb) ->
+ Acc) ->
%% The make_fun instruction's type depends on the return type of the
%% function in question, so we treat this as a function call.
- fdb_is(Is, Caller, fdb_update(Caller, Callee, FuncDb));
-fdb_is([_ | Is], Caller, FuncDb) ->
- fdb_is(Is, Caller, FuncDb);
-fdb_is([], _Caller, FuncDb) ->
- FuncDb.
-
-fdb_update(Caller, Callee, FuncDb) ->
- CallerVertex = maps:get(Caller, FuncDb, #func_info{}),
- CalleeVertex = maps:get(Callee, FuncDb, #func_info{}),
+ fdb_is(Is, [Callee|Acc]);
+fdb_is([_ | Is], Acc) ->
+ fdb_is(Is, Acc);
+fdb_is([], Acc) ->
+ Acc.
- Calls = ordsets:add_element(Callee, CallerVertex#func_info.out),
- CalledBy = ordsets:add_element(Caller, CalleeVertex#func_info.in),
+fdb_update_callees([{Callee,CalledBy}|Calls], FuncDb0) ->
+ CalleeVertex = map_get(Callee, FuncDb0),
+ FuncDb = FuncDb0#{Callee := CalleeVertex#func_info{in=CalledBy}},
+ fdb_update_callees(Calls, FuncDb);
+fdb_update_callees([], FuncDb) ->
+ FuncDb.
- FuncDb#{ Caller => CallerVertex#func_info{out=Calls},
- Callee => CalleeVertex#func_info{in=CalledBy} }.
%% Returns the post-order of all local calls in this module. That is,
%% called functions will be ordered before the functions calling them.
--
2.51.0