From c85af9e7f3bc39152c3d0177a28351301c5c1032 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Tue, 26 Feb 2019 22:51:33 +0100 Subject: [PATCH 01/19] Generate code for fate ops from spec. --- src/aeb_fate_asm.erl | 11 +- src/aeb_fate_code.erl | 140 ++++++++++++--- src/aeb_fate_data.erl | 31 +++- src/aeb_fate_generate_ops.erl | 329 ++++++++++++++++++++++++++++++++++ 4 files changed, 474 insertions(+), 37 deletions(-) create mode 100644 src/aeb_fate_generate_ops.erl diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 1ea0bcb..3c163e4 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -10,10 +10,15 @@ %%% DUP %%% Identifiers start with a lower case letter %%% an_identifier -%%% References to function arguments start with arg +%%% References to function arguments start with arg followed by an integer %%% arg0 -%%% References to variables/registers start with var +%%% References to variables/registers start with var followed by an integer %%% var0 +%%% References to stack postions is either a (for stack 0) +%%% or start with stack followed by an integer +%%% stack1 +%%% a +%%% %%% Immediates can be of 9 types: %%% 1. Integers %%% 42 @@ -815,7 +820,7 @@ to_list_of_types(Tokens) -> {[Type], Rest} end. - +-spec serialize_type(aeb_fate_date:fate_type_type()) -> [byte()]. serialize_type(integer) -> [0]; serialize_type(boolean) -> [1]; serialize_type({list, T}) -> [2 | serialize_type(T)]; diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl index 26c881a..68bd94a 100644 --- a/src/aeb_fate_code.erl +++ b/src/aeb_fate_code.erl @@ -1,5 +1,76 @@ +%% Provide constructor functuions for Fate instructions. +%% Provide types and documentation for Fate instructions. + + -module(aeb_fate_code). +-include_lib("aebytecode/include/aeb_fate_data.hrl"). + +-type fate_arg_immediate(T) :: {immediate, T}. +-type fate_arg_var() :: {var, integer()}. +-type fate_arg_arg() :: {arg, integer()}. +-type fate_arg_stack() :: {stack, integer()}. +-type fate_arg() :: fate_arg_immediate() + | fate_arg_var() + | fate_arg_arg() + | fate_arg_stack(). + +-type fate_arg_immediate() :: {immediate, aeb_fate_data:fate_type()}. + +-type fate_return() :: 'RETURN'. +-type fate_returnr() :: {'RETURNR', fate_arg()}. +-type fate_call() :: {'CALL', + fate_arg_immediate(aeb_fate_data:fate_string())}. +-type fate_call_t() :: {'CALL_T', + fate_arg_immediate(aeb_fate_data:fate_string())}. +-type fate_call_r() :: {'CALL_R', fate_arg(), + fate_arg_immediate(aeb_fate_data:fate_string())}. +-type fate_call_tr() :: {'CALL_TR', fate_arg(), + fate_arg_immediate(aeb_fate_data:fate_string())}. +-type fate_jump() :: {'JUMP', + fate_arg_immediate(aeb_fate_data:fate_integer())}. +-type fate_jumpif() :: {'JUMPIF', fate_arg(), + fate_arg_immediate(aeb_fate_data:fate_integer())}. +-type fate_switch_v2() :: {'SWITCH_V2', fate_arg(), + fate_arg_immediate(aeb_fate_data:fate_integer()), + fate_arg_immediate(aeb_fate_data:fate_integer())}. +-type fate_switch_v3() :: {'SWITCH_V3', fate_arg(), + fate_arg_immediate(aeb_fate_data:fate_integer()), + fate_arg_immediate(aeb_fate_data:fate_integer()), + fate_arg_immediate(aeb_fate_data:fate_integer())}. +-type fate_switch_vn() :: {'SWITCH_VN', fate_arg(), + fate_arg_immediate(aeb_fate_data:fate_integer()), + [fate_arg_immediate(aeb_fate_data:fate_integer())]}. +-type fate_push() :: {'PUSH', fate_arg()}. +-type fate_inca() :: 'INCA'. +-type fate_inc() :: {'INC', fate_arg()}. +-type fate_deca() :: 'DECA'. +-type fate_dec() :: {'DEC', fate_arg()}. + + +-type fate_code() :: fate_return() + | fate_returnr() + | fate_call() + | fate_call_t() + | fate_call_r() + | fate_call_tr() + | fate_jump() + | fate_jumpif() + | fate_switch_v2() + | fate_switch_v3() + | fate_switch_vn() + | fate_push() + | fate_inca() + | fate_inc() + | fate_deca() + | fate_dec() + . + +-export_type([ fate_code/0 + + , fate_arg/0 + ]). + -export([ return/0 , return/1 , call/1 @@ -10,8 +81,7 @@ , jumpif/2 , switch/3 , switch/4 - , switch/5 - , switch/6 + , switch_n/2 , push/1 , inc/0 , inc/1 @@ -76,64 +146,80 @@ -define(i(__X__), {immediate, __X__ }). +-spec return() -> fate_return(). return() -> 'RETURN'. +-spec return(fate_arg()) -> fate_returnr(). return(Arg) -> {'RETURNR', Arg}. -call(Function) when is_binary(Function)-> +-spec call(aeb_fate_data:fate_string()) -> fate_call(). +call(Function) when ?IS_FATE_STRING(Function)-> {'CALL', ?i(Function) }. -call_t(Function) when is_binary(Function) -> +-spec call_t(aeb_fate_data:fate_string()) -> fate_call_t(). +call_t(Function) when ?IS_FATE_STRING(Function) -> {'CALL_T', ?i(Function)}. -call_r(Contract, Function) when is_binary(Function) -> +-spec call_r(fate_arg(), aeb_fate_data:fate_string()) -> fate_call_r(). +call_r(Contract, Function) when ?IS_FATE_STRING(Function) -> {'CALL_R', Contract, ?i(Function)}. -call_tr(Contract, Function) when is_binary(Function) -> +-spec call_tr(fate_arg(), aeb_fate_data:fate_string()) -> fate_call_tr(). +call_tr(Contract, Function) when ?IS_FATE_STRING(Function) -> {'CALL_TR', Contract, ?i(Function)}. -jump(BB) when is_integer(BB) -> +-spec jump(aeb_fate_data:fate_integer()) -> fate_jump(). +jump(BB) when ?IS_FATE_INTEGER(BB) -> {'JUMP', ?i(BB)}. -jumpif(Arg, BB) when is_integer(BB) -> +-spec jumpif(fate_arg(), aeb_fate_data:fate_integer()) -> fate_jumpif(). +jumpif(Arg, BB) when ?IS_FATE_INTEGER(BB) -> {'JUMPIF', Arg, ?i(BB)}. -switch(Arg, BB1, BB2) when is_integer(BB1), - is_integer(BB2) -> +-spec switch(fate_arg(), + aeb_fate_data:fate_integer(), + aeb_fate_data:fate_integer()) + -> fate_switch_v2(). +switch(Arg, BB1, BB2) when ?IS_FATE_INTEGER(BB1), + ?IS_FATE_INTEGER(BB2) -> {'SWITCH_V2', Arg, ?i(BB1), ?i(BB2)}. -switch(Arg, BB1, BB2, BB3) when is_integer(BB1), - is_integer(BB2), - is_integer(BB3) -> +-spec switch(fate_arg(), + aeb_fate_data:fate_integer(), + aeb_fate_data:fate_integer(), + aeb_fate_data:fate_integer()) + -> fate_switch_v3(). +switch(Arg, BB1, BB2, BB3) when ?IS_FATE_INTEGER(BB1), + ?IS_FATE_INTEGER(BB2), + ?IS_FATE_INTEGER(BB3) -> {'SWITCH_V3', Arg, ?i(BB1), ?i(BB2), ?i(BB3)}. -switch(Arg, BB1, BB2, BB3, BB4) when is_integer(BB1), - is_integer(BB2), - is_integer(BB3), - is_integer(BB4) -> - {'SWITCH_V4', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4)}. - -switch(Arg, BB1, BB2, BB3, BB4, BB5) when is_integer(BB1), - is_integer(BB2), - is_integer(BB3), - is_integer(BB4), - is_integer(BB5) -> - {'SWITCH_V5', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4), ?i(BB5)}. +-spec switch_n(fate_arg(), + [aeb_fate_data:fate_integer()]) + -> fate_switch_vn(). +switch_n(Arg, BBS) when is_list(BBS) -> + N = length(BBS), + {'SWITCH_VN', Arg, ?i(N), [?i(BB) || BB <- BBS]}. +-spec push(fate_arg()) -> fate_push(). push(Arg) -> {'PUSH', Arg}. +-spec inc() -> fate_inca(). inc() -> 'INCA'. +-spec inc(fate_arg()) -> fate_inc(). inc(Arg) -> {'INC', Arg}. +-spec dec() -> fate_deca(). dec() -> 'DECA'. +-spec dec(fate_arg()) -> fate_dec(). dec(Arg) -> {'DEC', Arg}. @@ -182,7 +268,7 @@ or_op(Dest, Left, Right) -> not_op(Dest, Arg) -> {'NOT', Dest, Arg}. -tuple(Size) when is_integer(Size) -> +tuple(Size) when ?IS_FATE_INTEGER(Size) -> {'TUPLE', ?i(Size)}. element_op(Type, Dest, N, T) -> @@ -290,7 +376,7 @@ bits_diff(To, Bits, Bit) -> dup() -> 'DUPA'. -dup(N) when is_integer(N) -> +dup(N) when ?IS_FATE_INTEGER(N) -> {'DUP', ?i(N)}. pop() -> diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 243b204..f0a6898 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -15,10 +15,18 @@ -type fate_variant() :: ?FATE_VARIANT_T. --type fate_void() :: ?FATE_VOID_T. - -type fate_tuple() :: ?FATE_TUPLE_T. +-type fate_type_type() :: integer + | boolean + | {list, fate_type()} + | {map, fate_type(), fate_type()} + | {tuple, [fate_type()]} + | address + | bits + | {variant, integer()}. + + -type fate_type() :: fate_boolean() | fate_integer() @@ -30,11 +38,21 @@ | fate_address() | fate_variant() | fate_map() - | fate_list() - | fate_tuple() - | fate_void(). %% Not sure we need this. + | fate_type_type(). --export_type([fate_type/0]). +-export_type([fate_type/0 + , fate_boolean/0 + , fate_integer/0 + , fate_nil/0 + , fate_list/0 + , fate_unit/0 + , fate_tuple/0 + , fate_string/0 + , fate_address/0 + , fate_variant/0 + , fate_map/0 + , fate_type_type/0 + ]). -export([ make_integer/1 , make_boolean/1 @@ -111,7 +129,6 @@ decode(M) when ?IS_FATE_MAP(M) -> -spec format(fate_type()) -> iolist(). format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I)); -format(?FATE_VOID) -> "void"; format(?FATE_TRUE) -> "true"; format(?FATE_FALSE) -> "false"; format(?FATE_NIL) -> "[]"; diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl new file mode 100644 index 0000000..259da48 --- /dev/null +++ b/src/aeb_fate_generate_ops.erl @@ -0,0 +1,329 @@ + + +-module(aeb_fate_generate_ops). + +-export([generate/0]). + +-define(ati(__X__), {immediate, __X__}). +-define(atv(__X__), {var, __X__}). +-define(ata(__X__), {arg, __X__}). +-define(ats(__X__), {stack, __X__}). + +-define(ta(), "fate_arg()"). +-define(ti(__X__), "fate_arg_immediate(" __X__ ")"). +-define(ts, "aeb_fate_data:fate_string()"). + +generate() -> + Ops = gen(ops_defs()), + io:format("ops: ~p\n", [Ops]), + HrlFile = "aeb_new_fate_opcodes.hrl", + generate_header_file(HrlFile, Ops), + generate_opcodes_ops(aeb_new_fate_opcodes, HrlFile, Ops), + generate_code_ops(aeb_new_fate_code, Ops). + +ops_defs() -> + %% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation + [ { 'NOP', 16#00, 0, false, 1, atomic, nop, "The no op. does nothing."} + , { 'RETURN', 16#01, 0, true, 2, atomic, return, "Return from function call pop stack to arg0."} + , { 'RETURNR', 16#68, 1, true, 2, [a], returnr, "Return from function call pop stack to arg0."} + , { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack."} + , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function."} + , { 'CALL_R', 16#03, 2, true, 8, [a, is], call_r, "Remote call to given contract and function."} + , { 'CALL_TR', 16#05, 2, true, 8, [a, is], call_tr, "Remote tail call to given contract and function."} + , { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block."} + , { 'JUMPIF', 16#07, 2, true, 4, [a, ii], jumpif, "Conditional jump to a basic block."} + , { 'SWITCH_V2',16#6a, 3, true, 4, [a, ii, ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_V3',16#6b, 4, true, 4, [a, ii, ii, ii], switch,"Conditional jump to a basic block on variant tag."} + , { 'SWITCH_VN',16#6c, 2, true, 4, [a, li], switch,"Conditional jump to a basic block on variant tag."} + , { 'PUSH', 16#09, 1, false, 2, [a], push, "Push argument to stack."} + , { 'INCA', 16#71, 0, false, 2, atomic, inc, "Increment accumulator."} + , { 'INC', 16#53, 1, false, 2, [a], inc, "Increment argument."} + , { 'DECA', 16#72, 0, false, 2, atomic, dec, "Decrement accumulator."} + , { 'DEC', 16#54, 1, false, 2, [a], dec, "Decrement argument."} + , { 'ADD', 16#11, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."} + , { 'SUB', 16#13, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."} + , { 'MUL', 16#12, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."} + , { 'DIV', 16#14, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."} + , { 'MOD', 16#15, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."} + , { 'POW', 16#16, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."} + , { 'LT', 16#17, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."} + , { 'GT', 16#18, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."} + , { 'EQ', 16#19, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."} + , { 'ELT', 16#1a, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."} + , { 'EGT', 16#1b, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."} + , { 'NEQ', 16#1c, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."} + , { 'AND', 16#1d, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."} + , { 'OR', 16#1e, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} + , { 'NOT', 16#1f, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."} + , { 'TUPLE', 16#20, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} + , { 'ELEMET', 16#21, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"} + , { 'MAP_EMPTY',16#22, 1, false, 3, [a], map_empty, "Arg0 := #{}."} + , { 'MAP_LOOKUP',16#23, 3, false, 3, [a, a, a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} + , { 'MAP_LOOKUPD',16#69, 4, false, 3, [a, a, a, a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."} + , { 'MAP_UPDATE',16#24, 4, false, 3, [a, a, a, a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} + , { 'MAP_DELETE',16#25, 3, false, 3, [a, a, a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} + , { 'MAP_MEMBER',16#26, 3, false, 3, [a, a, a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} + , { 'NIL', 16#28, 1, false, 3, [a], nil, "Arg0 := []."} + , { 'IS_NIL', 16#29, 2, false, 3, [a, a], is_nil, "Arg0 := true if Arg1 == []."} + , {'CONS', 16#2a, 3, false, 3, [a, a, a], cons, "Arg0 := [Arg1|Arg2]."} + , {'HD', 16#2b, 2, false, 3, [a, a], hd, "Arg0 := head of list Arg1."} + , {'TL', 16#2c, 2, false, 3, [a, a], tl, "Arg0 := tail of list Arg1."} + , {'LENGTH', 16#2d, 2, false, 3, [a, a], length, "Arg0 := length of list Arg1."} + + , {'STR_EQ', 16#2e, 3, false, 3, [a, a, a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."} + , {'STR_JOIN', 16#2f, 3, false, 3, [a, a, a], str_join, "Arg0 := string Arg1 followed by string Arg2."} + , {'INT_TO_STR', 16#55, 2, false, 3, [a, a], int_to_str, "Arg0 := turn integer Arg1 into a string."} + , {'ADDR_TO_STR', 16#30, 2, false, 3, [a, a], addr_to_str, "Arg0 := turn address Arg1 into a string."} + , {'STR_REVERSE', 16#31, 2, false, 3, [a, a], str_reverse, "Arg0 := the reverse of string Arg1."} + , {'INT_TO_ADDR', 16#32, 2, false, 3, [a, a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} + , {'VARIANT', 16#33, 4, false, 3, [a, a, a, a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."} + , {'VARIANT_TEST', 16#34,3, false, 3, [a, a, a], variant_test,"Arg0 := true if variant Arg1 has the tag Arg2."} + , {'VARIANT_ELEMENT',16#35,3,false, 3, [a, a, a], variant_element,"Arg0 := element number Arg2 from variant Arg1."} + , {'BITS_NONEA', 16#6e, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."} + , {'BITS_NONE', 16#36, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."} + , {'BITS_ALLA', 16#6f, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."} + , {'BITS_ALL', 16#37, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."} + , {'BITS_ALL_N', 16#6d, 2, false, 3, [a, a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."} + , {'BITS_SET', 16#38, 3, false, 3, [a, a, a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."} + , {'BITS_CLEAR',16#39, 3, false, 3, [a, a, a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."} + , {'BITS_TEST', 16#3a, 3, false, 3, [a, a, a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."} , {'BITS_SUM', 16#3b, 2, false, 3, [a, a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."} + , {'BITS_OR', 16#3c, 3, false, 3, [a, a, a], bits_or, "Arg0 := Arg1 v Arg2."} + , {'BITS_AND', 16#3d, 3, false, 3, [a, a, a], bits_and, "Arg0 := Arg1 ^ Arg2."} + , {'BITS_DIFF', 16#3e, 3, false, 3, [a, a, a], bits_diff, "Arg0 := Arg1 - Arg2."} + , {'DUPA', 16#70, 0, false, 3, atomic, dup, "push copy of accumulator on stack."} + , {'DUP', 16#0a, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} + , {'POP', 16#0b, 1, false, 3, [a], pop, "Arg0 := top of stack."} + , {'STORE', 16#10, 2, false, 3, [a, a], store, "Arg0 := Arg1."} + ]. + + +generate_header_file(Filename, Ops) -> + {ok, File} = file:open(Filename, [write]), + Defines = lists:flatten([gen_defines(Op) || Op <- Ops]), + io:format(File, "~s", [prelude("Provides opcode defines.\n")]), + io:format(File, "%% FATE opcodes\n~s", [Defines]), + file:close(File). + +generate_opcodes_ops(Modulename, HrlFile, Ops) -> + Filename = atom_to_list(Modulename) ++ ".erl", + + {ok, File} = file:open(Filename, [write]), + Mnemonic = lists:flatten([gen_mnemonic(Op) || Op <- Ops]), + ToOp = lists:flatten([gen_m_to_op(Op) || Op <- Ops]), + Args = lists:flatten([gen_args(Op) || Op <- Ops]), + EndBB = lists:flatten([gen_bb(Op) || Op <- Ops]), + + io:format(File, "~s", [prelude("Provides opcode primitives.\n")]), + io:format(File, "~s", [ops_exports(Modulename, HrlFile, + ["args/1\n" + " , end_bb/1\n" + " , mnemonic/1\n" + " , m_to_op/1\n" + ])]), + + io:format(File, "%% FATE mnemonics\n~s", [Mnemonic]), + io:format(File, "mnemonic(Op) -> exit({bad_opcode, Op}).\n\n", []), + + io:format(File, "%% FATE opcodes\n~s", [ToOp]), + io:format(File, "m_to_op(M) -> exit({bad_mnemonic, M}).\n\n", []), + + io:format(File, "%% FATE numbers of args to op.\n~s", [Args]), + io:format(File, "args(Op) -> exit({bad_opcode, Op}).\n\n", []), + + io:format(File, "%% Does FATE Op end a Basic Block?\n~s", [EndBB]), + io:format(File, "end_bb(_) -> false.\n\n", []), + + file:close(File). + +generate_code_ops(Modulename, Ops) -> + Filename = atom_to_list(Modulename) ++ ".erl", + + {ok, File} = file:open(Filename, [write]), + Types = lists:flatten([gen_type(Op) || Op <- Ops]), + TypeExports = lists:flatten([gen_type_exports(Op) || Op <- Ops]), + [#{type_name := FirstType} | RestOfOps] = Ops, + FateTypes = lists:flatten([gen_fate_code_type(Op) || Op <- RestOfOps]), + ConstructorExports = lists:flatten([gen_constructor_exports(Op) || Op <- Ops]), + Constructors = lists:flatten([gen_constructors(Op) || Op <- Ops]), + + io:format(File, "~s", [prelude(" Provide constructor functuions for " + "Fate instructions.\n%%% Provide types" + " and documentation for Fate " + "instructions.\n")]), + io:format(File, "-module(~w).\n\n", [Modulename]), + io:format(File, "-include_lib(\"aebytecode/include/aeb_fate_data.hrl\").\n\n" + "-define(i(__X__), {immediate, __X__ }).\n\n" + "-type fate_arg_immediate(T) :: {immediate, T}.\n" + "-type fate_arg_var() :: {var, integer()}.\n" + "-type fate_arg_arg() :: {arg, integer()}.\n" + "-type fate_arg_stack() :: {stack, integer()}.\n" + "-type fate_arg() :: fate_arg_immediate()\n" + " | fate_arg_var()\n" + " | fate_arg_arg()\n" + " | fate_arg_stack().\n\n" + "-type fate_arg_immediate() :: {immediate, aeb_fate_data:fate_type()}.\n" + , []), + + io:format(File, "~s", [Types]), + io:format(File, "-type fate_code() :: ~s\n~s .\n\n", + [FirstType, FateTypes]), + io:format(File, "-export_type([ fate_code/0\n~s ]).\n\n", [TypeExports]), + io:format(File, "-export([ foo/0\n~s ]).\n\n", [ConstructorExports]), + io:format(File, "~s\n", [Constructors]), + + io:format(File, "foo() -> \"A temp hack.\".\n", []), + + + file:close(File). + +gen_type(#{type_name := TypeName, type := Type}) -> + lists:flatten(io_lib:format("-type ~-25s :: ~s.\n", + [TypeName, Type])). + +gen_fate_code_type(#{type_name := TypeName}) -> + lists:flatten(io_lib:format(" | ~s\n", [TypeName])). + +gen_type_exports(#{type_name := TypeName}) -> + lists:flatten(io_lib:format(" , ~s/0\n", [TypeName--"()"])). + +gen_constructor_exports(#{constructor_type := Function}) -> + lists:flatten(io_lib:format(" , ~s\n", [Function])). + +gen_constructors(#{constructor := Function, format := atomic, + type_name := Type, opname := Name}) -> + lists:flatten(io_lib:format("-spec ~s() -> ~s.\n" + "~s() ->\n" + " ~w.\n\n", + [Function, Type, Function, Name])); +gen_constructors(#{constructor := Function, format := ArgSpec, + type_name := Type, opname := Name}) -> + ArgTypeSpecs = gen_arg_type_specs(ArgSpec), + Args = gen_arg_names(0, ArgSpec), + UseArgs = gen_arg_uses(0, ArgSpec), + lists:flatten(io_lib:format("-spec ~s(~s) -> ~s.\n" + "~s(~s) ->\n" + " {~w, ~s}.\n\n", + [Function, ArgTypeSpecs, Type, + Function, Args, Name, UseArgs])). + +gen_arg_type_specs([]) -> []; +gen_arg_type_specs([a]) -> "fate_arg()"; +gen_arg_type_specs([is]) -> "aeb_fate_data:fate_string()"; +gen_arg_type_specs([ii]) -> "aeb_fate_data:fate_integer()"; +gen_arg_type_specs([li]) -> "[aeb_fate_data:fate_integer()]"; +gen_arg_type_specs([t]) -> "aeb_fate_data:fate_type_type()"; +gen_arg_type_specs([a | Args]) -> "fate_arg(), " ++ gen_arg_type_specs(Args); +gen_arg_type_specs([is | Args]) -> "aeb_fate_data:fate_string(), " ++ gen_arg_type_specs(Args); +gen_arg_type_specs([ii | Args]) -> "aeb_fate_data:fate_integer(), " ++ gen_arg_type_specs(Args); +gen_arg_type_specs([li | Args]) -> "[aeb_fate_data:fate_integer()], " ++ gen_arg_type_specs(Args); +gen_arg_type_specs([t | Args]) -> "aeb_fate_data:fate_type_type(), " ++ gen_arg_type_specs(Args). + + +gen_arg_names(_, []) -> + []; +gen_arg_names(N, [_]) -> io_lib:format("Arg~w", [N]); +gen_arg_names(N, [_|Args]) -> + io_lib:format("Arg~w, ", [N]) ++ gen_arg_names(N+1, Args). + +gen_arg_uses(_, []) -> + []; +gen_arg_uses(N, [a]) -> io_lib:format("Arg~w", [N]); +gen_arg_uses(N, [is]) -> io_lib:format("{immediate, Arg~w}", [N]); +gen_arg_uses(N, [ii]) -> io_lib:format("{immediate, Arg~w}", [N]); +gen_arg_uses(N, [li]) -> io_lib:format("[{immediate, I} || I <- Arg~w]", [N]); +gen_arg_uses(N, [t]) -> io_lib:format("Arg~w", [N]); +gen_arg_uses(N, [a | Args]) -> + io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args); +gen_arg_uses(N, [is | Args]) -> + io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args); +gen_arg_uses(N, [ii | Args]) -> + io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args); +gen_arg_uses(N, [li | Args]) -> + io_lib:format("[{immediate, I} || I <- Arg~w], ", [N]) ++ gen_arg_uses(N+1, Args); +gen_arg_uses(N, [t | Args]) -> + io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args). + + +ops_exports(Module, HrlFile, Exports) -> + lists:flatten(io_lib:format( + "-module(~w).\n\n" + "-export([ ~s ]).\n\n" + "-include_lib(\"aebytecode/include/" ++ HrlFile ++"\").\n\n" + "%%====================================================================\n" + "%% API\n" + "%%====================================================================\n", + [Module, Exports])). + +gen_mnemonic(#{opname := Name, macro := Macro}) -> + lists:flatten(io_lib:format("mnemonic(~21s) -> ~21w ;\n", + [Macro, Name])). + + +gen_m_to_op(#{opname := Name, macro := Macro}) -> + lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n", + [Name, Macro])). + +gen_args(#{macro := Macro, args := Args}) -> + lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n", + [Macro, Args])). + +gen_bb(#{macro := Macro, end_bb := EndBB}) -> + lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n", + [Macro, EndBB])). + + +prelude(Doc) -> + "%%%-------------------------------------------------------------------\n" + "%%% @copyright (C) 2019, Aeternity Anstalt\n" + "%%%\n" + "%%% === === N O T E : This file is generated do not edit. === ===\n" + "%%%\n" + "%%% Source is in aeb_fate_generate_ops.erl\n" + "%%% @doc\n" + "%%% "++Doc++ + "%%% @end\n" + "%%%-------------------------------------------------------------------\n\n". + + +gen_defines(#{opname := Name, opcode := OpCode}) -> + lists:flatten(io_lib:format("-define(~-17w, 16#~2.16.0b).\n", [Name, OpCode])). + +gen([]) -> + []; +gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) -> + Name = atom_to_list(OpName), + LowerName = string:to_lower(Name), + TypeName = "fate_" ++ LowerName ++ "()", + Macro = "?" ++ Name, + Type = case FateFormat of + atomic -> io_lib:format("~w", [OpName]); + ArgTypes -> + io_lib:format("{~w, ~s}", [OpName, expand_types(ArgTypes)]) + end, + ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Args]), + + [#{ opname => OpName + , opcode => OpCode + , args => Args + , end_bb => EndBB + , format => FateFormat + , macro => Macro + , type_name => TypeName + , doc => Doc + , gas => Gas + , type => Type + , constructor => Constructor + , constructor_type => ConstructorType + }| gen(Rest)]. + + +expand_types([]) -> ""; +expand_types([T]) -> expand_type(T); +expand_types([T|Ts]) ->expand_type(T) ++ ", " ++ expand_types(Ts). + +expand_type(a) -> "fate_arg()"; +expand_type(is) -> "fate_arg_immediate(aeb_fate_data:fate_string())"; +expand_type(ii) -> "fate_arg_immediate(aeb_fate_data:fate_integer())"; +expand_type(li) -> "[fate_arg_immediate(aeb_fate_data:fate_integer())]"; +expand_type(t) -> "aeb_fate_data:fate_type_type()". + -- 2.30.2 From ab150ce7f89afb6baf19c0b3518b74fcaf5a488a Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 27 Feb 2019 11:55:53 +0100 Subject: [PATCH 02/19] Generate the code from the makefile. Remove generated files. --- .circleci/config.yml | 2 +- .gitignore | 3 + Makefile | 16 +- include/aeb_fate_opcodes.hrl | 121 ----------- src/aeb_fate_code.erl | 389 ---------------------------------- src/aeb_fate_generate_ops.erl | 86 +++++--- src/aeb_fate_opcodes.erl | 363 ------------------------------- 7 files changed, 69 insertions(+), 911 deletions(-) delete mode 100644 include/aeb_fate_opcodes.hrl delete mode 100644 src/aeb_fate_code.erl delete mode 100644 src/aeb_fate_opcodes.erl diff --git a/.circleci/config.yml b/.circleci/config.yml index e34c31c..c1b8283 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,7 +19,7 @@ jobs: - dialyzer-cache-v1- - run: name: Build - command: rebar3 compile + command: make - run: name: Static Analysis command: rebar3 dialyzer diff --git a/.gitignore b/.gitignore index 4ad6576..385e93a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ aeb_asm_scan.erl aeb_fate_asm_scan.erl _build/ aefateasm +include/aeb_fate_opcodes.hrl +src/aeb_fate_code.erl +src/aeb_fate_opcodes.erl diff --git a/Makefile b/Makefile index 1d04708..f4e9034 100644 --- a/Makefile +++ b/Makefile @@ -4,17 +4,29 @@ REBAR ?= rebar3 all: local -local: +local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl @$(REBAR) as local release -console: +console: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl @$(REBAR) as local shell clean: @$(REBAR) clean + rm -f src/aeb_fate_opcodes.erl + rm -f src/aeb_fate_code.erl + rm -f include/aeb_fate_opcodes.hrl distclean: clean @rm -rf _build/ test: local @$(REBAR) as local eunit + +ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin + erlc -o $(dir $@) $< + +src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl: ebin/aeb_fate_generate_ops.beam + erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/ + +ebin: + mkdir ebin diff --git a/include/aeb_fate_opcodes.hrl b/include/aeb_fate_opcodes.hrl deleted file mode 100644 index 90f5b6a..0000000 --- a/include/aeb_fate_opcodes.hrl +++ /dev/null @@ -1,121 +0,0 @@ - -%% FATE opcodes --define('NOP' , 16#00). --define('RETURN' , 16#01). --define('CALL' , 16#02). --define('CALL_R' , 16#03). --define('CALL_T' , 16#04). --define('CALL_TR' , 16#05). --define('JUMP' , 16#06). --define('JUMPIF' , 16#07). --define('SWITCH' , 16#08). --define('PUSH' , 16#09). --define('DUP' , 16#0a). --define('POP' , 16#0b). --define('STORE' , 16#10). --define('ADD' , 16#11). --define('MUL' , 16#12). --define('SUB' , 16#13). --define('DIV' , 16#14). --define('MOD' , 16#15). --define('POW' , 16#16). --define('LT' , 16#17). --define('GT' , 16#18). --define('EQ' , 16#19). --define('ELT' , 16#1a). --define('EGT' , 16#1b). --define('NEQ' , 16#1c). --define('AND' , 16#1d). --define('OR' , 16#1e). --define('NOT' , 16#1f). --define('TUPLE' , 16#20). --define('ELEMENT' , 16#21). --define('MAP_EMPTY' , 16#22). --define('MAP_LOOKUP' , 16#23). --define('MAP_UPDATE' , 16#24). --define('MAP_DELETE' , 16#25). --define('MAP_MEMBER' , 16#26). --define('MAP_FROM_LIST' , 16#27). --define('NIL' , 16#28). --define('IS_NIL' , 16#29). --define('CONS' , 16#2a). --define('HD' , 16#2b). --define('TL' , 16#2c). --define('LENGTH' , 16#2d). --define('STR_EQ' , 16#2e). --define('STR_JOIN' , 16#2f). --define('ADDR_TO_STR' , 16#30). --define('STR_REVERSE' , 16#31). --define('INT_TO_ADDR' , 16#32). --define('VARIANT' , 16#33). --define('VARIANT_TEST' , 16#34). --define('VARIANT_ELEMENT', 16#35). --define('BITS_NONE' , 16#36). --define('BITS_ALL' , 16#37). --define('BITS_SET' , 16#38). --define('BITS_CLEAR' , 16#39). --define('BITS_TEST' , 16#3a). --define('BITS_SUM' , 16#3b). --define('BITS_OR' , 16#3c). --define('BITS_AND' , 16#3d). --define('BITS_DIFF' , 16#3e). --define('ADDRESS' , 16#3f). --define('BALANCE' , 16#40). --define('ORIGIN' , 16#41). --define('CALLER' , 16#42). --define('GASPRICE' , 16#43). --define('BLOCKHASH' , 16#44). --define('BENEFICIARY' , 16#45). --define('TIMESTAMP' , 16#46). --define('NUMBER' , 16#47). --define('DIFFICULTY' , 16#48). --define('GASLIMIT' , 16#49). --define('GAS' , 16#4a). --define('LOG0' , 16#4b). --define('LOG1' , 16#4c). --define('LOG2' , 16#4d). --define('LOG3' , 16#4e). --define('LOG4' , 16#4f). --define('ABORT' , 16#50). --define('EXIT' , 16#51). --define('DEACTIVATE' , 16#52). --define('INC' , 16#53). --define('DEC' , 16#54). --define('INT_TO_STR' , 16#55). --define('SPEND' , 16#56). --define('ORACLE_REGISTER', 16#57). --define('ORACLE_QUERY' , 16#58). --define('ORACLE_RESPOND' , 16#59). --define('ORACLE_EXTEND' , 16#5a). --define('ORACLE_GET_ANSWER', 16#5b). --define('ORACLE_GET_QUESTION', 16#5c). --define('ORACLE_QUERY_FEE', 16#5d). --define('AENS_RESOLVE' , 16#5e). --define('AENS_PRECLAIM' , 16#5f). --define('AENS_CLAIM' , 16#60). --define('AENS_UPDATE' , 16#61). --define('AENS_TRANSFER' , 16#62). --define('AENS_REVOKE' , 16#63). --define('ECVERIFY' , 16#64). --define('SHA3' , 16#65). --define('SHA256' , 16#66). --define('BLAKE2B' , 16#67). --define('RETURNR' , 16#68). --define('MAP_LOOKUPD' , 16#69). --define('SWITCH_V2' , 16#6a). --define('SWITCH_V3' , 16#6b). --define('SWITCH_VN' , 16#6c). --define('BITS_ALL_N' , 16#6d). --define('BITS_NONEA' , 16#6e). --define('BITS_ALLA' , 16#6f). --define('DUPA' , 16#70). --define('INCA' , 16#71). --define('DECA' , 16#72). --define('POPA' , 16#73). - - --define('FUNCTION' , 16#fe). --define('EXTEND' , 16#ff). - --define( COMMENT(X), {comment, X}). - diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl deleted file mode 100644 index 68bd94a..0000000 --- a/src/aeb_fate_code.erl +++ /dev/null @@ -1,389 +0,0 @@ -%% Provide constructor functuions for Fate instructions. -%% Provide types and documentation for Fate instructions. - - --module(aeb_fate_code). - --include_lib("aebytecode/include/aeb_fate_data.hrl"). - --type fate_arg_immediate(T) :: {immediate, T}. --type fate_arg_var() :: {var, integer()}. --type fate_arg_arg() :: {arg, integer()}. --type fate_arg_stack() :: {stack, integer()}. --type fate_arg() :: fate_arg_immediate() - | fate_arg_var() - | fate_arg_arg() - | fate_arg_stack(). - --type fate_arg_immediate() :: {immediate, aeb_fate_data:fate_type()}. - --type fate_return() :: 'RETURN'. --type fate_returnr() :: {'RETURNR', fate_arg()}. --type fate_call() :: {'CALL', - fate_arg_immediate(aeb_fate_data:fate_string())}. --type fate_call_t() :: {'CALL_T', - fate_arg_immediate(aeb_fate_data:fate_string())}. --type fate_call_r() :: {'CALL_R', fate_arg(), - fate_arg_immediate(aeb_fate_data:fate_string())}. --type fate_call_tr() :: {'CALL_TR', fate_arg(), - fate_arg_immediate(aeb_fate_data:fate_string())}. --type fate_jump() :: {'JUMP', - fate_arg_immediate(aeb_fate_data:fate_integer())}. --type fate_jumpif() :: {'JUMPIF', fate_arg(), - fate_arg_immediate(aeb_fate_data:fate_integer())}. --type fate_switch_v2() :: {'SWITCH_V2', fate_arg(), - fate_arg_immediate(aeb_fate_data:fate_integer()), - fate_arg_immediate(aeb_fate_data:fate_integer())}. --type fate_switch_v3() :: {'SWITCH_V3', fate_arg(), - fate_arg_immediate(aeb_fate_data:fate_integer()), - fate_arg_immediate(aeb_fate_data:fate_integer()), - fate_arg_immediate(aeb_fate_data:fate_integer())}. --type fate_switch_vn() :: {'SWITCH_VN', fate_arg(), - fate_arg_immediate(aeb_fate_data:fate_integer()), - [fate_arg_immediate(aeb_fate_data:fate_integer())]}. --type fate_push() :: {'PUSH', fate_arg()}. --type fate_inca() :: 'INCA'. --type fate_inc() :: {'INC', fate_arg()}. --type fate_deca() :: 'DECA'. --type fate_dec() :: {'DEC', fate_arg()}. - - --type fate_code() :: fate_return() - | fate_returnr() - | fate_call() - | fate_call_t() - | fate_call_r() - | fate_call_tr() - | fate_jump() - | fate_jumpif() - | fate_switch_v2() - | fate_switch_v3() - | fate_switch_vn() - | fate_push() - | fate_inca() - | fate_inc() - | fate_deca() - | fate_dec() - . - --export_type([ fate_code/0 - - , fate_arg/0 - ]). - --export([ return/0 - , return/1 - , call/1 - , call_t/1 - , call_r/2 - , call_tr/2 - , jump/1 - , jumpif/2 - , switch/3 - , switch/4 - , switch_n/2 - , push/1 - , inc/0 - , inc/1 - , dec/0 - , dec/1 - , add/3 - , sub/3 - , mul/3 - , divide/3 - , modulo/3 - , pow/3 - , lt/3 - , gt/3 - , elt/3 - , egt/3 - , eq/3 - , neq/3 - , and_op/3 - , or_op/3 - , not_op/2 - , tuple/1 - , element_op/4 - , map_empty/1 - , map_lookup/3 - , map_lookup/4 - , map_update/4 - , map_member/3 - , map_from_list/2 - , nil/1 - , is_nil/2 - , cons/3 - , hd/2 - , tl/2 - , length/2 - , str_eq/3 - , str_join/3 - , int_to_str/2 - , addr_to_str/2 - , str_reverse/2 - , int_to_addr/2 - , variant_test/3 - , variant_element/3 - , variant/4 - , bits_none/0 - , bits_none/1 - , bits_all/0 - , bits_all/1 - , bits_all_n/2 - , bits_set/3 - , bits_clear/3 - , bits_test/3 - , bits_sum/2 - , bits_or/3 - , bits_and/3 - , bits_diff/3 - , dup/0 - , dup/1 - , pop/0 - , store/2 - , nop/0 - ]). - --define(i(__X__), {immediate, __X__ }). - --spec return() -> fate_return(). -return() -> - 'RETURN'. - --spec return(fate_arg()) -> fate_returnr(). -return(Arg) -> - {'RETURNR', Arg}. - --spec call(aeb_fate_data:fate_string()) -> fate_call(). -call(Function) when ?IS_FATE_STRING(Function)-> - {'CALL', ?i(Function) }. - --spec call_t(aeb_fate_data:fate_string()) -> fate_call_t(). -call_t(Function) when ?IS_FATE_STRING(Function) -> - {'CALL_T', ?i(Function)}. - --spec call_r(fate_arg(), aeb_fate_data:fate_string()) -> fate_call_r(). -call_r(Contract, Function) when ?IS_FATE_STRING(Function) -> - {'CALL_R', Contract, ?i(Function)}. - --spec call_tr(fate_arg(), aeb_fate_data:fate_string()) -> fate_call_tr(). -call_tr(Contract, Function) when ?IS_FATE_STRING(Function) -> - {'CALL_TR', Contract, ?i(Function)}. - --spec jump(aeb_fate_data:fate_integer()) -> fate_jump(). -jump(BB) when ?IS_FATE_INTEGER(BB) -> - {'JUMP', ?i(BB)}. - --spec jumpif(fate_arg(), aeb_fate_data:fate_integer()) -> fate_jumpif(). -jumpif(Arg, BB) when ?IS_FATE_INTEGER(BB) -> - {'JUMPIF', Arg, ?i(BB)}. - --spec switch(fate_arg(), - aeb_fate_data:fate_integer(), - aeb_fate_data:fate_integer()) - -> fate_switch_v2(). -switch(Arg, BB1, BB2) when ?IS_FATE_INTEGER(BB1), - ?IS_FATE_INTEGER(BB2) -> - {'SWITCH_V2', Arg, ?i(BB1), ?i(BB2)}. - --spec switch(fate_arg(), - aeb_fate_data:fate_integer(), - aeb_fate_data:fate_integer(), - aeb_fate_data:fate_integer()) - -> fate_switch_v3(). -switch(Arg, BB1, BB2, BB3) when ?IS_FATE_INTEGER(BB1), - ?IS_FATE_INTEGER(BB2), - ?IS_FATE_INTEGER(BB3) -> - {'SWITCH_V3', Arg, ?i(BB1), ?i(BB2), ?i(BB3)}. - --spec switch_n(fate_arg(), - [aeb_fate_data:fate_integer()]) - -> fate_switch_vn(). -switch_n(Arg, BBS) when is_list(BBS) -> - N = length(BBS), - {'SWITCH_VN', Arg, ?i(N), [?i(BB) || BB <- BBS]}. - --spec push(fate_arg()) -> fate_push(). -push(Arg) -> - {'PUSH', Arg}. - --spec inc() -> fate_inca(). -inc() -> - 'INCA'. - --spec inc(fate_arg()) -> fate_inc(). -inc(Arg) -> - {'INC', Arg}. - --spec dec() -> fate_deca(). -dec() -> - 'DECA'. - --spec dec(fate_arg()) -> fate_dec(). -dec(Arg) -> - {'DEC', Arg}. - -add(Dest, Left, Right) -> - {'ADD', Dest, Left, Right}. - -sub(Dest, Left, Right) -> - {'SUB', Dest, Left, Right}. - -mul(Dest, Left, Right) -> - {'MUL', Dest, Left, Right}. - -divide(Dest, Left, Right) -> - {'DIV', Dest, Left, Right}. - -modulo(Dest, Left, Right) -> - {'MOD', Dest, Left, Right}. - -pow(Dest, Left, Right) -> - {'POW', Dest, Left, Right}. - -lt(Dest, Left, Right) -> - {'LT', Dest, Left, Right}. - -gt(Dest, Left, Right) -> - {'GT', Dest, Left, Right}. - -elt(Dest, Left, Right) -> - {'ELT', Dest, Left, Right}. - -egt(Dest, Left, Right) -> - {'EGT', Dest, Left, Right}. - -eq(Dest, Left, Right) -> - {'EQ', Dest, Left, Right}. - -neq(Dest, Left, Right) -> - {'NEQ', Dest, Left, Right}. - -and_op(Dest, Left, Right) -> - {'AND', Dest, Left, Right}. - -or_op(Dest, Left, Right) -> - {'OR', Dest, Left, Right}. - -not_op(Dest, Arg) -> - {'NOT', Dest, Arg}. - -tuple(Size) when ?IS_FATE_INTEGER(Size) -> - {'TUPLE', ?i(Size)}. - -element_op(Type, Dest, N, T) -> - {'ELEMENT', Type, Dest, N, T}. - -map_empty(Dest) -> - {'MAP_EMPTY', Dest}. - -map_lookup(Dest, Map, Key) -> - {'MAP_LOOKUP', Dest, Map, Key}. - -map_lookup(Dest, Map, Key, Default) -> - {'MAP_LOOKUPD', Dest, Map, Key, Default}. - -map_update(Dest, Map, Key, Value) -> - {'MAP_UPDATE', Dest, Map, Key, Value}. - -map_member(Dest, Map, Key) -> - {'MAP_MEMBER', Dest, Map, Key}. - -map_from_list(Dest, List) -> - {'MAP_MEMBER', Dest, List}. - -nil(Dest) -> - {'NIL', Dest}. - -is_nil(Dest, List) -> - {'IS_NIL', Dest, List}. - -cons(Dest, Hd, Tl) -> - {'CONS', Dest, Hd, Tl}. - -hd(Dest, List) -> - {'HD', Dest, List}. - -tl(Dest, List) -> - {'TL', Dest, List}. - -length(Dest, List) -> - {'LENGTH', Dest, List}. - -str_eq(Dest, Str1, Str2) -> - {'STR_EQ', Dest, Str1, Str2}. - -str_join(Dest, Str1, Str2) -> - {'STR_JOIN', Dest, Str1, Str2}. - -int_to_str(Dest, Str) -> - {'INT_TO_STR', Dest, Str}. - -addr_to_str(Dest, Str) -> - {'ADDR_TO_STR', Dest, Str}. - -str_reverse(Dest, Str) -> - {'STR_REVERSE', Dest, Str}. - -int_to_addr(Dest, Str) -> - {'INT_TO_ADDR', Dest, Str}. - -variant_test(Dest, Variant, Tag) -> - {'VARIANT_TEST', Dest, Variant, Tag}. - -variant_element( Dest, Variant, Index) -> - {'VARIANT_ELEMENT', Dest, Variant, Index}. - -variant(Dest, SizeA, TagA, ElementsA) -> - {'VARIANT', Dest, SizeA, TagA, ElementsA}. - -bits_none() -> - 'BITS_NONEA'. - -bits_none(To) -> - {'BITS_NONE', To}. - -bits_all() -> - 'BITS_ALLA'. - -bits_all(To) -> - {'BITS_ALL', To}. - -bits_all_n(To, N) -> - {'BITS_ALL_N', To, N}. - -bits_set(To, Bits, Bit) -> - {'BITS_SET', To, Bits, Bit}. - -bits_clear(To, Bits, Bit) -> - {'BITS_CLEAR', To, Bits, Bit}. - -bits_test(To, Bits, Bit) -> - {'BITS_TEST', To, Bits, Bit}. - -bits_sum(To, Bits) -> - {'BITS_SUM', To, Bits}. - -bits_or(To, Bits, Bit) -> - {'BITS_OR', To, Bits, Bit}. - -bits_and(To, Bits, Bit) -> - {'BITS_AND', To, Bits, Bit}. - -bits_diff(To, Bits, Bit) -> - {'BITS_DIFF', To, Bits, Bit}. - -dup() -> - 'DUPA'. - -dup(N) when ?IS_FATE_INTEGER(N) -> - {'DUP', ?i(N)}. - -pop() -> - 'POP'. - -store(Var, What) -> - {'STORE', Var, What}. - -nop() -> - 'NOP'. diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 259da48..0fc740c 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -1,34 +1,33 @@ - - -module(aeb_fate_generate_ops). --export([generate/0]). +-export([ gen_and_halt/1 + , generate/0]). --define(ati(__X__), {immediate, __X__}). --define(atv(__X__), {var, __X__}). --define(ata(__X__), {arg, __X__}). --define(ats(__X__), {stack, __X__}). - --define(ta(), "fate_arg()"). --define(ti(__X__), "fate_arg_immediate(" __X__ ")"). --define(ts, "aeb_fate_data:fate_string()"). +gen_and_halt([SrcDirArg, IncludeDirArg]) -> + generate(atom_to_list(SrcDirArg), + atom_to_list(IncludeDirArg)), + halt(). generate() -> + generate("src/", "include/"). + +generate(Src, Include) -> Ops = gen(ops_defs()), - io:format("ops: ~p\n", [Ops]), - HrlFile = "aeb_new_fate_opcodes.hrl", + %% io:format("ops: ~p\n", [Ops]), + HrlFile = Include ++ "aeb_fate_opcodes.hrl", generate_header_file(HrlFile, Ops), - generate_opcodes_ops(aeb_new_fate_opcodes, HrlFile, Ops), - generate_code_ops(aeb_new_fate_code, Ops). + generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops), + generate_code_ops(aeb_fate_code, Src, Ops). + ops_defs() -> %% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation - [ { 'NOP', 16#00, 0, false, 1, atomic, nop, "The no op. does nothing."} - , { 'RETURN', 16#01, 0, true, 2, atomic, return, "Return from function call pop stack to arg0."} - , { 'RETURNR', 16#68, 1, true, 2, [a], returnr, "Return from function call pop stack to arg0."} + [ { 'NOP', 16#f0, 0, false, 1, atomic, nop, "The no op. does nothing."} + , { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0."} + , { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call pop stack to arg0."} , { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack."} - , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function."} , { 'CALL_R', 16#03, 2, true, 8, [a, is], call_r, "Remote call to given contract and function."} + , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function."} , { 'CALL_TR', 16#05, 2, true, 8, [a, is], call_tr, "Remote tail call to given contract and function."} , { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block."} , { 'JUMPIF', 16#07, 2, true, 4, [a, ii], jumpif, "Conditional jump to a basic block."} @@ -56,20 +55,21 @@ ops_defs() -> , { 'OR', 16#1e, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} , { 'NOT', 16#1f, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."} , { 'TUPLE', 16#20, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} - , { 'ELEMET', 16#21, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"} + , { 'ELEMENT', 16#21, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"} , { 'MAP_EMPTY',16#22, 1, false, 3, [a], map_empty, "Arg0 := #{}."} , { 'MAP_LOOKUP',16#23, 3, false, 3, [a, a, a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} , { 'MAP_LOOKUPD',16#69, 4, false, 3, [a, a, a, a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."} , { 'MAP_UPDATE',16#24, 4, false, 3, [a, a, a, a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} , { 'MAP_DELETE',16#25, 3, false, 3, [a, a, a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} , { 'MAP_MEMBER',16#26, 3, false, 3, [a, a, a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} + , { 'MAP_FROM_LIST',16#27, 2, false, 3, [a, a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."} , { 'NIL', 16#28, 1, false, 3, [a], nil, "Arg0 := []."} , { 'IS_NIL', 16#29, 2, false, 3, [a, a], is_nil, "Arg0 := true if Arg1 == []."} , {'CONS', 16#2a, 3, false, 3, [a, a, a], cons, "Arg0 := [Arg1|Arg2]."} , {'HD', 16#2b, 2, false, 3, [a, a], hd, "Arg0 := head of list Arg1."} , {'TL', 16#2c, 2, false, 3, [a, a], tl, "Arg0 := tail of list Arg1."} , {'LENGTH', 16#2d, 2, false, 3, [a, a], length, "Arg0 := length of list Arg1."} - + , {'STR_EQ', 16#2e, 3, false, 3, [a, a, a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."} , {'STR_JOIN', 16#2f, 3, false, 3, [a, a, a], str_join, "Arg0 := string Arg1 followed by string Arg2."} , {'INT_TO_STR', 16#55, 2, false, 3, [a, a], int_to_str, "Arg0 := turn integer Arg1 into a string."} @@ -78,7 +78,7 @@ ops_defs() -> , {'INT_TO_ADDR', 16#32, 2, false, 3, [a, a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} , {'VARIANT', 16#33, 4, false, 3, [a, a, a, a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."} , {'VARIANT_TEST', 16#34,3, false, 3, [a, a, a], variant_test,"Arg0 := true if variant Arg1 has the tag Arg2."} - , {'VARIANT_ELEMENT',16#35,3,false, 3, [a, a, a], variant_element,"Arg0 := element number Arg2 from variant Arg1."} + , {'VARIANT_ELEMNT',16#35,3,false, 3, [a, a, a], variant_element,"Arg0 := element number Arg2 from variant Arg1."} , {'BITS_NONEA', 16#6e, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."} , {'BITS_NONE', 16#36, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."} , {'BITS_ALLA', 16#6f, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."} @@ -94,6 +94,21 @@ ops_defs() -> , {'DUP', 16#0a, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} , {'POP', 16#0b, 1, false, 3, [a], pop, "Arg0 := top of stack."} , {'STORE', 16#10, 2, false, 3, [a, a], store, "Arg0 := Arg1."} + %% TODO: Check the documentation and update it. + , {'ADDRESS', 16#3f, 1, false, 3, [a], address, "Arg0 := The current contract address."} + , {'BALANCE', 16#3f, 1, false, 3, [a], balance, "Arg0 := The current contract address."} + , {'ORIGIN', 16#40, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."} + , {'CALLER', 16#41, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."} + , {'GASPRICE', 16#42, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."} + , {'BLOCKHASH', 16#43, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has at height? + , {'BENEFICIARY',16#44, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} + , {'TIMESTAMP', 16#45, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} + , {'NUMBER', 16#46, 1, false, 3, [a], number, "Arg0 := The block height."} + , {'DIFFICULTY',16#47, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} + , {'GASLIMIT', 16#48, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} + , {'GAS', 16#49, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} + , {'ABORT', 16#4f, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} + , {'EXIT', 16#4e, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} ]. @@ -102,10 +117,13 @@ generate_header_file(Filename, Ops) -> Defines = lists:flatten([gen_defines(Op) || Op <- Ops]), io:format(File, "~s", [prelude("Provides opcode defines.\n")]), io:format(File, "%% FATE opcodes\n~s", [Defines]), + io:format(File, "~s", + ["-define('FUNCTION' , 16#fe).\n" + "-define('EXTEND' , 16#ff).\n\n"]), file:close(File). -generate_opcodes_ops(Modulename, HrlFile, Ops) -> - Filename = atom_to_list(Modulename) ++ ".erl", +generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) -> + Filename = SrcDir ++ atom_to_list(Modulename) ++ ".erl", {ok, File} = file:open(Filename, [write]), Mnemonic = lists:flatten([gen_mnemonic(Op) || Op <- Ops]), @@ -135,8 +153,8 @@ generate_opcodes_ops(Modulename, HrlFile, Ops) -> file:close(File). -generate_code_ops(Modulename, Ops) -> - Filename = atom_to_list(Modulename) ++ ".erl", +generate_code_ops(Modulename, SrcDir, Ops) -> + Filename = SrcDir ++ atom_to_list(Modulename) ++ ".erl", {ok, File} = file:open(Filename, [write]), Types = lists:flatten([gen_type(Op) || Op <- Ops]), @@ -163,7 +181,6 @@ generate_code_ops(Modulename, Ops) -> " | fate_arg_stack().\n\n" "-type fate_arg_immediate() :: {immediate, aeb_fate_data:fate_type()}.\n" , []), - io:format(File, "~s", [Types]), io:format(File, "-type fate_code() :: ~s\n~s .\n\n", [FirstType, FateTypes]), @@ -172,7 +189,6 @@ generate_code_ops(Modulename, Ops) -> io:format(File, "~s\n", [Constructors]), io:format(File, "foo() -> \"A temp hack.\".\n", []), - file:close(File). @@ -222,7 +238,7 @@ gen_arg_type_specs([t | Args]) -> "aeb_fate_data:fate_type_type(), " ++ gen_arg_ gen_arg_names(_, []) -> []; gen_arg_names(N, [_]) -> io_lib:format("Arg~w", [N]); -gen_arg_names(N, [_|Args]) -> +gen_arg_names(N, [_|Args]) -> io_lib:format("Arg~w, ", [N]) ++ gen_arg_names(N+1, Args). gen_arg_uses(_, []) -> @@ -232,15 +248,15 @@ gen_arg_uses(N, [is]) -> io_lib:format("{immediate, Arg~w}", [N]); gen_arg_uses(N, [ii]) -> io_lib:format("{immediate, Arg~w}", [N]); gen_arg_uses(N, [li]) -> io_lib:format("[{immediate, I} || I <- Arg~w]", [N]); gen_arg_uses(N, [t]) -> io_lib:format("Arg~w", [N]); -gen_arg_uses(N, [a | Args]) -> +gen_arg_uses(N, [a | Args]) -> io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args); -gen_arg_uses(N, [is | Args]) -> +gen_arg_uses(N, [is | Args]) -> io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args); -gen_arg_uses(N, [ii | Args]) -> +gen_arg_uses(N, [ii | Args]) -> io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args); -gen_arg_uses(N, [li | Args]) -> +gen_arg_uses(N, [li | Args]) -> io_lib:format("[{immediate, I} || I <- Arg~w], ", [N]) ++ gen_arg_uses(N+1, Args); -gen_arg_uses(N, [t | Args]) -> +gen_arg_uses(N, [t | Args]) -> io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args). @@ -248,7 +264,7 @@ ops_exports(Module, HrlFile, Exports) -> lists:flatten(io_lib:format( "-module(~w).\n\n" "-export([ ~s ]).\n\n" - "-include_lib(\"aebytecode/include/" ++ HrlFile ++"\").\n\n" + "-include_lib(\"aebytecode/" ++ HrlFile ++"\").\n\n" "%%====================================================================\n" "%% API\n" "%%====================================================================\n", diff --git a/src/aeb_fate_opcodes.erl b/src/aeb_fate_opcodes.erl deleted file mode 100644 index 84d93f7..0000000 --- a/src/aeb_fate_opcodes.erl +++ /dev/null @@ -1,363 +0,0 @@ -%%%------------------------------------------------------------------- -%%% @copyright (C) 2019, Aeternity Anstalt -%%% @doc -%%% Opcodes -%%% @end -%%%------------------------------------------------------------------- - --module(aeb_fate_opcodes). - --export([ args/1 - , end_bb/1 - , mnemonic/1 - , m_to_op/1 - , opcode/1 - ]). - --include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). - - -%%==================================================================== -%% API -%%==================================================================== - -opcode(X) when X >= 0, X =< 255 -> X; -opcode({comment,X}) -> ?COMMENT(X). - -mnemonic(?NOP) -> 'NOP' ; -mnemonic(?RETURN) -> 'RETURN' ; -mnemonic(?CALL) -> 'CALL' ; -mnemonic(?CALL_R) -> 'CALL_R' ; -mnemonic(?CALL_T) -> 'CALL_T' ; -mnemonic(?CALL_TR) -> 'CALL_TR' ; -mnemonic(?JUMP) -> 'JUMP' ; -mnemonic(?JUMPIF) -> 'JUMPIF' ; -mnemonic(?PUSH) -> 'PUSH' ; -mnemonic(?DUP) -> 'DUP' ; -mnemonic(?DUPA) -> 'DUPA' ; -mnemonic(?POP) -> 'POP' ; -mnemonic(?STORE) -> 'STORE' ; -mnemonic(?ADD) -> 'ADD' ; -mnemonic(?MUL) -> 'MUL' ; -mnemonic(?SUB) -> 'SUB' ; -mnemonic(?DIV) -> 'DIV' ; -mnemonic(?MOD) -> 'MOD' ; -mnemonic(?POW) -> 'POW' ; -mnemonic(?LT) -> 'LT' ; -mnemonic(?GT) -> 'GT' ; -mnemonic(?EQ) -> 'EQ' ; -mnemonic(?ELT) -> 'ELT' ; -mnemonic(?EGT) -> 'EGT' ; -mnemonic(?NEQ) -> 'NEQ' ; -mnemonic(?AND) -> 'AND' ; -mnemonic(?OR) -> 'OR' ; -mnemonic(?NOT) -> 'NOT' ; -mnemonic(?TUPLE) -> 'TUPLE' ; -mnemonic(?ELEMENT) -> 'ELEMENT' ; -mnemonic(?MAP_EMPTY) -> 'MAP_EMPTY' ; -mnemonic(?MAP_LOOKUP) -> 'MAP_LOOKUP' ; -mnemonic(?MAP_UPDATE) -> 'MAP_UPDATE' ; -mnemonic(?MAP_DELETE) -> 'MAP_DELETE' ; -mnemonic(?MAP_MEMBER) -> 'MAP_MEMBER' ; -mnemonic(?MAP_FROM_LIST) -> 'MAP_FROM_LIST' ; -mnemonic(?NIL) -> 'NIL' ; -mnemonic(?IS_NIL) -> 'IS_NIL' ; -mnemonic(?CONS) -> 'CONS' ; -mnemonic(?HD) -> 'HD' ; -mnemonic(?TL) -> 'TL' ; -mnemonic(?LENGTH) -> 'LENGTH' ; -mnemonic(?STR_EQ) -> 'STR_EQ' ; -mnemonic(?STR_JOIN) -> 'STR_JOIN' ; -mnemonic(?ADDR_TO_STR) -> 'ADDR_TO_STR' ; -mnemonic(?STR_REVERSE) -> 'STR_REVERSE' ; -mnemonic(?INT_TO_ADDR) -> 'INT_TO_ADDR' ; -mnemonic(?VARIANT) -> 'VARIANT' ; -mnemonic(?VARIANT_TEST) -> 'VARIANT_TEST' ; -mnemonic(?VARIANT_ELEMENT) -> 'VARIANT_ELEMENT' ; -mnemonic(?BITS_NONE) -> 'BITS_NONE' ; -mnemonic(?BITS_NONEA) -> 'BITS_NONEA' ; -mnemonic(?BITS_ALL) -> 'BITS_ALL' ; -mnemonic(?BITS_ALLA) -> 'BITS_ALLA' ; -mnemonic(?BITS_SET) -> 'BITS_SET' ; -mnemonic(?BITS_CLEAR) -> 'BITS_CLEAR' ; -mnemonic(?BITS_TEST) -> 'BITS_TEST' ; -mnemonic(?BITS_SUM) -> 'BITS_SUM' ; -mnemonic(?BITS_OR) -> 'BITS_OR' ; -mnemonic(?BITS_AND) -> 'BITS_AND' ; -mnemonic(?BITS_DIFF) -> 'BITS_DIFF' ; -mnemonic(?ADDRESS) -> 'ADDRESS' ; -mnemonic(?BALANCE) -> 'BALANCE' ; -mnemonic(?ORIGIN) -> 'ORIGIN' ; -mnemonic(?CALLER) -> 'CALLER' ; -mnemonic(?GASPRICE) -> 'GASPRICE' ; -mnemonic(?BLOCKHASH) -> 'BLOCKHASH' ; -mnemonic(?BENEFICIARY) -> 'BENEFICIARY' ; -mnemonic(?TIMESTAMP) -> 'TIMESTAMP' ; -mnemonic(?NUMBER) -> 'NUMBER' ; -mnemonic(?DIFFICULTY) -> 'DIFFICULTY' ; -mnemonic(?GASLIMIT) -> 'GASLIMIT' ; -mnemonic(?GAS) -> 'GAS' ; -mnemonic(?LOG0) -> 'LOG0' ; -mnemonic(?LOG1) -> 'LOG1' ; -mnemonic(?LOG2) -> 'LOG2' ; -mnemonic(?LOG3) -> 'LOG3' ; -mnemonic(?LOG4) -> 'LOG4' ; -mnemonic(?ABORT) -> 'ABORT' ; -mnemonic(?EXIT) -> 'EXIT' ; -mnemonic(?DEACTIVATE) -> 'DEACTIVATE' ; -mnemonic(?INC) -> 'INC' ; -mnemonic(?DEC) -> 'DEC' ; -mnemonic(?INCA) -> 'INCA' ; -mnemonic(?DECA) -> 'DECA' ; -mnemonic(?INT_TO_STR) -> 'INT_TO_STR' ; -mnemonic(?SPEND) -> 'SPEND' ; -mnemonic(?ORACLE_REGISTER) -> 'ORACLE_REGISTER' ; -mnemonic(?ORACLE_QUERY) -> 'ORACLE_QUERY' ; -mnemonic(?ORACLE_RESPOND) -> 'ORACLE_RESPOND' ; -mnemonic(?ORACLE_EXTEND) -> 'ORACLE_EXTEND' ; -mnemonic(?ORACLE_GET_ANSWER) -> 'ORACLE_GET_ANSWER' ; -mnemonic(?ORACLE_GET_QUESTION) -> 'ORACLE_GET_QUESTION' ; -mnemonic(?ORACLE_QUERY_FEE) -> 'ORACLE_QUERY_FEE' ; -mnemonic(?AENS_RESOLVE) -> 'AENS_RESOLVE' ; -mnemonic(?AENS_PRECLAIM) -> 'AENS_PRECLAIM' ; -mnemonic(?AENS_CLAIM) -> 'AENS_CLAIM' ; -mnemonic(?AENS_UPDATE) -> 'AENS_UPDATE' ; -mnemonic(?AENS_TRANSFER) -> 'AENS_TRANSFER' ; -mnemonic(?AENS_REVOKE) -> 'AENS_REVOKE' ; -mnemonic(?ECVERIFY) -> 'ECVERIFY' ; -mnemonic(?SHA3) -> 'SHA3' ; -mnemonic(?SHA256) -> 'SHA256' ; -mnemonic(?BLAKE2B) -> 'BLAKE2B' ; -mnemonic(?RETURNR) -> 'RETURNR' ; -mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ; -mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ; -mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ; -mnemonic(?SWITCH_VN) -> 'SWITCH_VN' ; -mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ; -mnemonic(?FUNCTION) -> 'FUNCTION' ; -mnemonic(?EXTEND) -> 'EXTEND'. - - -m_to_op('NOP') -> ?NOP ; -m_to_op('RETURN') -> ?RETURN ; -m_to_op('CALL') -> ?CALL ; -m_to_op('CALL_R') -> ?CALL_R ; -m_to_op('CALL_T') -> ?CALL_T ; -m_to_op('CALL_TR') -> ?CALL_TR ; -m_to_op('JUMP') -> ?JUMP ; -m_to_op('JUMPIF') -> ?JUMPIF ; -m_to_op('PUSH') -> ?PUSH ; -m_to_op('DUP') -> ?DUP ; -m_to_op('DUPA') -> ?DUPA ; -m_to_op('POP') -> ?POP ; -m_to_op('STORE') -> ?STORE ; -m_to_op('ADD') -> ?ADD ; -m_to_op('MUL') -> ?MUL ; -m_to_op('SUB') -> ?SUB ; -m_to_op('DIV') -> ?DIV ; -m_to_op('MOD') -> ?MOD ; -m_to_op('POW') -> ?POW ; -m_to_op('LT') -> ?LT ; -m_to_op('GT') -> ?GT ; -m_to_op('EQ') -> ?EQ ; -m_to_op('ELT') -> ?ELT ; -m_to_op('EGT') -> ?EGT ; -m_to_op('NEQ') -> ?NEQ ; -m_to_op('AND') -> ?AND ; -m_to_op('OR') -> ?OR ; -m_to_op('NOT') -> ?NOT ; -m_to_op('TUPLE') -> ?TUPLE ; -m_to_op('ELEMENT') -> ?ELEMENT ; -m_to_op('MAP_EMPTY') -> ?MAP_EMPTY ; -m_to_op('MAP_LOOKUP') -> ?MAP_LOOKUP ; -m_to_op('MAP_UPDATE') -> ?MAP_UPDATE ; -m_to_op('MAP_DELETE') -> ?MAP_DELETE ; -m_to_op('MAP_MEMBER') -> ?MAP_MEMBER ; -m_to_op('MAP_FROM_LIST') -> ?MAP_FROM_LIST ; -m_to_op('NIL') -> ?NIL ; -m_to_op('IS_NIL') -> ?IS_NIL ; -m_to_op('CONS') -> ?CONS ; -m_to_op('HD') -> ?HD ; -m_to_op('TL') -> ?TL ; -m_to_op('LENGTH') -> ?LENGTH ; -m_to_op('STR_EQ') -> ?STR_EQ ; -m_to_op('STR_JOIN') -> ?STR_JOIN ; -m_to_op('ADDR_TO_STR') -> ?ADDR_TO_STR ; -m_to_op('STR_REVERSE') -> ?STR_REVERSE ; -m_to_op('INT_TO_ADDR') -> ?INT_TO_ADDR ; -m_to_op('VARIANT') -> ?VARIANT ; -m_to_op('VARIANT_TEST') -> ?VARIANT_TEST ; -m_to_op('VARIANT_ELEMENT') -> ?VARIANT_ELEMENT ; -m_to_op('BITS_NONEA') -> ?BITS_NONEA ; -m_to_op('BITS_ALL') -> ?BITS_ALL ; -m_to_op('BITS_ALLA') -> ?BITS_ALLA ; -m_to_op('BITS_SET') -> ?BITS_SET ; -m_to_op('BITS_CLEAR') -> ?BITS_CLEAR ; -m_to_op('BITS_TEST') -> ?BITS_TEST ; -m_to_op('BITS_SUM') -> ?BITS_SUM ; -m_to_op('BITS_OR') -> ?BITS_OR ; -m_to_op('BITS_AND') -> ?BITS_AND ; -m_to_op('BITS_DIFF') -> ?BITS_DIFF ; -m_to_op('ADDRESS') -> ?ADDRESS ; -m_to_op('BALANCE') -> ?BALANCE ; -m_to_op('ORIGIN') -> ?ORIGIN ; -m_to_op('CALLER') -> ?CALLER ; -m_to_op('GASPRICE') -> ?GASPRICE ; -m_to_op('BLOCKHASH') -> ?BLOCKHASH ; -m_to_op('BENEFICIARY') -> ?BENEFICIARY ; -m_to_op('TIMESTAMP') -> ?TIMESTAMP ; -m_to_op('NUMBER') -> ?NUMBER ; -m_to_op('DIFFICULTY') -> ?DIFFICULTY ; -m_to_op('GASLIMIT') -> ?GASLIMIT ; -m_to_op('GAS') -> ?GAS ; -m_to_op('LOG0') -> ?LOG0 ; -m_to_op('LOG1') -> ?LOG1 ; -m_to_op('LOG2') -> ?LOG2 ; -m_to_op('LOG3') -> ?LOG3 ; -m_to_op('LOG4') -> ?LOG4 ; -m_to_op('ABORT') -> ?ABORT ; -m_to_op('EXIT') -> ?EXIT ; -m_to_op('DEACTIVATE') -> ?DEACTIVATE ; -m_to_op('INC') -> ?INC ; -m_to_op('DEC') -> ?DEC ; -m_to_op('INCA') -> ?INCA ; -m_to_op('DECA') -> ?DECA ; -m_to_op('INT_TO_STR') -> ?INT_TO_STR ; -m_to_op('SPEND') -> ?SPEND ; -m_to_op('ORACLE_REGISTER') -> ?ORACLE_REGISTER ; -m_to_op('ORACLE_QUERY') -> ?ORACLE_QUERY ; -m_to_op('ORACLE_RESPOND') -> ?ORACLE_RESPOND ; -m_to_op('ORACLE_EXTEND') -> ?ORACLE_EXTEND ; -m_to_op('ORACLE_GET_ANSWER') -> ?ORACLE_GET_ANSWER ; -m_to_op('ORACLE_GET_QUESTION') -> ?ORACLE_GET_QUESTION ; -m_to_op('ORACLE_QUERY_FEE') -> ?ORACLE_QUERY_FEE ; -m_to_op('AENS_RESOLVE') -> ?AENS_RESOLVE ; -m_to_op('AENS_PRECLAIM') -> ?AENS_PRECLAIM ; -m_to_op('AENS_CLAIM') -> ?AENS_CLAIM ; -m_to_op('AENS_UPDATE') -> ?AENS_UPDATE ; -m_to_op('AENS_TRANSFER') -> ?AENS_TRANSFER ; -m_to_op('AENS_REVOKE') -> ?AENS_REVOKE ; -m_to_op('ECVERIFY') -> ?ECVERIFY ; -m_to_op('SHA3') -> ?SHA3 ; -m_to_op('SHA256') -> ?SHA256 ; -m_to_op('BLAKE2B') -> ?BLAKE2B ; -m_to_op('RETURNR') -> ?RETURNR ; -m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ; -m_to_op('SWITCH_V2') -> ?SWITCH_V2 ; -m_to_op('SWITCH_V3') -> ?SWITCH_V3 ; -m_to_op('SWITCH_VN') -> ?SWITCH_VN ; -m_to_op('FUNCTION') -> ?FUNCTION ; -m_to_op('EXTEND') -> ?EXTEND. - - - -args(?NOP) -> 0; -args(?RETURN) -> 0; -args(?INCA) -> 0; -args(?DECA) -> 0; -args(?DUPA) -> 0; -args(?BITS_NONEA) -> 0; -args(?BITS_ALLA) -> 0; - -args(?INC) -> 1; -args(?DEC) -> 1; -args(?RETURNR) -> 1; -args(?PUSH) -> 1; -args(?JUMP) -> 1; -args(?CALL) -> 1; -args(?CALL_T) -> 1; -args(?TUPLE) -> 1; -args(?MAP_EMPTY) -> 1; -args(?DUP) -> 1; -args(?POP) -> 1; -args(?NIL) -> 1; -args(?BITS_NONE) -> 1; -args(?BITS_ALL) -> 1; -args(?ADDRESS) -> 1; -args(?BALANCE) -> 1; -args(?ORIGIN) -> 1; -args(?CALLER) -> 1; -args(?GASPRICE) -> 1; -args(?BLOCKHASH) -> 1; -args(?BENEFICIARY) -> 1; -args(?TIMESTAMP) -> 1; -args(?NUMBER) -> 1; -args(?DIFFICULTY)-> 1; -args(?GASLIMIT) -> 1; -args(?GAS) -> 1; -args(?ABORT) -> 1; -args(?EXIT) -> 1; - -args(?JUMPIF) -> 2; -args(?CALL_R) -> 2; -args(?CALL_TR) -> 2; -args(?HD) -> 2; -args(?TL) -> 2; -args(?NOT) -> 2; -args(?STORE) -> 2; -args(?LENGTH) -> 2; -args(?IS_NIL) -> 2; -args(?BITS_SUM) -> 2; -args(?BITS_ALL_N) -> 2; -args(?ADDR_TO_STR) -> 2; -args(?STR_REVERSE) -> 2; -args(?INT_TO_ADDR) -> 2; -args(?MAP_FROM_LIST) -> 2; - - -args(?ADD) -> 3; -args(?SUB) -> 3; -args(?MUL) -> 3; -args(?DIV) -> 3; -args(?MOD) -> 3; -args(?POW) -> 3; -args(?AND) -> 3; -args(?OR) -> 3; -args(?LT) -> 3; -args(?GT) -> 3; -args(?EGT) -> 3; -args(?ELT) -> 3; -args(?EQ) -> 3; -args(?NEQ) -> 3; -args(?CONS) -> 3; -args(?STR_EQ) -> 3; -args(?STR_JOIN) -> 3; -args(?MAP_MEMBER) -> 3; -args(?MAP_LOOKUP) -> 3; -args(?MAP_DELETE) -> 3; -args(?BITS_OR) -> 3; -args(?BITS_AND) -> 3; -args(?BITS_SET) -> 3; -args(?BITS_DIFF) -> 3; -args(?BITS_TEST) -> 3; -args(?BITS_CLEAR) -> 3; -args(?VARIANT_TEST) -> 3; -args(?VARIANT_ELEMENT) -> 3; -args(?INT_TO_STR) -> 3; -args(?SWITCH_V2) -> 3; - -args(?SWITCH_V3) -> 4; -args(?ELEMENT) -> 4; -args(?VARIANT) -> 4; -args(?MAP_UPDATE) -> 4; -args(?MAP_LOOKUPD) -> 4; - -args(?SWITCH_VN) -> 2; - -args(_) -> 0. %% TODO do not allow this - -end_bb(?RETURN) -> true; -end_bb(?RETURNR) -> true; -end_bb(?JUMP) -> true; -end_bb(?JUMPIF) -> true; -end_bb(?CALL) -> true; -end_bb(?CALL_T) -> true; -end_bb(?CALL_R) -> true; -end_bb(?CALL_TR) -> true; -end_bb(?SWITCH_V2) -> true; -end_bb(?SWITCH_V3) -> true; -end_bb(?SWITCH_VN) -> true; -end_bb(?ABORT) -> true; -end_bb(?EXIT) -> true; - -end_bb(_) -> false. -- 2.30.2 From c624f4956c424987b9f588b8d9be7fb60563ab47 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 27 Feb 2019 15:25:57 +0100 Subject: [PATCH 03/19] Test targets and cleanup. --- .circleci/config.yml | 6 +- Makefile | 9 ++ src/aeb_fate_asm.erl | 2 +- src/aeb_fate_generate_ops.erl | 176 +++++++++++++++++----------------- 4 files changed, 101 insertions(+), 92 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c1b8283..2d09292 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,13 +22,13 @@ jobs: command: make - run: name: Static Analysis - command: rebar3 dialyzer + command: make dialyzer - run: name: Eunit - command: rebar3 eunit + command: make eunit - run: name: Common Tests - command: rebar3 ct + command: make ct - save_cache: key: dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }} paths: diff --git a/Makefile b/Makefile index f4e9034..fd94da3 100644 --- a/Makefile +++ b/Makefile @@ -16,12 +16,21 @@ clean: rm -f src/aeb_fate_code.erl rm -f include/aeb_fate_opcodes.hrl +dialyzer: local + @$(REBAR) as local dialyzer + + + distclean: clean @rm -rf _build/ +euint: local + @$(REBAR) as local eunit + test: local @$(REBAR) as local eunit + ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin erlc -o $(dir $@) $< diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 3c163e4..5e320ef 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -820,7 +820,7 @@ to_list_of_types(Tokens) -> {[Type], Rest} end. --spec serialize_type(aeb_fate_date:fate_type_type()) -> [byte()]. +-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()]. serialize_type(integer) -> [0]; serialize_type(boolean) -> [1]; serialize_type({list, T}) -> [2 | serialize_type(T)]; diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 0fc740c..486fc54 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -19,96 +19,96 @@ generate(Src, Include) -> generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops), generate_code_ops(aeb_fate_code, Src, Ops). - +%% TODO: Some real gas numbers... ops_defs() -> - %% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation - [ { 'NOP', 16#f0, 0, false, 1, atomic, nop, "The no op. does nothing."} - , { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0."} - , { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call pop stack to arg0."} - , { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack."} - , { 'CALL_R', 16#03, 2, true, 8, [a, is], call_r, "Remote call to given contract and function."} - , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function."} - , { 'CALL_TR', 16#05, 2, true, 8, [a, is], call_tr, "Remote tail call to given contract and function."} - , { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block."} - , { 'JUMPIF', 16#07, 2, true, 4, [a, ii], jumpif, "Conditional jump to a basic block."} - , { 'SWITCH_V2',16#6a, 3, true, 4, [a, ii, ii], switch, "Conditional jump to a basic block on variant tag."} - , { 'SWITCH_V3',16#6b, 4, true, 4, [a, ii, ii, ii], switch,"Conditional jump to a basic block on variant tag."} - , { 'SWITCH_VN',16#6c, 2, true, 4, [a, li], switch,"Conditional jump to a basic block on variant tag."} - , { 'PUSH', 16#09, 1, false, 2, [a], push, "Push argument to stack."} - , { 'INCA', 16#71, 0, false, 2, atomic, inc, "Increment accumulator."} - , { 'INC', 16#53, 1, false, 2, [a], inc, "Increment argument."} - , { 'DECA', 16#72, 0, false, 2, atomic, dec, "Decrement accumulator."} - , { 'DEC', 16#54, 1, false, 2, [a], dec, "Decrement argument."} - , { 'ADD', 16#11, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."} - , { 'SUB', 16#13, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."} - , { 'MUL', 16#12, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."} - , { 'DIV', 16#14, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."} - , { 'MOD', 16#15, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."} - , { 'POW', 16#16, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."} - , { 'LT', 16#17, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."} - , { 'GT', 16#18, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."} - , { 'EQ', 16#19, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."} - , { 'ELT', 16#1a, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."} - , { 'EGT', 16#1b, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."} - , { 'NEQ', 16#1c, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."} - , { 'AND', 16#1d, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."} - , { 'OR', 16#1e, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} - , { 'NOT', 16#1f, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."} - , { 'TUPLE', 16#20, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} - , { 'ELEMENT', 16#21, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"} - , { 'MAP_EMPTY',16#22, 1, false, 3, [a], map_empty, "Arg0 := #{}."} - , { 'MAP_LOOKUP',16#23, 3, false, 3, [a, a, a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} - , { 'MAP_LOOKUPD',16#69, 4, false, 3, [a, a, a, a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."} - , { 'MAP_UPDATE',16#24, 4, false, 3, [a, a, a, a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} - , { 'MAP_DELETE',16#25, 3, false, 3, [a, a, a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} - , { 'MAP_MEMBER',16#26, 3, false, 3, [a, a, a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} - , { 'MAP_FROM_LIST',16#27, 2, false, 3, [a, a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."} - , { 'NIL', 16#28, 1, false, 3, [a], nil, "Arg0 := []."} - , { 'IS_NIL', 16#29, 2, false, 3, [a, a], is_nil, "Arg0 := true if Arg1 == []."} - , {'CONS', 16#2a, 3, false, 3, [a, a, a], cons, "Arg0 := [Arg1|Arg2]."} - , {'HD', 16#2b, 2, false, 3, [a, a], hd, "Arg0 := head of list Arg1."} - , {'TL', 16#2c, 2, false, 3, [a, a], tl, "Arg0 := tail of list Arg1."} - , {'LENGTH', 16#2d, 2, false, 3, [a, a], length, "Arg0 := length of list Arg1."} + %% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation + [ { 'NOP', 16#f0, 0, false, 1, atomic, nop, "The no op. does nothing."} + , { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0."} + , { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call pop stack to arg0."} + , { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack."} + , { 'CALL_R', 16#03, 2, true, 8, [a,is], call_r, "Remote call to given contract and function."} + , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function."} + , { 'CALL_TR', 16#05, 2, true, 8, [a,is], call_tr, "Remote tail call to given contract and function."} + , { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block."} + , { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block."} + , { 'SWITCH_V2', 16#6a, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_V3', 16#6b, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_VN', 16#6c, 2, true, 4, [a,li], switch, "Conditional jump to a basic block on variant tag."} + , { 'PUSH', 16#09, 1, false, 2, [a], push, "Push argument to stack."} + , { 'INCA', 16#71, 0, false, 2, atomic, inc, "Increment accumulator."} + , { 'INC', 16#53, 1, false, 2, [a], inc, "Increment argument."} + , { 'DECA', 16#72, 0, false, 2, atomic, dec, "Decrement accumulator."} + , { 'DEC', 16#54, 1, false, 2, [a], dec, "Decrement argument."} + , { 'ADD', 16#11, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."} + , { 'SUB', 16#13, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."} + , { 'MUL', 16#12, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."} + , { 'DIV', 16#14, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."} + , { 'MOD', 16#15, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."} + , { 'POW', 16#16, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."} + , { 'LT', 16#17, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."} + , { 'GT', 16#18, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."} + , { 'EQ', 16#19, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."} + , { 'ELT', 16#1a, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."} + , { 'EGT', 16#1b, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."} + , { 'NEQ', 16#1c, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."} + , { 'AND', 16#1d, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."} + , { 'OR', 16#1e, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} + , { 'NOT', 16#1f, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."} + , { 'TUPLE', 16#20, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} + , { 'ELEMENT', 16#21, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"} + , { 'MAP_EMPTY', 16#22, 1, false, 3, [a], map_empty, "Arg0 := #{}."} + , { 'MAP_LOOKUP', 16#23, 3, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} + , { 'MAP_LOOKUPD', 16#69, 4, false, 3, [a,a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."} + , { 'MAP_UPDATE', 16#24, 4, false, 3, [a,a,a,a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} + , { 'MAP_DELETE', 16#25, 3, false, 3, [a,a,a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} + , { 'MAP_MEMBER', 16#26, 3, false, 3, [a,a,a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} + , { 'MAP_FROM_LIST',16#27, 2, false, 3, [a,a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."} + , { 'NIL', 16#28, 1, false, 3, [a], nil, "Arg0 := []."} + , { 'IS_NIL', 16#29, 2, false, 3, [a,a], is_nil, "Arg0 := true if Arg1 == []."} + , {'CONS', 16#2a, 3, false, 3, [a,a,a], cons, "Arg0 := [Arg1|Arg2]."} + , {'HD', 16#2b, 2, false, 3, [a,a], hd, "Arg0 := head of list Arg1."} + , {'TL', 16#2c, 2, false, 3, [a,a], tl, "Arg0 := tail of list Arg1."} + , {'LENGTH', 16#2d, 2, false, 3, [a,a], length, "Arg0 := length of list Arg1."} + , {'STR_EQ', 16#2e, 3, false, 3, [a,a,a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."} + , {'STR_JOIN', 16#2f, 3, false, 3, [a,a,a], str_join, "Arg0 := string Arg1 followed by string Arg2."} + , {'INT_TO_STR', 16#55, 2, false, 3, [a,a], int_to_str, "Arg0 := turn integer Arg1 into a string."} + , {'ADDR_TO_STR', 16#30, 2, false, 3, [a,a], addr_to_str, "Arg0 := turn address Arg1 into a string."} + , {'STR_REVERSE', 16#31, 2, false, 3, [a,a], str_reverse, "Arg0 := the reverse of string Arg1."} + , {'INT_TO_ADDR', 16#32, 2, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} + , {'VARIANT', 16#33, 4, false, 3, [a,a,a,a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."} + , {'VARIANT_TEST', 16#34, 3, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."} + , {'VARIANT_ELEMENT',16#35, 3, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."} + , {'BITS_NONEA', 16#6e, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."} + , {'BITS_NONE', 16#36, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."} + , {'BITS_ALLA', 16#6f, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."} + , {'BITS_ALL', 16#37, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."} + , {'BITS_ALL_N', 16#6d, 2, false, 3, [a,a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."} + , {'BITS_SET', 16#38, 3, false, 3, [a,a,a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."} + , {'BITS_CLEAR', 16#39, 3, false, 3, [a,a,a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."} + , {'BITS_TEST', 16#3a, 3, false, 3, [a,a,a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."} + , {'BITS_SUM', 16#3b, 2, false, 3, [a,a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."} + , {'BITS_OR', 16#3c, 3, false, 3, [a,a,a], bits_or, "Arg0 := Arg1 v Arg2."} + , {'BITS_AND', 16#3d, 3, false, 3, [a,a,a], bits_and, "Arg0 := Arg1 ^ Arg2."} + , {'BITS_DIFF', 16#3e, 3, false, 3, [a,a,a], bits_diff, "Arg0 := Arg1 - Arg2."} + , {'DUPA', 16#70, 0, false, 3, atomic, dup, "push copy of accumulator on stack."} + , {'DUP', 16#0a, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} + , {'POP', 16#0b, 1, false, 3, [a], pop, "Arg0 := top of stack."} + , {'STORE', 16#10, 2, false, 3, [a,a], store, "Arg0 := Arg1."} + , {'ADDRESS', 16#3f, 1, false, 3, [a], address, "Arg0 := The current contract address."} + , {'BALANCE', 16#40, 1, false, 3, [a], balance, "Arg0 := The current contract address."} + , {'ORIGIN', 16#41, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."} + , {'CALLER', 16#42, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."} + , {'GASPRICE', 16#43, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."} + , {'BLOCKHASH', 16#44, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has at height? + , {'BENEFICIARY', 16#45, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} + , {'TIMESTAMP', 16#46, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} + , {'NUMBER', 16#47, 1, false, 3, [a], number, "Arg0 := The block height."} + , {'DIFFICULTY', 16#48, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} + , {'GASLIMIT', 16#49, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} + , {'GAS', 16#4a, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} + , {'ABORT', 16#50, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} + , {'EXIT', 16#51, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} - , {'STR_EQ', 16#2e, 3, false, 3, [a, a, a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."} - , {'STR_JOIN', 16#2f, 3, false, 3, [a, a, a], str_join, "Arg0 := string Arg1 followed by string Arg2."} - , {'INT_TO_STR', 16#55, 2, false, 3, [a, a], int_to_str, "Arg0 := turn integer Arg1 into a string."} - , {'ADDR_TO_STR', 16#30, 2, false, 3, [a, a], addr_to_str, "Arg0 := turn address Arg1 into a string."} - , {'STR_REVERSE', 16#31, 2, false, 3, [a, a], str_reverse, "Arg0 := the reverse of string Arg1."} - , {'INT_TO_ADDR', 16#32, 2, false, 3, [a, a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} - , {'VARIANT', 16#33, 4, false, 3, [a, a, a, a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."} - , {'VARIANT_TEST', 16#34,3, false, 3, [a, a, a], variant_test,"Arg0 := true if variant Arg1 has the tag Arg2."} - , {'VARIANT_ELEMNT',16#35,3,false, 3, [a, a, a], variant_element,"Arg0 := element number Arg2 from variant Arg1."} - , {'BITS_NONEA', 16#6e, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."} - , {'BITS_NONE', 16#36, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."} - , {'BITS_ALLA', 16#6f, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."} - , {'BITS_ALL', 16#37, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."} - , {'BITS_ALL_N', 16#6d, 2, false, 3, [a, a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."} - , {'BITS_SET', 16#38, 3, false, 3, [a, a, a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."} - , {'BITS_CLEAR',16#39, 3, false, 3, [a, a, a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."} - , {'BITS_TEST', 16#3a, 3, false, 3, [a, a, a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."} , {'BITS_SUM', 16#3b, 2, false, 3, [a, a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."} - , {'BITS_OR', 16#3c, 3, false, 3, [a, a, a], bits_or, "Arg0 := Arg1 v Arg2."} - , {'BITS_AND', 16#3d, 3, false, 3, [a, a, a], bits_and, "Arg0 := Arg1 ^ Arg2."} - , {'BITS_DIFF', 16#3e, 3, false, 3, [a, a, a], bits_diff, "Arg0 := Arg1 - Arg2."} - , {'DUPA', 16#70, 0, false, 3, atomic, dup, "push copy of accumulator on stack."} - , {'DUP', 16#0a, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} - , {'POP', 16#0b, 1, false, 3, [a], pop, "Arg0 := top of stack."} - , {'STORE', 16#10, 2, false, 3, [a, a], store, "Arg0 := Arg1."} - %% TODO: Check the documentation and update it. - , {'ADDRESS', 16#3f, 1, false, 3, [a], address, "Arg0 := The current contract address."} - , {'BALANCE', 16#3f, 1, false, 3, [a], balance, "Arg0 := The current contract address."} - , {'ORIGIN', 16#40, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."} - , {'CALLER', 16#41, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."} - , {'GASPRICE', 16#42, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."} - , {'BLOCKHASH', 16#43, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has at height? - , {'BENEFICIARY',16#44, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} - , {'TIMESTAMP', 16#45, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} - , {'NUMBER', 16#46, 1, false, 3, [a], number, "Arg0 := The block height."} - , {'DIFFICULTY',16#47, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} - , {'GASLIMIT', 16#48, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} - , {'GAS', 16#49, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} - , {'ABORT', 16#4f, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} - , {'EXIT', 16#4e, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} ]. -- 2.30.2 From 9411a131fc1e4b52d41aad48c9d58e28c06d34d2 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 27 Feb 2019 15:28:11 +0100 Subject: [PATCH 04/19] Spell eunit the right way. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fd94da3..6153766 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ dialyzer: local distclean: clean @rm -rf _build/ -euint: local +eunit: local @$(REBAR) as local eunit test: local -- 2.30.2 From 268208ec9887d4008b206ae473e273f5cc9c3b96 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 27 Feb 2019 15:36:48 +0100 Subject: [PATCH 05/19] Use test target for ci. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2d09292..0c9ac9f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,7 +28,7 @@ jobs: command: make eunit - run: name: Common Tests - command: make ct + command: make test - save_cache: key: dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }} paths: -- 2.30.2 From 161b5a610684c1cdde7f41bdacfdb264b27dc8a7 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 27 Feb 2019 17:47:08 +0100 Subject: [PATCH 06/19] Renumber opcodes. Add primops. --- src/aeb_fate_generate_ops.erl | 201 +++++++++++++++++++--------------- 1 file changed, 115 insertions(+), 86 deletions(-) diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 486fc54..3bb8a26 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -22,93 +22,122 @@ generate(Src, Include) -> %% TODO: Some real gas numbers... ops_defs() -> %% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation - [ { 'NOP', 16#f0, 0, false, 1, atomic, nop, "The no op. does nothing."} - , { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0."} - , { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call pop stack to arg0."} + [ { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0."} + , { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call copy Arg0 to arg0."} , { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack."} , { 'CALL_R', 16#03, 2, true, 8, [a,is], call_r, "Remote call to given contract and function."} , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function."} , { 'CALL_TR', 16#05, 2, true, 8, [a,is], call_tr, "Remote tail call to given contract and function."} , { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block."} , { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block."} - , { 'SWITCH_V2', 16#6a, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."} - , { 'SWITCH_V3', 16#6b, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."} - , { 'SWITCH_VN', 16#6c, 2, true, 4, [a,li], switch, "Conditional jump to a basic block on variant tag."} - , { 'PUSH', 16#09, 1, false, 2, [a], push, "Push argument to stack."} - , { 'INCA', 16#71, 0, false, 2, atomic, inc, "Increment accumulator."} - , { 'INC', 16#53, 1, false, 2, [a], inc, "Increment argument."} - , { 'DECA', 16#72, 0, false, 2, atomic, dec, "Decrement accumulator."} - , { 'DEC', 16#54, 1, false, 2, [a], dec, "Decrement argument."} - , { 'ADD', 16#11, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."} - , { 'SUB', 16#13, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."} - , { 'MUL', 16#12, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."} - , { 'DIV', 16#14, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."} - , { 'MOD', 16#15, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."} - , { 'POW', 16#16, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."} - , { 'LT', 16#17, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."} - , { 'GT', 16#18, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."} - , { 'EQ', 16#19, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."} - , { 'ELT', 16#1a, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."} - , { 'EGT', 16#1b, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."} - , { 'NEQ', 16#1c, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."} - , { 'AND', 16#1d, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."} - , { 'OR', 16#1e, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} - , { 'NOT', 16#1f, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."} - , { 'TUPLE', 16#20, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} - , { 'ELEMENT', 16#21, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"} - , { 'MAP_EMPTY', 16#22, 1, false, 3, [a], map_empty, "Arg0 := #{}."} - , { 'MAP_LOOKUP', 16#23, 3, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} - , { 'MAP_LOOKUPD', 16#69, 4, false, 3, [a,a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."} - , { 'MAP_UPDATE', 16#24, 4, false, 3, [a,a,a,a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} - , { 'MAP_DELETE', 16#25, 3, false, 3, [a,a,a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} - , { 'MAP_MEMBER', 16#26, 3, false, 3, [a,a,a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} - , { 'MAP_FROM_LIST',16#27, 2, false, 3, [a,a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."} - , { 'NIL', 16#28, 1, false, 3, [a], nil, "Arg0 := []."} - , { 'IS_NIL', 16#29, 2, false, 3, [a,a], is_nil, "Arg0 := true if Arg1 == []."} - , {'CONS', 16#2a, 3, false, 3, [a,a,a], cons, "Arg0 := [Arg1|Arg2]."} - , {'HD', 16#2b, 2, false, 3, [a,a], hd, "Arg0 := head of list Arg1."} - , {'TL', 16#2c, 2, false, 3, [a,a], tl, "Arg0 := tail of list Arg1."} - , {'LENGTH', 16#2d, 2, false, 3, [a,a], length, "Arg0 := length of list Arg1."} - , {'STR_EQ', 16#2e, 3, false, 3, [a,a,a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."} - , {'STR_JOIN', 16#2f, 3, false, 3, [a,a,a], str_join, "Arg0 := string Arg1 followed by string Arg2."} - , {'INT_TO_STR', 16#55, 2, false, 3, [a,a], int_to_str, "Arg0 := turn integer Arg1 into a string."} - , {'ADDR_TO_STR', 16#30, 2, false, 3, [a,a], addr_to_str, "Arg0 := turn address Arg1 into a string."} - , {'STR_REVERSE', 16#31, 2, false, 3, [a,a], str_reverse, "Arg0 := the reverse of string Arg1."} - , {'INT_TO_ADDR', 16#32, 2, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} - , {'VARIANT', 16#33, 4, false, 3, [a,a,a,a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."} - , {'VARIANT_TEST', 16#34, 3, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."} - , {'VARIANT_ELEMENT',16#35, 3, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."} - , {'BITS_NONEA', 16#6e, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."} - , {'BITS_NONE', 16#36, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."} - , {'BITS_ALLA', 16#6f, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."} - , {'BITS_ALL', 16#37, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."} - , {'BITS_ALL_N', 16#6d, 2, false, 3, [a,a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."} - , {'BITS_SET', 16#38, 3, false, 3, [a,a,a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."} - , {'BITS_CLEAR', 16#39, 3, false, 3, [a,a,a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."} - , {'BITS_TEST', 16#3a, 3, false, 3, [a,a,a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."} - , {'BITS_SUM', 16#3b, 2, false, 3, [a,a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."} - , {'BITS_OR', 16#3c, 3, false, 3, [a,a,a], bits_or, "Arg0 := Arg1 v Arg2."} - , {'BITS_AND', 16#3d, 3, false, 3, [a,a,a], bits_and, "Arg0 := Arg1 ^ Arg2."} - , {'BITS_DIFF', 16#3e, 3, false, 3, [a,a,a], bits_diff, "Arg0 := Arg1 - Arg2."} - , {'DUPA', 16#70, 0, false, 3, atomic, dup, "push copy of accumulator on stack."} - , {'DUP', 16#0a, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} - , {'POP', 16#0b, 1, false, 3, [a], pop, "Arg0 := top of stack."} - , {'STORE', 16#10, 2, false, 3, [a,a], store, "Arg0 := Arg1."} - , {'ADDRESS', 16#3f, 1, false, 3, [a], address, "Arg0 := The current contract address."} - , {'BALANCE', 16#40, 1, false, 3, [a], balance, "Arg0 := The current contract address."} - , {'ORIGIN', 16#41, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."} - , {'CALLER', 16#42, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."} - , {'GASPRICE', 16#43, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."} - , {'BLOCKHASH', 16#44, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has at height? - , {'BENEFICIARY', 16#45, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} - , {'TIMESTAMP', 16#46, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} - , {'NUMBER', 16#47, 1, false, 3, [a], number, "Arg0 := The block height."} - , {'DIFFICULTY', 16#48, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} - , {'GASLIMIT', 16#49, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} - , {'GAS', 16#4a, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} - , {'ABORT', 16#50, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} - , {'EXIT', 16#51, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} + , { 'SWITCH_V2', 16#08, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_V3', 16#09, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_VN', 16#0a, 2, true, 4, [a,li], switch, "Conditional jump to a basic block on variant tag."} + , { 'PUSH', 16#0b, 1, false, 2, [a], push, "Push argument to stack."} + , { 'DUPA', 16#0c, 0, false, 3, atomic, dup, "push copy of accumulator on stack."} + , { 'DUP', 16#0d, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} + , { 'POP', 16#0e, 1, false, 3, [a], pop, "Arg0 := top of stack."} + , { 'STORE', 16#0f, 2, false, 3, [a,a], store, "Arg0 := Arg1."} + , { 'INCA', 16#10, 0, false, 2, atomic, inc, "Increment accumulator."} + , { 'INC', 16#11, 1, false, 2, [a], inc, "Increment argument."} + , { 'DECA', 16#12, 0, false, 2, atomic, dec, "Decrement accumulator."} + , { 'DEC', 16#13, 1, false, 2, [a], dec, "Decrement argument."} + , { 'ADD', 16#14, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."} + , { 'SUB', 16#15, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."} + , { 'MUL', 16#16, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."} + , { 'DIV', 16#17, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."} + , { 'MOD', 16#18, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."} + , { 'POW', 16#19, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."} + , { 'LT', 16#20, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."} + , { 'GT', 16#21, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."} + , { 'EQ', 16#22, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."} + , { 'ELT', 16#23, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."} + , { 'EGT', 16#24, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."} + , { 'NEQ', 16#25, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."} + , { 'AND', 16#26, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."} + , { 'OR', 16#27, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} + , { 'NOT', 16#28, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."} + , { 'TUPLE', 16#29, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} + , { 'ELEMENT', 16#2a, 4, false, 3, [t,a,a,a], element_op, "Arg1 := element(Arg2, Arg3). The element should be of type Arg1"} + , { 'MAP_EMPTY', 16#2b, 1, false, 3, [a], map_empty, "Arg0 := #{}."} + , { 'MAP_LOOKUP', 16#2c, 3, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} + , { 'MAP_LOOKUPD', 16#2d, 4, false, 3, [a,a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."} + , { 'MAP_UPDATE', 16#2e, 4, false, 3, [a,a,a,a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} + , { 'MAP_DELETE', 16#2f, 3, false, 3, [a,a,a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} + , { 'MAP_MEMBER', 16#30, 3, false, 3, [a,a,a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} + , { 'MAP_FROM_LIST',16#31, 2, false, 3, [a,a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."} + , { 'NIL', 16#32, 1, false, 3, [a], nil, "Arg0 := []."} + , { 'IS_NIL', 16#33, 2, false, 3, [a,a], is_nil, "Arg0 := true if Arg1 == []."} + , { 'CONS', 16#34, 3, false, 3, [a,a,a], cons, "Arg0 := [Arg1|Arg2]."} + , { 'HD', 16#35, 2, false, 3, [a,a], hd, "Arg0 := head of list Arg1."} + , { 'TL', 16#36, 2, false, 3, [a,a], tl, "Arg0 := tail of list Arg1."} + , { 'LENGTH', 16#37, 2, false, 3, [a,a], length, "Arg0 := length of list Arg1."} + , { 'STR_EQ', 16#38, 3, false, 3, [a,a,a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."} + , { 'STR_JOIN', 16#39, 3, false, 3, [a,a,a], str_join, "Arg0 := string Arg1 followed by string Arg2."} + , { 'INT_TO_STR', 16#40, 2, false, 3, [a,a], int_to_str, "Arg0 := turn integer Arg1 into a string."} + , { 'ADDR_TO_STR', 16#41, 2, false, 3, [a,a], addr_to_str, "Arg0 := turn address Arg1 into a string."} + , { 'STR_REVERSE', 16#42, 2, false, 3, [a,a], str_reverse, "Arg0 := the reverse of string Arg1."} + , { 'INT_TO_ADDR', 16#43, 2, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} + , { 'VARIANT', 16#44, 4, false, 3, [a,a,a,a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."} + , { 'VARIANT_TEST', 16#45, 3, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."} + , { 'VARIANT_ELEMENT',16#46, 3, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."} + , { 'BITS_NONEA', 16#47, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."} + , { 'BITS_NONE', 16#48, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."} + , { 'BITS_ALLA', 16#49, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."} + , { 'BITS_ALL', 16#50, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."} + , { 'BITS_ALL_N', 16#51, 2, false, 3, [a,a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."} + , { 'BITS_SET', 16#52, 3, false, 3, [a,a,a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."} + , { 'BITS_CLEAR', 16#53, 3, false, 3, [a,a,a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."} + , { 'BITS_TEST', 16#54, 3, false, 3, [a,a,a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."} + , { 'BITS_SUM', 16#55, 2, false, 3, [a,a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."} + , { 'BITS_OR', 16#56, 3, false, 3, [a,a,a], bits_or, "Arg0 := Arg1 v Arg2."} + , { 'BITS_AND', 16#57, 3, false, 3, [a,a,a], bits_and, "Arg0 := Arg1 ^ Arg2."} + , { 'BITS_DIFF', 16#58, 3, false, 3, [a,a,a], bits_diff, "Arg0 := Arg1 - Arg2."} + , { 'ADDRESS', 16#59, 1, false, 3, [a], address, "Arg0 := The current contract address."} + , { 'BALANCE', 16#5a, 1, false, 3, [a], balance, "Arg0 := The current contract address."} + , { 'ORIGIN', 16#5b, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."} + , { 'CALLER', 16#5c, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."} + , { 'GASPRICE', 16#5d, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."} + , { 'BLOCKHASH', 16#5e, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has at height? + , { 'BENEFICIARY', 16#5f, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} + , { 'TIMESTAMP', 16#60, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} + , { 'NUMBER', 16#61, 1, false, 3, [a], number, "Arg0 := The block height."} + , { 'DIFFICULTY', 16#62, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} + , { 'GASLIMIT', 16#63, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} + , { 'GAS', 16#64, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} + , { 'LOG0', 16#65, 2, false, 3, [a,a], log, "Create a log message in the call object."} + , { 'LOG1', 16#66, 3, false, 3, [a,a,a], log, "Create a log message with one topic in the call object."} + , { 'LOG2', 16#67, 4, false, 3, [a,a,a,a], log, "Create a log message with two topics in the call object."} + , { 'LOG3', 16#68, 5, false, 3, [a,a,a,a,a], log, "Create a log message with three topics in the call object."} + , { 'LOG4', 16#69, 6, false, 3, [a,a,a,a,a,a], log, "Create a log message with four topics in the call object."} + , { 'DEACTIVATE', 16#6a, 0, false, 3, atomic, deactivate, "Mark the current contract for deactication."} + %% Transaction ops + , { 'SPEND', 16#6b, 2, false,3, [a,a], spend, "Transfer Arg0 tokens to account Arg1. (If the contract account has at least that many tokens."} + , { 'ORACLE_REGISTER', 16#6c, 6, false,3, [a,a,a,a,a,a], oracle_register, "Mark the current contract for deactication."} + %% TODO: + , { 'ORACLE_QUERY', 16#6d, 0, false,3, atomic, oracle_query, ""} + , { 'ORACLE_RESPOND', 16#6e, 0, false,3, atomic, oracle_respond, ""} + , { 'ORACLE_EXTEND', 16#6f, 0, false,3, atomic, oracle_extend, ""} + , { 'ORACLE_GET_ANSWER', 16#70, 0, false,3, atomic, oracle_get_answer, ""} + , { 'ORACLE_GET_QUESTION', 16#71, 0, false,3, atomic,oracle_get_question, ""} + , { 'ORACLE_QUERY_FEE', 16#72, 0, false,3, atomic, oracle_query_fee, ""} + , { 'AENS_RESOLVE', 16#73, 0, false,3, atomic, aens_resolve, ""} + , { 'AENS_PRECLAIM', 16#74, 0, false,3, atomic, aens_preclaim, ""} + , { 'AENS_CLAIM', 16#75, 0, false,3, atomic, aens_claim, ""} + , { 'AENS_UPDATE', 16#76, 0, false,3, atomic, aend_update, ""} + , { 'AENS_TRANSFER', 16#77, 0, false,3, atomic, aens_transfer, ""} + , { 'AENS_REVOKE', 16#78, 0, false,3, atomic, aens_revoke, ""} + , { 'ECVERIFY', 16#79, 0, false,3, atomic, ecverify, ""} + , { 'SHA3', 16#7a, 0, false,3, atomic, sha3, ""} + , { 'SHA256', 16#7b, 0, false,3, atomic, sha256, ""} + , { 'BLAKE2B', 16#7c, 0, false,3, atomic, blake2b, ""} + + , {'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} + , {'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} + , { 'NOP', 16#fd, 0, false, 1, atomic, nop, "The no op. does nothing."} + %% FUNCTION 16#fe "Function declaration and entrypoint." + %% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes." ]. @@ -118,8 +147,8 @@ generate_header_file(Filename, Ops) -> io:format(File, "~s", [prelude("Provides opcode defines.\n")]), io:format(File, "%% FATE opcodes\n~s", [Defines]), io:format(File, "~s", - ["-define('FUNCTION' , 16#fe).\n" - "-define('EXTEND' , 16#ff).\n\n"]), + ["-define('FUNCTION' , 16#fe).\n" + "-define('EXTEND' , 16#ff).\n\n"]), file:close(File). generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) -> @@ -193,7 +222,7 @@ generate_code_ops(Modulename, SrcDir, Ops) -> file:close(File). gen_type(#{type_name := TypeName, type := Type}) -> - lists:flatten(io_lib:format("-type ~-25s :: ~s.\n", + lists:flatten(io_lib:format("-type ~-26s :: ~s.\n", [TypeName, Type])). gen_fate_code_type(#{type_name := TypeName}) -> @@ -302,7 +331,7 @@ prelude(Doc) -> gen_defines(#{opname := Name, opcode := OpCode}) -> - lists:flatten(io_lib:format("-define(~-17w, 16#~2.16.0b).\n", [Name, OpCode])). + lists:flatten(io_lib:format("-define(~-26w, 16#~2.16.0b).\n", [Name, OpCode])). gen([]) -> []; @@ -313,11 +342,11 @@ gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) - Macro = "?" ++ Name, Type = case FateFormat of atomic -> io_lib:format("~w", [OpName]); - ArgTypes -> + ArgTypes -> io_lib:format("{~w, ~s}", [OpName, expand_types(ArgTypes)]) end, ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Args]), - + [#{ opname => OpName , opcode => OpCode , args => Args -- 2.30.2 From dd1a6a9c3d36762dcb979c15c53f4fe8dc70f734 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 28 Feb 2019 07:55:51 +0100 Subject: [PATCH 07/19] Generate tokens in scanner from definitions. --- .gitignore | 1 + Makefile | 6 +- src/aeb_fate_asm_scan.template | 99 ++++++++++++++++ src/aeb_fate_asm_scan.xrl | 207 --------------------------------- src/aeb_fate_generate_ops.erl | 45 +++++-- 5 files changed, 139 insertions(+), 219 deletions(-) create mode 100644 src/aeb_fate_asm_scan.template delete mode 100644 src/aeb_fate_asm_scan.xrl diff --git a/.gitignore b/.gitignore index 385e93a..cccd365 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ rel/example_project .rebar aeb_asm_scan.erl aeb_fate_asm_scan.erl +aeb_fate_asm_scan.xrl _build/ aefateasm include/aeb_fate_opcodes.hrl diff --git a/Makefile b/Makefile index 6153766..071e04c 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,10 @@ REBAR ?= rebar3 all: local -local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl +local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl @$(REBAR) as local release -console: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl +console: local @$(REBAR) as local shell clean: @@ -34,7 +34,7 @@ test: local ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin erlc -o $(dir $@) $< -src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl: ebin/aeb_fate_generate_ops.beam +src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl: ebin/aeb_fate_generate_ops.beam erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/ ebin: diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template new file mode 100644 index 0000000..f894aa5 --- /dev/null +++ b/src/aeb_fate_asm_scan.template @@ -0,0 +1,99 @@ +%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- +%%%------------------------------------------------------------------- +%%% @copyright (C) 2019, aeternity Anstalt +%%% @doc +%%% Handling FATE code. +%%% @end +###REPLACEWITHNOTE### +%%%------------------------------------------------------------------- + +Definitions. +DIGIT = [0-9] +HEXDIGIT = [0-9a-fA-F] +LOWER = [a-z_] +UPPER = [A-Z] +INT = {DIGIT}+ +HEX = 0x{HEXDIGIT}+ +HASH = #{HEXDIGIT}+ +WS = [\000-\s] +ID = {LOWER}[a-zA-Z0-9_]* + + +Rules. +arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. +var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}. +a : {token, {stack, TokenLine, 0}}. +a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}. + +true : {token, {boolean, TokenLine, true}}. +false : {token, {boolean, TokenLine, false}}. + +###REPLACEWITHOPTOKENS### + +FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. + +{ID} : + {token, {id, TokenLine, TokenChars}}. +{HEX} : + {token, {int, TokenLine, parse_hex(TokenChars)}}. +{INT} : + {token, {int, TokenLine, parse_int(TokenChars)}}. +{HASH} : + {token, {hash, TokenLine, parse_hash(TokenChars)}}. + + +%% Symbols +\-\> : {token, {'to', TokenLine}}. +\: : {token, {'to', TokenLine}}. +, : {token, {',', TokenLine}}. +\( : {token, {'(', TokenLine}}. +\) : {token, {')', TokenLine}}. +\[ : {token, {'[', TokenLine}}. +\] : {token, {']', TokenLine}}. +\{ : {token, {'{', TokenLine}}. +\} : {token, {'}', TokenLine}}. + +;;.* : + {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. + +\. : skip_token. + + +%% Whitespace ignore +{WS} : skip_token. + +%% Comments (TODO: nested comments) + + +. : {error, "Unexpected token: " ++ TokenChars}. + +Erlang code. + +-export([scan/1]). + +-dialyzer({nowarn_function, yyrev/2}). + +-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). + +-include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). + + +parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). + +parse_int(Chars) -> list_to_integer(Chars). + +parse_arg("arg" ++ N) -> list_to_integer(N). +parse_var("var" ++ N) -> list_to_integer(N). +parse_acc("a" ++ N) -> list_to_integer(N). + + +parse_hash("#" ++ Chars) -> + N = list_to_integer(Chars, 16), + <>. + +scan(S) -> + string(S). + +drop_prefix(C, [C|Rest]) -> + drop_prefix(C, Rest); +drop_prefix(_, Tail) -> Tail. diff --git a/src/aeb_fate_asm_scan.xrl b/src/aeb_fate_asm_scan.xrl deleted file mode 100644 index 6d10f56..0000000 --- a/src/aeb_fate_asm_scan.xrl +++ /dev/null @@ -1,207 +0,0 @@ -%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- -%%%------------------------------------------------------------------- -%%% @copyright (C) 2019, aeternity Anstalt -%%% @doc -%%% Handling FATE code. -%%% @end -%%% Created : 9 Jan 2019 -%%%------------------------------------------------------------------- - -Definitions. -DIGIT = [0-9] -HEXDIGIT = [0-9a-fA-F] -LOWER = [a-z_] -UPPER = [A-Z] -INT = {DIGIT}+ -HEX = 0x{HEXDIGIT}+ -HASH = #{HEXDIGIT}+ -WS = [\000-\s] -ID = {LOWER}[a-zA-Z0-9_]* - - -Rules. -arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. -var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}. -a : {token, {stack, TokenLine, 0}}. -a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}. - -true : {token, {boolean, TokenLine, true}}. -false : {token, {boolean, TokenLine, false}}. - -RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}. -RETURNR : {token, {mnemonic, TokenLine, 'RETURNR'}}. -CALL : {token, {mnemonic, TokenLine, 'CALL'}}. -NOP : {token, {mnemonic, TokenLine, 'NOP'}}. - -CALL_R : {token, {mnemonic, TokenLine, 'CALL_R'}}. -CALL_T : {token, {mnemonic, TokenLine, 'CALL_T'}}. -CALL_TR : {token, {mnemonic, TokenLine, 'CALL_TR'}}. -JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}. -JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}. -SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}. -SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}. -SWITCH_VN : {token, {mnemonic, TokenLine, 'SWITCH_VN'}}. - -PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}. -DUP : {token, {mnemonic, TokenLine, 'DUP'}}. -DUPA : {token, {mnemonic, TokenLine, 'DUPA'}}. -POP : {token, {mnemonic, TokenLine, 'POP'}}. - -STORE : {token, {mnemonic, TokenLine, 'STORE'}}. - -ADD : {token, {mnemonic, TokenLine, 'ADD'}}. -MUL : {token, {mnemonic, TokenLine, 'MUL'}}. -SUB : {token, {mnemonic, TokenLine, 'SUB'}}. -DIV : {token, {mnemonic, TokenLine, 'DIV'}}. -MOD : {token, {mnemonic, TokenLine, 'MOD'}}. -POW : {token, {mnemonic, TokenLine, 'POW'}}. - -INC : {token, {mnemonic, TokenLine, 'INC'}}. -DEC : {token, {mnemonic, TokenLine, 'DEC'}}. -INCA : {token, {mnemonic, TokenLine, 'INCA'}}. -DECA : {token, {mnemonic, TokenLine, 'DECA'}}. - -LT : {token, {mnemonic, TokenLine, 'LT'}}. -GT : {token, {mnemonic, TokenLine, 'GT'}}. -EQ : {token, {mnemonic, TokenLine, 'EQ'}}. -ELT : {token, {mnemonic, TokenLine, 'ELT'}}. -EGT : {token, {mnemonic, TokenLine, 'EGT'}}. -NEQ : {token, {mnemonic, TokenLine, 'NEQ'}}. - -AND : {token, {mnemonic, TokenLine, 'AND'}}. -OR : {token, {mnemonic, TokenLine, 'OR'}}. -NOT : {token, {mnemonic, TokenLine, 'NOT'}}. - -TUPLE : {token, {mnemonic, TokenLine, 'TUPLE'}}. -ELEMENT : {token, {mnemonic, TokenLine, 'ELEMENT'}}. - -MAP_EMPTY : {token, {mnemonic, TokenLine, 'MAP_EMPTY'}}. -MAP_LOOKUP : {token, {mnemonic, TokenLine, 'MAP_LOOKUP'}}. -MAP_LOOKUPD : {token, {mnemonic, TokenLine, 'MAP_LOOKUPD'}}. -MAP_UPDATE : {token, {mnemonic, TokenLine, 'MAP_UPDATE'}}. -MAP_MEMBER : {token, {mnemonic, TokenLine, 'MAP_MEMBER'}}. -MAP_DELETE : {token, {mnemonic, TokenLine, 'MAP_DELETE'}}. -MAP_FROM_LIST : {token, {mnemonic, TokenLine, 'MAP_FROM_LIST'}}. - -NIL : {token, {mnemonic, TokenLine, 'NIL'}}. -IS_NIL : {token, {mnemonic, TokenLine, 'IS_NIL'}}. -CONS : {token, {mnemonic, TokenLine, 'CONS'}}. -HD : {token, {mnemonic, TokenLine, 'HD'}}. -TL : {token, {mnemonic, TokenLine, 'TL'}}. -LENGTH : {token, {mnemonic, TokenLine, 'LENGTH'}}. - -STR_EQ : {token, {mnemonic, TokenLine, 'STR_EQ'}}. -STR_JOIN : {token, {mnemonic, TokenLine, 'STR_JOIN'}}. -INT_TO_STR : {token, {mnemonic, TokenLine, 'INT_TO_STR'}}. -ADDR_TO_STR : {token, {mnemonic, TokenLine, 'ADDR_TO_STR'}}. -STR_REVERSE : {token, {mnemonic, TokenLine, 'STR_REVERSE'}}. - -INT_TO_ADDR : {token, {mnemonic, TokenLine, 'INT_TO_ADDR'}}. - -VARIANT : {token, {mnemonic, TokenLine, 'VARIANT'}}. -VARIANT_TEST : {token, {mnemonic, TokenLine, 'VARIANT_TEST'}}. -VARIANT_ELEMENT : {token, {mnemonic, TokenLine, 'VARIANT_ELEMENT'}}. - -BITS_NONE : {token, {mnemonic, TokenLine, 'BITS_NONE'}}. -BITS_NONEA : {token, {mnemonic, TokenLine, 'BITS_NONEA'}}. -BITS_ALL : {token, {mnemonic, TokenLine, 'BITS_ALL'}}. -BITS_ALLA : {token, {mnemonic, TokenLine, 'BITS_ALLA'}}. -BITS_ALL_N : {token, {mnemonic, TokenLine, 'BITS_ALL_N'}}. -BITS_SET : {token, {mnemonic, TokenLine, 'BITS_SET'}}. -BITS_CLEAR : {token, {mnemonic, TokenLine, 'BITS_CLEAR'}}. -BITS_TEST : {token, {mnemonic, TokenLine, 'BITS_TEST'}}. -BITS_SUM : {token, {mnemonic, TokenLine, 'BITS_SUM'}}. -BITS_OR : {token, {mnemonic, TokenLine, 'BITS_OR'}}. -BITS_AND : {token, {mnemonic, TokenLine, 'BITS_AND'}}. -BITS_DIFF : {token, {mnemonic, TokenLine, 'BITS_DIFF'}}. - - -ADDRESS : {token, {mnemonic, TokenLine, 'ADDRESS'}}. -BALANCE : {token, {mnemonic, TokenLine, 'BALANCE'}}. -ORIGIN : {token, {mnemonic, TokenLine, 'ORIGIN'}}. -CALLER : {token, {mnemonic, TokenLine, 'CALLER'}}. -GASPRICE : {token, {mnemonic, TokenLine, 'GASPRICE'}}. -BLOCKHASH : {token, {mnemonic, TokenLine, 'BLOCKHASH'}}. -BENEFICIARY : {token, {mnemonic, TokenLine, 'BENEFICIARY'}}. -TIMESTAMP : {token, {mnemonic, TokenLine, 'TIMESTAMP'}}. -NUMBER : {token, {mnemonic, TokenLine, 'NUMBER'}}. -DIFFICULTY : {token, {mnemonic, TokenLine, 'DIFFICULTY'}}. -GASLIMIT : {token, {mnemonic, TokenLine, 'GASLIMIT'}}. -GAS : {token, {mnemonic, TokenLine, 'GAS'}}. -LOG0 : {token, {mnemonic, TokenLine, 'LOG0'}}. -LOG1 : {token, {mnemonic, TokenLine, 'LOG1'}}. -LOG2 : {token, {mnemonic, TokenLine, 'LOG2'}}. -LOG3 : {token, {mnemonic, TokenLine, 'LOG3'}}. -LOG4 : {token, {mnemonic, TokenLine, 'LOG4'}}. -ABORT : {token, {mnemonic, TokenLine, 'ABORT'}}. -EXIT : {token, {mnemonic, TokenLine, 'EXIT'}}. -DEACTIVATE : {token, {mnemonic, TokenLine, 'DEACTIVATE'}}. -COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}. - -FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. - -{ID} : - {token, {id, TokenLine, TokenChars}}. -{HEX} : - {token, {int, TokenLine, parse_hex(TokenChars)}}. -{INT} : - {token, {int, TokenLine, parse_int(TokenChars)}}. -{HASH} : - {token, {hash, TokenLine, parse_hash(TokenChars)}}. - - -%% Symbols -\-\> : {token, {'to', TokenLine}}. -\: : {token, {'to', TokenLine}}. -, : {token, {',', TokenLine}}. -\( : {token, {'(', TokenLine}}. -\) : {token, {')', TokenLine}}. -\[ : {token, {'[', TokenLine}}. -\] : {token, {']', TokenLine}}. -\{ : {token, {'{', TokenLine}}. -\} : {token, {'}', TokenLine}}. - -;;.* : - {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. - -\. : skip_token. - - -%% Whitespace ignore -{WS} : skip_token. - -%% Comments (TODO: nested comments) - - -. : {error, "Unexpected token: " ++ TokenChars}. - -Erlang code. - --export([scan/1]). - --dialyzer({nowarn_function, yyrev/2}). - --ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). - --include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). - - -parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). - -parse_int(Chars) -> list_to_integer(Chars). - -parse_arg("arg" ++ N) -> list_to_integer(N). -parse_var("var" ++ N) -> list_to_integer(N). -parse_acc("a" ++ N) -> list_to_integer(N). - - -parse_hash("#" ++ Chars) -> - N = list_to_integer(Chars, 16), - <>. - -scan(S) -> - string(S). - -drop_prefix(C, [C|Rest]) -> - drop_prefix(C, Rest); -drop_prefix(_, Tail) -> Tail. diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 3bb8a26..925caf9 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -17,19 +17,20 @@ generate(Src, Include) -> HrlFile = Include ++ "aeb_fate_opcodes.hrl", generate_header_file(HrlFile, Ops), generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops), - generate_code_ops(aeb_fate_code, Src, Ops). + generate_code_ops(aeb_fate_code, Src, Ops), + generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops). %% TODO: Some real gas numbers... ops_defs() -> %% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation - [ { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0."} - , { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call copy Arg0 to arg0."} - , { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack."} - , { 'CALL_R', 16#03, 2, true, 8, [a,is], call_r, "Remote call to given contract and function."} - , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function."} - , { 'CALL_TR', 16#05, 2, true, 8, [a,is], call_tr, "Remote tail call to given contract and function."} - , { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block."} - , { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block."} + [ { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0. The type of the retun value has to match the return type of the function."} + , { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call copy Arg0 to arg0. The type of the retun value has to match the return type of the function."} + , { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack. The types of the arguments has to match the argument typs of the function."} + , { 'CALL_R', 16#03, 2, true, 8, [a,is], call_r, "Remote call to given contract and function. The types of the arguments has to match the argument typs of the function."} + , { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."} + , { 'CALL_TR', 16#05, 2, true, 8, [a,is], call_tr, "Remote tail call to given contract and function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."} + , { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block. The basic block has to exist in the current function."} + , { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block. If Arg0 then jump to Arg1."} , { 'SWITCH_V2', 16#08, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."} , { 'SWITCH_V3', 16#09, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."} , { 'SWITCH_VN', 16#0a, 2, true, 4, [a,li], switch, "Conditional jump to a basic block on variant tag."} @@ -372,3 +373,29 @@ expand_type(ii) -> "fate_arg_immediate(aeb_fate_data:fate_integer())"; expand_type(li) -> "[fate_arg_immediate(aeb_fate_data:fate_integer())]"; expand_type(t) -> "aeb_fate_data:fate_type_type()". +generate_scanner(TemplateFile, Outfile, Path, Ops) -> + {ok, Template} = file:read_file(filename:join(Path,TemplateFile)), + Tokens = lists:flatten([gen_token(Op) || Op <- Ops]), + NewFile = insert_tokens_in_template(Template, Tokens), + file:write_file(filename:join(Path, Outfile), NewFile). + +gen_token(#{opname := OpName}) -> + Name = atom_to_list(OpName), + io_lib:format("~-28s: {token, {mnemonic, TokenLine, ~w}}.\n", + [Name, OpName]). + +insert_tokens_in_template(<<"###REPLACEWITHOPTOKENS###", Rest/binary >>, Tokens) -> + [Tokens, Rest]; +insert_tokens_in_template(<<"###REPLACEWITHNOTE###", Rest/binary >>, Tokens) -> + [ + "%%%\n" + "%%% === === N O T E : This file is generated do not edit. === ===\n" + "%%%\n" + "%%% Source is in aeb_fate_generate_ops.erl\n" + "%%% and aeb_fate_asm_scan.template" + | insert_tokens_in_template(Rest, Tokens)]; +insert_tokens_in_template(<>, Tokens) -> + [B|insert_tokens_in_template(Rest, Tokens)]. + + + -- 2.30.2 From 8dd8e89c1ecbd830b729685c62fffb91998dbc3a Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 28 Feb 2019 08:13:26 +0100 Subject: [PATCH 08/19] Rename NUMBER op to GENERATION and add MICROBLOCK instruction. --- src/aeb_fate_generate_ops.erl | 57 ++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 925caf9..3eb7def 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -102,37 +102,38 @@ ops_defs() -> , { 'BLOCKHASH', 16#5e, 1, false, 3, [a], blockhash, "Arg0 := The current blockhash."} %% TODO: Do we support has at height? , { 'BENEFICIARY', 16#5f, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} , { 'TIMESTAMP', 16#60, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} - , { 'NUMBER', 16#61, 1, false, 3, [a], number, "Arg0 := The block height."} - , { 'DIFFICULTY', 16#62, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} - , { 'GASLIMIT', 16#63, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} - , { 'GAS', 16#64, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} + , { 'GENERATION', 16#61, 1, false, 3, [a], generation, "Arg0 := The block height of the cureent generation."} + , { 'MICROBLOCK', 16#62, 1, false, 3, [a], microblock, "Arg0 := The current micro block number."} + , { 'DIFFICULTY', 16#63, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} + , { 'GASLIMIT', 16#64, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} + , { 'GAS', 16#65, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} - , { 'LOG0', 16#65, 2, false, 3, [a,a], log, "Create a log message in the call object."} - , { 'LOG1', 16#66, 3, false, 3, [a,a,a], log, "Create a log message with one topic in the call object."} - , { 'LOG2', 16#67, 4, false, 3, [a,a,a,a], log, "Create a log message with two topics in the call object."} - , { 'LOG3', 16#68, 5, false, 3, [a,a,a,a,a], log, "Create a log message with three topics in the call object."} - , { 'LOG4', 16#69, 6, false, 3, [a,a,a,a,a,a], log, "Create a log message with four topics in the call object."} - , { 'DEACTIVATE', 16#6a, 0, false, 3, atomic, deactivate, "Mark the current contract for deactication."} + , { 'LOG0', 16#66, 2, false, 3, [a,a], log, "Create a log message in the call object."} + , { 'LOG1', 16#67, 3, false, 3, [a,a,a], log, "Create a log message with one topic in the call object."} + , { 'LOG2', 16#68, 4, false, 3, [a,a,a,a], log, "Create a log message with two topics in the call object."} + , { 'LOG3', 16#69, 5, false, 3, [a,a,a,a,a], log, "Create a log message with three topics in the call object."} + , { 'LOG4', 16#6a, 6, false, 3, [a,a,a,a,a,a], log, "Create a log message with four topics in the call object."} + , { 'DEACTIVATE', 16#6b, 0, false, 3, atomic, deactivate, "Mark the current contract for deactication."} %% Transaction ops - , { 'SPEND', 16#6b, 2, false,3, [a,a], spend, "Transfer Arg0 tokens to account Arg1. (If the contract account has at least that many tokens."} - , { 'ORACLE_REGISTER', 16#6c, 6, false,3, [a,a,a,a,a,a], oracle_register, "Mark the current contract for deactication."} + , { 'SPEND', 16#6c, 2, false,3, [a,a], spend, "Transfer Arg0 tokens to account Arg1. (If the contract account has at least that many tokens."} + , { 'ORACLE_REGISTER', 16#6d, 6, false,3, [a,a,a,a,a,a], oracle_register, "Mark the current contract for deactication."} %% TODO: - , { 'ORACLE_QUERY', 16#6d, 0, false,3, atomic, oracle_query, ""} - , { 'ORACLE_RESPOND', 16#6e, 0, false,3, atomic, oracle_respond, ""} - , { 'ORACLE_EXTEND', 16#6f, 0, false,3, atomic, oracle_extend, ""} - , { 'ORACLE_GET_ANSWER', 16#70, 0, false,3, atomic, oracle_get_answer, ""} - , { 'ORACLE_GET_QUESTION', 16#71, 0, false,3, atomic,oracle_get_question, ""} - , { 'ORACLE_QUERY_FEE', 16#72, 0, false,3, atomic, oracle_query_fee, ""} - , { 'AENS_RESOLVE', 16#73, 0, false,3, atomic, aens_resolve, ""} - , { 'AENS_PRECLAIM', 16#74, 0, false,3, atomic, aens_preclaim, ""} - , { 'AENS_CLAIM', 16#75, 0, false,3, atomic, aens_claim, ""} - , { 'AENS_UPDATE', 16#76, 0, false,3, atomic, aend_update, ""} - , { 'AENS_TRANSFER', 16#77, 0, false,3, atomic, aens_transfer, ""} - , { 'AENS_REVOKE', 16#78, 0, false,3, atomic, aens_revoke, ""} - , { 'ECVERIFY', 16#79, 0, false,3, atomic, ecverify, ""} - , { 'SHA3', 16#7a, 0, false,3, atomic, sha3, ""} - , { 'SHA256', 16#7b, 0, false,3, atomic, sha256, ""} - , { 'BLAKE2B', 16#7c, 0, false,3, atomic, blake2b, ""} + , { 'ORACLE_QUERY', 16#6e, 0, false,3, atomic, oracle_query, ""} + , { 'ORACLE_RESPOND', 16#6f, 0, false,3, atomic, oracle_respond, ""} + , { 'ORACLE_EXTEND', 16#70, 0, false,3, atomic, oracle_extend, ""} + , { 'ORACLE_GET_ANSWER', 16#71, 0, false,3, atomic, oracle_get_answer, ""} + , { 'ORACLE_GET_QUESTION', 16#72, 0, false,3, atomic,oracle_get_question, ""} + , { 'ORACLE_QUERY_FEE', 16#73, 0, false,3, atomic, oracle_query_fee, ""} + , { 'AENS_RESOLVE', 16#74, 0, false,3, atomic, aens_resolve, ""} + , { 'AENS_PRECLAIM', 16#75, 0, false,3, atomic, aens_preclaim, ""} + , { 'AENS_CLAIM', 16#76, 0, false,3, atomic, aens_claim, ""} + , { 'AENS_UPDATE', 16#77, 0, false,3, atomic, aend_update, ""} + , { 'AENS_TRANSFER', 16#78, 0, false,3, atomic, aens_transfer, ""} + , { 'AENS_REVOKE', 16#79, 0, false,3, atomic, aens_revoke, ""} + , { 'ECVERIFY', 16#7a, 0, false,3, atomic, ecverify, ""} + , { 'SHA3', 16#7b, 0, false,3, atomic, sha3, ""} + , { 'SHA256', 16#7c, 0, false,3, atomic, sha256, ""} + , { 'BLAKE2B', 16#7d, 0, false,3, atomic, blake2b, ""} , {'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} , {'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} -- 2.30.2 From b5daedaf95d34be9b23e90bf24117f5c498fc466 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Thu, 28 Feb 2019 10:38:33 +0100 Subject: [PATCH 09/19] Since Tag < Size, Size cannot be zero --- src/aeb_fate_encoding.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl index 1fcbd9e..9bdadbb 100644 --- a/src/aeb_fate_encoding.erl +++ b/src/aeb_fate_encoding.erl @@ -136,10 +136,8 @@ serialize(Map) when ?IS_FATE_MAP(Map) -> <>; -serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 =< Size - , Size < 256 - , 0 =< Tag - , Tag < Size -> +serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 < Size, Size < 256, + 0 =< Tag, Tag < Size -> <>. -- 2.30.2 From 899bff911145c96b21ad4a0410f8eb66dfccedec Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Thu, 28 Feb 2019 13:33:26 +0100 Subject: [PATCH 10/19] unit is printed `()` --- src/aeb_fate_data.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index f0a6898..31eda7b 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -133,7 +133,7 @@ format(?FATE_TRUE) -> "true"; format(?FATE_FALSE) -> "false"; format(?FATE_NIL) -> "[]"; format(L) when ?IS_FATE_LIST(L) -> format_list(?FATE_LIST_VALUE(L)); -format(?FATE_UNIT) -> "unit"; +format(?FATE_UNIT) -> "()"; format(?FATE_TUPLE(T)) -> "{ " ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] ++ "}"; format(S) when ?IS_FATE_STRING(S) -> [S]; -- 2.30.2 From a51a864059053acfe247e221f5f7a3e4a5b6bce4 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Thu, 28 Feb 2019 13:33:47 +0100 Subject: [PATCH 11/19] Formatting differently --- src/aeb_fate_data.erl | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index 31eda7b..e5404dd 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -135,28 +135,23 @@ format(?FATE_NIL) -> "[]"; format(L) when ?IS_FATE_LIST(L) -> format_list(?FATE_LIST_VALUE(L)); format(?FATE_UNIT) -> "()"; format(?FATE_TUPLE(T)) -> - "{ " ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] ++ "}"; + ["( ", lists:join(", ", [ format(E) || E <- erlang:tuple_to_list(T)]), " )"]; format(S) when ?IS_FATE_STRING(S) -> [S]; format(?FATE_VARIANT(Size, Tag, T)) -> - "( " ++ integer_to_list(Size) ++ ", " - ++ integer_to_list(Tag) ++ ", " - ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] - ++ " )"; + ["(| ", + lists:join("| ", [integer_to_list(Size), integer_to_list(Tag) | + [format(E) || E <- erlang:tuple_to_list(T)]]), + " |)"]; format(M) when ?IS_FATE_MAP(M) -> - "#{ " - ++ format_kvs(maps:to_list(?FATE_MAP_VALUE(M))) - ++" }"; -format(?FATE_ADDRESS(Address)) -> address_to_base58(Address); + ["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"]; +format(?FATE_ADDRESS(Address)) -> ["#", address_to_base58(Address)]; format(V) -> exit({not_a_fate_type, V}). -format_list([]) -> " ]"; -format_list([E]) -> format(E) ++ " ]"; -format_list([H|T]) -> format(H) ++ ", " ++ format_list(T). +format_list(List) -> + ["[ ", lists:join(", ", [format(E) || E <- List]), " ]"]. -format_kvs([]) -> ""; -format_kvs([{K,V}]) -> "( " ++ format(K) ++ " => " ++ format(V) ++ " )"; -format_kvs([{K,V} | Rest]) -> - "( " ++ format(K) ++ " => " ++ format(V) ++ " ), " ++ format_kvs(Rest). +format_kvs(List) -> + lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]). %% -- Local base 58 library -- 2.30.2 From 9d056dc620f4a7c238ecde8f8c5965f0b33f58b0 Mon Sep 17 00:00:00 2001 From: Thomas Arts Date: Thu, 28 Feb 2019 13:34:03 +0100 Subject: [PATCH 12/19] Add eqc profile --- rebar.config | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rebar.config b/rebar.config index 08234fd..2b014b7 100644 --- a/rebar.config +++ b/rebar.config @@ -45,5 +45,8 @@ "robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* " "/njs /njh /nfl /ndl & exit /b 0"} % silence things ]} - ]}]}. - + ]}, + {eqc, [{erl_opts, [{parse_transform, eqc_cover}]}, + {extra_src_dirs, ["quickcheck"]} %% May not be called eqc! + ]} + ]}. -- 2.30.2 From c04d86f6910495f6c771beb937c0e002b8f032fc Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Tue, 26 Feb 2019 22:51:33 +0100 Subject: [PATCH 13/19] Generate code for fate ops from spec. --- src/aeb_fate_generate_ops.erl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 3eb7def..5c09d77 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -397,6 +397,3 @@ insert_tokens_in_template(<<"###REPLACEWITHNOTE###", Rest/binary >>, Tokens) -> | insert_tokens_in_template(Rest, Tokens)]; insert_tokens_in_template(<>, Tokens) -> [B|insert_tokens_in_template(Rest, Tokens)]. - - - -- 2.30.2 From 0271b2336e22a04ef47b85c0abc6524f4d6ccf8e Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 27 Feb 2019 11:55:53 +0100 Subject: [PATCH 14/19] Generate the code from the makefile. Remove generated files. --- Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Makefile b/Makefile index 071e04c..e9d9d08 100644 --- a/Makefile +++ b/Makefile @@ -19,8 +19,6 @@ clean: dialyzer: local @$(REBAR) as local dialyzer - - distclean: clean @rm -rf _build/ @@ -30,7 +28,6 @@ eunit: local test: local @$(REBAR) as local eunit - ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin erlc -o $(dir $@) $< -- 2.30.2 From d7ef24fe371a54331b7a06d08c0ca6bdffef10eb Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Wed, 27 Feb 2019 15:25:57 +0100 Subject: [PATCH 15/19] Test targets and cleanup. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index e9d9d08..bfaadcc 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,7 @@ eunit: local test: local @$(REBAR) as local eunit + ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin erlc -o $(dir $@) $< -- 2.30.2 From 933abb6d7c3868cd70876542427f6ffea3b26fd0 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 28 Feb 2019 11:18:49 +0100 Subject: [PATCH 16/19] Generate op pretty printer. --- .gitignore | 1 + Makefile | 8 +- src/aeb_fate_asm.erl | 149 +--------------------------------- src/aeb_fate_generate_ops.erl | 105 +++++++++++++++++++++++- 4 files changed, 112 insertions(+), 151 deletions(-) diff --git a/.gitignore b/.gitignore index cccd365..1887839 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ aefateasm include/aeb_fate_opcodes.hrl src/aeb_fate_code.erl src/aeb_fate_opcodes.erl +src/aeb_fate_pp.erl diff --git a/Makefile b/Makefile index bfaadcc..e255d7a 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ REBAR ?= rebar3 all: local -local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl +local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl @$(REBAR) as local release console: local @@ -14,8 +14,12 @@ clean: @$(REBAR) clean rm -f src/aeb_fate_opcodes.erl rm -f src/aeb_fate_code.erl + rm -f src/aeb_fate_asm_scan.xrl + rm -f src/aeb_fate_asm_scan.erl + rm -f src/aeb_fate_pp.erl rm -f include/aeb_fate_opcodes.hrl + dialyzer: local @$(REBAR) as local dialyzer @@ -32,7 +36,7 @@ test: local ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin erlc -o $(dir $@) $< -src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl: ebin/aeb_fate_generate_ops.beam +src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl: ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/ ebin: diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 5e320ef..bc96684 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -180,157 +180,10 @@ format_code([], _) -> ""; format_code([Op|Rest], Symbols) -> [" ", - format_op(Op, Symbols), + aeb_fate_pp:format_op(Op, Symbols), "\n", format_code(Rest, Symbols)]. -format_op('RETURN', _) -> "RETURN"; -format_op({'RETURNR', Arg}, _) -> ["RETURNR ", format_arg(Arg)]; -format_op({'CALL', {immediate, Function}}, Symbols) -> - ["CALL ", lookup(Function, Symbols)]; -format_op({'CALL_T', {immediate, Function}}, Symbols) -> - ["CALL_T ", lookup(Function, Symbols)]; -format_op({'CALL_R', {immediate, Contract}, {immediate, Function}}, Symbols) -> - ["CALL_R ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)]; -format_op({'CALL_R', Contract, {immediate, Function}}, Symbols) -> - ["CALL_R ", format_arg(Contract), "." , lookup(Function, Symbols)]; -format_op({'CALL_TR', {immediate, Contract}, {immediate, Function}}, Symbols) -> - ["CALL_TR ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)]; -format_op({'CALL_TR', Contract, {immediate, Function}}, Symbols) -> - ["CALL_TR ", format_arg(Contract), "." , lookup(Function, Symbols)]; -format_op({'JUMP', {immediate, BB}}, _) -> - ["JUMP ", io_lib:format("~p", [BB])]; -format_op({'JUMPIF', Arg, {immediate, BB}}, _) -> - ["JUMPIF ", format_arg(Arg), " ", io_lib:format("~p", [BB])]; -format_op({'SWITCH_V2', Variant, {immediate, BB1}, {immediate, BB2}}, _) -> - ["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2]; -format_op({'SWITCH_V3', Variant, {immediate, BB1}, {immediate, BB2}, {immediate, BB3}}, _) -> - ["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2, " ", BB3]; -format_op({'SWITCH_VN', Variant, BBs}, _) -> - ["SWITCH_VN ", format_arg(Variant), [[" ", BB] || {immedate, BB} <- BBs]]; -format_op({'PUSH', Arg0}, _) -> - ["PUSH ", format_arg(Arg0)]; -format_op('INCA', _) -> "INCA"; -format_op({'INC', Name}, _) -> ["INC ", format_arg(Name)]; -format_op({'DEC', Name}, _) -> ["DEC ", format_arg(Name)]; -format_op('DECA', _) -> "DECA"; -format_op({'ADD', Dest, Left, Right}, _) -> - ["ADD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'SUB', Dest, Left, Right}, _) -> - ["SUB ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'MUL', Dest, Left, Right}, _) -> - ["MUL ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'DIV', Dest, Left, Right}, _) -> - ["DIV ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'MOD', Dest, Left, Right}, _) -> - ["MOD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'POW', Dest, Left, Right}, _) -> - ["POW ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'LT', Dest, Left, Right}, _) -> - ["LT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'GT', Dest, Left, Right}, _) -> - ["GT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'ELT', Dest, Left, Right}, _) -> - ["ELT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'EGT', Dest, Left, Right}, _) -> - ["EGT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'EQ', Dest, Left, Right}, _) -> - ["EQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'NEQ', Dest, Left, Right}, _) -> - ["NEQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'AND', Dest, Left, Right}, _) -> - ["AND ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'OR', Dest, Left, Right}, _) -> - ["OR ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)]; -format_op({'NOT', Dest, Name}, _) -> - ["NOT ", format_arg(Dest), " ", format_arg(Name)]; -format_op({'TUPLE', {immediate, Size}}, _) -> - ["TUPLE ", io_lib:format("~p", [Size])]; -format_op({'ELEMENT', Type, Dest, Which, Tuple}, _) -> - [ "ELEMENT " - , io_lib:format("~p ", [Type]) - , format_arg(Dest), " " - , format_arg(Which), " " - , format_arg(Tuple)]; -format_op({'MAP_EMPTY', Dest}, _) -> - ["MAP_EMPTY ", format_arg(Dest)]; -format_op({'MAP_LOOKUP', Dest, Map, Key}, _) -> - ["MAP_LOOKUP ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key)]; -format_op({'MAP_DELETE', Dest, Map, Key}, _) -> - ["MAP_DELETE ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key)]; -format_op({'MAP_LOOKUPD', Dest, Map, Key, Default}, _) -> - ["MAP_LOOKUPD ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key), " ", format_arg(Default)]; -format_op({'MAP_UPDATE', Dest, Map, Key, Value}, _) -> - ["MAP_UPDATE ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key), " ", format_arg(Value)]; -format_op({'MAP_MEMBER', Dest, Map, Key}, _) -> - ["MAP_MEMBER ", format_arg(Dest), " " - , format_arg(Map), " ", format_arg(Key)]; -format_op({'MAP_FROM_LIST', Dest, List}, _) -> - ["MAP_FROM_LIST ", format_arg(Dest), " ", format_arg(List)]; -format_op({'NIL', Dest}, _) -> - ["NIL ", format_arg(Dest)]; -format_op({'IS_NIL', Dest, List}, _) -> - ["IS_NIL ", format_arg(Dest), " ", format_arg(List)]; -format_op({'CONS', Dest, Hd, Tl}, _) -> - ["CONS ", format_arg(Dest), " ", format_arg(Hd), " ", format_arg(Tl)]; -format_op({'HD', Dest, List}, _) -> - ["HD ", format_arg(Dest), " ", format_arg(List)]; -format_op({'TL', Dest, List}, _) -> - ["TL ", format_arg(Dest), " ", format_arg(List)]; -format_op({'LENGTH', Dest, List}, _) -> - ["LENGTH ", format_arg(Dest), " ", format_arg(List)]; -format_op({'STR_EQ', Dest, Str1, Str2}, _) -> - ["STR_EQ ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)]; -format_op({'STR_JOIN', Dest, Str1, Str2}, _) -> - ["STR_JOIN ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)]; -format_op({'INT_TO_STR', Dest, Str}, _) -> - ["INT_TO_STR ", format_arg(Dest), " ", format_arg(Str)]; -format_op({'ADDR_TO_STR', Dest, Str}, _) -> - ["ADDR_TO_STR ", format_arg(Dest), " ", format_arg(Str)]; -format_op({'STR_REVERSE', Dest, Str}, _) -> - ["STR_REVERSE ", format_arg(Dest), " ", format_arg(Str)]; -format_op({'INT_TO_ADDR', Dest, Str}, _) -> - ["INT_TO_ADDR ", format_arg(Dest), " ", format_arg(Str)]; -format_op({'VARIANT_TEST', Dest, Variant, Tag}, _) -> - ["VARIANT_TEST ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Tag)]; -format_op({'VARIANT_ELEMENT', Dest, Variant, Index}, _) -> - ["VARIANT_ELEMENT ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Index)]; -format_op({'VARIANT', Dest, SizeA, TagA, ElementsA}, _) -> - ["VARIANT ", format_arg(Dest), " ", format_arg(SizeA), " " - , format_arg(TagA), " ", format_arg(ElementsA)]; -format_op('BITS_NONEA', _) -> "BITS_NONEA "; -format_op({'BITS_NONE', To}, _) -> ["BITS_NONE ", format_arg(To)]; -format_op('BITS_ALLA', _) -> "BITS_ALLA"; -format_op({'BITS_ALL', To}, _) -> ["BITS_ALL ", format_arg(To)]; -format_op({'BITS_ALL_N', To, N}, _) -> - ["BITS_ALL_N ", format_arg(To), " ", format_arg(N)]; -format_op({'BITS_SET', To, Bits, Bit}, _) -> - ["BITS_SET ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_CLEAR', To, Bits, Bit}, _) -> - ["BITS_CLEAR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_TEST', To, Bits, Bit}, _) -> - ["BITS_TEST ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_SUM', To, Bits}, _) -> - ["BITS_SUM ", format_arg(To), " ", format_arg(Bits)]; -format_op({'BITS_OR', To, Bits, Bit}, _) -> - ["BITS_OR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_AND', To, Bits, Bit}, _) -> - ["BITS_AND ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op({'BITS_DIFF', To, Bits, Bit}, _) -> - ["BITS_DIFF ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)]; -format_op('DUPA', _) -> "DUPA"; -format_op({'DUP', {immediate, N}}, _) -> - ["DUP ", io_lib:format("~p", [N])]; -format_op({'POP', Dest}, _) -> - ["POP ", format_arg(Dest)]; -format_op({'STORE', Var, What}, _) -> - ["STORE ", format_arg(Var), " ", format_arg(What)]; -format_op('NOP', _) -> "NOP". - read_file(Filename) -> {ok, File} = file:read_file(Filename), diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 5c09d77..432710f 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -18,7 +18,8 @@ generate(Src, Include) -> generate_header_file(HrlFile, Ops), generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops), generate_code_ops(aeb_fate_code, Src, Ops), - generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops). + generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops), + gen_asm_pp(aeb_fate_pp, Src, Ops). %% TODO: Some real gas numbers... ops_defs() -> @@ -397,3 +398,105 @@ insert_tokens_in_template(<<"###REPLACEWITHNOTE###", Rest/binary >>, Tokens) -> | insert_tokens_in_template(Rest, Tokens)]; insert_tokens_in_template(<>, Tokens) -> [B|insert_tokens_in_template(Rest, Tokens)]. + +gen_asm_pp(Module, Path, Ops) -> + Filename = filename:join(Path, atom_to_list(Module)) ++ ".erl", + {ok, File} = file:open(Filename, [write]), + Formats = lists:flatten([gen_format(Op)++"\n" || Op <- Ops]), + + io:format(File, "~s", [prelude(" Provide pretty printing functuions for " + "Fate instructions.\n")]), + io:format(File, "-module(~w).\n\n", [Module]), + io:format(File, + "-export([format_op/2]).\n\n" + "format_arg(t, T) ->\n" + " io_lib:format(\"~~p \", [T]);\n" + "format_arg(li, List) ->\n" + " [[\" \", E] || {immedate, E} <- List];\n" + "format_arg(_, {immediate, I}) ->\n" + " aeb_fate_data:format(I);\n" + "format_arg(a, {arg, N}) -> io_lib:format(\"arg~~p\", [N]);\n" + "format_arg(a, {var, N}) -> io_lib:format(\"var~~p\", [N]);\n" + "format_arg(a, {stack, 0}) -> \"a\";\n" + "format_arg(a, {stack, N}) -> io_lib:format(\"a~~p\", [N]).\n\n" + "lookup(Name, Symbols) ->\n" + " maps:get(Name, Symbols, Name).\n\n" + "~s" + , [Formats]), + + io:format(File, "format_op(Op, _Symbols) -> io_lib:format(\";; Bad Op: ~~w\\n\", [Op]).\n", []), + file:close(File). + +gen_format(#{opname := Name}) when ('CALL' =:= Name) or (Name =:= 'CALL_T') -> + io_lib:format("format_op({~w, {immediate, Function}}, Symbols) ->\n" + "[\"~s \", lookup(Function, Symbols)];", + [Name, atom_to_list(Name)]); +gen_format(#{opname := Name}) when (Name =:= 'CALL_R') or (Name =:= 'CALL_TR') -> + io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}}, Symbols) ->\n" + "[\"~s \", lookup(Contract, Symbols), \".\", lookup(Function, Symbols)];\n" + "format_op({~w, Contract, {immediate, Function}}, Symbols) ->\n" + "[\"~s \", format_arg(a, Contract), \".\", lookup(Function, Symbols)];", + [Name, atom_to_list(Name), Name, atom_to_list(Name)]); +gen_format(#{opname := Name, format := atomic}) -> + io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]); +gen_format(#{opname := Name, format := Args}) -> + NameAsString = atom_to_list(Name), + case Args of + [T0] -> + io_lib:format( + "format_op({~w, Arg0}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0)];", + [Name, NameAsString, T0]); + [T0, T1] -> + io_lib:format( + "format_op({~w, Arg0, Arg1}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)];", + [Name, NameAsString, T0, T1]); + [T0, T1, T2] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)];", + [Name, NameAsString, T0, T1, T2]); + [T0, T1, T2, T3] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)];", + [Name, NameAsString, T0, T1, T2, T3]); + [T0, T1, T2, T3, T4] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)," + "\" \", format_arg(~w, Arg4)];", + [Name, NameAsString, T0, T1, T2, T3, T4]); + [T0, T1, T2, T3, T4, T5] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)," + "\" \", format_arg(~w, Arg4)," + "\" \", format_arg(~w, Arg5)];", + [Name, NameAsString, T0, T1, T2, T3, T4, T5]); + [T0, T1, T2, T3, T4, T5, T6] -> + io_lib:format( + "format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6}, _) ->\n" + " [\"~s \", format_arg(~w, Arg0), " + "\" \", format_arg(~w, Arg1)," + "\" \", format_arg(~w, Arg2)," + "\" \", format_arg(~w, Arg3)," + "\" \", format_arg(~w, Arg4)," + "\" \", format_arg(~w, Arg5)," + "\" \", format_arg(~w, Arg6)];", + [Name, NameAsString, T0, T1, T2, T3, T4, T5, T6]) + end. + -- 2.30.2 From 37a6a5ca74231476c3cfecdfcfccc1a5b51c2f4a Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 28 Feb 2019 11:52:47 +0100 Subject: [PATCH 17/19] Removed unused function. --- src/aeb_fate_asm.erl | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index bc96684..9aea5ad 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -2,6 +2,21 @@ %%% @copyright (C) 2019, Aeternity Anstalt %%% @doc Assembler for Fate machine code. %%% +%%% Fate code exists in 3 formats: +%%% +%%% 1. Fate byte code. This format is under consensus. +%%% 2. Fate assembler. This is a text represenation of fate code. +%%% This is not under consensus and other +%%% implemenation and toolchains could have +%%% their own format. +%%% 3. Internal. This is an Erlang representation of fate code +%%% Used by this particular engin implementation. +%%% +%%% This library handles all tree representations. +%%% The byte code format is described in a separate document. +%%% The internal format is described in a separate document. +%%% The text representation is described here: +%%% %%% Assembler code can be read from a file. %%% The assembler has the following format %%% Comments start with 2 semicolons and runs till end of line @@ -157,14 +172,6 @@ format_arg_types([T|Ts]) -> , ", " , format_arg_types(Ts)]. -format_arg({immediate, I}) -> - aeb_fate_data:format(I); -format_arg({arg, N}) -> io_lib:format("arg~p", [N]); -format_arg({var, N}) -> io_lib:format("var~p", [N]); -format_arg({stack, 0}) -> "a"; -format_arg({stack, N}) -> io_lib:format("a~p", [N]). - - format_type(T) -> %% TODO: Limit to ok types. io_lib:format("~p", [T]). -- 2.30.2 From 1c7436edab10787fa728ffae36482164024fbbee Mon Sep 17 00:00:00 2001 From: Dincho Todorov Date: Thu, 28 Feb 2019 15:52:00 +0200 Subject: [PATCH 18/19] Polish Makefile file references (#11) --- Makefile | 23 +++++++---------------- ebin/.gitkeep | 0 2 files changed, 7 insertions(+), 16 deletions(-) create mode 100644 ebin/.gitkeep diff --git a/Makefile b/Makefile index e255d7a..916426e 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,10 @@ - - +GENERATED_SRC = src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl +GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template REBAR ?= rebar3 all: local -local: src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl +local: $(GENERATED_SRC) @$(REBAR) as local release console: local @@ -12,13 +12,8 @@ console: local clean: @$(REBAR) clean - rm -f src/aeb_fate_opcodes.erl - rm -f src/aeb_fate_code.erl - rm -f src/aeb_fate_asm_scan.xrl - rm -f src/aeb_fate_asm_scan.erl - rm -f src/aeb_fate_pp.erl - rm -f include/aeb_fate_opcodes.hrl - + rm -f $(GENERATED_SRC) + rm -f ebin/* dialyzer: local @$(REBAR) as local dialyzer @@ -32,12 +27,8 @@ eunit: local test: local @$(REBAR) as local eunit - -ebin/aeb_fate_generate_ops.beam: src/aeb_fate_generate_ops.erl ebin +ebin/%.beam: src/%.erl erlc -o $(dir $@) $< -src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl: ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template +$(GENERATED_SRC): $(GENERATOR_DEPS) erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/ - -ebin: - mkdir ebin diff --git a/ebin/.gitkeep b/ebin/.gitkeep new file mode 100644 index 0000000..e69de29 -- 2.30.2 From d78b78e4945bd249142dbd9d0a77de800c9b57c6 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 28 Feb 2019 17:52:00 +0100 Subject: [PATCH 19/19] Parse all types of values except variants. --- src/aeb_fate_asm.erl | 77 +++++++++++++++++++++++++++++++--- src/aeb_fate_asm_scan.template | 26 ++++++++++-- src/aeb_fate_data.erl | 4 +- src/aeb_fate_encoding.erl | 2 +- test/aeb_fate_asm_test.erl | 3 ++ test/asm_code/immediates.fate | 68 ++++++++++++++++++++++++++++++ test/asm_code/mapofmap.fate | 7 ++++ 7 files changed, 175 insertions(+), 12 deletions(-) create mode 100644 test/asm_code/immediates.fate create mode 100644 test/asm_code/mapofmap.fate diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index 9aea5ad..5606894 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -48,14 +48,15 @@ %%% false %%% 5. Strings %%% "Hello" -%%% 6. Empty map +%%% 6. Map %%% {} +%%% { 1 => { "foo" => true, "bar" => false} %%% 7. Lists %%% [] %%% [1, 2] %%% 8. Bit field %%% <000> -%%% <1010> +%%% <1010 1010> %%% <> %%% !<> %%% 9. Tuples @@ -569,7 +570,11 @@ deserialize_type(<<5, Rest/binary>>) -> {bits, Rest}; deserialize_type(<<6, Rest/binary>>) -> {K, Rest2} = deserialize_type(Rest), {V, Rest3} = deserialize_type(Rest2), - {{map, K, V}, Rest3}. + {{map, K, V}, Rest3}; +deserialize_type(<<7, Rest/binary>>) -> + {string, Rest}. + + deserialize_types(0, Binary, Acc) -> {lists:reverse(Acc), Binary}; @@ -616,6 +621,19 @@ to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) -> to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> {Hash, Env2} = insert_symbol(ID, Env), to_bytecode(Rest, Address, Env2, [{immediate, Hash}|Code], Opts); +to_bytecode([{'{',_line}|Rest], Address, Env, Code, Opts) -> + {Map, Rest2} = parse_map(Rest), + to_bytecode(Rest2, Address, Env, [{immediate, Map}|Code], Opts); +to_bytecode([{'[',_line}|Rest], Address, Env, Code, Opts) -> + {List, Rest2} = parse_list(Rest), + to_bytecode(Rest2, Address, Env, [{immediate, List}|Code], Opts); +to_bytecode([{'(',_line}|Rest], Address, Env, Code, Opts) -> + {Elements, Rest2} = parse_tuple(Rest), + Tuple = aeb_fate_data:make_tuple(list_to_tuple(Elements)), + to_bytecode(Rest2, Address, Env, [{immediate, Tuple}|Code], Opts); +to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{immediate, Bits}|Code], Opts); + to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) -> Env2 = insert_annotation(comment, Line, Comment, Env), to_bytecode(Rest, Address, Env2, Code, Opts); @@ -632,6 +650,51 @@ to_bytecode([], Address, Env, Code, Opts) -> end, Env2. +parse_map([{'}',_line}|Rest]) -> + {#{}, Rest}; +parse_map(Tokens) -> + {Key, [{arrow, _} | Rest]} = parse_value(Tokens), + {Value, Rest2} = parse_value(Rest), + case Rest2 of + [{',',_} | Rest3] -> + {Map, Rest4} = parse_map(Rest3), + {Map#{Key => Value}, Rest4}; + [{'}',_} | Rest3] -> + {#{Key => Value}, Rest3} + end. + +parse_list([{']',_line}|Rest]) -> + {[], Rest}; +parse_list(Tokens) -> + {Head , Rest} = parse_value(Tokens), + case Rest of + [{',',_} | Rest2] -> + {Tail, Rest3} = parse_list(Rest2), + {[Head | Tail], Rest3}; + [{']',_} | Rest3] -> + {[Head], Rest3} + end. + +parse_tuple([{')',_line}|Rest]) -> + {[], Rest}; +parse_tuple(Tokens) -> + {Head , Rest} = parse_value(Tokens), + case Rest of + [{',',_} | Rest2] -> + {Tail, Rest3} = parse_tuple(Rest2), + {[Head | Tail], Rest3}; + [{')',_} | Rest3] -> + {[Head], Rest3} + end. + + +parse_value([{int,_line, Int} | Rest]) -> {Int, Rest}; +parse_value([{boolean,_line, Bool} | Rest]) -> {Bool, Rest}; +parse_value([{hash,_line, Hash} | Rest]) -> {Hash, Rest}; +parse_value([{'{',_line} | Rest]) -> parse_map(Rest); +parse_value([{'[',_line} | Rest]) -> parse_list(Rest); +parse_value([{'(',_line} | Rest]) -> parse_tuple(Rest). + to_fun_def([{id, _, Name}, {'(', _} | Rest]) -> {ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest), @@ -689,9 +752,11 @@ serialize_type({tuple, Ts}) -> N when N =< 255 -> [3, N | [serialize_type(T) || T <- Ts]] end; -serialize_type(address) -> 4; -serialize_type(bits) -> 5; -serialize_type({map, K, V}) -> [6 | serialize_type(K) ++ serialize_type(V)]. +serialize_type(address) -> [4]; +serialize_type(bits) -> [5]; +serialize_type({map, K, V}) -> [6 | serialize_type(K) ++ serialize_type(V)]; +serialize_type(string) -> [7]. + %% ------------------------------------------------------------------- diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template index f894aa5..fa4d49d 100644 --- a/src/aeb_fate_asm_scan.template +++ b/src/aeb_fate_asm_scan.template @@ -17,7 +17,8 @@ HEX = 0x{HEXDIGIT}+ HASH = #{HEXDIGIT}+ WS = [\000-\s] ID = {LOWER}[a-zA-Z0-9_]* - +STRING = "[^"]*" +BITS = (\!)?\<[\s01]*\> Rules. arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. @@ -38,13 +39,22 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. {token, {int, TokenLine, parse_hex(TokenChars)}}. {INT} : {token, {int, TokenLine, parse_int(TokenChars)}}. +-{INT} : + {token, {int, TokenLine, parse_int(TokenChars)}}. {HASH} : {token, {hash, TokenLine, parse_hash(TokenChars)}}. +{STRING} : + {token, {hash, TokenLine, list_to_binary(TokenChars)}}. +{BITS} : + {token, {bits, TokenLine, bits(TokenChars)}}. %% Symbols -\-\> : {token, {'to', TokenLine}}. -\: : {token, {'to', TokenLine}}. +\-\> : {token, {to, TokenLine}}. +\: : {token, {to, TokenLine}}. + +\=\> : {token, {arrow, TokenLine}}. + , : {token, {',', TokenLine}}. \( : {token, {'(', TokenLine}}. \) : {token, {')', TokenLine}}. @@ -97,3 +107,13 @@ scan(S) -> drop_prefix(C, [C|Rest]) -> drop_prefix(C, Rest); drop_prefix(_, Tail) -> Tail. + +bits([$!, $< | Rest]) -> + bits(Rest, -1); +bits([$< | Rest]) -> + bits(Rest, 0). + +bits([$> |_Rest], Acc) -> Acc; +bits([$0 | Rest], Acc) -> bits(Rest, Acc bsl 1); +bits([$1 | Rest], Acc) -> bits(Rest, (Acc bsl 1) bor 1); +bits([$ | Rest], Acc) -> bits(Rest, Acc). diff --git a/src/aeb_fate_data.erl b/src/aeb_fate_data.erl index e5404dd..3037d64 100644 --- a/src/aeb_fate_data.erl +++ b/src/aeb_fate_data.erl @@ -23,7 +23,7 @@ | {map, fate_type(), fate_type()} | {tuple, [fate_type()]} | address - | bits + | bits | {variant, integer()}. @@ -147,7 +147,7 @@ format(M) when ?IS_FATE_MAP(M) -> format(?FATE_ADDRESS(Address)) -> ["#", address_to_base58(Address)]; format(V) -> exit({not_a_fate_type, V}). -format_list(List) -> +format_list(List) -> ["[ ", lists:join(", ", [format(E) || E <- List]), " ]"]. format_kvs(List) -> diff --git a/src/aeb_fate_encoding.erl b/src/aeb_fate_encoding.erl index 9bdadbb..d0e5c12 100644 --- a/src/aeb_fate_encoding.erl +++ b/src/aeb_fate_encoding.erl @@ -128,7 +128,7 @@ serialize(L) when ?IS_FATE_LIST(L) -> <> end; serialize(Map) when ?IS_FATE_MAP(Map) -> - L = [{_K,_V}|_] = maps:to_list(?FATE_MAP_VALUE(Map)), + L = [{_K,_V}|_] = lists:sort(maps:to_list(?FATE_MAP_VALUE(Map))), Size = length(L), %% TODO: check all K same type, and all V same type %% check K =/= map diff --git a/test/aeb_fate_asm_test.erl b/test/aeb_fate_asm_test.erl index dd32af6..fc20dbd 100644 --- a/test/aeb_fate_asm_test.erl +++ b/test/aeb_fate_asm_test.erl @@ -50,6 +50,8 @@ sources() -> , "remote" , "test" , "tuple" + , "mapofmap" + , "immediates" ]. check_roundtrip(File) -> @@ -62,4 +64,5 @@ check_roundtrip(File) -> {_Env2, ByteCode2} = assemble(DissasmCode), Code1 = aeb_fate_asm:strip(ByteCode), Code2 = aeb_fate_asm:strip(ByteCode2), + io:format("~s~n", [aeb_fate_asm:to_asm(disassemble(ByteCode2))]), ?assertEqual(Code1, Code2). diff --git a/test/asm_code/immediates.fate b/test/asm_code/immediates.fate new file mode 100644 index 0000000..d3a0077 --- /dev/null +++ b/test/asm_code/immediates.fate @@ -0,0 +1,68 @@ +;; CONTRACT immediates + +FUNCTION integer() : integer + RETURNR 42 + +FUNCTION neg_integer() : integer + RETURNR -2374683271468723648732648736498712634876147 + +FUNCTION hex_integer() : integer + RETURNR 0x0deadbeef0 + +FUNCTION bool() : boolean + RETURNR true + +FUNCTION bool_f() : boolean + RETURNR false + +FUNCTION string() : string + RETURNR "Hello" + +FUNCTION map() : {map, integer, boolean} + RETURNR {} + +FUNCTION map2() : {map, integer, boolean} + RETURNR {1 => true} + +FUNCTION map3() : {map, integer, boolean} + RETURNR {1 => true, + 2 => false} + +FUNCTION map4() : {map, integer, {map, string, boolean}} + RETURNR {1 => { "foo" => true, "bar" => false}, + 2 => {}, + 3 => { "foo" => false}} + +FUNCTION nil() : {list, integer} + RETURNR [] + +FUNCTION list1() : {list, integer} + RETURNR [1] + +FUNCTION list2() : {list, integer} + RETURNR [1, 2] + + +FUNCTION no_bits() : bits + RETURNR <> + +FUNCTION all_bits() : bits + RETURNR !<> + +FUNCTION some_bits() : bits + RETURNR <101010> + +FUNCTION many_bits() : bits + RETURNR !<010101> + +FUNCTION group_bits() : bits + RETURNR <1010 1010 0011 1001> + +FUNCTION unit() : {tuple, []} + RETURNR () + +FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]}]} + RETURNR (42, true, "FooBar", (1, 2)) + + + diff --git a/test/asm_code/mapofmap.fate b/test/asm_code/mapofmap.fate new file mode 100644 index 0000000..bbda554 --- /dev/null +++ b/test/asm_code/mapofmap.fate @@ -0,0 +1,7 @@ +;; CONTRACT mapofmap +FUNCTION map() : {map, integer, {map, string, boolean}} + RETURNR {1 => { "foo" => true, "bar" => false}, + 2 => {}, + 3 => { "foo" => false}} + + -- 2.30.2