Deadcode elimination (icode post pass)
This commit is contained in:
parent
478da2af33
commit
dfa286d43c
@ -21,7 +21,8 @@ convert_typed(TypedTree, Options) ->
|
||||
{contract, _, {con, _, Con}, _} -> Con;
|
||||
_ -> gen_error(last_declaration_must_be_contract)
|
||||
end,
|
||||
code(TypedTree, aeso_icode:set_name(Name, aeso_icode:new(Options))).
|
||||
Icode = code(TypedTree, aeso_icode:set_name(Name, aeso_icode:new(Options))),
|
||||
deadcode_elimination(Icode).
|
||||
|
||||
code([{contract, _Attribs, Con, Code}|Rest], Icode) ->
|
||||
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)),
|
||||
@ -786,3 +787,39 @@ builtin_call(Builtin, Args) ->
|
||||
add_builtins(Icode = #{functions := Funs}) ->
|
||||
Builtins = aeso_builtins:used_builtins(Funs),
|
||||
Icode#{functions := [ aeso_builtins:builtin_function(B) || B <- Builtins ] ++ Funs}.
|
||||
|
||||
|
||||
%% -------------------------------------------------------------------
|
||||
%% Deadcode elimination
|
||||
%% -------------------------------------------------------------------
|
||||
|
||||
deadcode_elimination(Icode = #{ functions := Funs }) ->
|
||||
PublicNames = [ Name || {Name, Ann, _, _, _} <- Funs, not lists:member(private, Ann) ],
|
||||
ArgsToPat = fun(Args) -> [ #var_ref{ name = X } || {X, _} <- Args ] end,
|
||||
Defs = maps:from_list([ {Name, {binder, ArgsToPat(Args), Body}} || {Name, _, Args, Body, _} <- Funs ]),
|
||||
UsedNames = chase_names(Defs, PublicNames, #{}),
|
||||
UsedFuns = [ Def || Def = {Name, _, _, _, _} <- Funs, maps:is_key(Name, UsedNames) ],
|
||||
Icode#{ functions := UsedFuns }.
|
||||
|
||||
chase_names(_Defs, [], Used) -> Used;
|
||||
chase_names(Defs, [X | Xs], Used) ->
|
||||
%% can happen when compiling __call contracts
|
||||
case maps:is_key(X, Used) orelse not maps:is_key(X, Defs) of
|
||||
true -> chase_names(Defs, Xs, Used); %% already chased
|
||||
false ->
|
||||
Def = maps:get(X, Defs),
|
||||
Vars = maps:keys(free_vars(Def)),
|
||||
chase_names(Defs, Vars ++ Xs, Used#{ X => true })
|
||||
end.
|
||||
|
||||
free_vars(#var_ref{ name = X }) -> #{ X => true };
|
||||
free_vars(#arg{ name = X }) -> #{ X => true };
|
||||
free_vars({binder, Pat, Body}) ->
|
||||
maps:without(maps:keys(free_vars(Pat)), free_vars(Body));
|
||||
free_vars(#switch{ expr = E, cases = Cases }) ->
|
||||
free_vars([E | [{binder, P, B} || {P, B} <- Cases]]);
|
||||
free_vars(#lambda{ args = Xs, body = E }) ->
|
||||
free_vars({binder, Xs, E});
|
||||
free_vars(T) when is_tuple(T) -> free_vars(tuple_to_list(T));
|
||||
free_vars([H | T]) -> maps:merge(free_vars(H), free_vars(T));
|
||||
free_vars(_) -> #{}.
|
||||
|
@ -20,7 +20,7 @@
|
||||
, args :: arg_list()
|
||||
, body :: expr()}).
|
||||
|
||||
-record(var_ref, { name :: string() | {builtin, atom() | tuple()}}).
|
||||
-record(var_ref, { name :: string() | list(string()) | {builtin, atom() | tuple()}}).
|
||||
|
||||
-record(prim_call_contract,
|
||||
{ gas :: expr()
|
||||
|
@ -26,7 +26,16 @@ simple_compile_test_() ->
|
||||
<<"Type errors\n",ErrorString/binary>> = compile(ContractName),
|
||||
check_errors(lists:sort(ExpectedErrors), ErrorString)
|
||||
end} ||
|
||||
{ContractName, ExpectedErrors} <- failing_contracts() ].
|
||||
{ContractName, ExpectedErrors} <- failing_contracts() ] ++
|
||||
[ {"Testing deadcode elimination",
|
||||
fun() ->
|
||||
#{ byte_code := NoDeadCode } = compile("nodeadcode"),
|
||||
#{ byte_code := DeadCode } = compile("deadcode"),
|
||||
SizeNoDeadCode = byte_size(NoDeadCode),
|
||||
SizeDeadCode = byte_size(DeadCode),
|
||||
?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + 40 < SizeNoDeadCode}),
|
||||
ok
|
||||
end} ].
|
||||
|
||||
check_errors(Expect, ErrorString) ->
|
||||
%% This removes the final single \n as well.
|
||||
@ -64,7 +73,9 @@ compilable_contracts() ->
|
||||
"stack",
|
||||
"test",
|
||||
"builtin_bug",
|
||||
"builtin_map_get_bug"
|
||||
"builtin_map_get_bug",
|
||||
"nodeadcode",
|
||||
"deadcode"
|
||||
].
|
||||
|
||||
%% Contracts that should produce type errors
|
||||
|
21
test/contracts/deadcode.aes
Normal file
21
test/contracts/deadcode.aes
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
namespace List =
|
||||
|
||||
function map1(f : 'a => 'b, xs : list('a)) =
|
||||
switch(xs)
|
||||
[] => []
|
||||
x :: xs => f(x) :: map1(f, xs)
|
||||
|
||||
function map2(f : 'a => 'b, xs : list('a)) =
|
||||
switch(xs)
|
||||
[] => []
|
||||
x :: xs => f(x) :: map2(f, xs)
|
||||
|
||||
contract Deadcode =
|
||||
|
||||
function inc1(xs : list(int)) : list(int) =
|
||||
List.map1((x) => x + 1, xs)
|
||||
|
||||
function inc2(xs : list(int)) : list(int) =
|
||||
List.map1((x) => x + 1, xs)
|
||||
|
21
test/contracts/nodeadcode.aes
Normal file
21
test/contracts/nodeadcode.aes
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
namespace List =
|
||||
|
||||
function map1(f : 'a => 'b, xs : list('a)) =
|
||||
switch(xs)
|
||||
[] => []
|
||||
x :: xs => f(x) :: map1(f, xs)
|
||||
|
||||
function map2(f : 'a => 'b, xs : list('a)) =
|
||||
switch(xs)
|
||||
[] => []
|
||||
x :: xs => f(x) :: map2(f, xs)
|
||||
|
||||
contract Deadcode =
|
||||
|
||||
function inc1(xs : list(int)) : list(int) =
|
||||
List.map1((x) => x + 1, xs)
|
||||
|
||||
function inc2(xs : list(int)) : list(int) =
|
||||
List.map2((x) => x + 1, xs)
|
||||
|
Loading…
x
Reference in New Issue
Block a user