Skip to content

Commit 18be4dc

Browse files
committed
Allow mode to be chosen
1 parent 148dac6 commit 18be4dc

2 files changed

Lines changed: 87 additions & 25 deletions

File tree

lib/elixir/src/elixir_compiler.erl

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ quoted(Forms, File, Callback) ->
2020

2121
elixir_lexical:run(
2222
Env,
23-
fun (LexicalEnv) -> maybe_fast_compile(Forms, LexicalEnv) end,
23+
fun (LexicalEnv) -> optimize_defmodule(Forms, LexicalEnv) end,
2424
fun (#{lexical_tracker := Pid}) -> Callback(File, Pid) end
2525
),
2626

@@ -33,45 +33,103 @@ file(File, Callback) ->
3333
{ok, Bin} = file:read_file(File),
3434
string(elixir_utils:characters_to_list(Bin), File, Callback).
3535

36-
%% Evaluates the given code through the Erlang compiler.
37-
%% It may end-up evaluating the code if it is deemed a
38-
%% more efficient strategy depending on the code snippet.
39-
maybe_fast_compile(Forms, E) ->
40-
case (?key(E, module) == nil) andalso allows_fast_compilation(Forms) andalso
36+
%% In case the forms only holds defmodules, we optimize
37+
%% it by expanding them directly.
38+
optimize_defmodule(Forms, E) ->
39+
case (?key(E, module) == nil) andalso only_defmodule(Forms) andalso
4140
(not elixir_config:is_bootstrap()) of
42-
true -> fast_compile(Forms, E);
41+
true -> expand_defmodule(Forms, E);
4342
false -> compile(Forms, [], [], E)
4443
end,
4544
ok.
4645

47-
compile(Quoted, ArgsList, _CompilerOpts, #{line := Line, file := File} = E) ->
48-
Block = no_tail_optimize([{line, Line}], Quoted),
49-
{Expanded, SE, EE} = elixir_expand:expand(Block, elixir_env:env_to_ex(E), E),
46+
%% A version of compilation that uses eval (interpreted)
47+
interpret(Quoted, ArgsList, #{line := Line} = E) ->
48+
{Expanded, SE, EE} = elixir_expand:expand(Quoted, elixir_env:env_to_ex(E), E),
5049
elixir_env:check_unused_vars(SE, EE),
5150

5251
{Vars, TS} = elixir_erl_var:from_env(E),
5352
{ErlExprs, _} = elixir_erl_pass:translate(Expanded, erl_anno:new(Line), TS),
54-
Forms = code_eval(ErlExprs, Line, File, Vars),
5553

56-
{value, Fun, _} = erl_eval:expr(Forms, #{}),
54+
ListBinding = lists:zipwith(fun({_, Var}, Arg) -> {Var, Arg} end, Vars, ArgsList),
55+
Binding = maps:from_list(ListBinding),
56+
57+
{value, Result, _} = elixir:erl_eval(ErlExprs, Binding, E),
58+
{Result, SE, EE}.
59+
60+
compile(Quoted, ArgsList, CompilerOpts, #{line := Line} = E) ->
61+
Block = no_tail_optimize([{line, Line}], Quoted),
62+
{Expanded, SE, EE} = elixir_expand:expand(Block, elixir_env:env_to_ex(E), E),
63+
elixir_env:check_unused_vars(SE, EE),
64+
65+
{Module, Fun, LabelledLocals} =
66+
elixir_erl_compiler:spawn(fun() -> spawned_compile(Expanded, CompilerOpts, E) end),
67+
5768
Args = list_to_tuple(ArgsList),
58-
{Fun(Args), SE, EE}.
69+
{dispatch(Module, Fun, Args, LabelledLocals), SE, EE}.
5970

60-
code_eval(Expr, Line, File, Vars) when is_binary(File), is_integer(Line) ->
71+
spawned_compile(ExExprs, CompilerOpts, #{line := Line, file := File} = E) ->
72+
{Vars, S} = elixir_erl_var:from_env(E),
73+
{ErlExprs, _} = elixir_erl_pass:translate(ExExprs, erl_anno:new(Line), S),
74+
75+
Module = retrieve_compiler_module(),
76+
Fun = code_fun(?key(E, module)),
77+
Forms = code_mod(Fun, ErlExprs, Line, File, Module, Vars),
78+
79+
{Module, Binary} = elixir_erl_compiler:noenv_forms(Forms, File, [nowarn_nomatch | CompilerOpts]),
80+
code:load_binary(Module, "", Binary),
81+
{Module, Fun, is_purgeable(Binary)}.
82+
83+
is_purgeable(<<"FOR1", _Size:32, "BEAM", Rest/binary>>) ->
84+
do_is_purgeable(Rest).
85+
86+
do_is_purgeable(<<>>) -> true;
87+
do_is_purgeable(<<"LocT", 4:32, 0:32, _/binary>>) -> true;
88+
do_is_purgeable(<<"LocT", _:32, _/binary>>) -> false;
89+
do_is_purgeable(<<_:4/binary, Size:32, Beam/binary>>) ->
90+
<<_:(4 * trunc((Size+3) / 4))/binary, Rest/binary>> = Beam,
91+
do_is_purgeable(Rest).
92+
93+
dispatch(Module, Fun, Args, Purgeable) ->
94+
Res = Module:Fun(Args),
95+
return_compiler_module(Module, Purgeable),
96+
Res.
97+
98+
code_fun(nil) -> '__FILE__';
99+
code_fun(_) -> '__MODULE__'.
100+
101+
code_mod(Fun, Expr, Line, File, Module, Vars) when is_binary(File), is_integer(Line) ->
61102
Ann = erl_anno:new(Line),
62103
Tuple = {tuple, Ann, [{var, Ann, Var} || {_, Var} <- Vars]},
63-
{'fun', Ann, {clauses, [{clause, Ann, [Tuple], [], [Expr]}]}}.
64-
65-
allows_fast_compilation({'__block__', _, Exprs}) ->
66-
lists:all(fun allows_fast_compilation/1, Exprs);
67-
allows_fast_compilation({defmodule, _, [_, [{do, _}]]}) ->
104+
Relative = elixir_utils:relative_to_cwd(File),
105+
106+
[{attribute, Ann, file, {elixir_utils:characters_to_list(Relative), 1}},
107+
{attribute, Ann, module, Module},
108+
{attribute, Ann, compile, no_auto_import},
109+
{attribute, Ann, export, [{Fun, 1}, {'__RELATIVE__', 0}]},
110+
{function, Ann, Fun, 1, [
111+
{clause, Ann, [Tuple], [], [Expr]}
112+
]},
113+
{function, Ann, '__RELATIVE__', 0, [
114+
{clause, Ann, [], [], [elixir_erl:elixir_to_erl(Relative)]}
115+
]}].
116+
117+
retrieve_compiler_module() ->
118+
elixir_code_server:call(retrieve_compiler_module).
119+
120+
return_compiler_module(Module, Purgeable) ->
121+
elixir_code_server:cast({return_compiler_module, Module, Purgeable}).
122+
123+
only_defmodule({'__block__', _, Exprs}) ->
124+
lists:all(fun only_defmodule/1, Exprs);
125+
only_defmodule({defmodule, _, [_, [{do, _}]]}) ->
68126
true;
69-
allows_fast_compilation(_) ->
127+
only_defmodule(_) ->
70128
false.
71129

72-
fast_compile({'__block__', _, Exprs}, E) ->
73-
lists:foldl(fun(Expr, _) -> fast_compile(Expr, E) end, nil, Exprs);
74-
fast_compile({defmodule, Meta, [Mod, [{do, Block}]]}, NoLineE) ->
130+
expand_defmodule({'__block__', _, Exprs}, E) ->
131+
lists:foldl(fun(Expr, _) -> expand_defmodule(Expr, E) end, nil, Exprs);
132+
expand_defmodule({defmodule, Meta, [Mod, [{do, Block}]]}, NoLineE) ->
75133
E = NoLineE#{line := ?line(Meta)},
76134

77135
Expanded = case Mod of

lib/elixir/src/elixir_module.erl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,8 +472,12 @@ build(Module, Line, File, E) ->
472472

473473
eval_form(Line, Module, DataBag, Block, Vars, Prune, E) ->
474474
%% Given Elixir modules can get very long to compile due to metaprogramming,
475-
%% we disable expansions that take linear time to code size.
476-
{Value, ExS, EE} = elixir_compiler:compile(Block, Vars, [no_bool_opt, no_ssa_opt], E),
475+
%% we disable expansions that have linear time to code size.
476+
{Value, ExS, EE} =
477+
case elixir_config:get(module_definition) of
478+
interpreted -> elixir_compiler:interpret(Block, Vars, E);
479+
compiled -> elixir_compiler:compile(Block, Vars, [no_bool_opt, no_ssa_opt], E)
480+
end,
477481
elixir_overridable:store_not_overridden(Module),
478482
EV = (elixir_env:reset_vars(EE))#{line := Line},
479483
EC = eval_callbacks(Line, DataBag, before_compile, [EV], EV),

0 commit comments

Comments
 (0)