Skip to content

Commit 3d4bffe

Browse files
committed
Remove duplicate nodes in BDD when performing unions
Our inference algorithm deduce types for each clause, subtracting previous clauses. For a function with three clauses, the type of each clause is roughtly: c1 c2 - c1 c3 - c2 - c1 To compute the domain, we union these clauses, leading to large types that mix unions and differences. By removing repeated nodes, the union of the entries above become: c1 or c2 or c3
1 parent 694930e commit 3d4bffe

1 file changed

Lines changed: 51 additions & 9 deletions

File tree

‎lib/elixir/lib/module/types/descr.ex‎

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3092,13 +3092,6 @@ defmodule Module.Types.Descr do
30923092
defp map_difference(bdd1, bdd2),
30933093
do: bdd_difference(bdd1, bdd2, &map_leaf_difference/3)
30943094

3095-
# We only apply this particular optimization when comparing leafs (type == :none).
3096-
# Applying it in other scenarios lead to slow compilation, likely because the
3097-
# introduction of `a_int` and `a_union` lead to additional nodes and large rehashes
3098-
# of the tree.
3099-
#
3100-
# Outside of this particular scenario, the `a_int` optimization has been useful,
3101-
# but we haven't measured benefits for `a_union`.
31023095
defp map_leaf_difference(bdd_leaf(tag, fields), bdd_leaf(:open, [{key, v2}]), type) do
31033096
{found?, v1} =
31043097
case fields_find(key, fields) do
@@ -6059,10 +6052,10 @@ defmodule Module.Types.Descr do
60596052
_ ->
60606053
case bdd_compare(bdd1, bdd2) do
60616054
{:lt, {_, lit1, c1, u1, d1}, bdd2} ->
6062-
bdd_node_new(lit1, c1, bdd_union(u1, bdd2), d1)
6055+
bdd_node_new(lit1, bdd_remove(c1, bdd2), bdd_union(u1, bdd2), bdd_remove(d1, bdd2))
60636056

60646057
{:gt, bdd1, {_, lit2, c2, u2, d2}} ->
6065-
bdd_node_new(lit2, c2, bdd_union(bdd1, u2), d2)
6058+
bdd_node_new(lit2, bdd_remove(c2, bdd1), bdd_union(bdd1, u2), bdd_remove(d2, bdd1))
60666059

60676060
{:eq, {_, lit, c1, u1, d1}, {_, _, c2, u2, d2}} ->
60686061
bdd_node_new(lit, bdd_union(c1, c2), bdd_union(u1, u2), bdd_union(d1, d2))
@@ -6083,6 +6076,55 @@ defmodule Module.Types.Descr do
60836076
end
60846077
end
60856078

6079+
# When doing BDD1 v BDD2, BDD2 is likely stored as a lazy union.
6080+
# However, if BDD2 appears in the constrained or dual nodes of BDD1,
6081+
# we want to remove them, to avoid carrying unecessary information.
6082+
# This function does precisely so.
6083+
defp bdd_remove(_bdd, :bdd_top), do: :bdd_bot
6084+
defp bdd_remove(bdd, :bdd_bot), do: bdd
6085+
defp bdd_remove(bdd, bdd_leaf(_, _) = leaf), do: bdd_remove_leaf(bdd, leaf)
6086+
defp bdd_remove(bdd1, bdd2), do: bdd_remove_bdd(bdd1, bdd2)
6087+
6088+
# (a and C) or U or (not a and D) or a
6089+
#
6090+
# is equivalent to:
6091+
#
6092+
# U or a or D
6093+
#
6094+
# Because:
6095+
# (a and C) or a => a
6096+
# (not a and D) or a => a or D
6097+
defp bdd_remove_leaf({_, leaf, _c, u, d}, leaf), do: leaf |> bdd_union(u) |> bdd_union(d)
6098+
defp bdd_remove_leaf(leaf, leaf), do: :bdd_bot
6099+
defp bdd_remove_leaf(bdd, _), do: bdd
6100+
6101+
defp bdd_remove_bdd(bdd, bdd), do: :bdd_bot
6102+
6103+
defp bdd_remove_bdd({_, lit1, c1, u1, d1} = bdd1, {_, lit2, c2, u2, d2} = bdd2) do
6104+
cond do
6105+
lit1 < lit2 ->
6106+
bdd_node_new(
6107+
lit1,
6108+
bdd_remove_bdd(c1, bdd2),
6109+
bdd_remove_bdd(u1, bdd2),
6110+
bdd_remove_bdd(d1, bdd2)
6111+
)
6112+
6113+
lit1 > lit2 ->
6114+
bdd_remove(bdd1, u2)
6115+
6116+
true ->
6117+
bdd_node_new(
6118+
lit1,
6119+
bdd_remove(bdd_remove(c1, c2), u2),
6120+
u1,
6121+
bdd_remove(bdd_remove(d1, d2), u2)
6122+
)
6123+
end
6124+
end
6125+
6126+
defp bdd_remove_bdd(bdd, _), do: bdd
6127+
60866128
def bdd_difference(bdd, bdd), do: :bdd_bot
60876129

60886130
def bdd_difference(bdd1, bdd2) do

0 commit comments

Comments
 (0)