Compile builtins

This commit is contained in:
Ulf Norell 2019-05-03 12:28:57 +02:00
parent 6f17477c72
commit 1f40d2a321
2 changed files with 257 additions and 68 deletions

View File

@ -24,9 +24,13 @@
-type var_name() :: string(). -type var_name() :: string().
-type sophia_name() :: [string()]. -type sophia_name() :: [string()].
-type binop() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' | -type builtin() :: atom().
'<' | '>' | '=<' | '>=' | '==' | '!='.
-type unop() :: '!'. -type op() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' |
'<' | '>' | '=<' | '>=' | '==' | '!=' | '!' |
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.
-type fexpr() :: {int, integer()} -type fexpr() :: {int, integer()}
| {string, binary()} | {string, binary()}
@ -38,6 +42,8 @@
| nil | nil
| {var, var_name()} | {var, var_name()}
| {def, fun_name()} | {def, fun_name()}
| {builtin, builtin(), non_neg_integer() | none} %% Unapplied builtin
| {builtin, builtin(), [fexpr()]}
| {con, arities(), tag(), [fexpr()]} | {con, arities(), tag(), [fexpr()]}
| {tuple, [fexpr()]} | {tuple, [fexpr()]}
| {proj, fexpr(), integer()} | {proj, fexpr(), integer()}
@ -46,8 +52,7 @@
| {map_set, fexpr(), fexpr(), fexpr()} % map, key, val | {map_set, fexpr(), fexpr(), fexpr()} % map, key, val
| {map_get, fexpr(), fexpr()} % map, key | {map_get, fexpr(), fexpr()} % map, key
| {map_get, fexpr(), fexpr(), fexpr()} % map, key, default | {map_get, fexpr(), fexpr(), fexpr()} % map, key, default
| {op, binop(), fexpr(), fexpr()} | {op, op(), [fexpr()]}
| {op, unop(), fexpr()}
| {'let', var_name(), fexpr(), fexpr()} | {'let', var_name(), fexpr(), fexpr()}
| {funcall, fexpr(), [fexpr()]} | {funcall, fexpr(), [fexpr()]}
| {switch, fsplit()}. | {switch, fsplit()}.
@ -104,6 +109,7 @@
-type type_env() :: #{ sophia_name() => type_def() }. -type type_env() :: #{ sophia_name() => type_def() }.
-type fun_env() :: #{ sophia_name() => fun_name() }. -type fun_env() :: #{ sophia_name() => fun_name() }.
-type con_env() :: #{ sophia_name() => con_tag() }. -type con_env() :: #{ sophia_name() => con_tag() }.
-type builtins() :: #{ sophia_name() => {builtin(), non_neg_integer() | none} }.
-type context() :: {main_contract, string()} -type context() :: {main_contract, string()}
| {namespace, string()} | {namespace, string()}
@ -112,6 +118,7 @@
-type env() :: #{ type_env := type_env(), -type env() :: #{ type_env := type_env(),
fun_env := fun_env(), fun_env := fun_env(),
con_env := con_env(), con_env := con_env(),
builtins := builtins(),
options := [option()], options := [option()],
context => context(), context => context(),
vars => [var_name()], vars => [var_name()],
@ -130,13 +137,47 @@ ast_to_fcode(Code, Options) ->
-spec init_env([option()]) -> env(). -spec init_env([option()]) -> env().
init_env(Options) -> init_env(Options) ->
#{ type_env => init_type_env(), #{ type_env => init_type_env(),
fun_env => #{}, %% TODO: builtin functions here? fun_env => #{},
con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] }, builtins => builtins(),
["Some"] => #con_tag{ tag = 1, arities = [0, 1] } con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] },
["Some"] => #con_tag{ tag = 1, arities = [0, 1] },
["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] },
["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] }
}, },
options => Options, options => Options,
functions => #{} }. functions => #{} }.
-spec builtins() -> builtins().
builtins() ->
MkName = fun(NS, Fun) ->
list_to_atom(string:to_lower(string:join(NS ++ [Fun], "_")))
end,
Scopes = [{[], [{"abort", 1}]},
{["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none},
{"timestamp", none}, {"block_height", none}, {"difficulty", none},
{"gas_limit", none}]},
{["Contract"], [{"address", none}, {"balance", none}]},
{["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none},
{"gas_left", 0}]},
{["Oracle"], [{"register", 4}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
{"respond", 4}, {"extend", 3}, {"get_answer", 2}]},
{["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 4}, {"transfer", 4},
{"revoke", 3}]},
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
{["Crypto"], [{"ecverify", 3}, {"ecverify_secp256k1", 3}, {"sha3", 1},
{"sha256", 1}, {"blake2b", 1}]},
{["Auth"], [{"tx_hash", none}]},
{["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
{["Int"], [{"to_str", 1}]},
{["Address"], [{"to_str", 1}]}
],
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|| {NS, Funs} <- Scopes,
{Fun, Arity} <- Funs ]).
-define(type(T), fun([]) -> T end). -define(type(T), fun([]) -> T end).
-define(type(X, T), fun([X]) -> T end). -define(type(X, T), fun([X]) -> T end).
-define(type(X, Y, T), fun([X, Y]) -> T end). -define(type(X, Y, T), fun([X, Y]) -> T end).
@ -333,7 +374,7 @@ expr_to_fcode(Env, {record_t, FieldTypes}, {record, _Ann, Rec, Fields}) ->
%% Lists %% Lists
expr_to_fcode(Env, _Type, {list, _, Es}) -> expr_to_fcode(Env, _Type, {list, _, Es}) ->
lists:foldr(fun(E, L) -> {op, '::', expr_to_fcode(Env, E), L} end, lists:foldr(fun(E, L) -> {op, '::', [expr_to_fcode(Env, E), L]} end,
nil, Es); nil, Es);
%% Conditionals %% Conditionals
@ -372,16 +413,25 @@ expr_to_fcode(Env, _Type, Expr = {app, _, {Op, _}, [_, _]}) when Op == '&&'; Op
Tree = expr_to_decision_tree(Env, Expr), Tree = expr_to_decision_tree(Env, Expr),
decision_tree_to_fcode(Tree); decision_tree_to_fcode(Tree);
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A, B]}) when is_atom(Op) -> expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A, B]}) when is_atom(Op) ->
{op, Op, expr_to_fcode(Env, A), expr_to_fcode(Env, B)}; {op, Op, [expr_to_fcode(Env, A), expr_to_fcode(Env, B)]};
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) -> expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
case Op of case Op of
'-' -> {op, '-', {int, 0}, expr_to_fcode(Env, A)}; '-' -> {op, '-', [{int, 0}, expr_to_fcode(Env, A)]};
'!' -> {op, '!', expr_to_fcode(Env, A)} '!' -> {op, '!', [expr_to_fcode(Env, A)]}
end; end;
%% Function calls %% Function calls
expr_to_fcode(Env, _Type, {app, _Ann, Fun, Args}) -> expr_to_fcode(Env, _Type, {app, _Ann, Fun = {typed, _, _, {fun_t, _, NamedArgsT, _, _}}, Args}) ->
{funcall, expr_to_fcode(Env, Fun), [expr_to_fcode(Env, Arg) || Arg <- Args]}; Args1 = get_named_args(NamedArgsT, Args),
FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1],
case expr_to_fcode(Env, Fun) of
{builtin, B, Ar} when is_integer(Ar) ->
case length(FArgs) of
N when N == Ar -> builtin_to_fcode(B, FArgs);
N when N < Ar -> error({todo, eta_expand, B, FArgs})
end;
FFun -> {funcall, FFun, FArgs}
end;
%% Maps %% Maps
expr_to_fcode(_Env, _Type, {map, _, []}) -> expr_to_fcode(_Env, _Type, {map, _, []}) ->
@ -628,6 +678,24 @@ stmts_to_fcode(Env, [{letval, _, {typed, _, {id, _, X}, _}, _, Expr} | Stmts]) -
stmts_to_fcode(Env, [Expr]) -> stmts_to_fcode(Env, [Expr]) ->
expr_to_fcode(Env, Expr). expr_to_fcode(Env, Expr).
%% -- Builtins --
op_builtins() ->
[map_from_list, map_to_list, map_delete, map_member, map_size,
string_length, string_concat, string_sha3, string_sha256, string_blake2b,
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
bits_difference, int_to_str, address_to_str].
builtin_to_fcode(map_lookup, [Key, Map]) ->
{map_get, Map, Key};
builtin_to_fcode(map_lookup_default, [Key, Map, Def]) ->
{map_get, Map, Key, Def};
builtin_to_fcode(Builtin, Args) ->
case lists:member(Builtin, op_builtins()) of
true -> {op, Builtin, Args};
false -> {builtin, Builtin, Args}
end.
%% -- Optimisations ---------------------------------------------------------- %% -- Optimisations ----------------------------------------------------------
%% - Deadcode elimination %% - Deadcode elimination
@ -739,9 +807,10 @@ resolve_var(#{ vars := Vars } = Env, [X]) ->
end; end;
resolve_var(Env, Q) -> resolve_fun(Env, Q). resolve_var(Env, Q) -> resolve_fun(Env, Q).
resolve_fun(#{ fun_env := Funs }, Q) -> resolve_fun(#{ fun_env := Funs, builtins := Builtin }, Q) ->
case maps:get(Q, Funs, not_found) of case maps:get(Q, maps:merge(Funs, Builtin), not_found) of
not_found -> fcode_error({unbound_variable, Q}); not_found -> fcode_error({unbound_variable, Q});
{B, Ar} -> {builtin, B, Ar};
Fun -> {def, Fun} Fun -> {def, Fun}
end. end.
@ -768,6 +837,19 @@ pat_vars({tuple, Ps}) -> pat_vars(Ps);
pat_vars({con, _, _, Ps}) -> pat_vars(Ps); pat_vars({con, _, _, Ps}) -> pat_vars(Ps);
pat_vars(Ps) when is_list(Ps) -> [X || P <- Ps, X <- pat_vars(P)]. pat_vars(Ps) when is_list(Ps) -> [X || P <- Ps, X <- pat_vars(P)].
get_named_args(NamedArgsT, Args) ->
IsNamed = fun({named_arg, _, _, _}) -> true;
(_) -> false end,
{Named, NotNamed} = lists:partition(IsNamed, Args),
NamedArgs = [get_named_arg(NamedArg, Named) || NamedArg <- NamedArgsT],
NamedArgs ++ NotNamed.
get_named_arg({named_arg_t, _, {id, _, Name}, _, Default}, Args) ->
case [ Val || {named_arg, _, {id, _, X}, Val} <- Args, X == Name ] of
[Val] -> Val;
[] -> Default
end.
%% -- Renaming -- %% -- Renaming --
-spec rename([{var_name(), var_name()}], fexpr()) -> fexpr(). -spec rename([{var_name(), var_name()}], fexpr()) -> fexpr().
@ -787,8 +869,7 @@ rename(Ren, Expr) ->
{tuple, Es} -> {tuple, [rename(Ren, E) || E <- Es]}; {tuple, Es} -> {tuple, [rename(Ren, E) || E <- Es]};
{proj, E, I} -> {proj, rename(Ren, E), I}; {proj, E, I} -> {proj, rename(Ren, E), I};
{set_proj, R, I, E} -> {set_proj, rename(Ren, R), I, rename(Ren, E)}; {set_proj, R, I, E} -> {set_proj, rename(Ren, R), I, rename(Ren, E)};
{op, Op, E1, E2} -> {op, Op, rename(Ren, E1), rename(Ren, E2)}; {op, Op, Es} -> {op, Op, [rename(Ren, E) || E <- Es]};
{op, Op, E} -> {op, Op, rename(Ren, E)};
{funcall, Fun, Es} -> {funcall, Fun, [rename(Ren, E) || E <- Es]}; {funcall, Fun, Es} -> {funcall, Fun, [rename(Ren, E) || E <- Es]};
{'let', X, E, Body} -> {'let', X, E, Body} ->
{Z, Ren1} = rename_binding(Ren, X), {Z, Ren1} = rename_binding(Ren, X),
@ -936,7 +1017,6 @@ pp_fexpr(nil) ->
pp_fexpr({var, X}) -> pp_text(X); pp_fexpr({var, X}) -> pp_text(X);
pp_fexpr({def, {entrypoint, E}}) -> pp_text(E); pp_fexpr({def, {entrypoint, E}}) -> pp_text(E);
pp_fexpr({def, {local_fun, Q}}) -> pp_text(string:join(Q, ".")); pp_fexpr({def, {local_fun, Q}}) -> pp_text(string:join(Q, "."));
pp_fexpr({def, {builtin, B}}) -> pp_text(B);
pp_fexpr({con, _, I, []}) -> pp_fexpr({con, _, I, []}) ->
pp_beside(pp_text("C"), pp_text(I)); pp_beside(pp_text("C"), pp_text(I));
pp_fexpr({con, _, I, Es}) -> pp_fexpr({con, _, I, Es}) ->
@ -948,17 +1028,33 @@ pp_fexpr({proj, E, I}) ->
pp_beside([pp_fexpr(E), pp_text("."), pp_text(I)]); pp_beside([pp_fexpr(E), pp_text("."), pp_text(I)]);
pp_fexpr({set_proj, E, I, A}) -> pp_fexpr({set_proj, E, I, A}) ->
pp_beside(pp_fexpr(E), pp_braces(pp_beside([pp_text(I), pp_text(" = "), pp_fexpr(A)]))); pp_beside(pp_fexpr(E), pp_braces(pp_beside([pp_text(I), pp_text(" = "), pp_fexpr(A)])));
pp_fexpr({op, Op, A, B}) -> pp_fexpr({op, Op, [A, B] = Args}) ->
pp_parens(pp_par([pp_fexpr(A), pp_text(Op), pp_fexpr(B)])); case is_infix(Op) of
pp_fexpr({op, Op, A}) -> false -> pp_call(pp_text(Op), Args);
pp_parens(pp_par([pp_text(Op), pp_fexpr(A)])); true -> pp_parens(pp_par([pp_fexpr(A), pp_text(Op), pp_fexpr(B)]))
end;
pp_fexpr({op, Op, [A] = Args}) ->
case is_infix(Op) of
false -> pp_call(pp_text(Op), Args);
true -> pp_parens(pp_par([pp_text(Op), pp_fexpr(A)]))
end;
pp_fexpr({op, Op, As}) ->
pp_beside(pp_text(Op), pp_fexpr({tuple, As}));
pp_fexpr({'let', X, A, B}) -> pp_fexpr({'let', X, A, B}) ->
pp_par([pp_beside([pp_text("let "), pp_text(X), pp_text(" = "), pp_fexpr(A), pp_text(" in")]), pp_par([pp_beside([pp_text("let "), pp_text(X), pp_text(" = "), pp_fexpr(A), pp_text(" in")]),
pp_fexpr(B)]); pp_fexpr(B)]);
pp_fexpr({builtin, B, none}) -> pp_text(B);
pp_fexpr({builtin, B, N}) when is_integer(N) ->
pp_beside([pp_text(B), pp_text("/"), pp_text(N)]);
pp_fexpr({builtin, B, As}) when is_list(As) ->
pp_call(pp_text(B), As);
pp_fexpr({funcall, Fun, As}) -> pp_fexpr({funcall, Fun, As}) ->
pp_beside(pp_fexpr(Fun), pp_fexpr({tuple, As})); pp_call(pp_fexpr(Fun), As);
pp_fexpr({switch, Split}) -> pp_split(Split). pp_fexpr({switch, Split}) -> pp_split(Split).
pp_call(Fun, Args) ->
pp_beside(Fun, pp_fexpr({tuple, Args})).
pp_ftype(T) when is_atom(T) -> pp_text(T); pp_ftype(T) when is_atom(T) -> pp_text(T);
pp_ftype({tuple, Ts}) -> pp_ftype({tuple, Ts}) ->
pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_ftype(T) || T <- Ts]))); pp_parens(pp_par(pp_punctuate(pp_text(","), [pp_ftype(T) || T <- Ts])));
@ -982,8 +1078,12 @@ pp_case({'case', Pat, Split}) ->
prettypr:nest(2, pp_split(Split))]). prettypr:nest(2, pp_split(Split))]).
pp_pat({tuple, Xs}) -> pp_fexpr({tuple, [{var, X} || X <- Xs]}); pp_pat({tuple, Xs}) -> pp_fexpr({tuple, [{var, X} || X <- Xs]});
pp_pat({'::', X, Xs}) -> pp_fexpr({op, '::', {var, X}, {var, Xs}}); pp_pat({'::', X, Xs}) -> pp_fexpr({op, '::', [{var, X}, {var, Xs}]});
pp_pat({con, As, I, Xs}) -> pp_fexpr({con, As, I, [{var, X} || X <- Xs]}); pp_pat({con, As, I, Xs}) -> pp_fexpr({con, As, I, [{var, X} || X <- Xs]});
pp_pat({var, X}) -> pp_fexpr({var, X}); pp_pat({var, X}) -> pp_fexpr({var, X});
pp_pat(Pat) -> pp_fexpr(Pat). pp_pat(Pat) -> pp_fexpr(Pat).
is_infix(Op) ->
C = hd(atom_to_list(Op)),
C < $a orelse C > $z.

View File

@ -223,31 +223,20 @@ to_scode(Env, {set_proj, R, I, E}) ->
to_scode(notail(Env), R), to_scode(notail(Env), R),
aeb_fate_code:setelement(?a, ?i(I), ?a, ?a)]; aeb_fate_code:setelement(?a, ?i(I), ?a, ?a)];
to_scode(Env, {op, Op, A, B}) -> to_scode(Env, {op, Op, Args}) ->
[ to_scode(notail(Env), B), call_to_scode(Env, op_to_scode(Op), Args);
to_scode(notail(Env), A),
binop_to_scode(Op) ];
to_scode(Env, {op, Op, A}) ->
[ to_scode(notail(Env), A),
unop_to_scode(Op) ];
%% Maps %% Maps
to_scode(_Env, map_empty) -> to_scode(_Env, map_empty) ->
[aeb_fate_code:map_empty(?a)]; [aeb_fate_code:map_empty(?a)];
to_scode(Env, {map_set, Map, Key, Val}) -> to_scode(Env, {map_set, Map, Key, Val}) ->
[to_scode(notail(Env), Val), call_to_scode(Env, aeb_fate_code:map_update(?a, ?a, ?a, ?a),
to_scode(notail(Env), Key), [Map, Key, Val]);
to_scode(notail(Env), Map),
aeb_fate_code:map_update(?a, ?a, ?a, ?a)];
to_scode(Env, {map_get, Map, Key}) -> to_scode(Env, {map_get, Map, Key}) ->
[to_scode(notail(Env), Key), call_to_scode(Env, aeb_fate_code:map_lookup(?a, ?a, ?a), [Map, Key]);
to_scode(notail(Env), Map),
aeb_fate_code:map_lookup(?a, ?a, ?a)];
to_scode(Env, {map_get, Map, Key, Default}) -> to_scode(Env, {map_get, Map, Key, Default}) ->
[to_scode(notail(Env), Default), call_to_scode(Env, aeb_fate_code:map_lookup(?a, ?a, ?a, ?a),
to_scode(notail(Env), Key), [Map, Key, Default]);
to_scode(notail(Env), Map),
aeb_fate_code:map_lookup(?a, ?a, ?a, ?a)];
to_scode(Env, {'let', X, {var, Y}, Body}) -> to_scode(Env, {'let', X, {var, Y}, Body}) ->
Env1 = bind_var(X, lookup_var(Env, Y), Env), Env1 = bind_var(X, lookup_var(Env, Y), Env),
@ -276,6 +265,9 @@ to_scode(Env, {funcall, Fun, Args}) ->
to_scode(Env, {switch, Case}) -> to_scode(Env, {switch, Case}) ->
split_to_scode(Env, Case); split_to_scode(Env, Case);
to_scode(Env, {builtin, B, Args}) ->
builtin_to_scode(Env, B, Args);
to_scode(_Env, Icode) -> ?TODO(Icode). to_scode(_Env, Icode) -> ?TODO(Icode).
split_to_scode(Env, {nosplit, Expr}) -> split_to_scode(Env, {nosplit, Expr}) ->
@ -387,24 +379,123 @@ match_tuple(Env, I, Elem, Arg, [X | Xs]) ->
match_tuple(Env, _, _, _, []) -> match_tuple(Env, _, _, _, []) ->
{[], Env}. {[], Env}.
%% -- Builtins --
call_to_scode(Env, CallCode, Args) ->
[[to_scode(notail(Env), A) || A <- lists:reverse(Args)],
CallCode].
builtin_to_scode(_Env, bits_none, none) ->
[aeb_fate_code:bits_none(?a)];
builtin_to_scode(_Env, bits_all, none) ->
[aeb_fate_code:bits_all(?a)];
builtin_to_scode(Env, abort, [_] = Args) ->
call_to_scode(Env, aeb_fate_code:abort(?a), Args);
builtin_to_scode(Env, chain_spend, [_, _] = Args) ->
call_to_scode(Env, [aeb_fate_code:spend(?a, ?a),
aeb_fate_code:tuple(0)], Args);
builtin_to_scode(Env, chain_balance, [_] = Args) ->
call_to_scode(Env, aeb_fate_code:balance_other(?a, ?a), Args);
builtin_to_scode(_Env, chain_block_hash, [{builtin, chain_block_height, none}]) ->
[aeb_fate_code:blockhash(?a)];
builtin_to_scode(_Env, chain_block_hash, [_]) ->
?TODO(fate_block_hash_at_height_instruction);
builtin_to_scode(_Env, chain_coinbase, none) ->
[aeb_fate_code:beneficiary(?a)];
builtin_to_scode(_Env, chain_timestamp, none) ->
[aeb_fate_code:timestamp(?a)];
builtin_to_scode(_Env, chain_block_height, none) ->
[aeb_fate_code:generation(?a)];
builtin_to_scode(_Env, chain_difficulty, none) ->
[aeb_fate_code:difficulty(?a)];
builtin_to_scode(_Env, chain_gas_limit, none) ->
[aeb_fate_code:gaslimit(?a)];
builtin_to_scode(_Env, contract_balance, none) ->
[aeb_fate_code:balance(?a)];
builtin_to_scode(_Env, contract_address, none) ->
[aeb_fate_code:address(?a)];
builtin_to_scode(_Env, call_origin, none) ->
[aeb_fate_code:origin(?a)];
builtin_to_scode(_Env, call_caller, none) ->
[aeb_fate_code:caller(?a)];
builtin_to_scode(_Env, call_value, none) ->
?TODO(fate_call_value_instruction);
builtin_to_scode(_Env, call_gas_price, none) ->
[aeb_fate_code:gasprice(?a)];
builtin_to_scode(_Env, call_gas_left, []) ->
[aeb_fate_code:gas(?a)];
builtin_to_scode(_Env, oracle_register, [_, _, _, _] = _Args) ->
?TODO(fate_oracle_register_instruction);
builtin_to_scode(_Env, oracle_query_fee, [_] = _Args) ->
?TODO(fate_oracle_query_fee_instruction);
builtin_to_scode(_Env, oracle_query, [_, _, _, _, _] = _Args) ->
?TODO(fate_oracle_query_instruction);
builtin_to_scode(_Env, oracle_get_question, [_, _] = _Args) ->
?TODO(fate_oracle_get_question_instruction);
builtin_to_scode(_Env, oracle_respond, [_, _, _, _] = _Args) ->
?TODO(fate_oracle_respond_instruction);
builtin_to_scode(_Env, oracle_extend, [_, _, _] = _Args) ->
?TODO(fate_oracle_extend_instruction);
builtin_to_scode(_Env, oracle_get_answer, [_, _] = _Args) ->
?TODO(fate_oracle_get_answer_instruction);
builtin_to_scode(_Env, aens_resolve, [_, _] = _Args) ->
?TODO(fate_aens_resolve_instruction);
builtin_to_scode(_Env, aens_preclaim, [_, _, _] = _Args) ->
?TODO(fate_aens_preclaim_instruction);
builtin_to_scode(_Env, aens_claim, [_, _, _, _] = _Args) ->
?TODO(fate_aens_claim_instruction);
builtin_to_scode(_Env, aens_transfer, [_, _, _, _] = _Args) ->
?TODO(fate_aens_transfer_instruction);
builtin_to_scode(_Env, aens_revoke, [_, _, _] = _Args) ->
?TODO(fate_aens_revoke_instruction);
builtin_to_scode(_Env, crypto_ecverify, [_, _, _] = _Args) ->
?TODO(fate_crypto_ecverify_instruction);
builtin_to_scode(_Env, crypto_ecverify_secp256k1, [_, _, _] = _Args) ->
?TODO(fate_crypto_ecverify_secp256k1_instruction);
builtin_to_scode(_Env, crypto_sha3, [_] = _Args) ->
?TODO(fate_crypto_sha3_instruction);
builtin_to_scode(_Env, crypto_sha256, [_] = _Args) ->
?TODO(fate_crypto_sha256_instruction);
builtin_to_scode(_Env, crypto_blake2b, [_] = _Args) ->
?TODO(fate_crypto_blake2b_instruction);
builtin_to_scode(_Env, auth_tx_hash, none) ->
?TODO(fate_auth_tx_hash_instruction);
builtin_to_scode(_, B, Args) ->
?TODO({builtin, B, Args}).
%% -- Operators -- %% -- Operators --
binop_to_scode('+') -> aeb_fate_code:add(?a, ?a, ?a); op_to_scode('+') -> aeb_fate_code:add(?a, ?a, ?a);
binop_to_scode('-') -> aeb_fate_code:sub(?a, ?a, ?a); op_to_scode('-') -> aeb_fate_code:sub(?a, ?a, ?a);
binop_to_scode('*') -> aeb_fate_code:mul(?a, ?a, ?a); op_to_scode('*') -> aeb_fate_code:mul(?a, ?a, ?a);
binop_to_scode('/') -> aeb_fate_code:divide(?a, ?a, ?a); op_to_scode('/') -> aeb_fate_code:divide(?a, ?a, ?a);
binop_to_scode(mod) -> aeb_fate_code:modulo(?a, ?a, ?a); op_to_scode(mod) -> aeb_fate_code:modulo(?a, ?a, ?a);
binop_to_scode('^') -> aeb_fate_code:pow(?a, ?a, ?a); op_to_scode('^') -> aeb_fate_code:pow(?a, ?a, ?a);
binop_to_scode('++') -> aeb_fate_code:append(?a, ?a, ?a); op_to_scode('++') -> aeb_fate_code:append(?a, ?a, ?a);
binop_to_scode('::') -> aeb_fate_code:cons(?a, ?a, ?a); op_to_scode('::') -> aeb_fate_code:cons(?a, ?a, ?a);
binop_to_scode('<') -> aeb_fate_code:lt(?a, ?a, ?a); op_to_scode('<') -> aeb_fate_code:lt(?a, ?a, ?a);
binop_to_scode('>') -> aeb_fate_code:gt(?a, ?a, ?a); op_to_scode('>') -> aeb_fate_code:gt(?a, ?a, ?a);
binop_to_scode('=<') -> aeb_fate_code:elt(?a, ?a, ?a); op_to_scode('=<') -> aeb_fate_code:elt(?a, ?a, ?a);
binop_to_scode('>=') -> aeb_fate_code:egt(?a, ?a, ?a); op_to_scode('>=') -> aeb_fate_code:egt(?a, ?a, ?a);
binop_to_scode('==') -> aeb_fate_code:eq(?a, ?a, ?a); op_to_scode('==') -> aeb_fate_code:eq(?a, ?a, ?a);
binop_to_scode('!=') -> aeb_fate_code:neq(?a, ?a, ?a). op_to_scode('!=') -> aeb_fate_code:neq(?a, ?a, ?a);
op_to_scode('!') -> aeb_fate_code:not_op(?a, ?a);
unop_to_scode('!') -> aeb_fate_code:not_op(?a, ?a). op_to_scode(map_from_list) -> aeb_fate_code:map_from_list(?a, ?a);
op_to_scode(map_to_list) -> ?TODO(fate_map_to_list_instruction);
op_to_scode(map_delete) -> aeb_fate_code:map_delete(?a, ?a, ?a);
op_to_scode(map_member) -> aeb_fate_code:map_member(?a, ?a, ?a);
op_to_scode(map_size) -> ?TODO(fate_map_size_instruction);
op_to_scode(string_length) -> ?TODO(fate_string_length_instruction);
op_to_scode(string_concat) -> aeb_fate_code:str_join(?a, ?a, ?a);
op_to_scode(bits_set) -> aeb_fate_code:bits_set(?a, ?a, ?a);
op_to_scode(bits_clear) -> aeb_fate_code:bits_clear(?a, ?a, ?a);
op_to_scode(bits_test) -> aeb_fate_code:bits_test(?a, ?a, ?a);
op_to_scode(bits_sum) -> aeb_fate_code:bits_sum(?a, ?a);
op_to_scode(bits_intersection) -> aeb_fate_code:bits_and(?a, ?a, ?a);
op_to_scode(bits_union) -> aeb_fate_code:bits_or(?a, ?a, ?a);
op_to_scode(bits_difference) -> aeb_fate_code:bits_diff(?a, ?a, ?a);
op_to_scode(address_to_str) -> aeb_fate_code:addr_to_str(?a, ?a);
op_to_scode(int_to_str) -> aeb_fate_code:int_to_str(?a, ?a).
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations %% 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. %% easier, and specialize to PUSH (which is cheaper) at the end.
@ -614,7 +705,8 @@ attributes(I) ->
{'BITS_AND', A, B, C} -> Pure(A, [B, C]); {'BITS_AND', A, B, C} -> Pure(A, [B, C]);
{'BITS_DIFF', A, B, C} -> Pure(A, [B, C]); {'BITS_DIFF', A, B, C} -> Pure(A, [B, C]);
{'ADDRESS', A} -> Pure(A, []); {'ADDRESS', A} -> Pure(A, []);
{'BALANCE', A} -> Pure(A, []); {'BALANCE', A} -> Impure(A, []);
{'BALANCE_OTHER', A, B} -> Impure(A, [B]);
{'ORIGIN', A} -> Pure(A, []); {'ORIGIN', A} -> Pure(A, []);
{'CALLER', A} -> Pure(A, []); {'CALLER', A} -> Pure(A, []);
{'GASPRICE', A} -> Pure(A, []); {'GASPRICE', A} -> Pure(A, []);
@ -633,6 +725,7 @@ attributes(I) ->
{'LOG4', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]); {'LOG4', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]);
'DEACTIVATE' -> Impure(none, []); 'DEACTIVATE' -> Impure(none, []);
{'SPEND', A, B} -> Impure(none, [A, B]); {'SPEND', A, B} -> Impure(none, [A, B]);
{'ORACLE_REGISTER', A, B, C, D, E, F} -> Impure(A, [B, C, D, E, F]); {'ORACLE_REGISTER', A, B, C, D, E, F} -> Impure(A, [B, C, D, E, F]);
'ORACLE_QUERY' -> Impure(?a, []); %% TODO 'ORACLE_QUERY' -> Impure(?a, []); %% TODO
'ORACLE_RESPOND' -> Impure(?a, []); %% TODO 'ORACLE_RESPOND' -> Impure(?a, []); %% TODO
@ -1219,14 +1312,10 @@ chase_labels([L | Ls], Map, Live) ->
chase_labels(New ++ Ls, Map, Live#{ L => true }). chase_labels(New ++ Ls, Map, Live#{ L => true }).
%% Replace PUSH, RETURN by RETURNR, drop returns after tail calls. %% Replace PUSH, RETURN by RETURNR, drop returns after tail calls.
tweak_returns(['RETURN', {'PUSH', A} | Code]) -> tweak_returns(['RETURN', {'PUSH', A} | Code]) -> [{'RETURNR', A} | Code];
[{'RETURNR', A} | Code]; tweak_returns(['RETURN' | Code = [{'CALL_T', _} | _]]) -> Code;
%% tweak_returns(['RETURN', {'PUSH', A} | Code]) -> tweak_returns(['RETURN' | Code = [{'CALL_TR', _, _} | _]]) -> Code;
%% [{'RETURNR', A} | Code]; tweak_returns(['RETURN' | Code = [{'ABORT', _} | _]]) -> Code;
tweak_returns(['RETURN' | Code = [{'CALL_T', _} | _]]) ->
Code;
tweak_returns(['RETURN' | Code = [{'CALL_TR', _, _} | _]]) ->
Code;
tweak_returns(Code) -> Code. tweak_returns(Code) -> Code.
%% -- Split basic blocks at CALL instructions -- %% -- Split basic blocks at CALL instructions --