From a367d5040a09f8052a56156e9470c93794ece7fe Mon Sep 17 00:00:00 2001 From: Ulf Norell Date: Fri, 25 Jan 2019 13:34:28 +0100 Subject: [PATCH] Add builtin bit field type --- src/aeso_ast_infer_types.erl | 7 +++++++ src/aeso_ast_to_icode.erl | 24 ++++++++++++++++++++++++ src/aeso_builtins.erl | 12 ++++++++++++ src/aeso_icode.erl | 1 + test/contracts/multi_sig.aes | 28 +++++++++++++--------------- 5 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 7bbe6b4..dcfdfb1 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -79,6 +79,7 @@ global_env() -> Event = {id, Ann, "event"}, State = {id, Ann, "state"}, Hash = {id, Ann, "hash"}, + Bits = {id, Ann, "bits"}, Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end, Query = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle_query"}, [Q, R]} end, Unit = {tuple_t, Ann, []}, @@ -159,6 +160,12 @@ global_env() -> {["String", "sha3"], Fun1(String, Hash)}, {["String", "sha256"], Fun1(String, Hash)}, {["String", "blake2b"], Fun1(String, Hash)}, + %% Bits + {["Bits", "set"], Fun([Bits, Int], Bits)}, + {["Bits", "clear"], Fun([Bits, Int], Bits)}, + {["Bits", "test"], Fun([Bits, Int], Bool)}, + {["Bits", "sum"], Fun1(Bits, Int)}, + {["Bits", "zero"], Bits}, %% Conversion {["Int", "to_str"], Fun1(Int, String)}, {["Address", "to_str"], Fun1(Address, String)} diff --git a/src/aeso_ast_to_icode.erl b/src/aeso_ast_to_icode.erl index 9f90550..023fd42 100644 --- a/src/aeso_ast_to_icode.erl +++ b/src/aeso_ast_to_icode.erl @@ -348,6 +348,30 @@ ast_body(?qid_app(["String", "concat"], [String1, String2], _, _), Icode) -> ast_body(?qid_app(["String", "sha3"], [String], _, _), Icode) -> #unop{ op = 'sha3', rand = ast_body(String, Icode) }; +%% -- Bits +ast_body(?qid_app(["Bits", "test"], [Bits, Ix], _, _), Icode) -> + %% (Bits bsr Ix) band 1 + #binop{ op = 'band' + , left = #binop{ op = 'bsr', left = ast_body(Ix, Icode), right = ast_body(Bits, Icode) } + , right = #integer{ value = 1 } }; +ast_body(?qid_app(["Bits", "set"], [Bits, Ix], _, _), Icode) -> + %% Bits bor (1 bsl Ix) + #binop{ op = 'bor' + , left = ast_body(Bits, Icode) + , right = #binop{ op = 'bsl', left = ast_body(Ix, Icode), right = #integer{ value = 1 } } }; +ast_body(?qid_app(["Bits", "clear"], [Bits, Ix], _, _), Icode) -> + %% Bits band (bnot (1 bsl Ix)) + #binop{ op = 'band' + , left = ast_body(Bits, Icode) + , right = #unop{ op = 'bnot' + , rand = #binop{ op = 'bsl' + , left = ast_body(Ix, Icode) + , right = #integer{ value = 1 } } } }; +ast_body({qid, _, ["Bits", "zero"]}, _Icode) -> + #integer{ value = 0 }; +ast_body(?qid_app(["Bits", "sum"], [Bits], _, _), Icode) -> + builtin_call(popcount, [ast_body(Bits, Icode), #integer{ value = 0 }]); + %% -- Conversion ast_body(?qid_app(["Int", "to_str"], [Int], _, _), Icode) -> builtin_call(int_to_str, [ast_body(Int, Icode)]); diff --git a/src/aeso_builtins.erl b/src/aeso_builtins.erl index 0b44692..383fcf4 100644 --- a/src/aeso_builtins.erl +++ b/src/aeso_builtins.erl @@ -148,6 +148,7 @@ builtin_function(BF) -> string_shift_copy -> bfun(BF, builtin_string_shift_copy()); str_equal_p -> bfun(BF, builtin_str_equal_p()); str_equal -> bfun(BF, builtin_str_equal()); + popcount -> bfun(BF, builtin_popcount()); int_to_str -> bfun(BF, builtin_int_to_str()); addr_to_str -> bfun(BF, builtin_addr_to_str()); {baseX_int, X} -> bfun(BF, builtin_baseX_int(X)); @@ -398,6 +399,17 @@ builtin_str_equal() -> )), word}. +%% Count the number of 1s in a bit field. +builtin_popcount() -> + %% function popcount(bits, acc) = + %% if (bits == 0) acc + %% else popcount(bits bsr 1, acc + bits band 1) + {[{"bits", word}, {"acc", word}], + {ifte, ?EQ(bits, 0), + ?V(acc), + ?call(popcount, [op('bsr', 1, bits), ?ADD(acc, op('band', bits, 1))]) + }, word}. + builtin_int_to_str() -> {[{"i", word}], ?call({baseX_int, 10}, [?V(i)]), word}. diff --git a/src/aeso_icode.erl b/src/aeso_icode.erl index 33fcc56..8ee3839 100644 --- a/src/aeso_icode.erl +++ b/src/aeso_icode.erl @@ -59,6 +59,7 @@ builtin_types() -> Word = fun([]) -> word end, #{ "bool" => Word , "int" => Word + , "bits" => Word , "string" => fun([]) -> string end , "address" => Word , "hash" => Word diff --git a/test/contracts/multi_sig.aes b/test/contracts/multi_sig.aes index 6c8c9ff..028b621 100644 --- a/test/contracts/multi_sig.aes +++ b/test/contracts/multi_sig.aes @@ -5,7 +5,7 @@ contract MultiSig = - record pending_state = { yetNeeded : uint, ownersDone : uint, index : uint } + record pending_state = { yetNeeded : int, ownersDone : bits, index : int } datatype event = Confirmation (address, hash) // of { .owner : Address, .operation : Hash } @@ -13,18 +13,18 @@ contract MultiSig = | OwnerChanged (address, address) // of { .oldOwner : Address, .newOwner : Address } | OwnerAdded (address) // of { .newOwner : Address } | OwnerRemoved (address) // of { .removedOwner : Address } - | ReqChanged (uint) // of { .newReq : uint } + | ReqChanged (int) // of { .newReq : int } - let maxOwners : uint = 250 + let maxOwners : int = 250 - record state = { nRequired : uint - , nOwners : uint - , owners : map(uint, address) - , ownerIndex : map(address, uint) + record state = { nRequired : int + , nOwners : int + , owners : map(int, address) + , ownerIndex : map(address, int) , pending : map(hash, pending_state) , pendingIndex : list(address) } - function init (owners : list(address), nRequired : uint) : state = + function init (owners : list(address), nRequired : int) : state = let n = length(owners) + 1 { nRequired = nRequired, nOwners = n, @@ -39,10 +39,9 @@ contract MultiSig = function revoke(operation : hash) = let ownerIx = lookup(state.ownerIndex, caller()) let pending = lookup(state.pendingIndex, operation) - let ownerIxBit = 1 bsl (ownerIx - 1) - let _ = require(pending.ownersDone band ownerIxBit > 0) + let _ = require(Bits.test(pending.ownersDone, ownerIx)) let pending' = pending { yetNeeded = pending.yetNeeded + 1 - , ownersDone = pending.ownersDone - ownerIxBit } + , ownersDone = Bits.clear(pending.ownersDone, ownerIx - 1) } put(state{ pendingIndex.operator = pending' }) event(Revoke(caller, operation)) @@ -91,7 +90,7 @@ contract MultiSig = , pendingIx = [] }, event = [OwnerRemoved(oldOwner)] } - function changeRequirement(newReq : uint) = + function changeRequirement(newReq : int) = let _ = require(newReq =< state.nOwners) switch(check_pending(callhash())) CheckFail(state') => { state = state' } @@ -102,7 +101,7 @@ contract MultiSig = event = [ReqChanged(newReq)] } - function getOwner(ownerIx0 : uint) = + function getOwner(ownerIx0 : int) = lookup(state.owners, ownerIx0 + 1) function isOwner(owner : address) = @@ -116,8 +115,7 @@ contract MultiSig = Some(pending) => let _ = require(isOwner(owner)) let ownerIx = lookup(state.ownerIndex, owner) - let ownerIxBit = 1 bsl (ownerIx - 1) - (pending.ownersDone band ownerIxBit) != 0 + Bits.test(pending.ownersDone, ownerIx - 1) /* Leave the rest for now... */