diff --git a/rebar.config b/rebar.config index c15418c..6349a32 100644 --- a/rebar.config +++ b/rebar.config @@ -2,7 +2,7 @@ {erl_opts, [debug_info]}. -{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref, "f129887"}}} +{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref, "53a055b"}}} , {getopt, "1.0.1"} , {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "2.8.0"}}} diff --git a/rebar.lock b/rebar.lock index 037273d..a88c51a 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,7 +1,7 @@ {"1.1.0", [{<<"aebytecode">>, {git,"https://github.com/aeternity/aebytecode.git", - {ref,"f1298870e526f4e9330447d3a281af5ef4e06e17"}}, + {ref,"53a055b90a7201761ab7bc2c55d6ddf5a1f38f27"}}, 0}, {<<"aeserialization">>, {git,"https://github.com/aeternity/aeserialization.git", diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index a113cea..5da40e0 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -31,7 +31,7 @@ map_get | map_get_d | map_set | map_from_list | map_to_list | map_delete | map_member | map_size | string_length | string_concat | bits_set | bits_clear | bits_test | bits_sum | - bits_intersection | bits_union | bits_difference. + bits_intersection | bits_union | bits_difference | contract_address. -type flit() :: {int, integer()} | {string, binary()} @@ -95,7 +95,7 @@ | bits | {variant, [[ftype()]]} | {function, [ftype()], ftype()} - | any. + | any | {tvar, var_name()}. -type fun_def() :: #{ attrs := [attribute()], args := [{var_name(), ftype()}], @@ -318,7 +318,7 @@ type_to_fcode(Env, Sub, {record_t, Fields}) -> type_to_fcode(_Env, _Sub, {bytes_t, _, _N}) -> string; %% TODO: add bytes type to FATE? type_to_fcode(_Env, Sub, {tvar, _, X}) -> - maps:get(X, Sub, any); + maps:get(X, Sub, {tvar, X}); type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) -> FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named], FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args], @@ -385,6 +385,8 @@ expr_to_fcode(Env, _Type, {tuple, _, Es}) -> %% Records expr_to_fcode(Env, Type, {proj, _Ann, Rec = {typed, _, _, RecType}, {id, _, X}}) -> case RecType of + {con, _, _} when X == "address" -> + {op, contract_address, [expr_to_fcode(Env, Rec)]}; {con, _, _} -> {fun_t, _, Named, Args, _} = Type, Arity = length(Named) + length(Args), @@ -876,9 +878,21 @@ optimize_fcode(Code = #{ functions := Funs }) -> Code#{ functions := maps:map(fun(Name, Def) -> optimize_fun(Code, Name, Def) end, Funs) }. -spec optimize_fun(fcode(), fun_name(), fun_def()) -> fun_def(). -optimize_fun(_Fcode, _Fun, Def = #{ body := _Body }) -> +optimize_fun(Fcode, Fun, Def = #{ body := Body }) -> %% io:format("Optimizing ~p =\n~s\n", [_Fun, prettypr:format(pp_fexpr(_Body))]), - Def. + Def#{ body := inliner(Fcode, Fun, Body) }. + +-spec inliner(fcode(), fun_name(), fexpr()) -> fexpr(). +inliner(Fcode, Fun, {def, Fun1, Args} = E) when Fun1 /= Fun -> + case should_inline(Fcode, Fun1) of + false -> E; + true -> inline(Fcode, Fun1, Args) + end; +inliner(_Fcode, _Fun, E) -> E. + +should_inline(_Fcode, _Fun1) -> false == list_to_atom("true"). %% Dialyzer + +inline(_Fcode, Fun, Args) -> {def, Fun, Args}. %% TODO %% -- Helper functions ------------------------------------------------------- @@ -1286,6 +1300,7 @@ pp_call(Fun, Args) -> pp_ftype(T) when is_atom(T) -> pp_text(T); pp_ftype(any) -> pp_text("_"); +pp_ftype({tvar, X}) -> pp_text(X); pp_ftype({tuple, Ts}) -> pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_ftype(T) || T <- Ts]))); pp_ftype({list, T}) -> diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 213ab2c..d20a8b9 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -138,16 +138,44 @@ functions_to_scode(ContractName, Functions, Options) -> return := Type}} <- maps:to_list(Functions)]). function_to_scode(ContractName, Functions, _Name, Args, Body, ResType, _Options) -> - ArgTypes = [ type_to_scode(T) || {_, T} <- Args ], + {ArgTypes, ResType1} = typesig_to_scode(Args, ResType), SCode = to_scode(init_env(ContractName, Functions, Args), Body), - {{ArgTypes, type_to_scode(ResType)}, SCode}. + {{ArgTypes, ResType1}, SCode}. +-define(tvars, '$tvars'). + +typesig_to_scode(Args, Res) -> + put(?tvars, {0, #{}}), + R = {[type_to_scode(T) || {_, T} <- Args], type_to_scode(Res)}, + erase(?tvars), + R. + +type_to_scode(integer) -> integer; +type_to_scode(boolean) -> boolean; +type_to_scode(string) -> string; +type_to_scode(address) -> address; +type_to_scode(hash) -> hash; +type_to_scode(signature) -> signature; +type_to_scode(contract) -> contract; +type_to_scode(oracle) -> oracle; +type_to_scode(oracle_query) -> oracle_query; +type_to_scode(name) -> name; +type_to_scode(channel) -> channel; +type_to_scode(bits) -> bits; +type_to_scode(any) -> any; type_to_scode({variant, Cons}) -> {variant, lists:map(fun(T) -> type_to_scode({tuple, T}) end, Cons)}; type_to_scode({list, Type}) -> {list, type_to_scode(Type)}; type_to_scode({tuple, Types}) -> {tuple, lists:map(fun type_to_scode/1, Types)}; type_to_scode({map, Key, Val}) -> {map, type_to_scode(Key), type_to_scode(Val)}; type_to_scode({function, _Args, _Res}) -> {tuple, [string, any]}; -type_to_scode(T) -> T. +type_to_scode({tvar, X}) -> + {I, Vars} = get(?tvars), + case maps:get(X, Vars, false) of + false -> + put(?tvars, {I + 1, Vars#{ X => I }}), + {tvar, I}; + J -> {tvar, J} + end. add_default_init_function(SFuns, StateType) when StateType /= {tuple, []} -> %% Only add default if the type is unit. @@ -511,7 +539,8 @@ op_to_scode(bits_intersection) -> aeb_fate_ops:bits_and(?a, ?a, ?a); op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a); op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a); op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a); -op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a). +op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a); +op_to_scode(contract_address) -> ?TODO(fate_contract_to_address_conversion). %% PUSH and STORE ?a are the same, so we use STORE to make optimizations %% easier, and specialize to PUSH (which is cheaper) at the end. @@ -1352,12 +1381,12 @@ chase_labels([L | Ls], Map, Live) -> chase_labels(New ++ Ls, Map, Live#{ L => true }). %% Replace PUSH, RETURN by RETURNR, drop returns after tail calls. -tweak_returns(['RETURN', {'PUSH', A} | Code]) -> [{'RETURNR', A} | Code]; -tweak_returns(['RETURN' | Code = [{'CALL_T', _} | _]]) -> Code; -tweak_returns(['RETURN' | Code = [{'CALL_TR', _, _} | _]]) -> Code; -tweak_returns(['RETURN' | Code = [{'CALL_GT', _} | _]]) -> Code; -tweak_returns(['RETURN' | Code = [{'CALL_GTR', _, _} | _]])-> Code; -tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code; +tweak_returns(['RETURN', {'PUSH', A} | Code]) -> [{'RETURNR', A} | Code]; +tweak_returns(['RETURN' | Code = [{'CALL_T', _} | _]]) -> Code; +tweak_returns(['RETURN' | Code = [{'CALL_TR', _, _, _} | _]]) -> Code; +tweak_returns(['RETURN' | Code = [{'CALL_GT', _} | _]]) -> Code; +tweak_returns(['RETURN' | Code = [{'CALL_GTR', _, _, _, _} | _]]) -> Code; +tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code; tweak_returns(Code) -> Code. %% -- Split basic blocks at CALL instructions -- @@ -1373,6 +1402,8 @@ split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL'; element(1, I) == 'CALL_GR'; element(1, I) == 'jumpif' -> split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]); +split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) -> + lists:reverse([{Ref, lists:reverse([I | Acc])} | Blocks]); split_calls(Ref, [I | Code], Acc, Blocks) -> split_calls(Ref, Code, [I | Acc], Blocks). diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index f6ac15a..331e996 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -24,7 +24,8 @@ simple_compile_test_() -> type_info := _} when Backend == aevm -> ?assertMatch(Code when is_binary(Code), ByteCode); Code when Backend == fate, is_tuple(Code) -> - ?assertMatch(#{}, aeb_fate_code:functions(Code)); + Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)), + ?assertMatch({X, X}, {Code1, Code}); ErrBin -> io:format("\n~s", [ErrBin]), error(ErrBin)