Skip to content

Commit 3c7cc9a

Browse files
author
José Valim
committed
Merge branch 'jv-tuple-opt'
2 parents dffd8c5 + c7f67bc commit 3c7cc9a

File tree

12 files changed

+749
-52
lines changed

12 files changed

+749
-52
lines changed
Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
# This is an optimization Elixir runs on function clauses.
2+
# Whenever a variables matches against a record (a tagged
3+
# tuple), this information is stored in order to optimize
4+
# record calls.
5+
defmodule Kernel.RecordRewriter do
6+
@moduledoc false
7+
8+
def optimize_clause(clause) do
9+
optimize_clause(clause, :orddict.new)
10+
end
11+
12+
## Clause
13+
14+
defp optimize_clause({ :clause, line, args, guards, body }, dict) do
15+
{ args, dict } = optimize_args(args, dict)
16+
{ body, dict, res } = optimize_body(body, dict, [])
17+
{ { :clause, line, args, guards, body }, dict, res }
18+
end
19+
20+
defp optimize_args(args, dict) do
21+
Enum.map_reduce args, dict, fn(arg, acc) ->
22+
{ new_arg, new_acc, _res } = optimize_expr(arg, acc)
23+
{ new_arg, new_acc }
24+
end
25+
end
26+
27+
defp optimize_body([], dict, _acc) do
28+
{ [], dict, nil }
29+
end
30+
31+
defp optimize_body([h], dict, acc) do
32+
{ new_expr, new_dict, new_res } = optimize_expr(h, dict)
33+
{ Enum.reverse([new_expr|acc]), new_dict, new_res }
34+
end
35+
36+
defp optimize_body([h|t], dict, acc) do
37+
{ new_expr, new_dict, _ } = optimize_expr(h, dict)
38+
optimize_body(t, new_dict, [new_expr|acc])
39+
end
40+
41+
## Record helpers
42+
43+
defp record_fields(record) do
44+
if Code.ensure_loaded?(record) && function_exported?(record, :__record__, 1) do
45+
try do
46+
fields = lc { k, _ } inlist record.__record__(:fields), do: k
47+
optimizable = record.__record__(:optimizable)
48+
{ fields, optimizable }
49+
rescue
50+
[UndefinedFunctionError, FunctionClauseError] -> { [], [] }
51+
end
52+
end
53+
end
54+
55+
defp record_field_info(function) do
56+
case atom_to_list(function) do
57+
'update_' ++ field -> { :update, list_to_atom(function) }
58+
_ -> { :accessor, function }
59+
end
60+
end
61+
62+
defp optimize_call(line, { record, _ } = res, left, { :atom, _, function }, args) do
63+
{ fields, optimizable } = record_fields(record)
64+
65+
if List.member?(optimizable, { function, length(args) + 1 }) do
66+
{ kind, field } = record_field_info(function)
67+
if index = Enum.find_index(fields, field == &1) do
68+
optimize_call(line, res, kind, index, left, args)
69+
end
70+
end
71+
end
72+
73+
defp optimize_call(_line, _res, _left, _right, _args) do
74+
nil
75+
end
76+
77+
defp optimize_call(line, _res, :accessor, index, left, []) do
78+
call = { :call, line,
79+
{ :remote, line, { :atom, 0, :erlang }, { :atom, 0, :element } },
80+
[{ :integer, 0, index + 2 }, left]
81+
}
82+
{ call, nil }
83+
end
84+
85+
defp optimize_call(line, res, :accessor, index, left, [arg]) do
86+
call = { :call, line,
87+
{ :remote, line, { :atom, 0, :erlang }, { :atom, 0, :setelement } },
88+
[{ :integer, 0, index + 2 }, left, arg]
89+
}
90+
{ call, res }
91+
end
92+
93+
defp optimize_call(_line, _res, :update, _index, _left, [_arg]) do
94+
nil
95+
end
96+
97+
## Expr
98+
99+
defp optimize_expr({ :call, call_line, { :remote, line, left, right }, args }, dict) do
100+
{ left, dict, res } = optimize_expr(left, dict)
101+
{ right, dict, _ } = optimize_expr(right, dict)
102+
{ args, dict } = optimize_args(args, dict)
103+
104+
case optimize_call(call_line, res, left, right, args) do
105+
{ call, call_res } ->
106+
{ call, dict, call_res }
107+
nil ->
108+
{ { :call, call_line, { :remote, line, left, right }, args }, dict, nil }
109+
end
110+
end
111+
112+
defp optimize_expr({ :call, line, expr, args }, dict) do
113+
{ expr, dict, _ } = optimize_expr(expr, dict)
114+
{ args, dict } = optimize_args(args, dict)
115+
{ { :call, line, expr, args }, dict, nil }
116+
end
117+
118+
defp optimize_expr({ :match, line, left, right }, dict) do
119+
{ left, dict, left_res } = optimize_expr(left, dict)
120+
{ right, dict, right_res } = optimize_expr(right, dict)
121+
122+
match = { :match, line, left, right }
123+
124+
if left_res do
125+
dict = assign_vars(extract_vars(right, []), dict, left_res)
126+
end
127+
128+
if right_res do
129+
dict = assign_vars(extract_vars(left, []), dict, right_res)
130+
end
131+
132+
{ match, dict, right_res || left_res }
133+
end
134+
135+
defp optimize_expr({ :op, line, op, left, right }, dict) do
136+
{ left, dict, _ } = optimize_expr(left, dict)
137+
{ right, dict, _ } = optimize_expr(right, dict)
138+
{ { :op, line, op, left, right }, dict, nil }
139+
end
140+
141+
defp optimize_expr({ :op, line, op, expr }, dict) do
142+
{ expr, dict, _ } = optimize_expr(expr, dict)
143+
{ { :op, line, op, expr }, dict, nil }
144+
end
145+
146+
defp optimize_expr({ :bin, line, elements }, dict) do
147+
{ elements, dict } = optimize_args(elements, dict)
148+
{ { :bin, line, elements }, dict, nil }
149+
end
150+
151+
defp optimize_expr({ :bin_element, line, expr, type1, type2 }, dict) do
152+
{ expr, dict, _ } = optimize_expr(expr, dict)
153+
{ { :bin_element, line, expr, type1, type2 }, dict, nil }
154+
end
155+
156+
defp optimize_expr({ :cons, line, left, right }, dict) do
157+
{ left, dict, _ } = optimize_expr(left, dict)
158+
{ right, dict, _ } = optimize_expr(right, dict)
159+
{ { :cons, line, left, right }, dict, nil }
160+
end
161+
162+
defp optimize_expr({ :block, line, args }, dict) do
163+
{ args, dict, res } = optimize_body(args, dict, [])
164+
{ { :block, line, args }, dict, res }
165+
end
166+
167+
defp optimize_expr({ :tuple, line, args }, dict) do
168+
{ args, dict, args_res } = optimize_tuple_args(args, dict)
169+
args_res = if Enum.any?(args_res), do: args_res, else: nil
170+
171+
res =
172+
case args do
173+
[{ :atom, _, atom }|t] -> atom
174+
_ -> nil
175+
end
176+
177+
{ { :tuple, line, args }, dict, { res, args_res } }
178+
end
179+
180+
defp optimize_expr({ :var, _, name } = var, dict) do
181+
case :orddict.find(name, dict) do
182+
{ :ok, res } -> { var, dict, res }
183+
:error -> { var, dict, nil }
184+
end
185+
end
186+
187+
defp optimize_expr({ :case, line, expr, clauses }, dict) do
188+
{ expr, dict, _ } = optimize_expr(expr, dict)
189+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
190+
clauses = lc { clause, _, _ } inlist tuples, do: clause
191+
dict = join_dict(tuples)
192+
res = join_result(tuples)
193+
{ { :case, line, expr, clauses }, dict, res }
194+
end
195+
196+
defp optimize_expr({ :receive, line, clauses }, dict) do
197+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
198+
clauses = lc { clause, _, _ } inlist tuples, do: clause
199+
dict = join_dict(tuples)
200+
res = join_result(tuples)
201+
{ { :receive, line, clauses }, dict, res }
202+
end
203+
204+
defp optimize_expr({ :receive, line, clauses, after_key, after_value }, dict) do
205+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
206+
clauses = lc { clause, _, _ } inlist tuples, do: clause
207+
208+
{ after_key, dict, _ } = optimize_expr(after_key, dict)
209+
{ after_value, dict, res } = optimize_body(after_value, dict, [])
210+
211+
dict = join_dict(tuples, dict)
212+
res = join_result(tuples, res)
213+
214+
{ { :receive, line, clauses, after_key, after_value }, dict, res }
215+
end
216+
217+
defp optimize_expr({ :try, line, body, [], clauses, try_after }, dict) do
218+
tuples = lc clause inlist clauses, do: optimize_clause(clause, dict)
219+
clauses = lc { clause, _, _ } inlist tuples, do: clause
220+
221+
{ body, _, res } = optimize_body(body, dict, [])
222+
res = join_result(tuples, res)
223+
224+
{ try_after, _, _ } = optimize_body(try_after, dict, [])
225+
{ { :try, line, body, [], clauses, try_after }, dict, res }
226+
end
227+
228+
defp optimize_expr({ :fun, line, { :function, module, name, arity } }, dict) do
229+
{ module, dict, _ } = optimize_expr(module, dict)
230+
{ name, dict, _ } = optimize_expr(name, dict)
231+
{ arity, dict, _ } = optimize_expr(arity, dict)
232+
{ { :fun, line, { :function, module, name, arity } }, dict, nil }
233+
end
234+
235+
defp optimize_expr({ :fun, line, { :clauses, clauses } }, dict) do
236+
clauses = lc clause inlist clauses do
237+
{ clause, _, _ } = optimize_clause(clause, dict)
238+
clause
239+
end
240+
241+
{ { :fun, line, { :clauses, clauses } }, dict, nil }
242+
end
243+
244+
defp optimize_expr({ comprehension, line, expr, args }, dict) when comprehension in [:lc, :bc] do
245+
{ args, new_dict } = optimize_args(args, dict)
246+
{ expr, _, _ } = optimize_expr(expr, new_dict)
247+
{ { comprehension, line, expr, args }, dict, nil }
248+
end
249+
250+
defp optimize_expr({ generate, line, left, right }, dict) when generate in [:generate, :b_generate] do
251+
{ left, dict, _ } = optimize_expr(left, dict)
252+
{ right, dict, _ } = optimize_expr(right, dict)
253+
{ { generate, line, left, right }, dict, nil }
254+
end
255+
256+
defp optimize_expr(other, dict) when elem(other, 0) in [:string, :atom, :integer, :float, :nil, :fun] do
257+
{ other, dict, nil }
258+
end
259+
260+
## Helpers
261+
262+
defp optimize_tuple_args(args, dict) do
263+
{ final_args, { final_dict, final_acc } } =
264+
Enum.map_reduce args, { dict, [] }, fn(arg, { acc_dict, acc_res }) ->
265+
{ new_arg, new_acc, res } = optimize_expr(arg, acc_dict)
266+
{ new_arg, { new_acc, [res|acc_res] } }
267+
end
268+
269+
{ final_args, final_dict, Enum.reverse(final_acc) }
270+
end
271+
272+
defp assign_vars([key|t], dict, { _, value } = res) when is_list(key) and is_list(value) and length(key) == length(value) do
273+
assign_vars t, assign_nested_vars(key, dict, value), res
274+
end
275+
276+
defp assign_vars([key|t], dict, { value, _ } = res) when is_atom(key) and value != nil do
277+
dict =
278+
case :orddict.find(key, dict) do
279+
{ :ok, ^res } ->
280+
dict
281+
{ :ok, { ^value, _ } } ->
282+
:orddict.store(key, { value, nil }, dict)
283+
{ :ok, _ } ->
284+
# We are overriding a type of an existing variable,
285+
# which means the source code is invalid.
286+
:orddict.store(key, nil, dict)
287+
:error ->
288+
:orddict.store(key, res, dict)
289+
end
290+
291+
assign_vars t, dict, res
292+
end
293+
294+
defp assign_vars([_|t], dict, res) do
295+
assign_vars t, dict, res
296+
end
297+
298+
defp assign_vars([], dict, _res) do
299+
dict
300+
end
301+
302+
defp assign_nested_vars([vars|vt], dict, [res|rt]) do
303+
assign_nested_vars(vt, assign_vars(vars, dict, res), rt)
304+
end
305+
306+
defp assign_nested_vars([], dict, []) do
307+
dict
308+
end
309+
310+
defp extract_vars({ :match, _, left, right }, vars) do
311+
vars = extract_vars(right, vars)
312+
extract_vars(left, vars)
313+
end
314+
315+
defp extract_vars({ :var, _, name }, vars) do
316+
[name|vars]
317+
end
318+
319+
defp extract_vars({ :tuple, _, args }, vars) do
320+
[Enum.map(args, extract_vars(&1, []))|vars]
321+
end
322+
323+
defp extract_vars(_, vars) do
324+
vars
325+
end
326+
327+
defp join_dict([{ _, dict, _ }|t]) do
328+
join_dict(t, dict)
329+
end
330+
331+
defp join_dict([{ _, dict, _ }|t], other) do
332+
other = Enum.reduce other, other, fn
333+
{ key, { value, _ } = res }, acc ->
334+
case :orddict.find(key, dict) do
335+
{ :ok, ^res } -> acc
336+
{ :ok, { ^value, _ } } -> :orddict.store(key, { value, nil }, acc)
337+
{ :ok, _ } -> :orddict.store(key, nil, acc)
338+
:error -> :orddict.erase(key, acc)
339+
end
340+
end
341+
342+
join_dict(t, other)
343+
end
344+
345+
defp join_dict([], other) do
346+
other
347+
end
348+
349+
defp join_result([{ _, _, res }|t]) do
350+
join_result(t, res)
351+
end
352+
353+
defp join_result([{ _, _, res }|t], res) do
354+
join_result(t, res)
355+
end
356+
357+
defp join_result([{ _, _, { res, _ } }|t], { res, _ }) do
358+
join_result(t, { res, nil })
359+
end
360+
361+
defp join_result([{ _, _, _ }|_], _res) do
362+
nil
363+
end
364+
365+
defp join_result([], res) do
366+
res
367+
end
368+
end

lib/elixir/lib/macro/env.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ defmodule Macro.Env do
1616
functions: functions, macros: macros]
1717

1818
Record.deffunctions(fields, __MODULE__)
19-
Record.deftypes(fields, types, __ENV__)
19+
Record.deftypes(fields, types, __MODULE__)
2020

2121
@moduledoc """
2222
A record that contains compile time environment information,

0 commit comments

Comments
 (0)