From 8aed4d5cc6aef5196c2a933cf152dd2a06d23cb5 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 23 May 2019 11:44:04 +0200 Subject: [PATCH 1/2] Get rid of redundant arity field from op defs. Reorder and renumber ops. Fix bb_end for abort and exit. --- src/aeb_fate_generate_ops.erl | 259 ++++++++++++++-------------- test/asm_code/all_instructions.fate | 4 - 2 files changed, 129 insertions(+), 134 deletions(-) diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index e2068d1..0fa7b0f 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -27,131 +27,129 @@ generate(Src, Include) -> %% 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. 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, [a], call, "Call the function Arg0 with args on stack. The types of the arguments has to match the argument typs of the function."} - , { 'CALL_R', 16#03, 3, true, 8, [a,is,a], call_r, "Remote call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function."} - , { 'CALL_T', 16#04, 1, true, 4, [a], call_t, "Tail call to function Arg0. 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, 3, true, 8, [a,is,a], call_tr, "Remote tail call to contract Arg0 and function Arg1 with value Arg2. 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."} - , { '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, 3, false, 3, [a,a,a], element_op, "Arg1 := element(Arg2, Arg3)."} - , { '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."} - , { 'APPEND', 16#38, 3, false, 3, [a,a,a], append, "Arg0 := Arg1 ++ Arg2."} - , { '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 balance."} - , { '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, 2, false, 3, [a, a], blockhash, "Arg0 := The blockhash 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."} - , { '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."} + %% Opname, Opcode, end_bb, gas, format, Constructor, Documentation + [ { 'RETURN', 16#00, true, 2, [], 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, 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, true, 4, [a], call, "Call the function Arg0 with args on stack. The types of the arguments has to match the argument typs of the function."} + , { 'CALL_R', 16#03, true, 8, [a,is,a], call_r, "Remote call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function."} + , { 'CALL_T', 16#04, true, 4, [a], call_t, "Tail call to function Arg0. 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, true, 8, [a,is,a], call_tr, "Remote tail call to contract Arg0 and function Arg1 with value Arg2. 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_GR', 16#06, true, 8, [a,is,a,a], call_gr, "Remote call with gas cap in Arg3. Otherwise as CALL_R."} + , { 'CALL_GTR', 16#07, true, 8, [a,is,a,a], call_gtr, "Remote tail call with gas cap in Arg3. Otherwise as CALL_TR."} + , { 'JUMP', 16#08, true, 3, [ii], jump, "Jump to a basic block. The basic block has to exist in the current function."} + , { 'JUMPIF', 16#09, true, 4, [a,ii], jumpif, "Conditional jump to a basic block. If Arg0 then jump to Arg1."} + , { 'SWITCH_V2', 16#0a, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_V3', 16#0b, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."} + , { 'SWITCH_VN', 16#0c, true, 4, [a, li], switch, "Conditional jump to a basic block on variant tag."} + , { 'CALL_VALUE', 16#0d, false, 3, [a], call_value, "The value sent in the current remote call."} + , { 'PUSH', 16#0e, false, 2, [a], push, "Push argument to stack."} + , { 'DUPA', 16#0f, false, 3, [], dup, "push copy of accumulator on stack."} + , { 'DUP', 16#10, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} + , { 'POP', 16#11, false, 3, [a], pop, "Arg0 := top of stack."} + , { 'INCA', 16#12, false, 2, [], inc, "Increment accumulator."} + , { 'INC', 16#13, false, 2, [a], inc, "Increment argument."} + , { 'DECA', 16#14, false, 2, [], dec, "Decrement accumulator."} + , { 'DEC', 16#15, false, 2, [a], dec, "Decrement argument."} + , { 'ADD', 16#16, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."} + , { 'SUB', 16#17, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."} + , { 'MUL', 16#18, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."} + , { 'DIV', 16#19, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."} + , { 'MOD', 16#1a, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."} + , { 'POW', 16#1b, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."} + , { 'STORE', 16#1c, false, 3, [a,a], store, "Arg0 := Arg1."} + , { 'SHA3', 16#1d, false, 3, [], sha3, ""} + , { 'SHA256', 16#1e, false, 3, [], sha256, ""} + , { 'BLAKE2B', 16#1f, false, 3, [], blake2b, ""} + , { 'LT', 16#20, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."} + , { 'GT', 16#21, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."} + , { 'EQ', 16#22, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."} + , { 'ELT', 16#23, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."} + , { 'EGT', 16#24, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."} + , { 'NEQ', 16#25, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."} + , { 'AND', 16#26, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."} + , { 'OR', 16#27, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} + , { 'NOT', 16#28, false, 3, [a,a], not_op, "Arg0 := not Arg1."} + , { 'TUPLE', 16#29, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} + , { 'ELEMENT', 16#2a, false, 3, [a,a,a], element_op, "Arg1 := element(Arg2, Arg3)."} + , { 'MAP_EMPTY', 16#2b, false, 3, [a], map_empty, "Arg0 := #{}."} + , { 'MAP_LOOKUP', 16#2c, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} + , { 'MAP_LOOKUPD', 16#2d, 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, false, 3, [a,a,a,a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} + , { 'MAP_DELETE', 16#2f, false, 3, [a,a,a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} + , { 'MAP_MEMBER', 16#30, false, 3, [a,a,a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} + , { 'MAP_FROM_LIST', 16#31, false, 3, [a,a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."} + , { 'NIL', 16#32, false, 3, [a], nil, "Arg0 := []."} + , { 'IS_NIL', 16#33, false, 3, [a,a], is_nil, "Arg0 := true if Arg1 == []."} + , { 'CONS', 16#34, false, 3, [a,a,a], cons, "Arg0 := [Arg1|Arg2]."} + , { 'HD', 16#35, false, 3, [a,a], hd, "Arg0 := head of list Arg1."} + , { 'TL', 16#36, false, 3, [a,a], tl, "Arg0 := tail of list Arg1."} + , { 'LENGTH', 16#37, false, 3, [a,a], length, "Arg0 := length of list Arg1."} + , { 'APPEND', 16#38, false, 3, [a,a,a], append, "Arg0 := Arg1 ++ Arg2."} + , { 'STR_JOIN', 16#39, false, 3, [a,a,a], str_join, "Arg0 := string Arg1 followed by string Arg2."} + , { 'INT_TO_STR', 16#3a, false, 3, [a,a], int_to_str, "Arg0 := turn integer Arg1 into a string."} + , { 'ADDR_TO_STR', 16#3b, false, 3, [a,a], addr_to_str, "Arg0 := turn address Arg1 into a string."} + , { 'STR_REVERSE', 16#3c, false, 3, [a,a], str_reverse, "Arg0 := the reverse of string Arg1."} + , { 'INT_TO_ADDR', 16#3d, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} + , { 'VARIANT', 16#3e, 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#3f, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."} + , { 'VARIANT_ELEMENT', 16#40, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."} + , { 'BITS_NONEA', 16#41, false, 3, [], bits_none, "accumulator := empty bitmap."} + , { 'BITS_NONE', 16#42, false, 3, [a], bits_none, "Arg0 := empty bitmap."} + , { 'BITS_ALLA', 16#43, false, 3, [], bits_all, "accumulator := full bitmap."} + , { 'BITS_ALL', 16#44, false, 3, [a], bits_all, "Arg0 := full bitmap."} + , { 'BITS_ALL_N', 16#45, false, 3, [a,a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."} + , { 'BITS_SET', 16#46, false, 3, [a,a,a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."} + , { 'BITS_CLEAR', 16#47, false, 3, [a,a,a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."} + , { 'BITS_TEST', 16#48, false, 3, [a,a,a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."} + , { 'BITS_SUM', 16#49, false, 3, [a,a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."} + , { 'BITS_OR', 16#4a, false, 3, [a,a,a], bits_or, "Arg0 := Arg1 v Arg2."} + , { 'BITS_AND', 16#4b, false, 3, [a,a,a], bits_and, "Arg0 := Arg1 ^ Arg2."} + , { 'BITS_DIFF', 16#4c, false, 3, [a,a,a], bits_diff, "Arg0 := Arg1 - Arg2."} + , { 'ADDRESS', 16#4d, false, 3, [a], address, "Arg0 := The current contract address."} + , { 'BALANCE', 16#4e, false, 3, [a], balance, "Arg0 := The current contract balance."} + , { 'ORIGIN', 16#4f, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."} + , { 'CALLER', 16#50, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."} + , { 'GASPRICE', 16#51, false, 3, [a], gasprice, "Arg0 := The current gas price."} + , { 'BLOCKHASH', 16#52, false, 3, [a,a], blockhash, "Arg0 := The blockhash at height."} + , { 'BENEFICIARY', 16#53, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} + , { 'TIMESTAMP', 16#54, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} + , { 'GENERATION', 16#55, false, 3, [a], generation, "Arg0 := The block height of the cureent generation."} + , { 'MICROBLOCK', 16#56, false, 3, [a], microblock, "Arg0 := The current micro block number."} + , { 'DIFFICULTY', 16#57, false, 3, [a], difficulty, "Arg0 := The current difficulty."} + , { 'GASLIMIT', 16#58, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} + , { 'GAS', 16#59, false, 3, [a], gas, "Arg0 := The amount of gas left."} - , { '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."} + , { 'LOG0', 16#5a, false, 3, [a,a], log, "Create a log message in the call object."} + , { 'LOG1', 16#5b, false, 3, [a,a,a], log, "Create a log message with one topic in the call object."} + , { 'LOG2', 16#5c, false, 3, [a,a,a,a], log, "Create a log message with two topics in the call object."} + , { 'LOG3', 16#5d, false, 3, [a,a,a,a,a], log, "Create a log message with three topics in the call object."} + , { 'LOG4', 16#5e, false, 3, [a,a,a,a,a,a], log, "Create a log message with four topics in the call object."} + , { 'DEACTIVATE', 16#5f, false, 3, [], deactivate, "Mark the current contract for deactication."} %% Transaction ops - , { 'SPEND', 16#6c, 2, false,3, [a,a], spend, "Transfer Arg1 tokens to account Arg0. (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."} + , { 'SPEND', 16#60, false, 3, [a,a], spend, "Transfer Arg1 tokens to account Arg0. (If the contract account has at least that many tokens."} + , { 'ORACLE_REGISTER', 16#61, false, 3, [a,a,a,a,a,a], oracle_register, "Mark the current contract for deactication."} %% TODO: - , { '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, ""} - , { 'BALANCE_OTHER', 16#7e, 2, false,3, [a,a], balance_other, "Arg0 := The balance of address Arg1."} - , { 'SETELEMENT', 16#7f, 4, false,3, [a,a,a,a], setelement, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."} - , { 'CALL_GR', 16#80, 4, true,8, [a,is,a,a], call_gr, "Remote call with gas cap in Arg3. Otherwise as CALL_R."} - , { 'CALL_GTR', 16#81, 4, true,8, [a,is,a,a], call_gtr, "Remote tail call with gas cap in Arg3. Otherwise as CALL_TR."} - , { 'CALL_VALUE', 16#82, 1, false,3, [a], call_value, "The value sent in the current remote call."} + , { 'ORACLE_QUERY', 16#62, false, 3, [], oracle_query, ""} + , { 'ORACLE_RESPOND', 16#63, false, 3, [], oracle_respond, ""} + , { 'ORACLE_EXTEND', 16#64, false, 3, [], oracle_extend, ""} + , { 'ORACLE_GET_ANSWER', 16#65, false, 3, [], oracle_get_answer, ""} + , { 'ORACLE_GET_QUESTION', 16#66, false, 3, [], oracle_get_question, ""} + , { 'ORACLE_QUERY_FEE', 16#67, false, 3, [], oracle_query_fee, ""} + , { 'AENS_RESOLVE', 16#68, false, 3, [], aens_resolve, ""} + , { 'AENS_PRECLAIM', 16#69, false, 3, [], aens_preclaim, ""} + , { 'AENS_CLAIM', 16#6a, false, 3, [], aens_claim, ""} + , { 'AENS_UPDATE', 16#6b, false, 3, [], aend_update, ""} + , { 'AENS_TRANSFER', 16#6c, false, 3, [], aens_transfer, ""} + , { 'AENS_REVOKE', 16#6d, false, 3, [], aens_revoke, ""} + , { 'ECVERIFY', 16#6e, false, 3, [], ecverify, ""} + , { 'BALANCE_OTHER', 16#6f, false, 3, [a,a], balance_other, "Arg0 := The balance of address Arg1."} + , { 'SETELEMENT', 16#70, false, 3, [a,a,a,a], setelement, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."} - , { 'DUMMY7ARG', 16#f9, 7, false,3, [a,a,a,a,a,a,a], dummyarg, "Temporary dummy instruction to test 7 args."} - , { 'DUMMY8ARG', 16#fa, 8, false,3, [a,a,a,a,a,a,a,a],dummyarg, "Temporary dummy instruction to test 8 args."} - , { '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." + , { 'ABORT', 16#fb, true, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."} + , { 'EXIT', 16#fc, true, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} + , { 'NOP', 16#fd, false, 1, [], nop, "The no op. does nothing."} + %% FUNCTION 16#fe "Function declaration and entrypoint." + %% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes." ]. @@ -248,7 +246,7 @@ gen_type_exports(#{type_name := TypeName}) -> gen_constructor_exports(#{constructor_type := Function}) -> lists:flatten(io_lib:format(" , ~s\n", [Function])). -gen_constructors(#{constructor := Function, format := atomic, +gen_constructors(#{constructor := Function, format := [], type_name := Type, opname := Name}) -> lists:flatten(io_lib:format("-spec ~s() -> ~s.\n" "~s() ->\n" @@ -322,7 +320,7 @@ 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}) -> +gen_args(#{macro := Macro, arity := Args}) -> lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n", [Macro, Args])). @@ -349,13 +347,14 @@ gen_defines(#{opname := Name, opcode := OpCode}) -> gen([]) -> []; -gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) -> +gen([{OpName, OpCode, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) -> + Args = length(FateFormat), 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]); + [] -> io_lib:format("~w", [OpName]); ArgTypes -> io_lib:format("{~w, ~s}", [OpName, expand_types(ArgTypes)]) end, @@ -363,7 +362,7 @@ gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) - [#{ opname => OpName , opcode => OpCode - , args => Args + , arity => Args , end_bb => EndBB , format => FateFormat , macro => Macro @@ -458,7 +457,7 @@ gen_format(#{opname := Name}) when (Name =:= 'CALL_GR') or (Name =:= 'CALL_GTR') "format_arg(a, Value), \" \", " "format_arg(a, Gas)];\n", [Name, atom_to_list(Name), Name, atom_to_list(Name)]); -gen_format(#{opname := Name, format := atomic}) -> +gen_format(#{opname := Name, format := []}) -> io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]); gen_format(#{opname := Name, format := Args}) -> NameAsString = atom_to_list(Name), @@ -547,7 +546,7 @@ test_asm_generator(Filename) -> file:close(File). -gen_instruction(#{opname := Name, format := atomic}) -> +gen_instruction(#{opname := Name, format := []}) -> io_lib:format(" ~s\n", [Name]); gen_instruction(#{opname := Name, format := ArgTypes}) -> Args = lists:flatten(lists:join(" ", [gen_arg(A) || A <- ArgTypes])), @@ -669,7 +668,7 @@ generate_documentation(Filename) -> gen_doc(#{ opname := Name , opcode := OpCode - , args := _Args + , arity := _Args , end_bb := _EndBB , format := FateFormat , macro := _Macro @@ -682,7 +681,7 @@ gen_doc(#{ opname := Name }) -> Arguments = case FateFormat of - atomic -> ""; + [] -> ""; _ -> lists:join(" ", [format_arg_doc(A) || A <- diff --git a/test/asm_code/all_instructions.fate b/test/asm_code/all_instructions.fate index 81d57ce..664e61c 100644 --- a/test/asm_code/all_instructions.fate +++ b/test/asm_code/all_instructions.fate @@ -226,10 +226,6 @@ FUNCTION foo () : {tuple, []} BLAKE2B - DUMMY7ARG a a 7607708484837907159893701471377343595877 (| [2,1] | 0 | ( [], [ 45, { 1 => 3441201581501946066216994494994943246334} ] ) |) a0 var56 "foo" - - DUMMY8ARG 3673679924816289365509492271980889822579 a69 arg242 var237 a175 arg106 () var255 - ABORT a EXIT var120 -- 2.30.2 From 590697503e8d7bb990f4c83bf12d1b516ad90af1 Mon Sep 17 00:00:00 2001 From: Erik Stenman Date: Thu, 23 May 2019 12:35:24 +0200 Subject: [PATCH 2/2] FATE does not accept arbitrary stack positions, only the accumulator aka stack 0. --- src/aeb_fate_asm.erl | 8 ++-- src/aeb_fate_asm_scan.template | 5 +- src/aeb_fate_code.erl | 22 ++++++--- src/aeb_fate_generate_ops.erl | 11 ++--- test/asm_code/all_instructions.fate | 72 ++++++++++++++--------------- 5 files changed, 62 insertions(+), 56 deletions(-) diff --git a/src/aeb_fate_asm.erl b/src/aeb_fate_asm.erl index f0cea24..9db8655 100644 --- a/src/aeb_fate_asm.erl +++ b/src/aeb_fate_asm.erl @@ -30,9 +30,7 @@ %%% arg0 %%% 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 +%%% References to the top of the stack is the letter a (for accumulator) %%% a %%% %%% Immediate values can be of 11 types: @@ -243,8 +241,8 @@ to_bytecode([{arg,_line, N}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts); to_bytecode([{var,_line, N}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, [{var, N}|Code], Opts); -to_bytecode([{stack,_line, N}|Rest], Address, Env, Code, Opts) -> - to_bytecode(Rest, Address, Env, [{stack, N}|Code], Opts); +to_bytecode([{stack,_line}|Rest], Address, Env, Code, Opts) -> + to_bytecode(Rest, Address, Env, [{stack, 0}|Code], Opts); to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) -> to_bytecode(Rest, Address, Env, [{immediate, Int}|Code], Opts); to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) -> diff --git a/src/aeb_fate_asm_scan.template b/src/aeb_fate_asm_scan.template index cd0bb5f..bd67d28 100644 --- a/src/aeb_fate_asm_scan.template +++ b/src/aeb_fate_asm_scan.template @@ -27,8 +27,7 @@ BITS = (\!)?\<[\s01]*\> 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)}}. +a : {token, {stack, TokenLine}}. true : {token, {boolean, TokenLine, true}}. false : {token, {boolean, TokenLine, false}}. @@ -108,7 +107,7 @@ 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) -> diff --git a/src/aeb_fate_code.erl b/src/aeb_fate_code.erl index cceb9e8..a0ea3b5 100644 --- a/src/aeb_fate_code.erl +++ b/src/aeb_fate_code.erl @@ -174,10 +174,10 @@ serialize_code([{_,_}|_] = List ) -> Mods = << <<(modifier_bits(Type)):2>> || {Type, _} <- pad_args(lists:reverse(Args)) >>, case Mods of <> -> - [M1, M2 | [serialize_data(Type, Arg) || {Type, Arg} <- Args]] ++ + [M1, M2 | [serialize_data(Type, Arg) || {Type, Arg} <- Args, Type =/= stack]] ++ serialize_code(Rest); <> -> - [M1 | [serialize_data(Type, Arg) || {Type, Arg} <- Args]] ++ + [M1 | [serialize_data(Type, Arg) || {Type, Arg} <- Args, Type =/= stack]] ++ serialize_code(Rest) end; serialize_code([Op|Rest]) -> @@ -304,15 +304,25 @@ deserialize_op(Op, Rest, Code) -> deserialize_n_args(N, <>) when N =< 4 -> ArgMods = lists:sublist([M0, M1, M2, M3], N), lists:mapfoldl(fun(M, Acc) -> - {Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc), - {{bits_to_modifier(M), Arg}, Acc2} + case bits_to_modifier(M) of + stack -> + {{stack, 0}, Acc}; + Modifier -> + {Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc), + {{Modifier, Arg}, Acc2} + end end, Rest, ArgMods); deserialize_n_args(N, <>) when N =< 8 -> ArgMods = lists:sublist([M0, M1, M2, M3, M4, M5, M6, M7], N), lists:mapfoldl(fun(M, Acc) -> - {Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc), - {{bits_to_modifier(M), Arg}, Acc2} + case bits_to_modifier(M) of + stack -> + {{stack, 0}, Acc}; + Modifier -> + {Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc), + {{Modifier, Arg}, Acc2} + end end, Rest, ArgMods). deserialize_signature(Binary) -> diff --git a/src/aeb_fate_generate_ops.erl b/src/aeb_fate_generate_ops.erl index 0fa7b0f..6b978d9 100644 --- a/src/aeb_fate_generate_ops.erl +++ b/src/aeb_fate_generate_ops.erl @@ -43,7 +43,7 @@ ops_defs() -> , { 'SWITCH_VN', 16#0c, true, 4, [a, li], switch, "Conditional jump to a basic block on variant tag."} , { 'CALL_VALUE', 16#0d, false, 3, [a], call_value, "The value sent in the current remote call."} , { 'PUSH', 16#0e, false, 2, [a], push, "Push argument to stack."} - , { 'DUPA', 16#0f, false, 3, [], dup, "push copy of accumulator on stack."} + , { 'DUPA', 16#0f, false, 3, [], dup, "Duplicate top of stack."} , { 'DUP', 16#10, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} , { 'POP', 16#11, false, 3, [a], pop, "Arg0 := top of stack."} , { 'INCA', 16#12, false, 2, [], inc, "Increment accumulator."} @@ -92,7 +92,7 @@ ops_defs() -> , { 'INT_TO_ADDR', 16#3d, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} , { 'VARIANT', 16#3e, 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#3f, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."} - , { 'VARIANT_ELEMENT', 16#40, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."} + , { 'VARIANT_ELEMENT', 16#40, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."} , { 'BITS_NONEA', 16#41, false, 3, [], bits_none, "accumulator := empty bitmap."} , { 'BITS_NONE', 16#42, false, 3, [a], bits_none, "Arg0 := empty bitmap."} , { 'BITS_ALLA', 16#43, false, 3, [], bits_all, "accumulator := full bitmap."} @@ -215,7 +215,7 @@ generate_code_ops(Modulename, SrcDir, Ops) -> "-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_stack() :: {stack, 0}.\n" "-type fate_arg() :: fate_arg_immediate()\n" " | fate_arg_var()\n" " | fate_arg_arg()\n" @@ -425,8 +425,7 @@ gen_asm_pp(Module, Path, Ops) -> " 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" + "format_arg(a, {stack, 0}) -> \"a\".\n\n" "lookup(Name, Symbols) ->\n" " maps:get(Name, Symbols, io_lib:format(\"~~p\",[Name])).\n\n" "~s" @@ -562,7 +561,7 @@ gen_arg(t) -> "integer". any_arg() -> element(rand:uniform(5), {"a", stack_arg(), var_arg(), arg_arg(), imm_arg()}). -stack_arg() -> "a" ++ integer_to_list(rand:uniform(255)-1). +stack_arg() -> "a". arg_arg() -> "arg" ++ integer_to_list(rand:uniform(256)-1). var_arg() -> "var" ++ integer_to_list(rand:uniform(256)-1). imm_arg() -> diff --git a/test/asm_code/all_instructions.fate b/test/asm_code/all_instructions.fate index 664e61c..02ee883 100644 --- a/test/asm_code/all_instructions.fate +++ b/test/asm_code/all_instructions.fate @@ -6,7 +6,7 @@ FUNCTION foo () : {tuple, []} RETURN - RETURNR a13 + RETURNR a CALL "foo" @@ -24,7 +24,7 @@ FUNCTION foo () : {tuple, []} JUMPIF arg196 0x12c651665 - SWITCH_V2 a27 63 33 + SWITCH_V2 a 63 33 SWITCH_V3 var4 0x1d61723dd 79 7 @@ -36,29 +36,29 @@ FUNCTION foo () : {tuple, []} DUP a - POP a107 + POP a STORE arg183 var225 INCA - INC a25 + INC a DECA DEC a - ADD a217 a a + ADD a a a SUB arg35 arg165 var74 MUL 44 35 "foo" - DIV 263838340369912686645632650718169038811 a24 a + DIV 263838340369912686645632650718169038811 a a MOD var113 arg80 arg207 - POW a176 a a123 + POW a a a LT a 78 var81 @@ -66,11 +66,11 @@ FUNCTION foo () : {tuple, []} EQ 85 a arg164 - ELT a161 arg226 a168 + ELT a arg226 a - EGT a131 1 var250 + EGT a 1 var250 - NEQ a85 a a83 + NEQ a a a AND var255 0x294a24f6 var189 @@ -80,35 +80,35 @@ FUNCTION foo () : {tuple, []} TUPLE 5019186157739257888756115213149493826410 - ELEMENT arg148 var25 a219 + ELEMENT arg148 var25 a - MAP_EMPTY a135 + MAP_EMPTY a - MAP_LOOKUP a82 a a143 + MAP_LOOKUP a a a - MAP_LOOKUPD var112 arg35 a163 var112 + MAP_LOOKUPD var112 arg35 a var112 - MAP_UPDATE false a0 a56 a + MAP_UPDATE false a a a MAP_DELETE arg180 a var1 MAP_MEMBER a { true => 4} 94 - MAP_FROM_LIST () a159 + MAP_FROM_LIST () a NIL arg91 - IS_NIL a121 var6 + IS_NIL a var6 - CONS arg185 "foo" a114 + CONS arg185 "foo" a - HD a150 var124 + HD a var124 TL arg223 a - LENGTH var216 a143 + LENGTH var216 a - APPEND { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a177 + APPEND { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a STR_JOIN a a 7144184027126178769820155907121270843348 @@ -116,15 +116,15 @@ FUNCTION foo () : {tuple, []} ADDR_TO_STR a arg216 - STR_REVERSE a174 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv + STR_REVERSE a @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv INT_TO_ADDR arg127 var207 VARIANT a a 0x1f7b72200 a - VARIANT_TEST a26 arg217 a + VARIANT_TEST a arg217 a - VARIANT_ELEMENT a86 arg103 arg108 + VARIANT_ELEMENT a arg103 arg108 BITS_NONEA @@ -132,31 +132,31 @@ FUNCTION foo () : {tuple, []} BITS_ALLA - BITS_ALL a164 + BITS_ALL a - BITS_ALL_N a221 arg135 + BITS_ALL_N a arg135 - BITS_SET arg150 a48 { 0x1a715e2a6 => 3} + BITS_SET arg150 a { 0x1a715e2a6 => 3} BITS_CLEAR arg98 a arg164 - BITS_TEST a a242 (| [0,0,3] | 2 | (1, "foo", ()) |) + BITS_TEST a a (| [0,0,3] | 2 | (1, "foo", ()) |) - BITS_SUM a244 a71 + BITS_SUM a a BITS_OR var20 var186 a - BITS_AND a187 4 arg203 + BITS_AND a 4 arg203 BITS_DIFF var200 arg247 var20 - ADDRESS a237 + ADDRESS a - BALANCE a231 + BALANCE a ORIGIN arg216 - CALLER a27 + CALLER a GASPRICE arg119 @@ -178,15 +178,15 @@ FUNCTION foo () : {tuple, []} GAS var35 - LOG0 a a85 + LOG0 a a LOG1 arg94 arg86 arg208 - LOG2 a113 (| [0,1,3] | 2 | (1, "foo", ()) |) arg238 var108 + LOG2 a (| [0,1,3] | 2 | (1, "foo", ()) |) arg238 var108 LOG3 arg255 arg15 arg211 var139 arg44 - LOG4 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv a247 a 9 a38 a + LOG4 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv a a 9 a a DEACTIVATE -- 2.30.2