From 6dc0ff96aacfbd68c592f4ef7390e5cbf6ac3ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Tue, 30 Jan 2024 16:41:28 +0100 Subject: [PATCH] feat: Memory only brillig (#4215) This PR adds: - Memory only brillig https://github.com/noir-lang/noir/issues/4158 - Calldata and returndata https://github.com/noir-lang/noir/issues/4159 We reserve some memory slots at the start of the memory space. We reserve 1024 items for the stack frame, the calldata size and the returndata size. --- .../dsl/acir_format/serde/acir.hpp | 411 +++++++----- noir/acvm-repo/acir/codegen/acir.cpp | 371 ++++++----- noir/acvm-repo/acir/src/lib.rs | 6 +- .../acir/tests/test_program_serialization.rs | 83 ++- noir/acvm-repo/acvm/src/pwg/brillig.rs | 56 +- noir/acvm-repo/acvm/tests/solver.rs | 86 ++- .../test/shared/complex_foreign_call.ts | 11 +- .../acvm_js/test/shared/foreign_call.ts | 8 +- noir/acvm-repo/brillig/src/black_box.rs | 60 +- noir/acvm-repo/brillig/src/lib.rs | 4 +- noir/acvm-repo/brillig/src/opcodes.rs | 97 +-- noir/acvm-repo/brillig/src/value.rs | 4 +- noir/acvm-repo/brillig_vm/src/black_box.rs | 148 ++--- noir/acvm-repo/brillig_vm/src/lib.rs | 603 +++++++++-------- noir/acvm-repo/brillig_vm/src/memory.rs | 40 +- noir/acvm-repo/brillig_vm/src/registers.rs | 43 -- .../src/brillig/brillig_gen/brillig_block.rs | 43 +- .../brillig_gen/brillig_block_variables.rs | 4 +- .../brillig/brillig_gen/brillig_directive.rs | 38 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 213 ++---- .../noirc_evaluator/src/brillig/brillig_ir.rs | 203 +++--- .../src/brillig/brillig_ir/artifact.rs | 2 +- .../brillig/brillig_ir/brillig_variable.rs | 34 +- .../src/brillig/brillig_ir/debug_show.rs | 76 +-- .../src/brillig/brillig_ir/entry_point.rs | 621 +++++++++--------- .../src/brillig/brillig_ir/registers.rs | 22 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 64 +- .../brillig_nested_arrays/src/main.nr | 17 +- noir/tooling/debugger/src/context.rs | 66 +- noir/tooling/debugger/src/repl.rs | 51 -- .../end-to-end/src/e2e_2_pxes.test.ts | 2 +- .../end-to-end/src/e2e_persistence.test.ts | 3 + .../src/crates/types/src/abis/side_effect.nr | 15 - .../block_builder/solo_block_builder.test.ts | 2 +- 34 files changed, 1799 insertions(+), 1708 deletions(-) delete mode 100644 noir/acvm-repo/brillig_vm/src/registers.rs diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp index 97e73a25c69..c983baf68b0 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -476,16 +476,16 @@ struct BinaryIntOp { static BinaryIntOp bincodeDeserialize(std::vector); }; -struct RegisterIndex { +struct MemoryAddress { uint64_t value; - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { - Circuit::RegisterIndex pointer; + Circuit::MemoryAddress pointer; uint64_t size; friend bool operator==(const HeapArray&, const HeapArray&); @@ -494,8 +494,8 @@ struct HeapArray { }; struct HeapVector { - Circuit::RegisterIndex pointer; - Circuit::RegisterIndex size; + Circuit::MemoryAddress pointer; + Circuit::MemoryAddress size; friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; @@ -554,7 +554,7 @@ struct BlackBoxOp { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; @@ -566,7 +566,7 @@ struct BlackBoxOp { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; @@ -574,11 +574,11 @@ struct BlackBoxOp { }; struct SchnorrVerify { - Circuit::RegisterIndex public_key_x; - Circuit::RegisterIndex public_key_y; + Circuit::MemoryAddress public_key_x; + Circuit::MemoryAddress public_key_y; Circuit::HeapVector message; Circuit::HeapVector signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; @@ -587,7 +587,7 @@ struct BlackBoxOp { struct PedersenCommitment { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; + Circuit::MemoryAddress domain_separator; Circuit::HeapArray output; friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); @@ -597,8 +597,8 @@ struct BlackBoxOp { struct PedersenHash { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; - Circuit::RegisterIndex output; + Circuit::MemoryAddress domain_separator; + Circuit::MemoryAddress output; friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; @@ -606,8 +606,8 @@ struct BlackBoxOp { }; struct FixedBaseScalarMul { - Circuit::RegisterIndex low; - Circuit::RegisterIndex high; + Circuit::MemoryAddress low; + Circuit::MemoryAddress high; Circuit::HeapArray result; friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); @@ -616,10 +616,10 @@ struct BlackBoxOp { }; struct EmbeddedCurveAdd { - Circuit::RegisterIndex input1_x; - Circuit::RegisterIndex input1_y; - Circuit::RegisterIndex input2_x; - Circuit::RegisterIndex input2_y; + Circuit::MemoryAddress input1_x; + Circuit::MemoryAddress input1_y; + Circuit::MemoryAddress input2_x; + Circuit::MemoryAddress input2_y; Circuit::HeapArray result; friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); @@ -628,9 +628,9 @@ struct BlackBoxOp { }; struct BigIntAdd { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntAdd&, const BigIntAdd&); std::vector bincodeSerialize() const; @@ -638,9 +638,9 @@ struct BlackBoxOp { }; struct BigIntNeg { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntNeg&, const BigIntNeg&); std::vector bincodeSerialize() const; @@ -648,9 +648,9 @@ struct BlackBoxOp { }; struct BigIntMul { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntMul&, const BigIntMul&); std::vector bincodeSerialize() const; @@ -658,9 +658,9 @@ struct BlackBoxOp { }; struct BigIntDiv { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntDiv&, const BigIntDiv&); std::vector bincodeSerialize() const; @@ -670,7 +670,7 @@ struct BlackBoxOp { struct BigIntFromLeBytes { Circuit::HeapVector inputs; Circuit::HeapVector modulus; - Circuit::RegisterIndex output; + Circuit::MemoryAddress output; friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); std::vector bincodeSerialize() const; @@ -678,7 +678,7 @@ struct BlackBoxOp { }; struct BigIntToLeBytes { - Circuit::RegisterIndex input; + Circuit::MemoryAddress input; Circuit::HeapVector output; friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); @@ -689,7 +689,7 @@ struct BlackBoxOp { struct Poseidon2Permutation { Circuit::HeapVector message; Circuit::HeapArray output; - Circuit::RegisterIndex len; + Circuit::MemoryAddress len; friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); std::vector bincodeSerialize() const; @@ -733,14 +733,22 @@ struct BlackBoxOp { static BlackBoxOp bincodeDeserialize(std::vector); }; -struct RegisterOrMemory { +struct Value { + std::string inner; + + friend bool operator==(const Value&, const Value&); + std::vector bincodeSerialize() const; + static Value bincodeDeserialize(std::vector); +}; + +struct ValueOrArray { - struct RegisterIndex { - Circuit::RegisterIndex value; + struct MemoryAddress { + Circuit::MemoryAddress value; - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { @@ -759,28 +767,20 @@ struct RegisterOrMemory { static HeapVector bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; - friend bool operator==(const RegisterOrMemory&, const RegisterOrMemory&); + friend bool operator==(const ValueOrArray&, const ValueOrArray&); std::vector bincodeSerialize() const; - static RegisterOrMemory bincodeDeserialize(std::vector); -}; - -struct Value { - std::string inner; - - friend bool operator==(const Value&, const Value&); - std::vector bincodeSerialize() const; - static Value bincodeDeserialize(std::vector); + static ValueOrArray bincodeDeserialize(std::vector); }; struct BrilligOpcode { struct BinaryFieldOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryFieldOp op; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; @@ -788,11 +788,11 @@ struct BrilligOpcode { }; struct BinaryIntOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryIntOp op; uint32_t bit_size; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; @@ -800,7 +800,7 @@ struct BrilligOpcode { }; struct JumpIfNot { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIfNot&, const JumpIfNot&); @@ -809,7 +809,7 @@ struct BrilligOpcode { }; struct JumpIf { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIf&, const JumpIf&); @@ -825,6 +825,16 @@ struct BrilligOpcode { static Jump bincodeDeserialize(std::vector); }; + struct CalldataCopy { + Circuit::MemoryAddress destination_address; + uint64_t size; + uint64_t offset; + + friend bool operator==(const CalldataCopy&, const CalldataCopy&); + std::vector bincodeSerialize() const; + static CalldataCopy bincodeDeserialize(std::vector); + }; + struct Call { uint64_t location; @@ -834,7 +844,7 @@ struct BrilligOpcode { }; struct Const { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::Value value; friend bool operator==(const Const&, const Const&); @@ -850,8 +860,8 @@ struct BrilligOpcode { struct ForeignCall { std::string function; - std::vector destinations; - std::vector inputs; + std::vector destinations; + std::vector inputs; friend bool operator==(const ForeignCall&, const ForeignCall&); std::vector bincodeSerialize() const; @@ -859,8 +869,8 @@ struct BrilligOpcode { }; struct Mov { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source; friend bool operator==(const Mov&, const Mov&); std::vector bincodeSerialize() const; @@ -868,8 +878,8 @@ struct BrilligOpcode { }; struct Load { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source_pointer; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source_pointer; friend bool operator==(const Load&, const Load&); std::vector bincodeSerialize() const; @@ -877,8 +887,8 @@ struct BrilligOpcode { }; struct Store { - Circuit::RegisterIndex destination_pointer; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination_pointer; + Circuit::MemoryAddress source; friend bool operator==(const Store&, const Store&); std::vector bincodeSerialize() const; @@ -900,6 +910,9 @@ struct BrilligOpcode { }; struct Stop { + uint64_t return_data_offset; + uint64_t return_data_size; + friend bool operator==(const Stop&, const Stop&); std::vector bincodeSerialize() const; static Stop bincodeDeserialize(std::vector); @@ -910,6 +923,7 @@ struct BrilligOpcode { JumpIfNot, JumpIf, Jump, + CalldataCopy, Call, Const, Return, @@ -5212,6 +5226,63 @@ Circuit::BrilligOpcode::Jump serde::Deserializable namespace Circuit { +inline bool operator==(const BrilligOpcode::CalldataCopy& lhs, const BrilligOpcode::CalldataCopy& rhs) +{ + if (!(lhs.destination_address == rhs.destination_address)) { + return false; + } + if (!(lhs.size == rhs.size)) { + return false; + } + if (!(lhs.offset == rhs.offset)) { + return false; + } + return true; +} + +inline std::vector BrilligOpcode::CalldataCopy::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BrilligOpcode::CalldataCopy BrilligOpcode::CalldataCopy::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BrilligOpcode::CalldataCopy& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.destination_address, serializer); + serde::Serializable::serialize(obj.size, serializer); + serde::Serializable::serialize(obj.offset, serializer); +} + +template <> +template +Circuit::BrilligOpcode::CalldataCopy serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BrilligOpcode::CalldataCopy obj; + obj.destination_address = serde::Deserializable::deserialize(deserializer); + obj.size = serde::Deserializable::deserialize(deserializer); + obj.offset = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + inline bool operator==(const BrilligOpcode::Call& lhs, const BrilligOpcode::Call& rhs) { if (!(lhs.location == rhs.location)) { @@ -5654,6 +5725,12 @@ namespace Circuit { inline bool operator==(const BrilligOpcode::Stop& lhs, const BrilligOpcode::Stop& rhs) { + if (!(lhs.return_data_offset == rhs.return_data_offset)) { + return false; + } + if (!(lhs.return_data_size == rhs.return_data_size)) { + return false; + } return true; } @@ -5680,7 +5757,10 @@ template <> template void serde::Serializable::serialize(const Circuit::BrilligOpcode::Stop& obj, Serializer& serializer) -{} +{ + serde::Serializable::serialize(obj.return_data_offset, serializer); + serde::Serializable::serialize(obj.return_data_size, serializer); +} template <> template @@ -5688,6 +5768,8 @@ Circuit::BrilligOpcode::Stop serde::Deserializable Deserializer& deserializer) { Circuit::BrilligOpcode::Stop obj; + obj.return_data_offset = serde::Deserializable::deserialize(deserializer); + obj.return_data_size = serde::Deserializable::deserialize(deserializer); return obj; } @@ -6358,6 +6440,55 @@ Circuit::MemOp serde::Deserializable::deserialize(Deserializer& namespace Circuit { +inline bool operator==(const MemoryAddress& lhs, const MemoryAddress& rhs) +{ + if (!(lhs.value == rhs.value)) { + return false; + } + return true; +} + +inline std::vector MemoryAddress::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline MemoryAddress MemoryAddress::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::MemoryAddress& obj, Serializer& serializer) +{ + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Circuit::MemoryAddress serde::Deserializable::deserialize(Deserializer& deserializer) +{ + deserializer.increase_container_depth(); + Circuit::MemoryAddress obj; + obj.value = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + +namespace Circuit { + inline bool operator==(const Opcode& lhs, const Opcode& rhs) { if (!(lhs.value == rhs.value)) { @@ -6896,25 +7027,25 @@ Circuit::PublicInputs serde::Deserializable::deserialize( namespace Circuit { -inline bool operator==(const RegisterIndex& lhs, const RegisterIndex& rhs) +inline bool operator==(const Value& lhs, const Value& rhs) { - if (!(lhs.value == rhs.value)) { + if (!(lhs.inner == rhs.inner)) { return false; } return true; } -inline std::vector RegisterIndex::bincodeSerialize() const +inline std::vector Value::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterIndex RegisterIndex::bincodeDeserialize(std::vector input) +inline Value Value::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -6925,27 +7056,27 @@ inline RegisterIndex RegisterIndex::bincodeDeserialize(std::vector inpu template <> template -void serde::Serializable::serialize(const Circuit::RegisterIndex& obj, Serializer& serializer) +void serde::Serializable::serialize(const Circuit::Value& obj, Serializer& serializer) { serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); + serde::Serializable::serialize(obj.inner, serializer); serializer.decrease_container_depth(); } template <> template -Circuit::RegisterIndex serde::Deserializable::deserialize(Deserializer& deserializer) +Circuit::Value serde::Deserializable::deserialize(Deserializer& deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterIndex obj; - obj.value = serde::Deserializable::deserialize(deserializer); + Circuit::Value obj; + obj.inner = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } namespace Circuit { -inline bool operator==(const RegisterOrMemory& lhs, const RegisterOrMemory& rhs) +inline bool operator==(const ValueOrArray& lhs, const ValueOrArray& rhs) { if (!(lhs.value == rhs.value)) { return false; @@ -6953,17 +7084,17 @@ inline bool operator==(const RegisterOrMemory& lhs, const RegisterOrMemory& rhs) return true; } -inline std::vector RegisterOrMemory::bincodeSerialize() const +inline std::vector ValueOrArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterOrMemory RegisterOrMemory::bincodeDeserialize(std::vector input) +inline ValueOrArray ValueOrArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -6974,8 +7105,7 @@ inline RegisterOrMemory RegisterOrMemory::bincodeDeserialize(std::vector template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory& obj, - Serializer& serializer) +void serde::Serializable::serialize(const Circuit::ValueOrArray& obj, Serializer& serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); @@ -6984,10 +7114,10 @@ void serde::Serializable::serialize(const Circuit::Re template <> template -Circuit::RegisterOrMemory serde::Deserializable::deserialize(Deserializer& deserializer) +Circuit::ValueOrArray serde::Deserializable::deserialize(Deserializer& deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterOrMemory obj; + Circuit::ValueOrArray obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; @@ -6995,7 +7125,7 @@ Circuit::RegisterOrMemory serde::Deserializable::dese namespace Circuit { -inline bool operator==(const RegisterOrMemory::RegisterIndex& lhs, const RegisterOrMemory::RegisterIndex& rhs) +inline bool operator==(const ValueOrArray::MemoryAddress& lhs, const ValueOrArray::MemoryAddress& rhs) { if (!(lhs.value == rhs.value)) { return false; @@ -7003,17 +7133,17 @@ inline bool operator==(const RegisterOrMemory::RegisterIndex& lhs, const Registe return true; } -inline std::vector RegisterOrMemory::RegisterIndex::bincodeSerialize() const +inline std::vector ValueOrArray::MemoryAddress::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterOrMemory::RegisterIndex RegisterOrMemory::RegisterIndex::bincodeDeserialize(std::vector input) +inline ValueOrArray::MemoryAddress ValueOrArray::MemoryAddress::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -7024,25 +7154,25 @@ inline RegisterOrMemory::RegisterIndex RegisterOrMemory::RegisterIndex::bincodeD template <> template -void serde::Serializable::serialize( - const Circuit::RegisterOrMemory::RegisterIndex& obj, Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::ValueOrArray::MemoryAddress& obj, Serializer& serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::RegisterIndex serde::Deserializable::deserialize( +Circuit::ValueOrArray::MemoryAddress serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::RegisterOrMemory::RegisterIndex obj; + Circuit::ValueOrArray::MemoryAddress obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const RegisterOrMemory::HeapArray& lhs, const RegisterOrMemory::HeapArray& rhs) +inline bool operator==(const ValueOrArray::HeapArray& lhs, const ValueOrArray::HeapArray& rhs) { if (!(lhs.value == rhs.value)) { return false; @@ -7050,17 +7180,17 @@ inline bool operator==(const RegisterOrMemory::HeapArray& lhs, const RegisterOrM return true; } -inline std::vector RegisterOrMemory::HeapArray::bincodeSerialize() const +inline std::vector ValueOrArray::HeapArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterOrMemory::HeapArray RegisterOrMemory::HeapArray::bincodeDeserialize(std::vector input) +inline ValueOrArray::HeapArray ValueOrArray::HeapArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -7071,25 +7201,25 @@ inline RegisterOrMemory::HeapArray RegisterOrMemory::HeapArray::bincodeDeseriali template <> template -void serde::Serializable::serialize( - const Circuit::RegisterOrMemory::HeapArray& obj, Serializer& serializer) +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapArray& obj, + Serializer& serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapArray serde::Deserializable::deserialize( +Circuit::ValueOrArray::HeapArray serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::RegisterOrMemory::HeapArray obj; + Circuit::ValueOrArray::HeapArray obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const RegisterOrMemory::HeapVector& lhs, const RegisterOrMemory::HeapVector& rhs) +inline bool operator==(const ValueOrArray::HeapVector& lhs, const ValueOrArray::HeapVector& rhs) { if (!(lhs.value == rhs.value)) { return false; @@ -7097,17 +7227,17 @@ inline bool operator==(const RegisterOrMemory::HeapVector& lhs, const RegisterOr return true; } -inline std::vector RegisterOrMemory::HeapVector::bincodeSerialize() const +inline std::vector ValueOrArray::HeapVector::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterOrMemory::HeapVector RegisterOrMemory::HeapVector::bincodeDeserialize(std::vector input) +inline ValueOrArray::HeapVector ValueOrArray::HeapVector::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -7118,73 +7248,24 @@ inline RegisterOrMemory::HeapVector RegisterOrMemory::HeapVector::bincodeDeseria template <> template -void serde::Serializable::serialize( - const Circuit::RegisterOrMemory::HeapVector& obj, Serializer& serializer) +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapVector& obj, + Serializer& serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapVector serde::Deserializable::deserialize( +Circuit::ValueOrArray::HeapVector serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::RegisterOrMemory::HeapVector obj; + Circuit::ValueOrArray::HeapVector obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const Value& lhs, const Value& rhs) -{ - if (!(lhs.inner == rhs.inner)) { - return false; - } - return true; -} - -inline std::vector Value::bincodeSerialize() const -{ - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); -} - -inline Value Value::bincodeDeserialize(std::vector input) -{ - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw_or_abort("Some input bytes were not read"); - } - return value; -} - -} // end of namespace Circuit - -template <> -template -void serde::Serializable::serialize(const Circuit::Value& obj, Serializer& serializer) -{ - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.inner, serializer); - serializer.decrease_container_depth(); -} - -template <> -template -Circuit::Value serde::Deserializable::deserialize(Deserializer& deserializer) -{ - deserializer.increase_container_depth(); - Circuit::Value obj; - obj.inner = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); - return obj; -} - -namespace Circuit { - inline bool operator==(const Witness& lhs, const Witness& rhs) { if (!(lhs.value == rhs.value)) { diff --git a/noir/acvm-repo/acir/codegen/acir.cpp b/noir/acvm-repo/acir/codegen/acir.cpp index 0f94e91ab10..1b9b9c2e5bd 100644 --- a/noir/acvm-repo/acir/codegen/acir.cpp +++ b/noir/acvm-repo/acir/codegen/acir.cpp @@ -451,16 +451,16 @@ namespace Circuit { static BinaryIntOp bincodeDeserialize(std::vector); }; - struct RegisterIndex { + struct MemoryAddress { uint64_t value; - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { - Circuit::RegisterIndex pointer; + Circuit::MemoryAddress pointer; uint64_t size; friend bool operator==(const HeapArray&, const HeapArray&); @@ -469,8 +469,8 @@ namespace Circuit { }; struct HeapVector { - Circuit::RegisterIndex pointer; - Circuit::RegisterIndex size; + Circuit::MemoryAddress pointer; + Circuit::MemoryAddress size; friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; @@ -529,7 +529,7 @@ namespace Circuit { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; @@ -541,7 +541,7 @@ namespace Circuit { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; @@ -549,11 +549,11 @@ namespace Circuit { }; struct SchnorrVerify { - Circuit::RegisterIndex public_key_x; - Circuit::RegisterIndex public_key_y; + Circuit::MemoryAddress public_key_x; + Circuit::MemoryAddress public_key_y; Circuit::HeapVector message; Circuit::HeapVector signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; @@ -562,7 +562,7 @@ namespace Circuit { struct PedersenCommitment { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; + Circuit::MemoryAddress domain_separator; Circuit::HeapArray output; friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); @@ -572,8 +572,8 @@ namespace Circuit { struct PedersenHash { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; - Circuit::RegisterIndex output; + Circuit::MemoryAddress domain_separator; + Circuit::MemoryAddress output; friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; @@ -581,8 +581,8 @@ namespace Circuit { }; struct FixedBaseScalarMul { - Circuit::RegisterIndex low; - Circuit::RegisterIndex high; + Circuit::MemoryAddress low; + Circuit::MemoryAddress high; Circuit::HeapArray result; friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); @@ -591,10 +591,10 @@ namespace Circuit { }; struct EmbeddedCurveAdd { - Circuit::RegisterIndex input1_x; - Circuit::RegisterIndex input1_y; - Circuit::RegisterIndex input2_x; - Circuit::RegisterIndex input2_y; + Circuit::MemoryAddress input1_x; + Circuit::MemoryAddress input1_y; + Circuit::MemoryAddress input2_x; + Circuit::MemoryAddress input2_y; Circuit::HeapArray result; friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); @@ -603,9 +603,9 @@ namespace Circuit { }; struct BigIntAdd { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntAdd&, const BigIntAdd&); std::vector bincodeSerialize() const; @@ -613,9 +613,9 @@ namespace Circuit { }; struct BigIntNeg { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntNeg&, const BigIntNeg&); std::vector bincodeSerialize() const; @@ -623,9 +623,9 @@ namespace Circuit { }; struct BigIntMul { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntMul&, const BigIntMul&); std::vector bincodeSerialize() const; @@ -633,9 +633,9 @@ namespace Circuit { }; struct BigIntDiv { - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; - Circuit::RegisterIndex output; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; friend bool operator==(const BigIntDiv&, const BigIntDiv&); std::vector bincodeSerialize() const; @@ -645,7 +645,7 @@ namespace Circuit { struct BigIntFromLeBytes { Circuit::HeapVector inputs; Circuit::HeapVector modulus; - Circuit::RegisterIndex output; + Circuit::MemoryAddress output; friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); std::vector bincodeSerialize() const; @@ -653,7 +653,7 @@ namespace Circuit { }; struct BigIntToLeBytes { - Circuit::RegisterIndex input; + Circuit::MemoryAddress input; Circuit::HeapVector output; friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); @@ -664,7 +664,7 @@ namespace Circuit { struct Poseidon2Permutation { Circuit::HeapVector message; Circuit::HeapArray output; - Circuit::RegisterIndex len; + Circuit::MemoryAddress len; friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); std::vector bincodeSerialize() const; @@ -688,14 +688,22 @@ namespace Circuit { static BlackBoxOp bincodeDeserialize(std::vector); }; - struct RegisterOrMemory { + struct Value { + std::string inner; - struct RegisterIndex { - Circuit::RegisterIndex value; + friend bool operator==(const Value&, const Value&); + std::vector bincodeSerialize() const; + static Value bincodeDeserialize(std::vector); + }; + + struct ValueOrArray { - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + struct MemoryAddress { + Circuit::MemoryAddress value; + + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { @@ -714,28 +722,20 @@ namespace Circuit { static HeapVector bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const RegisterOrMemory&, const RegisterOrMemory&); - std::vector bincodeSerialize() const; - static RegisterOrMemory bincodeDeserialize(std::vector); - }; - - struct Value { - std::string inner; + std::variant value; - friend bool operator==(const Value&, const Value&); + friend bool operator==(const ValueOrArray&, const ValueOrArray&); std::vector bincodeSerialize() const; - static Value bincodeDeserialize(std::vector); + static ValueOrArray bincodeDeserialize(std::vector); }; struct BrilligOpcode { struct BinaryFieldOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryFieldOp op; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; @@ -743,11 +743,11 @@ namespace Circuit { }; struct BinaryIntOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryIntOp op; uint32_t bit_size; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; @@ -755,7 +755,7 @@ namespace Circuit { }; struct JumpIfNot { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIfNot&, const JumpIfNot&); @@ -764,7 +764,7 @@ namespace Circuit { }; struct JumpIf { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIf&, const JumpIf&); @@ -780,6 +780,16 @@ namespace Circuit { static Jump bincodeDeserialize(std::vector); }; + struct CalldataCopy { + Circuit::MemoryAddress destination_address; + uint64_t size; + uint64_t offset; + + friend bool operator==(const CalldataCopy&, const CalldataCopy&); + std::vector bincodeSerialize() const; + static CalldataCopy bincodeDeserialize(std::vector); + }; + struct Call { uint64_t location; @@ -789,7 +799,7 @@ namespace Circuit { }; struct Const { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::Value value; friend bool operator==(const Const&, const Const&); @@ -805,8 +815,8 @@ namespace Circuit { struct ForeignCall { std::string function; - std::vector destinations; - std::vector inputs; + std::vector destinations; + std::vector inputs; friend bool operator==(const ForeignCall&, const ForeignCall&); std::vector bincodeSerialize() const; @@ -814,8 +824,8 @@ namespace Circuit { }; struct Mov { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source; friend bool operator==(const Mov&, const Mov&); std::vector bincodeSerialize() const; @@ -823,8 +833,8 @@ namespace Circuit { }; struct Load { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source_pointer; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source_pointer; friend bool operator==(const Load&, const Load&); std::vector bincodeSerialize() const; @@ -832,8 +842,8 @@ namespace Circuit { }; struct Store { - Circuit::RegisterIndex destination_pointer; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination_pointer; + Circuit::MemoryAddress source; friend bool operator==(const Store&, const Store&); std::vector bincodeSerialize() const; @@ -855,12 +865,15 @@ namespace Circuit { }; struct Stop { + uint64_t return_data_offset; + uint64_t return_data_size; + friend bool operator==(const Stop&, const Stop&); std::vector bincodeSerialize() const; static Stop bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BrilligOpcode&, const BrilligOpcode&); std::vector bincodeSerialize() const; @@ -4310,6 +4323,50 @@ Circuit::BrilligOpcode::Jump serde::Deserializable return obj; } +namespace Circuit { + + inline bool operator==(const BrilligOpcode::CalldataCopy &lhs, const BrilligOpcode::CalldataCopy &rhs) { + if (!(lhs.destination_address == rhs.destination_address)) { return false; } + if (!(lhs.size == rhs.size)) { return false; } + if (!(lhs.offset == rhs.offset)) { return false; } + return true; + } + + inline std::vector BrilligOpcode::CalldataCopy::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BrilligOpcode::CalldataCopy BrilligOpcode::CalldataCopy::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BrilligOpcode::CalldataCopy &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.destination_address, serializer); + serde::Serializable::serialize(obj.size, serializer); + serde::Serializable::serialize(obj.offset, serializer); +} + +template <> +template +Circuit::BrilligOpcode::CalldataCopy serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BrilligOpcode::CalldataCopy obj; + obj.destination_address = serde::Deserializable::deserialize(deserializer); + obj.size = serde::Deserializable::deserialize(deserializer); + obj.offset = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BrilligOpcode::Call &lhs, const BrilligOpcode::Call &rhs) { @@ -4667,6 +4724,8 @@ Circuit::BrilligOpcode::Trap serde::Deserializable namespace Circuit { inline bool operator==(const BrilligOpcode::Stop &lhs, const BrilligOpcode::Stop &rhs) { + if (!(lhs.return_data_offset == rhs.return_data_offset)) { return false; } + if (!(lhs.return_data_size == rhs.return_data_size)) { return false; } return true; } @@ -4690,12 +4749,16 @@ namespace Circuit { template <> template void serde::Serializable::serialize(const Circuit::BrilligOpcode::Stop &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.return_data_offset, serializer); + serde::Serializable::serialize(obj.return_data_size, serializer); } template <> template Circuit::BrilligOpcode::Stop serde::Deserializable::deserialize(Deserializer &deserializer) { Circuit::BrilligOpcode::Stop obj; + obj.return_data_offset = serde::Deserializable::deserialize(deserializer); + obj.return_data_size = serde::Deserializable::deserialize(deserializer); return obj; } @@ -5238,6 +5301,48 @@ Circuit::MemOp serde::Deserializable::deserialize(Deserializer & return obj; } +namespace Circuit { + + inline bool operator==(const MemoryAddress &lhs, const MemoryAddress &rhs) { + if (!(lhs.value == rhs.value)) { return false; } + return true; + } + + inline std::vector MemoryAddress::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline MemoryAddress MemoryAddress::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::MemoryAddress &obj, Serializer &serializer) { + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Circuit::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { + deserializer.increase_container_depth(); + Circuit::MemoryAddress obj; + obj.value = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + namespace Circuit { inline bool operator==(const Opcode &lhs, const Opcode &rhs) { @@ -5682,20 +5787,20 @@ Circuit::PublicInputs serde::Deserializable::deserialize( namespace Circuit { - inline bool operator==(const RegisterIndex &lhs, const RegisterIndex &rhs) { - if (!(lhs.value == rhs.value)) { return false; } + inline bool operator==(const Value &lhs, const Value &rhs) { + if (!(lhs.inner == rhs.inner)) { return false; } return true; } - inline std::vector RegisterIndex::bincodeSerialize() const { + inline std::vector Value::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterIndex RegisterIndex::bincodeDeserialize(std::vector input) { + inline Value Value::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5706,38 +5811,38 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterIndex &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::Value &obj, Serializer &serializer) { serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); + serde::Serializable::serialize(obj.inner, serializer); serializer.decrease_container_depth(); } template <> template -Circuit::RegisterIndex serde::Deserializable::deserialize(Deserializer &deserializer) { +Circuit::Value serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterIndex obj; - obj.value = serde::Deserializable::deserialize(deserializer); + Circuit::Value obj; + obj.inner = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory &lhs, const RegisterOrMemory &rhs) { + inline bool operator==(const ValueOrArray &lhs, const ValueOrArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::bincodeSerialize() const { + inline std::vector ValueOrArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory RegisterOrMemory::bincodeDeserialize(std::vector input) { + inline ValueOrArray ValueOrArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5748,7 +5853,7 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5756,9 +5861,9 @@ void serde::Serializable::serialize(const Circuit::Re template <> template -Circuit::RegisterOrMemory serde::Deserializable::deserialize(Deserializer &deserializer) { +Circuit::ValueOrArray serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterOrMemory obj; + Circuit::ValueOrArray obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; @@ -5766,20 +5871,20 @@ Circuit::RegisterOrMemory serde::Deserializable::dese namespace Circuit { - inline bool operator==(const RegisterOrMemory::RegisterIndex &lhs, const RegisterOrMemory::RegisterIndex &rhs) { + inline bool operator==(const ValueOrArray::MemoryAddress &lhs, const ValueOrArray::MemoryAddress &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::RegisterIndex::bincodeSerialize() const { + inline std::vector ValueOrArray::MemoryAddress::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::RegisterIndex RegisterOrMemory::RegisterIndex::bincodeDeserialize(std::vector input) { + inline ValueOrArray::MemoryAddress ValueOrArray::MemoryAddress::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5790,34 +5895,34 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::RegisterIndex &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::MemoryAddress &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::RegisterIndex serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::RegisterIndex obj; +Circuit::ValueOrArray::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::MemoryAddress obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory::HeapArray &lhs, const RegisterOrMemory::HeapArray &rhs) { + inline bool operator==(const ValueOrArray::HeapArray &lhs, const ValueOrArray::HeapArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::HeapArray::bincodeSerialize() const { + inline std::vector ValueOrArray::HeapArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::HeapArray RegisterOrMemory::HeapArray::bincodeDeserialize(std::vector input) { + inline ValueOrArray::HeapArray ValueOrArray::HeapArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5828,34 +5933,34 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::HeapArray &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapArray &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::HeapArray obj; +Circuit::ValueOrArray::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::HeapArray obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory::HeapVector &lhs, const RegisterOrMemory::HeapVector &rhs) { + inline bool operator==(const ValueOrArray::HeapVector &lhs, const ValueOrArray::HeapVector &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::HeapVector::bincodeSerialize() const { + inline std::vector ValueOrArray::HeapVector::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::HeapVector RegisterOrMemory::HeapVector::bincodeDeserialize(std::vector input) { + inline ValueOrArray::HeapVector ValueOrArray::HeapVector::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5866,60 +5971,18 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::HeapVector &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapVector &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::HeapVector obj; +Circuit::ValueOrArray::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::HeapVector obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { - - inline bool operator==(const Value &lhs, const Value &rhs) { - if (!(lhs.inner == rhs.inner)) { return false; } - return true; - } - - inline std::vector Value::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline Value Value::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Circuit - -template <> -template -void serde::Serializable::serialize(const Circuit::Value &obj, Serializer &serializer) { - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.inner, serializer); - serializer.decrease_container_depth(); -} - -template <> -template -Circuit::Value serde::Deserializable::deserialize(Deserializer &deserializer) { - deserializer.increase_container_depth(); - Circuit::Value obj; - obj.inner = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); - return obj; -} - namespace Circuit { inline bool operator==(const Witness &lhs, const Witness &rhs) { diff --git a/noir/acvm-repo/acir/src/lib.rs b/noir/acvm-repo/acir/src/lib.rs index b7bcaa0c5c0..0a72cec6070 100644 --- a/noir/acvm-repo/acir/src/lib.rs +++ b/noir/acvm-repo/acir/src/lib.rs @@ -31,9 +31,7 @@ mod reflection { path::{Path, PathBuf}, }; - use brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, RegisterOrMemory, - }; + use brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, ValueOrArray}; use serde_reflection::{Tracer, TracerConfig}; use crate::{ @@ -69,7 +67,7 @@ mod reflection { tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); - tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::().unwrap(); let registry = tracer.registry().unwrap(); diff --git a/noir/acvm-repo/acir/tests/test_program_serialization.rs b/noir/acvm-repo/acir/tests/test_program_serialization.rs index 7d3b7b32d35..c94c5ae66b0 100644 --- a/noir/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/acvm-repo/acir/tests/test_program_serialization.rs @@ -20,7 +20,7 @@ use acir::{ native_types::{Expression, Witness}, }; use acir_field::FieldElement; -use brillig::{HeapArray, RegisterIndex, RegisterOrMemory}; +use brillig::{HeapArray, MemoryAddress, ValueOrArray}; #[test] fn addition_circuit() { @@ -173,15 +173,23 @@ fn simple_brillig_foreign_call() { inputs: vec![ BrilligInputs::Single(w_input.into()), // Input Register 0, ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ BrilligOutputs::Simple(w_inverted), // Output Register 1 ], - bytecode: vec![brillig::Opcode::ForeignCall { - function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], - }], + bytecode: vec![ + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 1, + offset: 0, + }, + brillig::Opcode::ForeignCall { + function: "invert".into(), + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], + }, + brillig::Opcode::Stop { return_data_offset: 0, return_data_size: 1 }, + ], predicate: None, }; @@ -196,10 +204,11 @@ fn simple_brillig_foreign_call() { let bytes = Circuit::serialize_circuit(&circuit); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 49, 10, 64, 33, 12, 67, 99, 63, 124, 60, 142, - 222, 192, 203, 56, 184, 56, 136, 120, 126, 5, 21, 226, 160, 139, 62, 40, 13, 45, 132, 68, - 3, 80, 232, 124, 164, 153, 121, 115, 99, 155, 59, 172, 122, 231, 101, 56, 175, 80, 86, 221, - 230, 31, 58, 196, 226, 83, 62, 53, 91, 16, 122, 10, 246, 84, 99, 243, 0, 30, 59, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 12, 68, 207, 148, 150, 118, + 234, 175, 216, 63, 232, 207, 116, 232, 210, 161, 136, 223, 175, 98, 132, 27, 212, 69, 31, + 132, 28, 23, 8, 119, 59, 0, 131, 204, 66, 154, 41, 222, 173, 219, 142, 113, 153, 121, 191, + 44, 231, 21, 237, 144, 88, 43, 249, 11, 71, 156, 77, 245, 251, 249, 231, 119, 189, 214, + 204, 89, 187, 11, 25, 130, 54, 1, 36, 1, 124, 242, 107, 1, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -221,39 +230,54 @@ fn complex_brillig_foreign_call() { let brillig_data = Brillig { inputs: vec![ - // Input Register 0 + // Input 0,1,2 BrilligInputs::Array(vec![ Expression::from(a), Expression::from(b), Expression::from(c), ]), - // Input Register 1 + // Input 3 BrilligInputs::Single(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, a), (fe_1, b), (fe_1, c)], q_c: fe_0, }), ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ - BrilligOutputs::Array(vec![a_times_2, b_times_3, c_times_4]), // Output Register 0 - BrilligOutputs::Simple(a_plus_b_plus_c), // Output Register 1 - BrilligOutputs::Simple(a_plus_b_plus_c_times_2), // Output Register 2 + BrilligOutputs::Array(vec![a_times_2, b_times_3, c_times_4]), // Output 0,1,2 + BrilligOutputs::Simple(a_plus_b_plus_c), // Output 3 + BrilligOutputs::Simple(a_plus_b_plus_c_times_2), // Output 4 ], bytecode: vec![ + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(32), + size: 3, + offset: 0, + }, + brillig::Opcode::Const { + destination: MemoryAddress(0), + value: brillig::Value::from(32_usize), + }, + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(1), + size: 1, + offset: 3, + }, // Oracles are named 'foreign calls' in brillig brillig::Opcode::ForeignCall { function: "complex".into(), inputs: vec![ - RegisterOrMemory::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(1)), + ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), + ValueOrArray::MemoryAddress(MemoryAddress::from(1)), ], destinations: vec![ - RegisterOrMemory::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(1)), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(2)), + ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), + ValueOrArray::MemoryAddress(MemoryAddress::from(35)), + ValueOrArray::MemoryAddress(MemoryAddress::from(36)), ], }, + brillig::Opcode::Stop { return_data_offset: 32, return_data_size: 5 }, ], predicate: None, }; @@ -269,13 +293,14 @@ fn complex_brillig_foreign_call() { let bytes = Circuit::serialize_circuit(&circuit); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 219, 10, 128, 48, 8, 117, 174, 139, 159, 179, - 254, 160, 127, 137, 222, 138, 122, 236, 243, 19, 114, 32, 22, 244, 144, 131, 118, 64, 156, - 178, 29, 14, 59, 74, 0, 16, 224, 66, 228, 64, 57, 7, 169, 53, 242, 189, 81, 114, 250, 134, - 33, 248, 113, 165, 82, 26, 177, 2, 141, 177, 128, 198, 60, 15, 63, 245, 219, 211, 23, 215, - 255, 139, 15, 251, 211, 112, 180, 28, 157, 212, 189, 100, 82, 179, 64, 170, 63, 109, 235, - 190, 204, 135, 166, 178, 150, 216, 62, 154, 252, 250, 70, 147, 35, 220, 119, 93, 227, 4, - 182, 131, 81, 25, 36, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 65, 10, 132, 48, 12, 76, 218, 237, 174, 123, + 242, 11, 130, 62, 160, 250, 2, 255, 34, 222, 20, 61, 250, 124, 11, 78, 49, 4, 193, 131, 21, + 52, 16, 210, 132, 105, 50, 77, 210, 140, 136, 152, 54, 177, 65, 13, 206, 12, 95, 74, 196, + 181, 176, 254, 154, 212, 156, 46, 151, 191, 139, 163, 121, 1, 71, 123, 3, 199, 184, 15, 15, + 157, 119, 202, 185, 36, 237, 159, 61, 248, 63, 159, 160, 46, 232, 23, 254, 15, 54, 67, 156, + 96, 11, 213, 119, 82, 248, 116, 179, 104, 188, 163, 125, 15, 89, 213, 253, 139, 154, 221, + 52, 206, 67, 191, 88, 5, 213, 52, 75, 113, 174, 96, 205, 201, 157, 24, 207, 197, 211, 157, + 6, 50, 18, 233, 158, 72, 89, 1, 53, 215, 75, 175, 196, 4, 0, 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/noir/acvm-repo/acvm/src/pwg/brillig.rs b/noir/acvm-repo/acvm/src/pwg/brillig.rs index 0db38c776e2..4b62f86f0eb 100644 --- a/noir/acvm-repo/acvm/src/pwg/brillig.rs +++ b/noir/acvm-repo/acvm/src/pwg/brillig.rs @@ -1,5 +1,5 @@ use acir::{ - brillig::{ForeignCallParam, ForeignCallResult, RegisterIndex, Value}, + brillig::{ForeignCallParam, ForeignCallResult, Value}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, OpcodeLocation, @@ -8,7 +8,7 @@ use acir::{ FieldElement, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use brillig_vm::{Registers, VMStatus, VM}; +use brillig_vm::{VMStatus, VM}; use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; @@ -69,16 +69,15 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { acir_index: usize, ) -> Result { // Set input values - let mut input_register_values: Vec = Vec::new(); - let mut input_memory: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); // Each input represents an expression or array of expressions to evaluate. // Iterate over each input and evaluate the expression(s) associated with it. - // Push the results into registers and/or memory. + // Push the results into memory. // If a certain expression is not solvable, we stall the ACVM and do not proceed with Brillig VM execution. for input in &brillig.inputs { match input { BrilligInputs::Single(expr) => match get_value(expr, initial_witness) { - Ok(value) => input_register_values.push(value.into()), + Ok(value) => calldata.push(value.into()), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -87,10 +86,9 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { }, BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values - let memory_pointer = input_memory.len(); for expr in expr_arr.iter() { match get_value(expr, initial_witness) { - Ok(value) => input_memory.push(value.into()), + Ok(value) => calldata.push(value.into()), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -98,28 +96,16 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { } } } - - // Push value of the array pointer as a register - input_register_values.push(Value::from(memory_pointer)); } } } - // Instantiate a Brillig VM given the solved input registers and memory + // Instantiate a Brillig VM given the solved calldata // along with the Brillig bytecode. - let input_registers = Registers::load(input_register_values); - let vm = VM::new(input_registers, input_memory, &brillig.bytecode, vec![], bb_solver); + let vm = VM::new(calldata, &brillig.bytecode, vec![], bb_solver); Ok(Self { vm, acir_index }) } - pub fn get_registers(&self) -> &Registers { - self.vm.get_registers() - } - - pub fn set_register(&mut self, register_index: usize, value: Value) { - self.vm.set_register(RegisterIndex(register_index), value); - } - pub fn get_memory(&self) -> &[Value] { self.vm.get_memory() } @@ -151,7 +137,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { // Return the "resolution" to the caller who may choose to make subsequent calls // (when it gets foreign call results for example). match vm_status { - VMStatus::Finished => Ok(BrilligSolverStatus::Finished), + VMStatus::Finished { .. } => Ok(BrilligSolverStatus::Finished), VMStatus::InProgress => Ok(BrilligSolverStatus::InProgress), VMStatus::Failure { message, call_stack } => { Err(OpcodeResolutionError::BrilligFunctionFailed { @@ -179,8 +165,8 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { // Finish the Brillig execution by writing the outputs to the witness map let vm_status = self.vm.get_status(); match vm_status { - VMStatus::Finished => { - self.write_brillig_outputs(witness, brillig)?; + VMStatus::Finished { return_data_offset, return_data_size } => { + self.write_brillig_outputs(witness, return_data_offset, return_data_size, brillig)?; Ok(()) } _ => panic!("Brillig VM has not completed execution"), @@ -190,24 +176,32 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { fn write_brillig_outputs( &self, witness_map: &mut WitnessMap, + return_data_offset: usize, + return_data_size: usize, brillig: &Brillig, ) -> Result<(), OpcodeResolutionError> { // Write VM execution results into the witness map - for (i, output) in brillig.outputs.iter().enumerate() { - let register_value = self.vm.get_registers().get(RegisterIndex::from(i)); + let memory = self.vm.get_memory(); + let mut current_ret_data_idx = return_data_offset; + for output in brillig.outputs.iter() { match output { BrilligOutputs::Simple(witness) => { - insert_value(witness, register_value.to_field(), witness_map)?; + insert_value(witness, memory[current_ret_data_idx].to_field(), witness_map)?; + current_ret_data_idx += 1; } BrilligOutputs::Array(witness_arr) => { - // Treat the register value as a pointer to memory - for (i, witness) in witness_arr.iter().enumerate() { - let value = &self.vm.get_memory()[register_value.to_usize() + i]; + for witness in witness_arr.iter() { + let value = memory[current_ret_data_idx]; insert_value(witness, value.to_field(), witness_map)?; + current_ret_data_idx += 1; } } } } + assert!( + current_ret_data_idx == return_data_offset + return_data_size, + "Brillig VM did not write the expected number of return values" + ); Ok(()) } diff --git a/noir/acvm-repo/acvm/tests/solver.rs b/noir/acvm-repo/acvm/tests/solver.rs index 486e04d5bf1..9ff4a4f4897 100644 --- a/noir/acvm-repo/acvm/tests/solver.rs +++ b/noir/acvm-repo/acvm/tests/solver.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig::{BinaryFieldOp, Opcode as BrilligOpcode, RegisterIndex, RegisterOrMemory, Value}, + brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, Value, ValueOrArray}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, opcodes::{BlockId, MemOp}, @@ -37,9 +37,9 @@ fn inversion_brillig_oracle_equivalence() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let brillig_data = Brillig { @@ -52,20 +52,26 @@ fn inversion_brillig_oracle_equivalence() { }), BrilligInputs::Single(Expression::default()), // Input Register 1 ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ BrilligOutputs::Simple(w_x_plus_y), // Output Register 0 - from input BrilligOutputs::Simple(w_oracle), // Output Register 1 BrilligOutputs::Simple(w_equal_res), // Output Register 2 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 3 }, ], predicate: None, }; @@ -151,9 +157,9 @@ fn double_inversion_brillig_oracle() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(4), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(4), }; let brillig_data = Brillig { @@ -180,18 +186,24 @@ fn double_inversion_brillig_oracle() { BrilligOutputs::Simple(w_equal_res), // Output Register 4 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 3, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(3))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(2))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 5 }, ], predicate: None, }; @@ -306,17 +318,23 @@ fn oracle_dependent_execution() { BrilligOutputs::Simple(w_y_inv), // Output Register 3 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 3, + offset: 0, + }, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(3))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(2))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 4 }, ], predicate: None, }; @@ -404,9 +422,9 @@ fn brillig_oracle_predicate() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let brillig_opcode = Opcode::Brillig(Brillig { @@ -425,12 +443,17 @@ fn brillig_oracle_predicate() { BrilligOutputs::Simple(w_lt_res), ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, ], predicate: Some(Expression::default()), @@ -502,21 +525,24 @@ fn unsatisfied_opcode_resolved_brillig() { let w_y = Witness(5); let w_result = Witness(6); + let calldata_copy_opcode = + BrilligOpcode::CalldataCopy { destination_address: MemoryAddress(0), size: 2, offset: 0 }; + let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; // Jump pass the trap if the values are equal, else // jump to the trap let location_of_stop = 3; let jmp_if_opcode = - BrilligOpcode::JumpIf { condition: RegisterIndex::from(2), location: location_of_stop }; + BrilligOpcode::JumpIf { condition: MemoryAddress::from(2), location: location_of_stop }; let trap_opcode = BrilligOpcode::Trap; - let stop_opcode = BrilligOpcode::Stop; + let stop_opcode = BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }; let brillig_opcode = Opcode::Brillig(Brillig { inputs: vec![ @@ -532,7 +558,7 @@ fn unsatisfied_opcode_resolved_brillig() { }), ], outputs: vec![BrilligOutputs::Simple(w_result)], - bytecode: vec![equal_opcode, jmp_if_opcode, trap_opcode, stop_opcode], + bytecode: vec![calldata_copy_opcode, equal_opcode, jmp_if_opcode, trap_opcode, stop_opcode], predicate: Some(Expression::one()), }); @@ -564,7 +590,7 @@ fn unsatisfied_opcode_resolved_brillig() { solver_status, ACVMStatus::Failure(OpcodeResolutionError::BrilligFunctionFailed { message: "explicit trap hit in brillig".to_string(), - call_stack: vec![OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }] + call_stack: vec![OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 }] }), "The first opcode is not satisfiable, expected an error indicating this" ); diff --git a/noir/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts b/noir/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts index 1b6f5e4319a..faae98bcf47 100644 --- a/noir/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts +++ b/noir/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts @@ -2,11 +2,12 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `complex_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 219, 10, 128, 48, 8, 117, 174, 139, 159, 179, 254, 160, 127, 137, 222, - 138, 122, 236, 243, 19, 114, 32, 22, 244, 144, 131, 118, 64, 156, 178, 29, 14, 59, 74, 0, 16, 224, 66, 228, 64, 57, 7, - 169, 53, 242, 189, 81, 114, 250, 134, 33, 248, 113, 165, 82, 26, 177, 2, 141, 177, 128, 198, 60, 15, 63, 245, 219, - 211, 23, 215, 255, 139, 15, 251, 211, 112, 180, 28, 157, 212, 189, 100, 82, 179, 64, 170, 63, 109, 235, 190, 204, 135, - 166, 178, 150, 216, 62, 154, 252, 250, 70, 147, 35, 220, 119, 93, 227, 4, 182, 131, 81, 25, 36, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 65, 10, 132, 48, 12, 76, 218, 237, 174, 123, 242, 11, 130, 62, 160, 250, + 2, 255, 34, 222, 20, 61, 250, 124, 11, 78, 49, 4, 193, 131, 21, 52, 16, 210, 132, 105, 50, 77, 210, 140, 136, 152, 54, + 177, 65, 13, 206, 12, 95, 74, 196, 181, 176, 254, 154, 212, 156, 46, 151, 191, 139, 163, 121, 1, 71, 123, 3, 199, 184, + 15, 15, 157, 119, 202, 185, 36, 237, 159, 61, 248, 63, 159, 160, 46, 232, 23, 254, 15, 54, 67, 156, 96, 11, 213, 119, + 82, 248, 116, 179, 104, 188, 163, 125, 15, 89, 213, 253, 139, 154, 221, 52, 206, 67, 191, 88, 5, 213, 52, 75, 113, + 174, 96, 205, 201, 157, 24, 207, 197, 211, 157, 6, 50, 18, 233, 158, 72, 89, 1, 53, 215, 75, 175, 196, 4, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/noir/acvm-repo/acvm_js/test/shared/foreign_call.ts b/noir/acvm-repo/acvm_js/test/shared/foreign_call.ts index 178ec3a09d1..7a65f29117c 100644 --- a/noir/acvm-repo/acvm_js/test/shared/foreign_call.ts +++ b/noir/acvm-repo/acvm_js/test/shared/foreign_call.ts @@ -2,10 +2,10 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `simple_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 49, 10, 64, 33, 12, 67, 99, 63, 124, 60, 142, 222, 192, 203, 56, 184, 56, - 136, 120, 126, 5, 21, 226, 160, 139, 62, 40, 13, 45, 132, 68, 3, 80, 232, 124, 164, 153, 121, 115, 99, 155, 59, 172, - 122, 231, 101, 56, 175, 80, 86, 221, 230, 31, 58, 196, 226, 83, 62, 53, 91, 16, 122, 10, 246, 84, 99, 243, 0, 30, 59, - 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 12, 68, 207, 148, 150, 118, 234, 175, 216, 63, 232, + 207, 116, 232, 210, 161, 136, 223, 175, 98, 132, 27, 212, 69, 31, 132, 28, 23, 8, 119, 59, 0, 131, 204, 66, 154, 41, + 222, 173, 219, 142, 113, 153, 121, 191, 44, 231, 21, 237, 144, 88, 43, 249, 11, 71, 156, 77, 245, 251, 249, 231, 119, + 189, 214, 204, 89, 187, 11, 25, 130, 54, 1, 36, 1, 124, 242, 107, 1, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000005'], diff --git a/noir/acvm-repo/brillig/src/black_box.rs b/noir/acvm-repo/brillig/src/black_box.rs index 22fac6f3ba3..9195d8aa6f3 100644 --- a/noir/acvm-repo/brillig/src/black_box.rs +++ b/noir/acvm-repo/brillig/src/black_box.rs @@ -1,4 +1,4 @@ -use crate::{opcodes::HeapVector, HeapArray, RegisterIndex}; +use crate::{opcodes::HeapVector, HeapArray, MemoryAddress}; use serde::{Deserialize, Serialize}; /// These opcodes provide an equivalent of ACIR blackbox functions. @@ -36,7 +36,7 @@ pub enum BlackBoxOp { public_key_x: HeapArray, public_key_y: HeapArray, signature: HeapArray, - result: RegisterIndex, + result: MemoryAddress, }, /// Verifies a ECDSA signature over the secp256r1 curve. EcdsaSecp256r1 { @@ -44,75 +44,75 @@ pub enum BlackBoxOp { public_key_x: HeapArray, public_key_y: HeapArray, signature: HeapArray, - result: RegisterIndex, + result: MemoryAddress, }, /// Verifies a Schnorr signature over a curve which is "pairing friendly" with the curve on which the Brillig bytecode is defined. SchnorrVerify { - public_key_x: RegisterIndex, - public_key_y: RegisterIndex, + public_key_x: MemoryAddress, + public_key_y: MemoryAddress, message: HeapVector, signature: HeapVector, - result: RegisterIndex, + result: MemoryAddress, }, /// Calculates a Pedersen commitment to the inputs. PedersenCommitment { inputs: HeapVector, - domain_separator: RegisterIndex, + domain_separator: MemoryAddress, output: HeapArray, }, /// Calculates a Pedersen hash to the inputs. PedersenHash { inputs: HeapVector, - domain_separator: RegisterIndex, - output: RegisterIndex, + domain_separator: MemoryAddress, + output: MemoryAddress, }, /// Performs scalar multiplication over the embedded curve. FixedBaseScalarMul { - low: RegisterIndex, - high: RegisterIndex, + low: MemoryAddress, + high: MemoryAddress, result: HeapArray, }, /// Performs addition over the embedded curve. EmbeddedCurveAdd { - input1_x: RegisterIndex, - input1_y: RegisterIndex, - input2_x: RegisterIndex, - input2_y: RegisterIndex, + input1_x: MemoryAddress, + input1_y: MemoryAddress, + input2_x: MemoryAddress, + input2_y: MemoryAddress, result: HeapArray, }, BigIntAdd { - lhs: RegisterIndex, - rhs: RegisterIndex, - output: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, }, BigIntNeg { - lhs: RegisterIndex, - rhs: RegisterIndex, - output: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, }, BigIntMul { - lhs: RegisterIndex, - rhs: RegisterIndex, - output: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, }, BigIntDiv { - lhs: RegisterIndex, - rhs: RegisterIndex, - output: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, }, BigIntFromLeBytes { inputs: HeapVector, modulus: HeapVector, - output: RegisterIndex, + output: MemoryAddress, }, BigIntToLeBytes { - input: RegisterIndex, + input: MemoryAddress, output: HeapVector, }, Poseidon2Permutation { message: HeapVector, output: HeapArray, - len: RegisterIndex, + len: MemoryAddress, }, Sha256Compression { input: HeapVector, diff --git a/noir/acvm-repo/brillig/src/lib.rs b/noir/acvm-repo/brillig/src/lib.rs index 5e033e3c792..2e9d80e2f3b 100644 --- a/noir/acvm-repo/brillig/src/lib.rs +++ b/noir/acvm-repo/brillig/src/lib.rs @@ -17,9 +17,7 @@ mod value; pub use black_box::BlackBoxOp; pub use foreign_call::{ForeignCallParam, ForeignCallResult}; -pub use opcodes::{ - BinaryFieldOp, BinaryIntOp, HeapArray, HeapVector, RegisterIndex, RegisterOrMemory, -}; +pub use opcodes::{BinaryFieldOp, BinaryIntOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}; pub use opcodes::{BrilligOpcode as Opcode, Label}; pub use value::Typ; pub use value::Value; diff --git a/noir/acvm-repo/brillig/src/opcodes.rs b/noir/acvm-repo/brillig/src/opcodes.rs index 79295cc6e5d..9a6f11c463f 100644 --- a/noir/acvm-repo/brillig/src/opcodes.rs +++ b/noir/acvm-repo/brillig/src/opcodes.rs @@ -4,100 +4,106 @@ use serde::{Deserialize, Serialize}; pub type Label = usize; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct RegisterIndex(pub usize); +pub struct MemoryAddress(pub usize); -/// `RegisterIndex` refers to the index in VM register space. -impl RegisterIndex { +/// `MemoryAddress` refers to the index in VM memory. +impl MemoryAddress { pub fn to_usize(self) -> usize { self.0 } } -impl From for RegisterIndex { +impl From for MemoryAddress { fn from(value: usize) -> Self { - RegisterIndex(value) + MemoryAddress(value) } } -/// A fixed-sized array starting from a Brillig register memory location. +/// A fixed-sized array starting from a Brillig memory location. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub struct HeapArray { - pub pointer: RegisterIndex, + pub pointer: MemoryAddress, pub size: usize, } -/// A register-sized vector passed starting from a Brillig register memory location and with a register-held size +/// A memory-sized vector passed starting from a Brillig memory location and with a memory-held size #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub struct HeapVector { - pub pointer: RegisterIndex, - pub size: RegisterIndex, + pub pointer: MemoryAddress, + pub size: MemoryAddress, } /// Lays out various ways an external foreign call's input and output data may be interpreted inside Brillig. -/// This data can either be an individual register value or memory. +/// This data can either be an individual value or memory. /// /// While we are usually agnostic to how memory is passed within Brillig, /// this needs to be encoded somehow when dealing with an external system. /// For simplicity, the extra type information is given right in the ForeignCall instructions. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] -pub enum RegisterOrMemory { - /// A single register value passed to or from an external call - /// It is an 'immediate' value - used without dereferencing memory. - /// For a foreign call input, the value is read directly from the register. - /// For a foreign call output, the value is written directly to the register. - RegisterIndex(RegisterIndex), +pub enum ValueOrArray { + /// A single value passed to or from an external call + /// It is an 'immediate' value - used without dereferencing. + /// For a foreign call input, the value is read directly from memory. + /// For a foreign call output, the value is written directly to memory. + MemoryAddress(MemoryAddress), /// An array passed to or from an external call /// In the case of a foreign call input, the array is read from this Brillig memory location + usize more cells. /// In the case of a foreign call output, the array is written to this Brillig memory location with the usize being here just as a sanity check for the size write. HeapArray(HeapArray), /// A vector passed to or from an external call - /// In the case of a foreign call input, the vector is read from this Brillig memory location + as many cells as the 2nd register indicates. - /// In the case of a foreign call output, the vector is written to this Brillig memory location and as 'size' cells, with size being stored in the second register. + /// In the case of a foreign call input, the vector is read from this Brillig memory location + as many cells as the 2nd address indicates. + /// In the case of a foreign call output, the vector is written to this Brillig memory location and as 'size' cells, with size being stored in the second address. HeapVector(HeapVector), } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum BrilligOpcode { - /// Takes the fields in registers `lhs` and `rhs` + /// Takes the fields in addresses `lhs` and `rhs` /// Performs the specified binary operation - /// and stores the value in the `result` register. + /// and stores the value in the `result` address. BinaryFieldOp { - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryFieldOp, - lhs: RegisterIndex, - rhs: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, }, - /// Takes the `bit_size` size integers in registers `lhs` and `rhs` + /// Takes the `bit_size` size integers in addresses `lhs` and `rhs` /// Performs the specified binary operation - /// and stores the value in the `result` register. + /// and stores the value in the `result` address. BinaryIntOp { - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryIntOp, bit_size: u32, - lhs: RegisterIndex, - rhs: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, }, JumpIfNot { - condition: RegisterIndex, + condition: MemoryAddress, location: Label, }, /// Sets the program counter to the value located at `destination` /// If the value at `condition` is non-zero JumpIf { - condition: RegisterIndex, + condition: MemoryAddress, location: Label, }, /// Sets the program counter to the label. Jump { location: Label, }, + /// Copies calldata after the offset to the specified address and length + CalldataCopy { + destination_address: MemoryAddress, + size: usize, + offset: usize, + }, /// We don't support dynamic jumps or calls /// See https://github.com/ethereum/aleth/issues/3404 for reasoning Call { location: Label, }, Const { - destination: RegisterIndex, + destination: MemoryAddress, value: Value, }, Return, @@ -109,28 +115,31 @@ pub enum BrilligOpcode { /// Interpreted by caller context, ie this will have different meanings depending on /// who the caller is. function: String, - /// Destination registers (may be single values or memory pointers). - destinations: Vec, - /// Input registers (may be single values or memory pointers). - inputs: Vec, + /// Destination addresses (may be single values or memory pointers). + destinations: Vec, + /// Input addresses (may be single values or memory pointers). + inputs: Vec, }, Mov { - destination: RegisterIndex, - source: RegisterIndex, + destination: MemoryAddress, + source: MemoryAddress, }, Load { - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, }, Store { - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, }, BlackBox(BlackBoxOp), /// Used to denote execution failure Trap, - /// Stop execution - Stop, + /// Stop execution, returning data after the offset + Stop { + return_data_offset: usize, + return_data_size: usize, + }, } /// Binary fixed-length field expressions diff --git a/noir/acvm-repo/brillig/src/value.rs b/noir/acvm-repo/brillig/src/value.rs index 73a7d897eb7..5a532cbc1a7 100644 --- a/noir/acvm-repo/brillig/src/value.rs +++ b/noir/acvm-repo/brillig/src/value.rs @@ -37,8 +37,8 @@ impl Value { /// Panics: If `Value` cannot fit into a u64 or `Value` does //// not fit into a usize. pub fn to_usize(&self) -> usize { - usize::try_from(self.inner.try_to_u64().expect("register does not fit into u64")) - .expect("register does not fit into usize") + usize::try_from(self.inner.try_to_u64().expect("value does not fit into u64")) + .expect("value does not fit into usize") } } diff --git a/noir/acvm-repo/brillig_vm/src/black_box.rs b/noir/acvm-repo/brillig_vm/src/black_box.rs index e9c25200c47..de20d194dde 100644 --- a/noir/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/acvm-repo/brillig_vm/src/black_box.rs @@ -5,23 +5,14 @@ use acvm_blackbox_solver::{ sha256, BlackBoxFunctionSolver, BlackBoxResolutionError, }; -use crate::{Memory, Registers}; +use crate::Memory; -fn read_heap_vector<'a>( - memory: &'a Memory, - registers: &Registers, - vector: &HeapVector, -) -> &'a [Value] { - memory - .read_slice(registers.get(vector.pointer).to_usize(), registers.get(vector.size).to_usize()) +fn read_heap_vector<'a>(memory: &'a Memory, vector: &HeapVector) -> &'a [Value] { + memory.read_slice(memory.read_ref(vector.pointer), memory.read(vector.size).to_usize()) } -fn read_heap_array<'a>( - memory: &'a Memory, - registers: &Registers, - array: &HeapArray, -) -> &'a [Value] { - memory.read_slice(registers.get(array.pointer).to_usize(), array.size) +fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [Value] { + memory.read_slice(memory.read_ref(array.pointer), array.size) } /// Extracts the last byte of every value @@ -42,36 +33,35 @@ fn to_value_vec(input: &[u8]) -> Vec { pub(crate) fn evaluate_black_box( op: &BlackBoxOp, solver: &Solver, - registers: &mut Registers, memory: &mut Memory, ) -> Result<(), BlackBoxResolutionError> { match op { BlackBoxOp::Sha256 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = sha256(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Blake2s { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = blake2s(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Blake3 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = blake3(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Keccak256 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = keccak256(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Keccakf1600 { message, output } => { - let state_vec: Vec = read_heap_vector(memory, registers, message) + let state_vec: Vec = read_heap_vector(memory, message) .iter() .map(|value| value.to_field().try_to_u64().unwrap()) .collect(); @@ -81,7 +71,7 @@ pub(crate) fn evaluate_black_box( let new_state: Vec = new_state.into_iter().map(|x| Value::from(x as usize)).collect(); - memory.write_slice(registers.get(output.pointer).to_usize(), &new_state); + memory.write_slice(memory.read_ref(output.pointer), &new_state); Ok(()) } BlackBoxOp::EcdsaSecp256k1 { @@ -89,42 +79,37 @@ pub(crate) fn evaluate_black_box( public_key_x, public_key_y, signature, - result: result_register, + result: result_address, } | BlackBoxOp::EcdsaSecp256r1 { hashed_msg, public_key_x, public_key_y, signature, - result: result_register, + result: result_address, } => { let bb_func = black_box_function_from_op(op); - let public_key_x: [u8; 32] = to_u8_vec(read_heap_array( - memory, - registers, - public_key_x, - )) - .try_into() - .map_err(|_| { - BlackBoxResolutionError::Failed(bb_func, "Invalid public key x length".to_string()) - })?; - let public_key_y: [u8; 32] = to_u8_vec(read_heap_array( - memory, - registers, - public_key_y, - )) - .try_into() - .map_err(|_| { - BlackBoxResolutionError::Failed(bb_func, "Invalid public key y length".to_string()) - })?; - let signature: [u8; 64] = to_u8_vec(read_heap_array(memory, registers, signature)) - .try_into() - .map_err(|_| { + let public_key_x: [u8; 32] = + to_u8_vec(read_heap_array(memory, public_key_x)).try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + bb_func, + "Invalid public key x length".to_string(), + ) + })?; + let public_key_y: [u8; 32] = + to_u8_vec(read_heap_array(memory, public_key_y)).try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + bb_func, + "Invalid public key y length".to_string(), + ) + })?; + let signature: [u8; 64] = + to_u8_vec(read_heap_array(memory, signature)).try_into().map_err(|_| { BlackBoxResolutionError::Failed(bb_func, "Invalid signature length".to_string()) })?; - let hashed_msg = to_u8_vec(read_heap_vector(memory, registers, hashed_msg)); + let hashed_msg = to_u8_vec(read_heap_vector(memory, hashed_msg)); let result = match op { BlackBoxOp::EcdsaSecp256k1 { .. } => { @@ -136,61 +121,61 @@ pub(crate) fn evaluate_black_box( _ => unreachable!("`BlackBoxOp` is guarded against being a non-ecdsa operation"), }; - registers.set(*result_register, result.into()); + memory.write(*result_address, result.into()); Ok(()) } BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, message, signature, result } => { - let public_key_x = registers.get(*public_key_x).to_field(); - let public_key_y = registers.get(*public_key_y).to_field(); - let message: Vec = to_u8_vec(read_heap_vector(memory, registers, message)); - let signature: Vec = to_u8_vec(read_heap_vector(memory, registers, signature)); + let public_key_x = memory.read(*public_key_x).to_field(); + let public_key_y = memory.read(*public_key_y).to_field(); + let message: Vec = to_u8_vec(read_heap_vector(memory, message)); + let signature: Vec = to_u8_vec(read_heap_vector(memory, signature)); let verified = solver.schnorr_verify(&public_key_x, &public_key_y, &signature, &message)?; - registers.set(*result, verified.into()); + memory.write(*result, verified.into()); Ok(()) } BlackBoxOp::FixedBaseScalarMul { low, high, result } => { - let low = registers.get(*low).to_field(); - let high = registers.get(*high).to_field(); + let low = memory.read(*low).to_field(); + let high = memory.read(*high).to_field(); let (x, y) = solver.fixed_base_scalar_mul(&low, &high)?; - memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { - let input1_x = registers.get(*input1_x).to_field(); - let input1_y = registers.get(*input1_y).to_field(); - let input2_x = registers.get(*input2_x).to_field(); - let input2_y = registers.get(*input2_y).to_field(); + let input1_x = memory.read(*input1_x).to_field(); + let input1_y = memory.read(*input1_y).to_field(); + let input2_x = memory.read(*input2_x).to_field(); + let input2_y = memory.read(*input2_y).to_field(); let (x, y) = solver.ec_add(&input1_x, &input1_y, &input2_x, &input2_y)?; - memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); let domain_separator: u32 = - registers.get(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).to_u128().try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), ) })?; let (x, y) = solver.pedersen_commitment(&inputs, domain_separator)?; - memory.write_slice(registers.get(output.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(output.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); let domain_separator: u32 = - registers.get(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).to_u128().try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), ) })?; let hash = solver.pedersen_hash(&inputs, domain_separator)?; - registers.set(*output, hash.into()); + memory.write(*output, hash.into()); Ok(()) } BlackBoxOp::BigIntAdd { .. } => todo!(), @@ -231,11 +216,11 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { #[cfg(test)] mod test { - use acir::brillig::BlackBoxOp; + use acir::brillig::{BlackBoxOp, MemoryAddress}; use crate::{ black_box::{evaluate_black_box, to_u8_vec, to_value_vec}, - DummyBlackBoxSolver, HeapArray, HeapVector, Memory, Registers, Value, + DummyBlackBoxSolver, HeapArray, HeapVector, Memory, }; #[test] @@ -243,27 +228,22 @@ mod test { let message: Vec = b"hello world".to_vec(); let message_length = message.len(); - let mut memory = Memory::from(vec![]); - let message_pointer = 0; + let mut memory = Memory::default(); + let message_pointer = 3; let result_pointer = message_pointer + message_length; - memory.write_slice(message_pointer, to_value_vec(&message).as_slice()); - - let mut registers = Registers { - inner: vec![ - Value::from(message_pointer), - Value::from(message_length), - Value::from(result_pointer), - ], - }; + memory.write(MemoryAddress(0), message_pointer.into()); + memory.write(MemoryAddress(1), message_length.into()); + memory.write(MemoryAddress(2), result_pointer.into()); + memory.write_slice(MemoryAddress(message_pointer), to_value_vec(&message).as_slice()); let op = BlackBoxOp::Sha256 { message: HeapVector { pointer: 0.into(), size: 1.into() }, output: HeapArray { pointer: 2.into(), size: 32 }, }; - evaluate_black_box(&op, &DummyBlackBoxSolver, &mut registers, &mut memory).unwrap(); + evaluate_black_box(&op, &DummyBlackBoxSolver, &mut memory).unwrap(); - let result = memory.read_slice(result_pointer, 32); + let result = memory.read_slice(MemoryAddress(result_pointer), 32); assert_eq!( to_u8_vec(result), diff --git a/noir/acvm-repo/brillig_vm/src/lib.rs b/noir/acvm-repo/brillig_vm/src/lib.rs index 0c258ab34d6..0c90fcb1416 100644 --- a/noir/acvm-repo/brillig_vm/src/lib.rs +++ b/noir/acvm-repo/brillig_vm/src/lib.rs @@ -12,8 +12,8 @@ //! [acvm]: https://crates.io/crates/acvm use acir::brillig::{ - BinaryFieldOp, BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapArray, HeapVector, Opcode, - RegisterIndex, RegisterOrMemory, Value, + BinaryFieldOp, BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapArray, HeapVector, + MemoryAddress, Opcode, Value, ValueOrArray, }; use acir::FieldElement; // Re-export `brillig`. @@ -22,7 +22,6 @@ pub use acir::brillig; mod arithmetic; mod black_box; mod memory; -mod registers; use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; use arithmetic::{evaluate_binary_bigint_op, evaluate_binary_field_op}; @@ -30,14 +29,16 @@ use black_box::evaluate_black_box; pub use memory::Memory; use num_bigint::BigUint; -pub use registers::Registers; /// The error call stack contains the opcode indexes of the call stack at the time of failure, plus the index of the opcode that failed. pub type ErrorCallStack = Vec; #[derive(Debug, PartialEq, Eq, Clone)] pub enum VMStatus { - Finished, + Finished { + return_data_offset: usize, + return_data_size: usize, + }, InProgress, Failure { message: String, @@ -61,8 +62,8 @@ pub enum VMStatus { #[derive(Debug, PartialEq, Eq, Clone)] /// VM encapsulates the state of the Brillig VM during execution. pub struct VM<'a, B: BlackBoxFunctionSolver> { - /// Register storage - registers: Registers, + /// Calldata to the brillig function + calldata: Vec, /// Instruction pointer program_counter: usize, /// A counter maintained throughout a Brillig process that determines @@ -86,20 +87,19 @@ pub struct VM<'a, B: BlackBoxFunctionSolver> { impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Constructs a new VM instance pub fn new( - inputs: Registers, - memory: Vec, + calldata: Vec, bytecode: &'a [Opcode], foreign_call_results: Vec, black_box_solver: &'a B, ) -> Self { Self { - registers: inputs, + calldata, program_counter: 0, foreign_call_counter: 0, foreign_call_results, bytecode, status: VMStatus::InProgress, - memory: memory.into(), + memory: Memory::default(), call_stack: Vec::new(), black_box_solver, } @@ -117,8 +117,8 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } /// Sets the current status of the VM to Finished (completed execution). - fn finish(&mut self) -> VMStatus { - self.status(VMStatus::Finished) + fn finish(&mut self, return_data_offset: usize, return_data_size: usize) -> VMStatus { + self.status(VMStatus::Finished { return_data_offset, return_data_size }) } /// Sets the status of the VM to `ForeignCallWait`. @@ -154,26 +154,17 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { pub fn process_opcodes(&mut self) -> VMStatus { while !matches!( self.process_opcode(), - VMStatus::Finished | VMStatus::Failure { .. } | VMStatus::ForeignCallWait { .. } + VMStatus::Finished { .. } | VMStatus::Failure { .. } | VMStatus::ForeignCallWait { .. } ) {} self.status.clone() } - /// Returns all of the registers in the VM. - pub fn get_registers(&self) -> &Registers { - &self.registers - } - - pub fn set_register(&mut self, register_index: RegisterIndex, value: Value) { - self.registers.set(register_index, value); - } - pub fn get_memory(&self) -> &[Value] { self.memory.values() } pub fn write_memory_at(&mut self, ptr: usize, value: Value) { - self.memory.write(ptr, value); + self.memory.write(MemoryAddress(ptr), value); } /// Process a single opcode and modify the program counter. @@ -196,22 +187,27 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { Opcode::JumpIf { condition, location: destination } => { // Check if condition is true // We use 0 to mean false and any other value to mean true - let condition_value = self.registers.get(*condition); + let condition_value = self.memory.read(*condition); if !condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } Opcode::JumpIfNot { condition, location: destination } => { - let condition_value = self.registers.get(*condition); + let condition_value = self.memory.read(*condition); if condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } + Opcode::CalldataCopy { destination_address, size, offset } => { + let values = &self.calldata[*offset..(*offset + size)]; + self.memory.write_slice(*destination_address, values); + self.increment_program_counter() + } Opcode::Return => { - if let Some(register) = self.call_stack.pop() { - self.set_program_counter(register.to_usize() + 1) + if let Some(return_location) = self.call_stack.pop() { + self.set_program_counter(return_location.to_usize() + 1) } else { self.fail("return opcode hit, but callstack already empty".to_string()) } @@ -227,7 +223,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { // but has the necessary results to proceed with execution. let resolved_inputs = inputs .iter() - .map(|input| self.get_register_value_or_memory_values(*input)) + .map(|input| self.get_memory_values(*input)) .collect::>(); return self.wait_for_foreign_call(function.clone(), resolved_inputs); } @@ -237,15 +233,15 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let mut invalid_foreign_call_result = false; for (destination, output) in destinations.iter().zip(values) { match destination { - RegisterOrMemory::RegisterIndex(value_index) => match output { + ValueOrArray::MemoryAddress(value_index) => match output { ForeignCallParam::Single(value) => { - self.registers.set(*value_index, *value); + self.memory.write(*value_index, *value); } _ => unreachable!( "Function result size does not match brillig bytecode (expected 1 result)" ), }, - RegisterOrMemory::HeapArray(HeapArray { pointer: pointer_index, size }) => { + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }) => { match output { ForeignCallParam::Array(values) => { if values.len() != *size { @@ -253,7 +249,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { break; } // Convert the destination pointer to a usize - let destination = self.registers.get(*pointer_index).to_usize(); + let destination = self.memory.read_ref(*pointer_index); // Write to our destination memory self.memory.write_slice(destination, values); } @@ -262,13 +258,13 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } } } - RegisterOrMemory::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { + ValueOrArray::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { match output { ForeignCallParam::Array(values) => { - // Set our size in the size register - self.registers.set(*size_index, Value::from(values.len())); + // Set our size in the size address + self.memory.write(*size_index, Value::from(values.len())); // Convert the destination pointer to a usize - let destination = self.registers.get(*pointer_index).to_usize(); + let destination = self.memory.read_ref(*pointer_index); // Write to our destination memory self.memory.write_slice(destination, values); } @@ -291,26 +287,28 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.foreign_call_counter += 1; self.increment_program_counter() } - Opcode::Mov { destination: destination_register, source: source_register } => { - let source_value = self.registers.get(*source_register); - self.registers.set(*destination_register, source_value); + Opcode::Mov { destination: destination_address, source: source_address } => { + let source_value = self.memory.read(*source_address); + self.memory.write(*destination_address, source_value); self.increment_program_counter() } Opcode::Trap => self.fail("explicit trap hit in brillig".to_string()), - Opcode::Stop => self.finish(), - Opcode::Load { destination: destination_register, source_pointer } => { - // Convert our source_pointer to a usize - let source = self.registers.get(*source_pointer); + Opcode::Stop { return_data_offset, return_data_size } => { + self.finish(*return_data_offset, *return_data_size) + } + Opcode::Load { destination: destination_address, source_pointer } => { + // Convert our source_pointer to an address + let source = self.memory.read_ref(*source_pointer); // Use our usize source index to lookup the value in memory - let value = &self.memory.read(source.to_usize()); - self.registers.set(*destination_register, *value); + let value = &self.memory.read(source); + self.memory.write(*destination_address, *value); self.increment_program_counter() } - Opcode::Store { destination_pointer, source: source_register } => { - // Convert our destination_pointer to a usize - let destination = self.registers.get(*destination_pointer).to_usize(); + Opcode::Store { destination_pointer, source: source_address } => { + // Convert our destination_pointer to an address + let destination = self.memory.read_ref(*destination_pointer); // Use our usize destination index to set the value in memory - self.memory.write(destination, self.registers.get(*source_register)); + self.memory.write(destination, self.memory.read(*source_address)); self.increment_program_counter() } Opcode::Call { location } => { @@ -319,16 +317,11 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.set_program_counter(*location) } Opcode::Const { destination, value } => { - self.registers.set(*destination, *value); + self.memory.write(*destination, *value); self.increment_program_counter() } Opcode::BlackBox(black_box_op) => { - match evaluate_black_box( - black_box_op, - self.black_box_solver, - &mut self.registers, - &mut self.memory, - ) { + match evaluate_black_box(black_box_op, self.black_box_solver, &mut self.memory) { Ok(()) => self.increment_program_counter(), Err(e) => self.fail(e.to_string()), } @@ -353,25 +346,22 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { assert!(self.program_counter < self.bytecode.len()); self.program_counter = value; if self.program_counter >= self.bytecode.len() { - self.status = VMStatus::Finished; + self.status = VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }; } self.status.clone() } - fn get_register_value_or_memory_values(&self, input: RegisterOrMemory) -> ForeignCallParam { + fn get_memory_values(&self, input: ValueOrArray) -> ForeignCallParam { match input { - RegisterOrMemory::RegisterIndex(value_index) => self.registers.get(value_index).into(), - RegisterOrMemory::HeapArray(HeapArray { pointer: pointer_index, size }) => { - let start = self.registers.get(pointer_index); - self.memory.read_slice(start.to_usize(), size).to_vec().into() + ValueOrArray::MemoryAddress(value_index) => self.memory.read(value_index).into(), + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }) => { + let start = self.memory.read_ref(pointer_index); + self.memory.read_slice(start, size).to_vec().into() } - RegisterOrMemory::HeapVector(HeapVector { - pointer: pointer_index, - size: size_index, - }) => { - let start = self.registers.get(pointer_index); - let size = self.registers.get(size_index); - self.memory.read_slice(start.to_usize(), size.to_usize()).to_vec().into() + ValueOrArray::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { + let start = self.memory.read_ref(pointer_index); + let size = self.memory.read(size_index); + self.memory.read_slice(start, size.to_usize()).to_vec().into() } } } @@ -381,17 +371,17 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { fn process_binary_field_op( &mut self, op: BinaryFieldOp, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, ) { - let lhs_value = self.registers.get(lhs); - let rhs_value = self.registers.get(rhs); + let lhs_value = self.memory.read(lhs); + let rhs_value = self.memory.read(rhs); let result_value = evaluate_binary_field_op(&op, lhs_value.to_field(), rhs_value.to_field()); - self.registers.set(result, result_value.into()); + self.memory.write(result, result_value.into()); } /// Process a binary operation. @@ -400,20 +390,20 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { &mut self, op: BinaryIntOp, bit_size: u32, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, ) -> Result<(), String> { - let lhs_value = self.registers.get(lhs); - let rhs_value = self.registers.get(rhs); + let lhs_value = self.memory.read(lhs); + let rhs_value = self.memory.read(rhs); // Convert to big integers let lhs_big = BigUint::from_bytes_be(&lhs_value.to_field().to_be_bytes()); let rhs_big = BigUint::from_bytes_be(&rhs_value.to_field().to_be_bytes()); let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size)?; // Convert back to field element - self.registers - .set(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); + self.memory + .write(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); Ok(()) } } @@ -468,111 +458,123 @@ mod tests { #[test] fn add_single_step_smoke() { - // Load values into registers and initialize the registers that - // will be used during bytecode processing - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); - - // Add opcode to add the value in register `0` and `1` - // and place the output in register `2` - let opcode = Opcode::BinaryIntOp { - op: BinaryIntOp::Add, - bit_size: 2, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + let calldata = vec![Value::from(27u128)]; + + // Add opcode to add the value in address `0` and `1` + // and place the output in address `2` + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 1, + offset: 0, }; // Start VM - let opcodes = [opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [calldata_copy]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); // Process a single VM opcode // // After processing a single opcode, we should have // the vm status as finished since there is only one opcode let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - // The register at index `2` should have the value of 3 since we had an + // The address at index `2` should have the value of 3 since we had an // add opcode - let VM { registers, .. } = vm; - let output_value = registers.get(RegisterIndex::from(2)); + let VM { memory, .. } = vm; + let output_value = memory.read(MemoryAddress::from(0)); - assert_eq!(output_value, Value::from(3u128)); + assert_eq!(output_value, Value::from(27u128)); } #[test] fn jmpif_opcode() { - let mut registers = vec![]; + let mut calldata = vec![]; let mut opcodes = vec![]; let lhs = { - registers.push(Value::from(2u128)); - RegisterIndex::from(registers.len() - 1) + calldata.push(Value::from(2u128)); + MemoryAddress::from(calldata.len() - 1) }; let rhs = { - registers.push(Value::from(2u128)); - RegisterIndex::from(registers.len() - 1) + calldata.push(Value::from(2u128)); + MemoryAddress::from(calldata.len() - 1) }; - let destination = { - registers.push(Value::from(0u128)); - RegisterIndex::from(registers.len() - 1) - }; + let destination = MemoryAddress::from(calldata.len()); + opcodes.push(Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }); let equal_cmp_opcode = Opcode::BinaryIntOp { op: BinaryIntOp::Equals, bit_size: 1, lhs, rhs, destination }; opcodes.push(equal_cmp_opcode); - opcodes.push(Opcode::Jump { location: 2 }); - opcodes.push(Opcode::JumpIf { condition: RegisterIndex::from(2), location: 3 }); + opcodes.push(Opcode::Jump { location: 3 }); + opcodes.push(Opcode::JumpIf { condition: MemoryAddress::from(2), location: 4 }); + + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); - let mut vm = - VM::new(Registers::load(registers), vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterIndex::from(2)); + let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_cmp_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); } #[test] fn jmpifnot_opcode() { - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); + let calldata = vec![Value::from(1u128), Value::from(2u128)]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }; + + let jump_opcode = Opcode::Jump { location: 3 }; let trap_opcode = Opcode::Trap; let not_equal_cmp_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; - let jump_opcode = Opcode::Jump { location: 2 }; - let jump_if_not_opcode = - Opcode::JumpIfNot { condition: RegisterIndex::from(2), location: 1 }; + Opcode::JumpIfNot { condition: MemoryAddress::from(2), location: 2 }; let add_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Add, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; - let opcodes = - [jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [ + calldata_copy, + jump_opcode, + trap_opcode, + not_equal_cmp_opcode, + jump_if_not_opcode, + add_opcode, + ]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -580,7 +582,7 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterIndex::from(2)); + let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_cmp_value, Value::from(false)); let status = vm.process_opcode(); @@ -591,107 +593,130 @@ mod tests { status, VMStatus::Failure { message: "explicit trap hit in brillig".to_string(), - call_stack: vec![1] + call_stack: vec![2] } ); - // The register at index `2` should have not changed as we jumped over the add opcode - let VM { registers, .. } = vm; - let output_value = registers.get(RegisterIndex::from(2)); + // The address at index `2` should have not changed as we jumped over the add opcode + let VM { memory, .. } = vm; + let output_value = memory.read(MemoryAddress::from(2)); assert_eq!(output_value, Value::from(false)); } #[test] fn mov_opcode() { - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); + let calldata = vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 3, + offset: 0, + }; let mov_opcode = - Opcode::Mov { destination: RegisterIndex::from(2), source: RegisterIndex::from(0) }; + Opcode::Mov { destination: MemoryAddress::from(2), source: MemoryAddress::from(0) }; + + let opcodes = &[calldata_copy, mov_opcode]; + let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); - let opcodes = &[mov_opcode]; - let mut vm = VM::new(input_registers, vec![], opcodes, vec![], &DummyBlackBoxSolver); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - let VM { registers, .. } = vm; + let VM { memory, .. } = vm; - let destination_value = registers.get(RegisterIndex::from(2)); + let destination_value = memory.read(MemoryAddress::from(2)); assert_eq!(destination_value, Value::from(1u128)); - let source_value = registers.get(RegisterIndex::from(0)); + let source_value = memory.read(MemoryAddress::from(0)); assert_eq!(source_value, Value::from(1u128)); } #[test] fn cmp_binary_ops() { let bit_size = 32; - let input_registers = Registers::load(vec![ + let calldata = vec![ Value::from(2u128), Value::from(2u128), Value::from(0u128), Value::from(5u128), Value::from(6u128), - ]); + ]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 5, + offset: 0, + }; let equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let not_equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(3), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(3), + destination: MemoryAddress::from(2), }; let less_than_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::LessThan, - lhs: RegisterIndex::from(3), - rhs: RegisterIndex::from(4), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(3), + rhs: MemoryAddress::from(4), + destination: MemoryAddress::from(2), }; let less_than_equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::LessThanEquals, - lhs: RegisterIndex::from(3), - rhs: RegisterIndex::from(4), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(3), + rhs: MemoryAddress::from(4), + destination: MemoryAddress::from(2), }; - let opcodes = [equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [ + calldata_copy, + equal_opcode, + not_equal_opcode, + less_than_opcode, + less_than_equal_opcode, + ]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_eq_value = vm.registers.get(RegisterIndex::from(2)); + let output_eq_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_eq_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_neq_value = vm.registers.get(RegisterIndex::from(2)); + let output_neq_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_neq_value, Value::from(false)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let lt_value = vm.registers.get(RegisterIndex::from(2)); + let lt_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(lt_value, Value::from(true)); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - let lte_value = vm.registers.get(RegisterIndex::from(2)); + let lte_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(lte_value, Value::from(true)); } #[test] @@ -703,20 +728,24 @@ mod tests { /// memory[i] = i as Value; /// i += 1; /// } - fn brillig_write_memory(memory: Vec) -> Vec { + fn brillig_write_memory(item_count: usize) -> Vec { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_tmp = RegisterIndex::from(2); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_tmp = MemoryAddress::from(2); + let r_pointer = MemoryAddress::from(3); + let start = [ // i = 0 Opcode::Const { destination: r_i, value: 0u128.into() }, // len = memory.len() (approximation) - Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + Opcode::Const { destination: r_len, value: Value::from(item_count as u128) }, + // pointer = free_memory_ptr + Opcode::Const { destination: r_pointer, value: 4u128.into() }, ]; let loop_body = [ // *i = i - Opcode::Store { destination_pointer: r_i, source: r_i }, + Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) @@ -727,6 +756,14 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // tmp = i < len Opcode::BinaryIntOp { destination: r_tmp, @@ -740,11 +777,11 @@ mod tests { ]; let opcodes = [&start[..], &loop_body[..]].concat(); - let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.get_memory().to_vec() + let vm = brillig_execute_and_get_vm(vec![], &opcodes); + vm.get_memory()[4..].to_vec() } - let memory = brillig_write_memory(vec![Value::from(0u128); 5]); + let memory = brillig_write_memory(5); let expected = vec![ Value::from(0u128), Value::from(1u128), @@ -754,7 +791,7 @@ mod tests { ]; assert_eq!(memory, expected); - let memory = brillig_write_memory(vec![Value::from(0u128); 1024]); + let memory = brillig_write_memory(1024); let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); assert_eq!(memory, expected); } @@ -771,10 +808,12 @@ mod tests { /// } fn brillig_sum_memory(memory: Vec) -> Value { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_sum = RegisterIndex::from(2); - let r_tmp = RegisterIndex::from(3); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_sum = MemoryAddress::from(2); + let r_tmp = MemoryAddress::from(3); + let r_pointer = MemoryAddress::from(4); + let start = [ // sum = 0 Opcode::Const { destination: r_sum, value: 0u128.into() }, @@ -782,10 +821,17 @@ mod tests { Opcode::Const { destination: r_i, value: 0u128.into() }, // len = array.len() (approximation) Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + // pointer = array_ptr + Opcode::Const { destination: r_pointer, value: 5u128.into() }, + Opcode::CalldataCopy { + destination_address: MemoryAddress(5), + size: memory.len(), + offset: 0, + }, ]; let loop_body = [ // tmp = *i - Opcode::Load { destination: r_tmp, source_pointer: r_i }, + Opcode::Load { destination: r_tmp, source_pointer: r_pointer }, // sum = sum + tmp Opcode::BinaryIntOp { destination: r_sum, @@ -804,6 +850,14 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // tmp = i < len Opcode::BinaryIntOp { destination: r_tmp, @@ -818,7 +872,7 @@ mod tests { let opcodes = [&start[..], &loop_body[..]].concat(); let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.registers.get(r_sum) + vm.memory.read(r_sum) } assert_eq!( @@ -844,21 +898,24 @@ mod tests { /// memory[i as usize] = i as Value; /// recursive_write(memory, i + 1, len); /// } - /// Note we represent a 100% in-register optimized form in brillig - fn brillig_recursive_write_memory(memory: Vec) -> Vec { + /// Note we represent a 100% in-stack optimized form in brillig + fn brillig_recursive_write_memory(size: usize) -> Vec { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_tmp = RegisterIndex::from(2); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_tmp = MemoryAddress::from(2); + let r_pointer = MemoryAddress::from(3); let start = [ // i = 0 Opcode::Const { destination: r_i, value: 0u128.into() }, - // len = memory.len() (approximation) - Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + // len = size + Opcode::Const { destination: r_len, value: size.into() }, + // pointer = free_memory_ptr + Opcode::Const { destination: r_pointer, value: 4u128.into() }, // call recursive_fn Opcode::Call { - location: 4, // Call after 'start' + location: 5, // Call after 'start' }, // end program by jumping to end Opcode::Jump { location: 100 }, @@ -876,10 +933,10 @@ mod tests { // if !tmp, goto end Opcode::JumpIf { condition: r_tmp, - location: start.len() + 6, // 7 ops in recursive_fn, go to 'Return' + location: start.len() + 7, // 8 ops in recursive_fn, go to 'Return' }, // *i = i - Opcode::Store { destination_pointer: r_i, source: r_i }, + Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) @@ -890,17 +947,25 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // call recursive_fn Opcode::Call { location: start.len() }, Opcode::Return {}, ]; let opcodes = [&start[..], &recursive_fn[..]].concat(); - let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.get_memory().to_vec() + let vm = brillig_execute_and_get_vm(vec![], &opcodes); + vm.get_memory()[4..].to_vec() } - let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 5]); + let memory = brillig_recursive_write_memory(5); let expected = vec![ Value::from(0u128), Value::from(1u128), @@ -910,20 +975,17 @@ mod tests { ]; assert_eq!(memory, expected); - let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 1024]); + let memory = brillig_recursive_write_memory(1024); let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); assert_eq!(memory, expected); } - fn empty_registers() -> Registers { - Registers::load(vec![Value::from(0u128); 16]) - } /// Helper to execute brillig code fn brillig_execute_and_get_vm( - memory: Vec, + calldata: Vec, opcodes: &[Opcode], ) -> VM<'_, DummyBlackBoxSolver> { - let mut vm = VM::new(empty_registers(), memory, opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); brillig_execute(&mut vm); assert_eq!(vm.call_stack, vec![]); vm @@ -932,7 +994,7 @@ mod tests { fn brillig_execute(vm: &mut VM) { loop { let status = vm.process_opcode(); - if matches!(status, VMStatus::Finished | VMStatus::ForeignCallWait { .. }) { + if matches!(status, VMStatus::Finished { .. } | VMStatus::ForeignCallWait { .. }) { break; } assert_eq!(status, VMStatus::InProgress); @@ -940,18 +1002,18 @@ mod tests { } #[test] - fn foreign_call_opcode_register_result() { - let r_input = RegisterIndex::from(0); - let r_result = RegisterIndex::from(1); + fn foreign_call_opcode_simple_result() { + let r_input = MemoryAddress::from(0); + let r_result = MemoryAddress::from(1); let double_program = vec![ - // Load input register with value 5 + // Load input address with value 5 Opcode::Const { destination: r_input, value: Value::from(5u128) }, - // Call foreign function "double" with the input register + // Call foreign function "double" with the input address Opcode::ForeignCall { function: "double".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(r_result)], - inputs: vec![RegisterOrMemory::RegisterIndex(r_input)], + destinations: vec![ValueOrArray::MemoryAddress(r_result)], + inputs: vec![ValueOrArray::MemoryAddress(r_input)], }, ]; @@ -975,19 +1037,20 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - // Check result register - let result_value = vm.registers.get(r_result); + // Check result address + let result_value = vm.memory.read(r_result); assert_eq!(result_value, Value::from(10u128)); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); } + #[test] fn foreign_call_opcode_memory_result() { - let r_input = RegisterIndex::from(0); - let r_output = RegisterIndex::from(1); + let r_input = MemoryAddress::from(0); + let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory let initial_matrix = @@ -998,18 +1061,23 @@ mod tests { vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; let invert_program = vec![ + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(2), + size: initial_matrix.len(), + offset: 0, + }, // input = 0 - Opcode::Const { destination: r_input, value: Value::from(0u128) }, + Opcode::Const { destination: r_input, value: 2_usize.into() }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(0u128) }, + Opcode::Const { destination: r_output, value: 2_usize.into() }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: initial_matrix.len(), })], - inputs: vec![RegisterOrMemory::HeapArray(HeapArray { + inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: initial_matrix.len(), })], @@ -1034,10 +1102,10 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(0, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1047,11 +1115,11 @@ mod tests { /// Calling a simple foreign call function that takes any string input, concatenates it with itself, and reverses the concatenation #[test] fn foreign_call_opcode_vector_input_and_output() { - let r_input_pointer = RegisterIndex::from(0); - let r_input_size = RegisterIndex::from(1); + let r_input_pointer = MemoryAddress::from(0); + let r_input_size = MemoryAddress::from(1); // We need to pass a location of appropriate size - let r_output_pointer = RegisterIndex::from(2); - let r_output_size = RegisterIndex::from(3); + let r_output_pointer = MemoryAddress::from(2); + let r_output_size = MemoryAddress::from(3); // Our first string to use the identity function with let input_string = @@ -1064,12 +1132,20 @@ mod tests { // First call: let string_double_program = vec![ - // input_pointer = 0 - Opcode::Const { destination: r_input_pointer, value: Value::from(0u128) }, + Opcode::CalldataCopy { + destination_address: MemoryAddress(4), + size: input_string.len(), + offset: 0, + }, + // input_pointer = 4 + Opcode::Const { destination: r_input_pointer, value: Value::from(4u128) }, // input_size = input_string.len() (constant here) Opcode::Const { destination: r_input_size, value: Value::from(input_string.len()) }, - // output_pointer = 0 + input_size = input_size - Opcode::Const { destination: r_output_pointer, value: Value::from(input_string.len()) }, + // output_pointer = 4 + input_size + Opcode::Const { + destination: r_output_pointer, + value: Value::from(4 + input_string.len()), + }, // output_size = input_size * 2 Opcode::Const { destination: r_output_size, @@ -1078,11 +1154,11 @@ mod tests { // output_pointer[0..output_size] = string_double(input_pointer[0...input_size]) Opcode::ForeignCall { function: "string_double".into(), - destinations: vec![RegisterOrMemory::HeapVector(HeapVector { + destinations: vec![ValueOrArray::HeapVector(HeapVector { pointer: r_output_pointer, size: r_output_size, })], - inputs: vec![RegisterOrMemory::HeapVector(HeapVector { + inputs: vec![ValueOrArray::HeapVector(HeapVector { pointer: r_input_pointer, size: r_input_size, })], @@ -1109,10 +1185,13 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(input_string.len(), output_string.len()).to_vec(); + let result_values = vm + .memory + .read_slice(MemoryAddress(4 + input_string.len()), output_string.len()) + .to_vec(); assert_eq!(result_values, output_string); // Ensure the foreign call counter has been incremented @@ -1121,8 +1200,8 @@ mod tests { #[test] fn foreign_call_opcode_memory_alloc_result() { - let r_input = RegisterIndex::from(0); - let r_output = RegisterIndex::from(1); + let r_input = MemoryAddress::from(0); + let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory let initial_matrix = @@ -1133,18 +1212,23 @@ mod tests { vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; let invert_program = vec![ + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(2), + size: initial_matrix.len(), + offset: 0, + }, // input = 0 - Opcode::Const { destination: r_input, value: Value::from(0u128) }, + Opcode::Const { destination: r_input, value: Value::from(2u128) }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(4u128) }, + Opcode::Const { destination: r_output, value: Value::from(6u128) }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: initial_matrix.len(), })], - inputs: vec![RegisterOrMemory::HeapArray(HeapArray { + inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: initial_matrix.len(), })], @@ -1169,14 +1253,14 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check initial memory still in place - let initial_values = vm.memory.read_slice(0, 4).to_vec(); + let initial_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); assert_eq!(initial_values, initial_matrix); // Check result in memory - let result_values = vm.memory.read_slice(4, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(6), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1185,9 +1269,9 @@ mod tests { #[test] fn foreign_call_opcode_multiple_array_inputs_result() { - let r_input_a = RegisterIndex::from(0); - let r_input_b = RegisterIndex::from(1); - let r_output = RegisterIndex::from(2); + let r_input_a = MemoryAddress::from(0); + let r_input_b = MemoryAddress::from(1); + let r_output = MemoryAddress::from(2); // Define a simple 2x2 matrix in memory let matrix_a = @@ -1209,28 +1293,27 @@ mod tests { ]; let matrix_mul_program = vec![ - // input = 0 - Opcode::Const { destination: r_input_a, value: Value::from(0u128) }, - // input = 0 - Opcode::Const { destination: r_input_b, value: Value::from(4u128) }, + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(3), + size: matrix_a.len() + matrix_b.len(), + offset: 0, + }, + // input = 3 + Opcode::Const { destination: r_input_a, value: Value::from(3u128) }, + // input = 7 + Opcode::Const { destination: r_input_b, value: Value::from(7u128) }, // output = 0 Opcode::Const { destination: r_output, value: Value::from(0u128) }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: matrix_a.len(), })], inputs: vec![ - RegisterOrMemory::HeapArray(HeapArray { - pointer: r_input_a, - size: matrix_a.len(), - }), - RegisterOrMemory::HeapArray(HeapArray { - pointer: r_input_b, - size: matrix_b.len(), - }), + ValueOrArray::HeapArray(HeapArray { pointer: r_input_a, size: matrix_a.len() }), + ValueOrArray::HeapArray(HeapArray { pointer: r_input_b, size: matrix_b.len() }), ], }, ]; @@ -1254,10 +1337,10 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(0, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(0), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented diff --git a/noir/acvm-repo/brillig_vm/src/memory.rs b/noir/acvm-repo/brillig_vm/src/memory.rs index 8a6993f1353..d1c81447170 100644 --- a/noir/acvm-repo/brillig_vm/src/memory.rs +++ b/noir/acvm-repo/brillig_vm/src/memory.rs @@ -1,41 +1,45 @@ +use acir::{brillig::MemoryAddress, FieldElement}; + use crate::Value; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Memory { // Memory is a vector of values. // We grow the memory when values past the end are set, extending with 0s. inner: Vec, } -impl From> for Memory { - fn from(values: Vec) -> Self { - Memory { inner: values } - } -} - impl Memory { /// Gets the value at pointer - pub fn read(&self, ptr: usize) -> Value { - self.inner[ptr] + pub fn read(&self, ptr: MemoryAddress) -> Value { + self.inner.get(ptr.to_usize()).copied().unwrap_or(0_u128.into()) } - pub fn read_slice(&self, ptr: usize, len: usize) -> &[Value] { - &self.inner[ptr..ptr + len] + pub fn read_ref(&self, ptr: MemoryAddress) -> MemoryAddress { + MemoryAddress(self.read(ptr).to_usize()) + } + + pub fn read_slice(&self, addr: MemoryAddress, len: usize) -> &[Value] { + &self.inner[addr.to_usize()..(addr.to_usize() + len)] } /// Sets the value at pointer `ptr` to `value` - pub fn write(&mut self, ptr: usize, value: Value) { - self.write_slice(ptr, &[value]); + pub fn write(&mut self, ptr: MemoryAddress, value: Value) { + self.resize_to_fit(ptr.to_usize() + 1); + self.inner[ptr.to_usize()] = value; } - /// Sets the values after pointer `ptr` to `values` - pub fn write_slice(&mut self, ptr: usize, values: &[Value]) { + fn resize_to_fit(&mut self, size: usize) { // Calculate new memory size - let new_size = std::cmp::max(self.inner.len(), ptr + values.len()); + let new_size = std::cmp::max(self.inner.len(), size); // Expand memory to new size with default values if needed - self.inner.resize(new_size, Value::from(0_usize)); + self.inner.resize(new_size, Value::from(FieldElement::zero())); + } - self.inner[ptr..ptr + values.len()].copy_from_slice(values); + /// Sets the values after pointer `ptr` to `values` + pub fn write_slice(&mut self, ptr: MemoryAddress, values: &[Value]) { + self.resize_to_fit(ptr.to_usize() + values.len()); + self.inner[ptr.to_usize()..(ptr.to_usize() + values.len())].copy_from_slice(values); } /// Returns the values of the memory diff --git a/noir/acvm-repo/brillig_vm/src/registers.rs b/noir/acvm-repo/brillig_vm/src/registers.rs deleted file mode 100644 index fcc596dd6c9..00000000000 --- a/noir/acvm-repo/brillig_vm/src/registers.rs +++ /dev/null @@ -1,43 +0,0 @@ -use acir::brillig::{RegisterIndex, Value}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Registers { - // Registers are a vector of values. - // We grow the register as registers past the end are set, extending with 0s. - pub inner: Vec, -} - -/// Aims to match a reasonable max register count for a SNARK prover. -/// As well, catches obvious erroneous use of registers. -/// This can be revisited if it proves not enough. -const MAX_REGISTERS: usize = 2_usize.pow(16); - -/// Registers will store field element values during the -/// duration of the execution of the bytecode. -impl Registers { - /// Create a Registers object initialized with definite values - pub fn load(values: Vec) -> Registers { - let inner = values.into_iter().collect(); - Self { inner } - } - - /// Gets the values at register with address `index` - pub fn get(&self, register_index: RegisterIndex) -> Value { - let index = register_index.to_usize(); - assert!(index < MAX_REGISTERS, "Reading register past maximum!"); - let value = self.inner.get(index); - match value { - Some(value) => *value, - None => 0u128.into(), - } - } - - /// Sets the value at register with address `index` to `value` - pub fn set(&mut self, RegisterIndex(index): RegisterIndex, value: Value) { - assert!(index < MAX_REGISTERS, "Writing register past maximum!"); - // if size isn't at least index + 1, resize - let new_register_size = std::cmp::max(index + 1, self.inner.len()); - self.inner.resize(new_register_size, 0u128.into()); - self.inner[index] = value; - } -} diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index b084042981b..ae8adeb10ec 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -13,7 +13,7 @@ use crate::ssa::ir::{ types::{NumericType, Type}, value::{Value, ValueId}, }; -use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, RegisterIndex, RegisterOrMemory}; +use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, ValueOrArray}; use acvm::brillig_vm::brillig::HeapVector; use acvm::FieldElement; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; @@ -342,16 +342,13 @@ impl<'block> BrilligBlock<'block> { ); for (i, output_register) in output_registers.iter().enumerate() { - if let RegisterOrMemory::HeapVector(HeapVector { size, .. }) = - output_register - { + if let ValueOrArray::HeapVector(HeapVector { size, .. }) = output_register { // Update the stack pointer so that we do not overwrite // dynamic memory returned from other external calls self.brillig_context.update_stack_pointer(*size); // Update the dynamic slice length maintained in SSA - if let RegisterOrMemory::RegisterIndex(len_index) = - output_registers[i - 1] + if let ValueOrArray::MemoryAddress(len_index) = output_registers[i - 1] { let element_size = dfg[result_ids[i]].get_type().element_size(); self.brillig_context.mov_instruction(len_index, *size); @@ -641,7 +638,7 @@ impl<'block> BrilligBlock<'block> { instruction_id: InstructionId, ) { // Convert the arguments to registers casting those to the types of the receiving function - let argument_registers: Vec = arguments + let argument_registers: Vec = arguments .iter() .flat_map(|argument_id| self.convert_ssa_value(*argument_id, dfg).extract_registers()) .collect(); @@ -677,7 +674,7 @@ impl<'block> BrilligBlock<'block> { }); // Collect the registers that should have been returned - let returned_registers: Vec = variables_assigned_to + let returned_registers: Vec = variables_assigned_to .iter() .flat_map(|returned_variable| returned_variable.extract_registers()) .collect(); @@ -695,7 +692,7 @@ impl<'block> BrilligBlock<'block> { fn validate_array_index( &mut self, array_variable: BrilligVariable, - index_register: RegisterIndex, + index_register: MemoryAddress, ) { let (size_as_register, should_deallocate_size) = match array_variable { BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { @@ -725,8 +722,8 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn retrieve_variable_from_array( &mut self, - array_pointer: RegisterIndex, - index_register: RegisterIndex, + array_pointer: MemoryAddress, + index_register: MemoryAddress, destination_variable: BrilligVariable, ) { match destination_variable { @@ -748,7 +745,7 @@ impl<'block> BrilligBlock<'block> { &mut self, source_variable: BrilligVariable, destination_variable: BrilligVariable, - index_register: RegisterIndex, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { let destination_pointer = match destination_variable { @@ -828,8 +825,8 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn store_variable_in_array_with_ctx( ctx: &mut BrilligContext, - destination_pointer: RegisterIndex, - index_register: RegisterIndex, + destination_pointer: MemoryAddress, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { match value_variable { @@ -837,7 +834,7 @@ impl<'block> BrilligBlock<'block> { ctx.array_set(destination_pointer, index_register, value_register); } BrilligVariable::BrilligArray(_) => { - let reference: RegisterIndex = ctx.allocate_register(); + let reference: MemoryAddress = ctx.allocate_register(); ctx.allocate_array_reference_instruction(reference); ctx.store_variable_instruction(reference, value_variable); ctx.array_set(destination_pointer, index_register, reference); @@ -855,8 +852,8 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn store_variable_in_array( &mut self, - destination_pointer: RegisterIndex, - index_register: RegisterIndex, + destination_pointer: MemoryAddress, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { Self::store_variable_in_array_with_ctx( @@ -1111,7 +1108,7 @@ impl<'block> BrilligBlock<'block> { /// of fields in the vector. fn update_slice_length( &mut self, - target_len: RegisterIndex, + target_len: MemoryAddress, source_value: ValueId, dfg: &DataFlowGraph, binary_op: BinaryIntOp, @@ -1124,7 +1121,7 @@ impl<'block> BrilligBlock<'block> { /// Converts an SSA cast to a sequence of Brillig opcodes. /// Casting is only necessary when shrinking the bit size of a numeric value. - fn convert_cast(&mut self, destination: RegisterIndex, source: RegisterIndex) { + fn convert_cast(&mut self, destination: MemoryAddress, source: MemoryAddress) { // We assume that `source` is a valid `target_type` as it's expected that a truncate instruction was emitted // to ensure this is the case. @@ -1136,7 +1133,7 @@ impl<'block> BrilligBlock<'block> { &mut self, binary: &Binary, dfg: &DataFlowGraph, - result_register: RegisterIndex, + result_register: MemoryAddress, ) { let binary_type = type_of_binary_operation(dfg[binary.lhs].get_type(), dfg[binary.rhs].get_type()); @@ -1233,12 +1230,12 @@ impl<'block> BrilligBlock<'block> { } } - /// Converts an SSA `ValueId` into a `RegisterIndex`. Initializes if necessary. + /// Converts an SSA `ValueId` into a `MemoryAddress`. Initializes if necessary. fn convert_ssa_register_value( &mut self, value_id: ValueId, dfg: &DataFlowGraph, - ) -> RegisterIndex { + ) -> MemoryAddress { let variable = self.convert_ssa_value(value_id, dfg); variable.extract_register() } @@ -1299,7 +1296,7 @@ impl<'block> BrilligBlock<'block> { fn convert_ssa_array_len( &mut self, array_id: ValueId, - result_register: RegisterIndex, + result_register: MemoryAddress, dfg: &DataFlowGraph, ) { let array_variable = self.convert_ssa_value(array_id, dfg); diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index f2e698c0aa9..49d40ca3697 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -1,4 +1,4 @@ -use acvm::brillig_vm::brillig::RegisterIndex; +use acvm::brillig_vm::brillig::MemoryAddress; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ @@ -75,7 +75,7 @@ impl BlockVariables { brillig_context: &mut BrilligContext, value: ValueId, dfg: &DataFlowGraph, - ) -> RegisterIndex { + ) -> MemoryAddress { let variable = self.define_variable(function_context, brillig_context, value, dfg); variable.extract_register() } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index a07865073ff..5ac2ecf06be 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -1,5 +1,5 @@ use acvm::acir::brillig::{ - BinaryFieldOp, BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, Value, + BinaryFieldOp, BinaryIntOp, MemoryAddress, Opcode as BrilligOpcode, Value, }; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; @@ -13,13 +13,14 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { // The input argument, ie the value that will be inverted. // We store the result in this register too. - let input = RegisterIndex::from(0); - let one_const = RegisterIndex::from(1); + let input = MemoryAddress::from(0); + let one_const = MemoryAddress::from(1); // Location of the stop opcode let stop_location = 3; GeneratedBrillig { byte_code: vec![ + BrilligOpcode::CalldataCopy { destination_address: input, size: 1, offset: 0 }, // If the input is zero, then we jump to the stop opcode BrilligOpcode::JumpIfNot { condition: input, location: stop_location }, // Put value one in register (1) @@ -31,7 +32,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { rhs: input, destination: input, }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, ], assert_messages: Default::default(), locations: Default::default(), @@ -52,36 +53,41 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { // `b` is (1) GeneratedBrillig { byte_code: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }, //q = a/b is set into register (2) BrilligOpcode::BinaryIntOp { op: BinaryIntOp::UnsignedDiv, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), bit_size, }, //(1)= q*b BrilligOpcode::BinaryIntOp { op: BinaryIntOp::Mul, - lhs: RegisterIndex::from(2), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(1), + lhs: MemoryAddress::from(2), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), bit_size, }, //(1) = a-q*b BrilligOpcode::BinaryIntOp { op: BinaryIntOp::Sub, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(1), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), bit_size, }, //(0) = q BrilligOpcode::Mov { - destination: RegisterIndex::from(0), - source: RegisterIndex::from(2), + destination: MemoryAddress::from(0), + source: MemoryAddress::from(2), }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, ], assert_messages: Default::default(), locations: Default::default(), diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 6402e6f9d97..981a16a01b2 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,4 +1,4 @@ -use acvm::brillig_vm::brillig::{BinaryIntOp, RegisterIndex}; +use acvm::brillig_vm::brillig::{BinaryIntOp, MemoryAddress}; use crate::brillig::brillig_ir::brillig_variable::{BrilligVariable, BrilligVector}; @@ -168,7 +168,7 @@ impl<'block> BrilligBlock<'block> { &mut self, target_vector: BrilligVector, source_vector: BrilligVector, - index: RegisterIndex, + index: MemoryAddress, items: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by items.len() @@ -240,7 +240,7 @@ impl<'block> BrilligBlock<'block> { &mut self, target_vector: BrilligVector, source_vector: BrilligVector, - index: RegisterIndex, + index: MemoryAddress, removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() @@ -328,7 +328,6 @@ mod tests { use std::vec; use acvm::acir::brillig::Value; - use acvm::brillig_vm::brillig::RegisterIndex; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; @@ -375,17 +374,15 @@ mod tests { fn test_case_push( push_back: bool, array: Vec, - expected_mem: Vec, item_to_push: Value, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len()), BrilligParameter::Simple, ]; - let returns = vec![ - BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1), - BrilligParameter::Simple, - ]; + let returns = + vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1)]; let (_, mut function_context, mut context) = create_test_environment(); @@ -423,55 +420,42 @@ mod tests { ); } - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm( - array.clone(), - vec![Value::from(0_usize), item_to_push], - &bytecode, + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(array.into_iter().chain(vec![item_to_push]).collect(), &bytecode); + assert_eq!(return_data_size, expected_return.len()); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return ); - - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() + 1)); } test_case_push( true, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), Value::from(27_usize), ], - Value::from(27_usize), ); - test_case_push(true, vec![], vec![Value::from(27_usize)], Value::from(27_usize)); + test_case_push(true, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); test_case_push( false, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(27_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), ); - test_case_push(false, vec![], vec![Value::from(27_usize)], Value::from(27_usize)); + test_case_push(false, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); } #[test] @@ -479,15 +463,14 @@ mod tests { fn test_case_pop( pop_back: bool, array: Vec, - expected_mem: Vec, - expected_removed_item: Value, + expected_return_array: Vec, + expected_return_item: Value, ) { let arguments = vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len())]; let returns = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() - 1), BrilligParameter::Simple, - BrilligParameter::Simple, ]; let (_, mut function_context, mut context) = create_test_environment(); @@ -526,51 +509,32 @@ mod tests { ); } - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - removed_item, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc, removed_item]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(array.clone(), vec![Value::from(0_usize)], &bytecode); - - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() - 1)); - assert_eq!(vm.get_registers().get(RegisterIndex(2)), expected_removed_item); + let expected_return: Vec<_> = + expected_return_array.into_iter().chain(vec![expected_return_item]).collect(); + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(array.clone(), &bytecode); + assert_eq!(return_data_size, expected_return.len()); + + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_pop( true, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(2_usize), - ], + vec![Value::from(1_usize), Value::from(2_usize)], Value::from(3_usize), ); - test_case_pop( - true, - vec![Value::from(1_usize)], - vec![Value::from(1_usize)], - Value::from(1_usize), - ); + test_case_pop(true, vec![Value::from(1_usize)], vec![], Value::from(1_usize)); test_case_pop( false, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(2_usize), - Value::from(3_usize), - ], + vec![Value::from(2_usize), Value::from(3_usize)], Value::from(1_usize), ); } @@ -579,19 +543,17 @@ mod tests { fn test_slice_insert_operation() { fn test_case_insert( array: Vec, - expected_mem: Vec, item: Value, index: Value, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len()), BrilligParameter::Simple, BrilligParameter::Simple, ]; - let returns = vec![ - BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1), - BrilligParameter::Simple, - ]; + let returns = + vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1)]; let (_, mut function_context, mut context) = create_test_environment(); @@ -623,87 +585,69 @@ mod tests { &[BrilligVariable::Simple(item_to_insert)], ); - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc]); + let calldata = array.into_iter().chain(vec![item]).chain(vec![index]).collect(); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm( - array.clone(), - vec![Value::from(0_usize), item, index], - &bytecode, - ); + let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata, &bytecode); + assert_eq!(return_data_size, expected_return.len()); - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() + 1)); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(1_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(27_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(1_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(0_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(27_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(0_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(2_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(27_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(2_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(3_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), Value::from(27_usize), ], - Value::from(27_usize), - Value::from(3_usize), ); test_case_insert( vec![], - vec![Value::from(27_usize)], Value::from(27_usize), Value::from(0_usize), + vec![Value::from(27_usize)], ); } @@ -711,8 +655,8 @@ mod tests { fn test_slice_remove_operation() { fn test_case_remove( array: Vec, - expected_mem: Vec, index: Value, + expected_array: Vec, expected_removed_item: Value, ) { let arguments = vec![ @@ -722,7 +666,6 @@ mod tests { let returns = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() - 1), BrilligParameter::Simple, - BrilligParameter::Simple, ]; let (_, mut function_context, mut context) = create_test_environment(); @@ -755,65 +698,47 @@ mod tests { &[BrilligVariable::Simple(removed_item)], ); - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - removed_item, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.size, removed_item]); + + let calldata: Vec<_> = array.into_iter().chain(vec![index]).collect(); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(array.clone(), vec![Value::from(0_usize), index], &bytecode); + let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata, &bytecode); - assert_eq!(vm.get_memory(), &expected_mem); + let expected_return: Vec<_> = + expected_array.into_iter().chain(vec![expected_removed_item]).collect(); + assert_eq!(return_data_size, expected_return.len()); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() - 1)); - assert_eq!(vm.get_registers().get(RegisterIndex(2)), expected_removed_item); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(2_usize), - Value::from(3_usize), - ], Value::from(0_usize), + vec![Value::from(2_usize), Value::from(3_usize)], Value::from(1_usize), ); test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(3_usize), - ], Value::from(1_usize), + vec![Value::from(1_usize), Value::from(3_usize)], Value::from(2_usize), ); test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(2_usize), - ], Value::from(2_usize), + vec![Value::from(1_usize), Value::from(2_usize)], Value::from(3_usize), ); test_case_remove( - vec![Value::from(1_usize)], vec![Value::from(1_usize)], Value::from(0_usize), + vec![], Value::from(1_usize), ); } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 3e6c3d4d7de..b094ff9c4c0 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -20,8 +20,8 @@ use self::{ }; use acvm::{ acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, RegisterIndex, - RegisterOrMemory, Value, + BinaryFieldOp, BinaryIntOp, BlackBoxOp, MemoryAddress, Opcode as BrilligOpcode, Value, + ValueOrArray, }, FieldElement, }; @@ -64,18 +64,18 @@ impl ReservedRegisters { } /// Returns the stack pointer register. This will get used to allocate memory in runtime. - pub(crate) fn stack_pointer() -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::StackPointer as usize) + pub(crate) fn stack_pointer() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::StackPointer as usize) } /// Returns the previous stack pointer register. This will be used to restore the registers after a fn call. - pub(crate) fn previous_stack_pointer() -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::PreviousStackPointer as usize) + pub(crate) fn previous_stack_pointer() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::PreviousStackPointer as usize) } /// Returns a user defined (non-reserved) register index. - fn user_register_index(index: usize) -> RegisterIndex { - RegisterIndex::from(index + ReservedRegisters::len()) + fn user_register_index(index: usize) -> MemoryAddress { + MemoryAddress::from(index + ReservedRegisters::len()) } } @@ -109,7 +109,7 @@ impl BrilligContext { } } - pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { + pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { self.registers = BrilligRegistersContext::from_preallocated_registers(allocated_registers); } @@ -127,27 +127,28 @@ impl BrilligContext { /// in `pointer_register` pub(crate) fn allocate_fixed_length_array( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, size: usize, ) { // debug_show handled by allocate_array_instruction let size_register = self.make_constant(size.into()); self.allocate_array_instruction(pointer_register, size_register); + self.deallocate_register(size_register); } /// Allocates an array of size contained in size_register and stores the /// pointer to the array in `pointer_register` pub(crate) fn allocate_array_instruction( &mut self, - pointer_register: RegisterIndex, - size_register: RegisterIndex, + pointer_register: MemoryAddress, + size_register: MemoryAddress, ) { self.debug_show.allocate_array_instruction(pointer_register, size_register); self.set_array_pointer(pointer_register); self.update_stack_pointer(size_register); } - pub(crate) fn set_array_pointer(&mut self, pointer_register: RegisterIndex) { + pub(crate) fn set_array_pointer(&mut self, pointer_register: MemoryAddress) { self.debug_show.mov_instruction(pointer_register, ReservedRegisters::stack_pointer()); self.push_opcode(BrilligOpcode::Mov { destination: pointer_register, @@ -155,7 +156,7 @@ impl BrilligContext { }); } - pub(crate) fn update_stack_pointer(&mut self, size_register: RegisterIndex) { + pub(crate) fn update_stack_pointer(&mut self, size_register: MemoryAddress) { self.memory_op( ReservedRegisters::stack_pointer(), size_register, @@ -168,7 +169,7 @@ impl BrilligContext { /// pointer to the array in `pointer_register` fn allocate_variable_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, size: usize, ) { self.debug_show.allocate_instruction(pointer_register); @@ -184,16 +185,17 @@ impl BrilligContext { ReservedRegisters::stack_pointer(), BinaryIntOp::Add, ); + self.deallocate_register(size_register); } pub(crate) fn allocate_simple_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, ) { self.allocate_variable_reference_instruction(pointer_register, 1); } - pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: RegisterIndex) { + pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: MemoryAddress) { self.allocate_variable_reference_instruction( pointer_register, BrilligArray::registers_count(), @@ -202,7 +204,7 @@ impl BrilligContext { pub(crate) fn allocate_vector_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, ) { self.allocate_variable_reference_instruction( pointer_register, @@ -213,9 +215,9 @@ impl BrilligContext { /// Gets the value in the array at index `index` and stores it in `result` pub(crate) fn array_get( &mut self, - array_ptr: RegisterIndex, - index: RegisterIndex, - result: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + result: MemoryAddress, ) { self.debug_show.array_get(array_ptr, index, result); // Computes array_ptr + index, ie array[index] @@ -235,9 +237,9 @@ impl BrilligContext { /// Sets the item in the array at index `index` to `value` pub(crate) fn array_set( &mut self, - array_ptr: RegisterIndex, - index: RegisterIndex, - value: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + value: MemoryAddress, ) { self.debug_show.array_set(array_ptr, index, value); // Computes array_ptr + index, ie array[index] @@ -258,9 +260,9 @@ impl BrilligContext { /// Into the array pointed by destination pub(crate) fn copy_array_instruction( &mut self, - source_pointer: RegisterIndex, - destination_pointer: RegisterIndex, - num_elements_register: RegisterIndex, + source_pointer: MemoryAddress, + destination_pointer: MemoryAddress, + num_elements_register: MemoryAddress, ) { self.debug_show.copy_array_instruction( source_pointer, @@ -280,9 +282,9 @@ impl BrilligContext { /// This instruction will issue a loop that will iterate iteration_count times /// The body of the loop should be issued by the caller in the on_iteration closure. - pub(crate) fn loop_instruction(&mut self, iteration_count: RegisterIndex, on_iteration: F) + pub(crate) fn loop_instruction(&mut self, iteration_count: MemoryAddress, on_iteration: F) where - F: FnOnce(&mut BrilligContext, RegisterIndex), + F: FnOnce(&mut BrilligContext, MemoryAddress), { let iterator_register = self.make_constant(0_u128.into()); @@ -327,7 +329,7 @@ impl BrilligContext { /// functions to allow the given function to mutably alias its environment. pub(crate) fn branch_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, mut f: impl FnMut(&mut BrilligContext, bool), ) { // Reserve 3 sections @@ -394,7 +396,7 @@ impl BrilligContext { /// Adds a unresolved `JumpIf` instruction to the bytecode. pub(crate) fn jump_if_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, target_label: T, ) { self.debug_show.jump_if_instruction(condition, target_label.to_string()); @@ -414,14 +416,14 @@ impl BrilligContext { } /// Allocates an unused register. - pub(crate) fn allocate_register(&mut self) -> RegisterIndex { + pub(crate) fn allocate_register(&mut self) -> MemoryAddress { self.registers.allocate_register() } /// Push a register to the deallocation list, ready for reuse. /// TODO(AD): currently, register deallocation is only done with immediate values. /// TODO(AD): See https://github.com/noir-lang/noir/issues/1720 - pub(crate) fn deallocate_register(&mut self, register_index: RegisterIndex) { + pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { self.registers.deallocate_register(register_index); } } @@ -431,7 +433,7 @@ impl BrilligContext { /// is false. pub(crate) fn constrain_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, assert_message: Option, ) { self.debug_show.constrain_instruction(condition); @@ -453,7 +455,7 @@ impl BrilligContext { /// Brillig does not have an explicit return instruction, so this /// method will move all register values to the first `N` values in /// the VM. - pub(crate) fn return_instruction(&mut self, return_registers: &[RegisterIndex]) { + pub(crate) fn return_instruction(&mut self, return_registers: &[MemoryAddress]) { self.debug_show.return_instruction(return_registers); let mut sources = Vec::with_capacity(return_registers.len()); let mut destinations = Vec::with_capacity(return_registers.len()); @@ -473,8 +475,8 @@ impl BrilligContext { /// It first moves all sources to new allocated registers to avoid overwriting. pub(crate) fn mov_registers_to_registers_instruction( &mut self, - sources: Vec, - destinations: Vec, + sources: Vec, + destinations: Vec, ) { let new_sources: Vec<_> = sources .iter() @@ -493,7 +495,7 @@ impl BrilligContext { /// Emits a `mov` instruction. /// /// Copies the value at `source` into `destination` - pub(crate) fn mov_instruction(&mut self, destination: RegisterIndex, source: RegisterIndex) { + pub(crate) fn mov_instruction(&mut self, destination: MemoryAddress, source: MemoryAddress) { self.debug_show.mov_instruction(destination, source); self.push_opcode(BrilligOpcode::Mov { destination, source }); } @@ -504,9 +506,9 @@ impl BrilligContext { /// and store the result in the `result` register. pub(crate) fn binary_instruction( &mut self, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, operation: BrilligBinaryOp, ) { self.debug_show.binary_instruction(lhs, rhs, result, operation.clone()); @@ -527,7 +529,7 @@ impl BrilligContext { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&mut self, result: RegisterIndex, constant: Value) { + pub(crate) fn const_instruction(&mut self, result: MemoryAddress, constant: Value) { self.debug_show.const_instruction(result, constant); self.push_opcode(BrilligOpcode::Const { destination: result, value: constant }); } @@ -538,9 +540,9 @@ impl BrilligContext { /// in Brillig. pub(crate) fn not_instruction( &mut self, - input: RegisterIndex, + input: MemoryAddress, bit_size: u32, - result: RegisterIndex, + result: MemoryAddress, ) { self.debug_show.not_instruction(input, bit_size, result); // Compile !x as ((-1) - x) @@ -565,8 +567,8 @@ impl BrilligContext { pub(crate) fn foreign_call_instruction( &mut self, func_name: String, - inputs: &[RegisterOrMemory], - outputs: &[RegisterOrMemory], + inputs: &[ValueOrArray], + outputs: &[ValueOrArray], ) { self.debug_show.foreign_call_instruction(func_name.clone(), inputs, outputs); let opcode = BrilligOpcode::ForeignCall { @@ -580,8 +582,8 @@ impl BrilligContext { /// Emits a load instruction pub(crate) fn load_instruction( &mut self, - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, ) { self.debug_show.load_instruction(destination, source_pointer); self.push_opcode(BrilligOpcode::Load { destination, source_pointer }); @@ -591,7 +593,7 @@ impl BrilligContext { pub(crate) fn load_variable_instruction( &mut self, destination: BrilligVariable, - variable_pointer: RegisterIndex, + variable_pointer: MemoryAddress, ) { match destination { BrilligVariable::Simple(register_index) => { @@ -630,8 +632,8 @@ impl BrilligContext { /// Emits a store instruction pub(crate) fn store_instruction( &mut self, - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, ) { self.debug_show.store_instruction(destination_pointer, source); self.push_opcode(BrilligOpcode::Store { destination_pointer, source }); @@ -640,7 +642,7 @@ impl BrilligContext { /// Stores a variable by saving its registers to memory pub(crate) fn store_variable_instruction( &mut self, - variable_pointer: RegisterIndex, + variable_pointer: MemoryAddress, source: BrilligVariable, ) { match source { @@ -650,7 +652,7 @@ impl BrilligContext { BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { self.store_instruction(variable_pointer, pointer); - let rc_pointer: RegisterIndex = self.allocate_register(); + let rc_pointer: MemoryAddress = self.allocate_register(); self.mov_instruction(rc_pointer, variable_pointer); self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 1_usize); self.store_instruction(rc_pointer, rc); @@ -664,7 +666,7 @@ impl BrilligContext { self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); self.store_instruction(size_pointer, size); - let rc_pointer: RegisterIndex = self.allocate_register(); + let rc_pointer: MemoryAddress = self.allocate_register(); self.mov_instruction(rc_pointer, variable_pointer); self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 2_usize); self.store_instruction(rc_pointer, rc); @@ -685,8 +687,8 @@ impl BrilligContext { /// For Brillig, all integer operations will overflow as its cheap. pub(crate) fn truncate_instruction( &mut self, - destination_of_truncated_value: RegisterIndex, - value_to_truncate: RegisterIndex, + destination_of_truncated_value: MemoryAddress, + value_to_truncate: MemoryAddress, bit_size: u32, ) { self.debug_show.truncate_instruction( @@ -715,11 +717,11 @@ impl BrilligContext { /// Emits a stop instruction pub(crate) fn stop_instruction(&mut self) { self.debug_show.stop_instruction(); - self.push_opcode(BrilligOpcode::Stop); + self.push_opcode(BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }); } /// Returns a register which holds the value of a constant - pub(crate) fn make_constant(&mut self, constant: Value) -> RegisterIndex { + pub(crate) fn make_constant(&mut self, constant: Value) -> MemoryAddress { let register = self.allocate_register(); self.const_instruction(register, constant); register @@ -736,9 +738,9 @@ impl BrilligContext { /// to other binary instructions. pub(crate) fn modulo_instruction( &mut self, - result_register: RegisterIndex, - left: RegisterIndex, - right: RegisterIndex, + result_register: MemoryAddress, + left: MemoryAddress, + right: MemoryAddress, bit_size: u32, signed: bool, ) { @@ -791,12 +793,12 @@ impl BrilligContext { } /// Returns the i'th register after the reserved ones - pub(crate) fn register(&self, i: usize) -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) + pub(crate) fn register(&self, i: usize) -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) } /// Saves all of the registers that have been used up until this point. - fn save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { + fn save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { // Save all of the used registers at this point in memory // because the function call will/may overwrite them. // @@ -822,7 +824,7 @@ impl BrilligContext { } /// Loads all of the registers that have been save by save_all_used_registers. - fn load_all_saved_registers(&mut self, used_registers: &[RegisterIndex]) { + fn load_all_saved_registers(&mut self, used_registers: &[MemoryAddress]) { // Load all of the used registers that we saved. // We do all the reverse operations of save_all_used_registers. // Iterate our registers in reverse @@ -839,7 +841,7 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a constant value in place pub(crate) fn usize_op_in_place( &mut self, - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryIntOp, constant: usize, ) { @@ -849,8 +851,8 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a constant value pub(crate) fn usize_op( &mut self, - operand: RegisterIndex, - destination: RegisterIndex, + operand: MemoryAddress, + destination: MemoryAddress, op: BinaryIntOp, constant: usize, ) { @@ -863,9 +865,9 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a memory address pub(crate) fn memory_op( &mut self, - lhs: RegisterIndex, - rhs: RegisterIndex, - destination: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + destination: MemoryAddress, op: BinaryIntOp, ) { self.binary_instruction( @@ -881,9 +883,9 @@ impl BrilligContext { // Move argument values to the front of the register indices. pub(crate) fn pre_call_save_registers_prep_args( &mut self, - arguments: &[RegisterIndex], + arguments: &[MemoryAddress], variables_to_save: &[BrilligVariable], - ) -> Vec { + ) -> Vec { // Save all the registers we have used to the stack. let saved_registers = self.save_registers_of_vars(variables_to_save); @@ -902,8 +904,8 @@ impl BrilligContext { // Load all the registers we have previous saved in save_registers_prep_args. pub(crate) fn post_call_prep_returns_load_registers( &mut self, - result_registers: &[RegisterIndex], - saved_registers: &[RegisterIndex], + result_registers: &[MemoryAddress], + saved_registers: &[MemoryAddress], ) { // Allocate our result registers and write into them // We assume the return values of our call are held in 0..num results register indices @@ -938,10 +940,10 @@ impl BrilligContext { /// And the radix register limb_count times to the target vector. pub(crate) fn radix_instruction( &mut self, - source: RegisterIndex, + source: MemoryAddress, target_vector: BrilligVector, - radix: RegisterIndex, - limb_count: RegisterIndex, + radix: MemoryAddress, + limb_count: MemoryAddress, big_endian: bool, ) { self.mov_instruction(target_vector.size, limb_count); @@ -951,7 +953,7 @@ impl BrilligContext { let shifted_register = self.allocate_register(); self.mov_instruction(shifted_register, source); - let modulus_register: RegisterIndex = self.allocate_register(); + let modulus_register: MemoryAddress = self.allocate_register(); self.loop_instruction(target_vector.size, |ctx, iterator_register| { // Compute the modulus @@ -1042,10 +1044,10 @@ pub(crate) mod tests { use std::vec; use acvm::acir::brillig::{ - BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapVector, RegisterIndex, - RegisterOrMemory, Value, + BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, + ValueOrArray, }; - use acvm::brillig_vm::{Registers, VMStatus, VM}; + use acvm::brillig_vm::{VMStatus, VM}; use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; use crate::brillig::brillig_ir::BrilligContext; @@ -1117,21 +1119,17 @@ pub(crate) mod tests { } pub(crate) fn create_and_run_vm( - memory: Vec, - param_registers: Vec, + calldata: Vec, bytecode: &[BrilligOpcode], - ) -> VM<'_, DummyBlackBoxSolver> { - let mut vm = VM::new( - Registers { inner: param_registers }, - memory, - bytecode, - vec![], - &DummyBlackBoxSolver, - ); + ) -> (VM<'_, DummyBlackBoxSolver>, usize, usize) { + let mut vm = VM::new(calldata, bytecode, vec![], &DummyBlackBoxSolver); let status = vm.process_opcodes(); - assert_eq!(status, VMStatus::Finished); - vm + if let VMStatus::Finished { return_data_offset, return_data_size } = status { + (vm, return_data_offset, return_data_size) + } else { + panic!("VM did not finish") + } } /// Test a Brillig foreign call returning a vector @@ -1150,18 +1148,18 @@ pub(crate) mod tests { let mut context = BrilligContext::new(true); let r_stack = ReservedRegisters::stack_pointer(); // Start stack pointer at 0 - context.const_instruction(r_stack, Value::from(0_usize)); - let r_input_size = RegisterIndex::from(ReservedRegisters::len()); - let r_array_ptr = RegisterIndex::from(ReservedRegisters::len() + 1); - let r_output_size = RegisterIndex::from(ReservedRegisters::len() + 2); - let r_equality = RegisterIndex::from(ReservedRegisters::len() + 3); + context.const_instruction(r_stack, Value::from(ReservedRegisters::len() + 3)); + let r_input_size = MemoryAddress::from(ReservedRegisters::len()); + let r_array_ptr = MemoryAddress::from(ReservedRegisters::len() + 1); + let r_output_size = MemoryAddress::from(ReservedRegisters::len() + 2); + let r_equality = MemoryAddress::from(ReservedRegisters::len() + 3); context.const_instruction(r_input_size, Value::from(12_usize)); // copy our stack frame to r_array_ptr context.mov_instruction(r_array_ptr, r_stack); context.foreign_call_instruction( "make_number_sequence".into(), - &[RegisterOrMemory::RegisterIndex(r_input_size)], - &[RegisterOrMemory::HeapVector(HeapVector { pointer: r_stack, size: r_output_size })], + &[ValueOrArray::MemoryAddress(r_input_size)], + &[ValueOrArray::HeapVector(HeapVector { pointer: r_stack, size: r_output_size })], ); // push stack frame by r_returned_size context.memory_op(r_stack, r_output_size, r_stack, BinaryIntOp::Add); @@ -1178,13 +1176,12 @@ pub(crate) mod tests { let bytecode = context.artifact().finish().byte_code; let number_sequence: Vec = (0_usize..12_usize).map(Value::from).collect(); let mut vm = VM::new( - Registers { inner: vec![] }, vec![], &bytecode, vec![ForeignCallResult { values: vec![ForeignCallParam::Array(number_sequence)] }], &DummyBlackBoxSolver, ); let status = vm.process_opcodes(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); } } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 9b8c3913123..437774da157 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -97,7 +97,7 @@ impl BrilligArtifact { // Replace STOP with RETURN because this is not the end of the program now. let stop_position = byte_code .iter() - .position(|opcode| matches!(opcode, BrilligOpcode::Stop)) + .position(|opcode| matches!(opcode, BrilligOpcode::Stop { .. })) .expect("Trying to link with a function that does not have a stop opcode"); byte_code[stop_position] = BrilligOpcode::Return; diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index 46c54d55ecb..b3fe30c40b4 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -1,12 +1,12 @@ -use acvm::brillig_vm::brillig::{HeapArray, HeapVector, RegisterIndex, RegisterOrMemory}; +use acvm::brillig_vm::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}; use serde::{Deserialize, Serialize}; /// The representation of a noir array in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) struct BrilligArray { - pub(crate) pointer: RegisterIndex, + pub(crate) pointer: MemoryAddress, pub(crate) size: usize, - pub(crate) rc: RegisterIndex, + pub(crate) rc: MemoryAddress, } impl BrilligArray { @@ -18,7 +18,7 @@ impl BrilligArray { 2 } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { vec![self.pointer, self.rc] } } @@ -26,9 +26,9 @@ impl BrilligArray { /// The representation of a noir slice in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) struct BrilligVector { - pub(crate) pointer: RegisterIndex, - pub(crate) size: RegisterIndex, - pub(crate) rc: RegisterIndex, + pub(crate) pointer: MemoryAddress, + pub(crate) size: MemoryAddress, + pub(crate) rc: MemoryAddress, } impl BrilligVector { @@ -40,7 +40,7 @@ impl BrilligVector { 3 } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { vec![self.pointer, self.size, self.rc] } } @@ -48,13 +48,13 @@ impl BrilligVector { /// The representation of a noir value in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) enum BrilligVariable { - Simple(RegisterIndex), + Simple(MemoryAddress), BrilligArray(BrilligArray), BrilligVector(BrilligVector), } impl BrilligVariable { - pub(crate) fn extract_register(self) -> RegisterIndex { + pub(crate) fn extract_register(self) -> MemoryAddress { match self { BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: Expected register, got {self:?}"), @@ -75,7 +75,7 @@ impl BrilligVariable { } } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { match self { BrilligVariable::Simple(register_index) => vec![register_index], BrilligVariable::BrilligArray(array) => array.extract_registers(), @@ -83,16 +83,12 @@ impl BrilligVariable { } } - pub(crate) fn to_register_or_memory(self) -> RegisterOrMemory { + pub(crate) fn to_register_or_memory(self) -> ValueOrArray { match self { - BrilligVariable::Simple(register_index) => { - RegisterOrMemory::RegisterIndex(register_index) - } - BrilligVariable::BrilligArray(array) => { - RegisterOrMemory::HeapArray(array.to_heap_array()) - } + BrilligVariable::Simple(register_index) => ValueOrArray::MemoryAddress(register_index), + BrilligVariable::BrilligArray(array) => ValueOrArray::HeapArray(array.to_heap_array()), BrilligVariable::BrilligVector(vector) => { - RegisterOrMemory::HeapVector(vector.to_heap_vector()) + ValueOrArray::HeapVector(vector.to_heap_vector()) } } } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index a8563dc9efe..28359f8dcea 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -3,8 +3,8 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::{ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE}; use acvm::acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, RegisterIndex, RegisterOrMemory, - Value, + BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, + ValueOrArray, }; /// Trait for converting values into debug-friendly strings. @@ -24,7 +24,7 @@ macro_rules! default_to_string_impl { default_to_string_impl! { str usize u32 } -impl DebugToString for RegisterIndex { +impl DebugToString for MemoryAddress { fn debug_to_string(&self) -> String { if *self == ReservedRegisters::stack_pointer() { "Stack".into() @@ -112,12 +112,12 @@ impl DebugToString for Value { } } -impl DebugToString for RegisterOrMemory { +impl DebugToString for ValueOrArray { fn debug_to_string(&self) -> String { match self { - RegisterOrMemory::RegisterIndex(index) => index.debug_to_string(), - RegisterOrMemory::HeapArray(heap_array) => heap_array.debug_to_string(), - RegisterOrMemory::HeapVector(vector) => vector.debug_to_string(), + ValueOrArray::MemoryAddress(index) => index.debug_to_string(), + ValueOrArray::HeapArray(heap_array) => heap_array.debug_to_string(), + ValueOrArray::HeapVector(vector) => vector.debug_to_string(), } } } @@ -152,15 +152,15 @@ impl DebugShow { /// Emits brillig bytecode to jump to a trap condition if `condition` /// is false. - pub(crate) fn constrain_instruction(&self, condition: RegisterIndex) { + pub(crate) fn constrain_instruction(&self, condition: MemoryAddress) { debug_println!(self.enable_debug_trace, " ASSERT {} != 0", condition); } /// Processes a return instruction. - pub(crate) fn return_instruction(&self, return_registers: &[RegisterIndex]) { + pub(crate) fn return_instruction(&self, return_registers: &[MemoryAddress]) { let registers_string = return_registers .iter() - .map(RegisterIndex::debug_to_string) + .map(MemoryAddress::debug_to_string) .collect::>() .join(", "); @@ -168,32 +168,32 @@ impl DebugShow { } /// Emits a `mov` instruction. - pub(crate) fn mov_instruction(&self, destination: RegisterIndex, source: RegisterIndex) { + pub(crate) fn mov_instruction(&self, destination: MemoryAddress, source: MemoryAddress) { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); } /// Processes a binary instruction according `operation`. pub(crate) fn binary_instruction( &self, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, operation: BrilligBinaryOp, ) { debug_println!(self.enable_debug_trace, " {} = {} {} {}", result, lhs, operation, rhs); } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&self, result: RegisterIndex, constant: Value) { + pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: Value) { debug_println!(self.enable_debug_trace, " CONST {} = {}", result, constant); } /// Processes a not instruction. Append with "_" as this is a high-level instruction. pub(crate) fn not_instruction( &self, - condition: RegisterIndex, + condition: MemoryAddress, bit_size: u32, - result: RegisterIndex, + result: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " i{}_NOT {} = !{}", bit_size, result, condition); } @@ -202,8 +202,8 @@ impl DebugShow { pub(crate) fn foreign_call_instruction( &self, func_name: String, - inputs: &[RegisterOrMemory], - outputs: &[RegisterOrMemory], + inputs: &[ValueOrArray], + outputs: &[ValueOrArray], ) { debug_println!( self.enable_debug_trace, @@ -217,8 +217,8 @@ impl DebugShow { /// Emits a load instruction pub(crate) fn load_instruction( &self, - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " LOAD {} = *{}", destination, source_pointer); } @@ -226,8 +226,8 @@ impl DebugShow { /// Emits a store instruction pub(crate) fn store_instruction( &self, - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " STORE *{} = {}", destination_pointer, source); } @@ -240,8 +240,8 @@ impl DebugShow { /// Debug function for allocate_array_instruction pub(crate) fn allocate_array_instruction( &self, - pointer_register: RegisterIndex, - size_register: RegisterIndex, + pointer_register: MemoryAddress, + size_register: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -252,16 +252,16 @@ impl DebugShow { } /// Debug function for allocate_instruction - pub(crate) fn allocate_instruction(&self, pointer_register: RegisterIndex) { + pub(crate) fn allocate_instruction(&self, pointer_register: MemoryAddress) { debug_println!(self.enable_debug_trace, " ALLOCATE {} ", pointer_register); } /// Debug function for array_get pub(crate) fn array_get( &self, - array_ptr: RegisterIndex, - index: RegisterIndex, - result: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + result: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -275,9 +275,9 @@ impl DebugShow { /// Debug function for array_set pub(crate) fn array_set( &self, - array_ptr: RegisterIndex, - index: RegisterIndex, - value: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + value: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " ARRAY_SET {}[{}] = {}", array_ptr, index, value); } @@ -285,9 +285,9 @@ impl DebugShow { /// Debug function for copy_array_instruction pub(crate) fn copy_array_instruction( &self, - source: RegisterIndex, - destination: RegisterIndex, - num_elements_register: RegisterIndex, + source: MemoryAddress, + destination: MemoryAddress, + num_elements_register: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -314,7 +314,7 @@ impl DebugShow { /// Debug function for jump_if_instruction pub(crate) fn jump_if_instruction( &self, - condition: RegisterIndex, + condition: MemoryAddress, target_label: T, ) { debug_println!( @@ -328,8 +328,8 @@ impl DebugShow { /// Debug function for cast_instruction pub(crate) fn truncate_instruction( &self, - destination: RegisterIndex, - source: RegisterIndex, + destination: MemoryAddress, + source: MemoryAddress, target_bit_size: u32, ) { debug_println!( diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 36ca414f38e..b9d95788b80 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -1,13 +1,13 @@ -use crate::brillig::brillig_ir::ReservedRegisters; - use super::{ artifact::{BrilligArtifact, BrilligParameter}, brillig_variable::{BrilligArray, BrilligVariable}, debug_show::DebugShow, registers::BrilligRegistersContext, - BrilligContext, + BrilligContext, ReservedRegisters, }; -use acvm::acir::brillig::{Opcode as BrilligOpcode, RegisterIndex}; +use acvm::acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}; + +pub(crate) const MAX_STACK_SIZE: usize = 1024; impl BrilligContext { /// Creates an entry point artifact that will jump to the function label provided. @@ -25,87 +25,85 @@ impl BrilligContext { debug_show: DebugShow::new(false), }; - context.entry_point_instruction(arguments); + context.entry_point_instruction(&arguments, &return_parameters); context.add_external_call_instruction(target_function); - context.exit_point_instruction(return_parameters); + context.exit_point_instruction(&arguments, &return_parameters); context.artifact() } /// Adds the instructions needed to handle entry point parameters - /// The runtime will leave the parameters in the first `n` registers. - /// Arrays will be passed as pointers to the first element, with all the nested arrays flattened. - /// First, reserve the registers that contain the parameters. - /// This function also sets the starting value of the reserved registers - fn entry_point_instruction(&mut self, arguments: Vec) { - let preallocated_registers: Vec<_> = - arguments.iter().enumerate().map(|(i, _)| RegisterIndex::from(i)).collect(); - self.set_allocated_registers(preallocated_registers.clone()); - - // Then allocate and initialize the variables that will hold the parameters - let argument_variables: Vec<_> = arguments + /// The runtime will leave the parameters in calldata. + /// Arrays will be passed flattened. + fn entry_point_instruction( + &mut self, + arguments: &[BrilligParameter], + return_parameters: &[BrilligParameter], + ) { + let calldata_size = BrilligContext::flattened_tuple_size(arguments); + let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); + + // Set initial value of stack pointer: MAX_STACK_SIZE + calldata_size + return_data_size + self.push_opcode(BrilligOpcode::Const { + destination: ReservedRegisters::stack_pointer(), + value: (MAX_STACK_SIZE + calldata_size + return_data_size).into(), + }); + + // Copy calldata + self.push_opcode(BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(MAX_STACK_SIZE), + size: calldata_size, + offset: 0, + }); + + // Allocate the variables for every argument: + let mut current_calldata_pointer = MAX_STACK_SIZE; + + let mut argument_variables: Vec<_> = arguments .iter() - .zip(preallocated_registers) - .map(|(argument, param_register)| match argument { + .map(|argument| match argument { BrilligParameter::Simple => { - let variable_register = self.allocate_register(); - self.mov_instruction(variable_register, param_register); - BrilligVariable::Simple(variable_register) + let simple_address = self.allocate_register(); + let var = BrilligVariable::Simple(simple_address); + self.mov_instruction(simple_address, MemoryAddress(current_calldata_pointer)); + current_calldata_pointer += 1; + var } - BrilligParameter::Array(item_types, item_count) => { - let pointer_register = self.allocate_register(); - let rc_register = self.allocate_register(); - self.mov_instruction(pointer_register, param_register); - self.const_instruction(rc_register, 1_usize.into()); - BrilligVariable::BrilligArray(BrilligArray { - pointer: pointer_register, - size: item_types.len() * item_count, + BrilligParameter::Array(_, _) => { + let pointer_to_the_array_in_calldata = + self.make_constant(current_calldata_pointer.into()); + let rc_register = self.make_constant(1_usize.into()); + let flattened_size = BrilligContext::flattened_size(argument); + let var = BrilligVariable::BrilligArray(BrilligArray { + pointer: pointer_to_the_array_in_calldata, + size: flattened_size, rc: rc_register, - }) + }); + + current_calldata_pointer += flattened_size; + var } BrilligParameter::Slice(_) => unimplemented!("Unsupported slices as parameter"), }) .collect(); - // Calculate the initial value for the stack pointer register - let size_arguments_memory: usize = arguments - .iter() - .map(|arg| match arg { - BrilligParameter::Simple => 0, - _ => BrilligContext::flattened_size(arg), - }) - .sum(); - - // Set the initial value of the stack pointer register - self.push_opcode(BrilligOpcode::Const { - destination: ReservedRegisters::stack_pointer(), - value: size_arguments_memory.into(), - }); - // Set the initial value of the previous stack pointer register - self.push_opcode(BrilligOpcode::Const { - destination: ReservedRegisters::previous_stack_pointer(), - value: 0_usize.into(), - }); - - // Deflatten the arrays - for (parameter, assigned_variable) in arguments.iter().zip(&argument_variables) { - if let BrilligParameter::Array(item_type, item_count) = parameter { - if item_type.iter().any(|param| !matches!(param, BrilligParameter::Simple)) { - let pointer_register = assigned_variable.extract_array().pointer; - let deflattened_register = - self.deflatten_array(item_type, *item_count, pointer_register); - self.mov_instruction(pointer_register, deflattened_register); + // Deflatten arrays + for (argument_variable, argument) in argument_variables.iter_mut().zip(arguments) { + if let ( + BrilligVariable::BrilligArray(array), + BrilligParameter::Array(item_type, item_count), + ) = (argument_variable, argument) + { + if BrilligContext::has_nested_arrays(item_type) { + let deflattened_address = + self.deflatten_array(item_type, array.size, array.pointer); + self.mov_instruction(array.pointer, deflattened_address); + array.size = item_type.len() * item_count; + self.deallocate_register(deflattened_address); } } } - - // Move the parameters to the first user defined registers, to follow function call convention. - for (i, register) in - argument_variables.into_iter().flat_map(|arg| arg.extract_registers()).enumerate() - { - self.mov_instruction(ReservedRegisters::user_register_index(i), register); - } } /// Computes the size of a parameter if it was flattened @@ -122,95 +120,130 @@ impl BrilligContext { } } + /// Computes the size of a parameter if it was flattened + fn flattened_tuple_size(tuple: &[BrilligParameter]) -> usize { + tuple.iter().map(BrilligContext::flattened_size).sum() + } + + /// Computes the size of a parameter if it was flattened + fn has_nested_arrays(tuple: &[BrilligParameter]) -> bool { + tuple.iter().any(|param| !matches!(param, BrilligParameter::Simple)) + } + /// Deflatten an array by recursively allocating nested arrays and copying the plain values. /// Returns the pointer to the deflattened items. fn deflatten_array( &mut self, item_type: &[BrilligParameter], item_count: usize, - flattened_array_pointer: RegisterIndex, - ) -> RegisterIndex { - let movement_register = self.allocate_register(); - let deflattened_array_pointer = self.allocate_register(); - - let target_item_size = item_type.len(); - let source_item_size: usize = item_type.iter().map(BrilligContext::flattened_size).sum(); - - self.allocate_fixed_length_array(deflattened_array_pointer, item_count * target_item_size); - - for item_index in 0..item_count { - let source_item_base_index = item_index * source_item_size; - let target_item_base_index = item_index * target_item_size; - - let mut source_offset = 0; - - for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_constant((source_item_base_index + source_offset).into()); - - let target_index = - self.make_constant((target_item_base_index + subitem_index).into()); - - match subitem { - BrilligParameter::Simple => { - self.array_get(flattened_array_pointer, source_index, movement_register); - self.array_set(deflattened_array_pointer, target_index, movement_register); - source_offset += 1; - } - BrilligParameter::Array(nested_array_item_type, nested_array_item_count) => { - let nested_array_pointer = self.allocate_register(); - self.mov_instruction(nested_array_pointer, flattened_array_pointer); - self.memory_op( - nested_array_pointer, - source_index, - nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, - ); - let deflattened_nested_array_pointer = self.deflatten_array( + flattened_array_pointer: MemoryAddress, + ) -> MemoryAddress { + if BrilligContext::has_nested_arrays(item_type) { + let movement_register = self.allocate_register(); + let deflattened_array_pointer = self.allocate_register(); + + let target_item_size = item_type.len(); + let source_item_size = BrilligContext::flattened_tuple_size(item_type); + + self.allocate_fixed_length_array( + deflattened_array_pointer, + item_count * target_item_size, + ); + + for item_index in 0..item_count { + let source_item_base_index = item_index * source_item_size; + let target_item_base_index = item_index * target_item_size; + + let mut source_offset = 0; + + for (subitem_index, subitem) in item_type.iter().enumerate() { + let source_index = + self.make_constant((source_item_base_index + source_offset).into()); + + let target_index = + self.make_constant((target_item_base_index + subitem_index).into()); + + match subitem { + BrilligParameter::Simple => { + self.array_get( + flattened_array_pointer, + source_index, + movement_register, + ); + self.array_set( + deflattened_array_pointer, + target_index, + movement_register, + ); + source_offset += 1; + } + BrilligParameter::Array( nested_array_item_type, - *nested_array_item_count, - nested_array_pointer, - ); - let reference = self.allocate_register(); - let rc = self.allocate_register(); - self.const_instruction(rc, 1_usize.into()); - - self.allocate_array_reference_instruction(reference); - self.store_variable_instruction( - reference, - BrilligVariable::BrilligArray(BrilligArray { + nested_array_item_count, + ) => { + let nested_array_pointer = self.allocate_register(); + self.mov_instruction(nested_array_pointer, flattened_array_pointer); + self.memory_op( + nested_array_pointer, + source_index, + nested_array_pointer, + acvm::brillig_vm::brillig::BinaryIntOp::Add, + ); + let deflattened_nested_array_pointer = self.deflatten_array( + nested_array_item_type, + *nested_array_item_count, + nested_array_pointer, + ); + let reference = self.allocate_register(); + let rc = self.allocate_register(); + self.const_instruction(rc, 1_usize.into()); + + self.allocate_array_reference_instruction(reference); + let array_variable = BrilligVariable::BrilligArray(BrilligArray { pointer: deflattened_nested_array_pointer, size: nested_array_item_type.len() * nested_array_item_count, rc, - }), - ); + }); + self.store_variable_instruction(reference, array_variable); - self.array_set(deflattened_array_pointer, target_index, reference); + self.array_set(deflattened_array_pointer, target_index, reference); - self.deallocate_register(nested_array_pointer); - self.deallocate_register(reference); - self.deallocate_register(rc); + self.deallocate_register(nested_array_pointer); + self.deallocate_register(reference); + array_variable + .extract_registers() + .into_iter() + .for_each(|register| self.deallocate_register(register)); - source_offset += BrilligContext::flattened_size(subitem); + source_offset += BrilligContext::flattened_size(subitem); + } + BrilligParameter::Slice(..) => unreachable!("ICE: Cannot deflatten slices"), } - BrilligParameter::Slice(..) => unreachable!("ICE: Cannot deflatten slices"), - } - self.deallocate_register(source_index); - self.deallocate_register(target_index); + self.deallocate_register(source_index); + self.deallocate_register(target_index); + } } - } - self.deallocate_register(movement_register); + self.deallocate_register(movement_register); - deflattened_array_pointer + deflattened_array_pointer + } else { + let deflattened_array_pointer = self.allocate_register(); + self.mov_instruction(deflattened_array_pointer, flattened_array_pointer); + deflattened_array_pointer + } } /// Adds the instructions needed to handle return parameters - /// The runtime expects the results in the first `n` registers. - /// Arrays are expected to be returned as pointers to the first element with all the nested arrays flattened. + /// The runtime expects the results in a contiguous memory region. + /// Arrays are expected to be returned with all the nested arrays flattened. /// However, the function called returns variables (that have extra data) and the returned arrays are deflattened. - fn exit_point_instruction(&mut self, return_parameters: Vec) { + fn exit_point_instruction( + &mut self, + arguments: &[BrilligParameter], + return_parameters: &[BrilligParameter], + ) { // First, we allocate the registers that hold the returned variables from the function call. self.set_allocated_registers(vec![]); let returned_variables: Vec<_> = return_parameters @@ -227,43 +260,45 @@ impl BrilligContext { BrilligParameter::Slice(..) => unreachable!("ICE: Cannot return slices"), }) .collect(); - // Now, we deflatten the returned arrays - for (return_param, returned_variable) in return_parameters.iter().zip(&returned_variables) { - if let BrilligParameter::Array(item_type, item_count) = return_param { - if item_type.iter().any(|item| !matches!(item, BrilligParameter::Simple)) { - let returned_pointer = returned_variable.extract_array().pointer; - let flattened_array_pointer = self.allocate_register(); - self.allocate_fixed_length_array( - flattened_array_pointer, - BrilligContext::flattened_size(return_param), + // Now, we deflatten the return data + let calldata_size = BrilligContext::flattened_tuple_size(arguments); + let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); + + // Return data has a reserved space after calldata + let return_data_offset = MAX_STACK_SIZE + calldata_size; + let mut return_data_index = return_data_offset; + + for (return_param, returned_variable) in return_parameters.iter().zip(&returned_variables) { + match return_param { + BrilligParameter::Simple => { + self.mov_instruction( + MemoryAddress(return_data_index), + returned_variable.extract_register(), ); + return_data_index += 1; + } + BrilligParameter::Array(item_type, item_count) => { + let returned_pointer = returned_variable.extract_array().pointer; + let pointer_to_return_data = self.make_constant(return_data_index.into()); self.flatten_array( item_type, *item_count, - flattened_array_pointer, + pointer_to_return_data, returned_pointer, ); - self.mov_instruction(returned_pointer, flattened_array_pointer); + self.deallocate_register(pointer_to_return_data); + return_data_index += BrilligContext::flattened_size(return_param); + } + BrilligParameter::Slice(..) => { + unreachable!("ICE: Cannot return slices from brillig entrypoints") } } } - // The VM expects us to follow the calling convention of returning - // their results in the first `n` registers. So we to move the return values - // to the first `n` registers once completed. - - // Move the results to registers 0..n - for (i, returned_variable) in returned_variables.into_iter().enumerate() { - let register = match returned_variable { - BrilligVariable::Simple(register) => register, - BrilligVariable::BrilligArray(array) => array.pointer, - BrilligVariable::BrilligVector(vector) => vector.pointer, - }; - self.push_opcode(BrilligOpcode::Mov { destination: i.into(), source: register }); - } - self.push_opcode(BrilligOpcode::Stop); + + self.push_opcode(BrilligOpcode::Stop { return_data_offset, return_data_size }); } // Flattens an array by recursively copying nested arrays and regular items. @@ -271,96 +306,119 @@ impl BrilligContext { &mut self, item_type: &[BrilligParameter], item_count: usize, - flattened_array_pointer: RegisterIndex, - deflattened_array_pointer: RegisterIndex, + flattened_array_pointer: MemoryAddress, + deflattened_array_pointer: MemoryAddress, ) { - let movement_register = self.allocate_register(); - - let source_item_size = item_type.len(); - let target_item_size: usize = item_type.iter().map(BrilligContext::flattened_size).sum(); - - for item_index in 0..item_count { - let source_item_base_index = item_index * source_item_size; - let target_item_base_index = item_index * target_item_size; - - let mut target_offset = 0; - - for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_constant((source_item_base_index + subitem_index).into()); - let target_index = - self.make_constant((target_item_base_index + target_offset).into()); - - match subitem { - BrilligParameter::Simple => { - self.array_get(deflattened_array_pointer, source_index, movement_register); - self.array_set(flattened_array_pointer, target_index, movement_register); - target_offset += 1; - } - BrilligParameter::Array(nested_array_item_type, nested_array_item_count) => { - let nested_array_reference = self.allocate_register(); - self.array_get( - deflattened_array_pointer, - source_index, - nested_array_reference, - ); - - let nested_array_variable = BrilligVariable::BrilligArray(BrilligArray { - pointer: self.allocate_register(), - size: nested_array_item_type.len() * nested_array_item_count, - rc: self.allocate_register(), - }); - - self.load_variable_instruction( - nested_array_variable, - nested_array_reference, - ); - - let flattened_nested_array_pointer = self.allocate_register(); - - self.mov_instruction( - flattened_nested_array_pointer, - flattened_array_pointer, - ); - - self.memory_op( - flattened_nested_array_pointer, - target_index, - flattened_nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, - ); - - self.flatten_array( + if BrilligContext::has_nested_arrays(item_type) { + let movement_register = self.allocate_register(); + + let source_item_size = item_type.len(); + let target_item_size: usize = + item_type.iter().map(BrilligContext::flattened_size).sum(); + + for item_index in 0..item_count { + let source_item_base_index = item_index * source_item_size; + let target_item_base_index = item_index * target_item_size; + + let mut target_offset = 0; + + for (subitem_index, subitem) in item_type.iter().enumerate() { + let source_index = + self.make_constant((source_item_base_index + subitem_index).into()); + let target_index = + self.make_constant((target_item_base_index + target_offset).into()); + + match subitem { + BrilligParameter::Simple => { + self.array_get( + deflattened_array_pointer, + source_index, + movement_register, + ); + self.array_set( + flattened_array_pointer, + target_index, + movement_register, + ); + target_offset += 1; + } + BrilligParameter::Array( nested_array_item_type, - *nested_array_item_count, - flattened_nested_array_pointer, - nested_array_variable.extract_array().pointer, - ); - - self.deallocate_register(nested_array_reference); - self.deallocate_register(flattened_nested_array_pointer); - nested_array_variable - .extract_registers() - .into_iter() - .for_each(|register| self.deallocate_register(register)); - - target_offset += BrilligContext::flattened_size(subitem); + nested_array_item_count, + ) => { + let nested_array_reference = self.allocate_register(); + self.array_get( + deflattened_array_pointer, + source_index, + nested_array_reference, + ); + + let nested_array_variable = + BrilligVariable::BrilligArray(BrilligArray { + pointer: self.allocate_register(), + size: nested_array_item_type.len() * nested_array_item_count, + rc: self.allocate_register(), + }); + + self.load_variable_instruction( + nested_array_variable, + nested_array_reference, + ); + + let flattened_nested_array_pointer = self.allocate_register(); + + self.mov_instruction( + flattened_nested_array_pointer, + flattened_array_pointer, + ); + + self.memory_op( + flattened_nested_array_pointer, + target_index, + flattened_nested_array_pointer, + acvm::brillig_vm::brillig::BinaryIntOp::Add, + ); + + self.flatten_array( + nested_array_item_type, + *nested_array_item_count, + flattened_nested_array_pointer, + nested_array_variable.extract_array().pointer, + ); + + self.deallocate_register(nested_array_reference); + self.deallocate_register(flattened_nested_array_pointer); + nested_array_variable + .extract_registers() + .into_iter() + .for_each(|register| self.deallocate_register(register)); + + target_offset += BrilligContext::flattened_size(subitem); + } + BrilligParameter::Slice(..) => unreachable!("ICE: Cannot flatten slices"), } - BrilligParameter::Slice(..) => unreachable!("ICE: Cannot flatten slices"), - } - self.deallocate_register(source_index); - self.deallocate_register(target_index); + self.deallocate_register(source_index); + self.deallocate_register(target_index); + } } - } - self.deallocate_register(movement_register); + self.deallocate_register(movement_register); + } else { + let item_count = self.make_constant((item_count * item_type.len()).into()); + self.copy_array_instruction( + deflattened_array_pointer, + flattened_array_pointer, + item_count, + ); + self.deallocate_register(item_count); + } } } #[cfg(test)] mod tests { - use acvm::brillig_vm::brillig::{RegisterIndex, Value}; + use acvm::brillig_vm::brillig::Value; use crate::brillig::brillig_ir::{ artifact::BrilligParameter, @@ -370,7 +428,7 @@ mod tests { #[test] fn entry_point_with_nested_array_parameter() { - let flattened_array = vec![ + let calldata = vec![ Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), @@ -391,44 +449,19 @@ mod tests { // Allocate the parameter let array_pointer = context.allocate_register(); + let array_value = context.allocate_register(); - context.return_instruction(&[array_pointer]); + context.load_instruction(array_pointer, array_pointer); + context.load_instruction(array_pointer, array_pointer); + context.load_instruction(array_value, array_pointer); - let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(flattened_array.clone(), vec![Value::from(0_usize)], &bytecode); - let memory = vm.get_memory(); + context.return_instruction(&[array_value]); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(flattened_array.len())); - assert_eq!( - memory, - &vec![ - // The original flattened values - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - // The pointer to the nested reference of the first item - Value::from(12_usize), - Value::from(3_usize), - // The pointer to the nested reference of the second item - Value::from(16_usize), - Value::from(6_usize), - // The nested array of the first item - Value::from(1_usize), - Value::from(2_usize), - // The nested reference of the first item - Value::from(10_usize), - Value::from(1_usize), - // The nested array of the second item - Value::from(4_usize), - Value::from(5_usize), - // The nested reference of the second item - Value::from(14_usize), - Value::from(1_usize), - ] - ); + let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(calldata.clone(), &bytecode); + assert_eq!(return_data_size, 1, "Return data size is incorrect"); + assert_eq!(vm.get_memory()[return_data_offset], Value::from(1_usize)); } #[test] @@ -463,46 +496,14 @@ mod tests { context.return_instruction(&brillig_array.extract_registers()); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(flattened_array.clone(), vec![Value::from(0_usize)], &bytecode); + let (vm, return_data_pointer, return_data_size) = + create_and_run_vm(flattened_array.clone(), &bytecode); let memory = vm.get_memory(); assert_eq!( - memory, - &vec![ - // The original flattened values - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - // The pointer to the nested reference of the first item - Value::from(12_usize), - Value::from(3_usize), - // The pointer to the nested reference of the second item - Value::from(16_usize), - Value::from(6_usize), - // The nested array of the first item - Value::from(1_usize), - Value::from(2_usize), - // The nested reference of the first item - Value::from(10_usize), - Value::from(1_usize), - // The nested array of the second item - Value::from(4_usize), - Value::from(5_usize), - // The nested reference of the second item - Value::from(14_usize), - Value::from(1_usize), - // The original flattened again - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - ] + memory[return_data_pointer..(return_data_pointer + flattened_array.len())], + flattened_array ); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), 18_usize.into()); + assert_eq!(return_data_size, flattened_array.len()); } } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index e7ab1492acb..8c0e36215a9 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -1,4 +1,6 @@ -use acvm::acir::brillig::RegisterIndex; +use acvm::acir::brillig::MemoryAddress; + +use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; use super::ReservedRegisters; @@ -8,7 +10,7 @@ use super::ReservedRegisters; /// Each has a stack base pointer from which all stack allocations can be offset. pub(crate) struct BrilligRegistersContext { /// A free-list of registers that have been deallocated and can be used again. - deallocated_registers: Vec, + deallocated_registers: Vec, /// A usize indicating the next un-used register. next_free_register_index: usize, } @@ -23,7 +25,7 @@ impl BrilligRegistersContext { } /// Creates a new register context from a set of registers allocated previously. - pub(crate) fn from_preallocated_registers(preallocated_registers: Vec) -> Self { + pub(crate) fn from_preallocated_registers(preallocated_registers: Vec) -> Self { let next_free_register_index = preallocated_registers.iter().fold( ReservedRegisters::len(), |free_register_index, preallocated_register| { @@ -36,8 +38,8 @@ impl BrilligRegistersContext { ); let mut deallocated_registers = Vec::new(); for i in ReservedRegisters::len()..next_free_register_index { - if !preallocated_registers.contains(&RegisterIndex::from(i)) { - deallocated_registers.push(RegisterIndex::from(i)); + if !preallocated_registers.contains(&MemoryAddress::from(i)) { + deallocated_registers.push(MemoryAddress::from(i)); } } @@ -45,7 +47,7 @@ impl BrilligRegistersContext { } /// Ensures a register is allocated. - pub(crate) fn ensure_register_is_allocated(&mut self, register: RegisterIndex) { + pub(crate) fn ensure_register_is_allocated(&mut self, register: MemoryAddress) { let index = register.to_usize(); if index < self.next_free_register_index { // If it could be allocated, check if it's in the deallocated list and remove it from there @@ -53,26 +55,28 @@ impl BrilligRegistersContext { } else { // If it couldn't yet be, expand the register space. self.next_free_register_index = index + 1; + assert!(self.next_free_register_index < MAX_STACK_SIZE, "Stack too deep"); } } /// Creates a new register. - pub(crate) fn allocate_register(&mut self) -> RegisterIndex { + pub(crate) fn allocate_register(&mut self) -> MemoryAddress { // If we have a register in our free list of deallocated registers, // consume it first. This prioritizes reuse. if let Some(register) = self.deallocated_registers.pop() { return register; } // Otherwise, move to our latest register. - let register = RegisterIndex::from(self.next_free_register_index); + let register = MemoryAddress::from(self.next_free_register_index); self.next_free_register_index += 1; + assert!(self.next_free_register_index < MAX_STACK_SIZE, "Stack too deep"); register } /// Push a register to the deallocation list, ready for reuse. /// TODO(AD): currently, register deallocation is only done with immediate values. /// TODO(AD): See https://github.com/noir-lang/noir/issues/1720 - pub(crate) fn deallocate_register(&mut self, register_index: RegisterIndex) { + pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { assert!(!self.deallocated_registers.contains(®ister_index)); self.deallocated_registers.push(register_index); } diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index da59d674213..217ea3f7584 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -11,7 +11,7 @@ use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; use acvm::acir::circuit::Opcode; use acvm::blackbox_solver; -use acvm::brillig_vm::{brillig::Value, Registers, VMStatus, VM}; +use acvm::brillig_vm::{brillig::Value, VMStatus, VM}; use acvm::{ acir::{ brillig::Opcode as BrilligOpcode, @@ -1557,23 +1557,17 @@ impl AcirContext { inputs: &[BrilligInputs], outputs_types: &[AcirType], ) -> Option> { - let (registers, memory) = execute_brillig(code, inputs)?; - - let outputs_var = vecmap(outputs_types.iter().enumerate(), |(index, output)| { - let register_value = registers.get(index.into()); - match output { - AcirType::NumericType(_) => { - let var = self.add_data(AcirVarData::Const(register_value.to_field())); - AcirValue::Var(var, output.clone()) - } - AcirType::Array(element_types, size) => { - let mem_ptr = register_value.to_usize(); - self.brillig_constant_array_output( - element_types, - *size, - &mut memory.iter().skip(mem_ptr), - ) - } + let mut memory = (execute_brillig(code, inputs)?).into_iter(); + + let outputs_var = vecmap(outputs_types.iter(), |output| match output { + AcirType::NumericType(_) => { + let var = self.add_data(AcirVarData::Const( + memory.next().expect("Missing return data").to_field(), + )); + AcirValue::Var(var, output.clone()) + } + AcirType::Array(element_types, size) => { + self.brillig_constant_array_output(element_types, *size, &mut memory) } }); @@ -1581,11 +1575,11 @@ impl AcirContext { } /// Recursively create [`AcirValue`]s for returned arrays. This is necessary because a brillig returned array can have nested arrays as elements. - fn brillig_constant_array_output<'a>( + fn brillig_constant_array_output( &mut self, element_types: &[AcirType], size: usize, - memory_iter: &mut impl Iterator, + memory_iter: &mut impl Iterator, ) -> AcirValue { let mut array_values = im::Vector::new(); for _ in 0..size { @@ -1828,42 +1822,28 @@ pub(crate) struct AcirVar(usize); /// Returns the finished state of the Brillig VM if execution can complete. /// /// Returns `None` if complete execution of the Brillig bytecode is not possible. -fn execute_brillig( - code: &[BrilligOpcode], - inputs: &[BrilligInputs], -) -> Option<(Registers, Vec)> { +fn execute_brillig(code: &[BrilligOpcode], inputs: &[BrilligInputs]) -> Option> { // Set input values - let mut input_register_values: Vec = Vec::with_capacity(inputs.len()); - let mut input_memory: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); + // Each input represents a constant or array of constants. // Iterate over each input and push it into registers and/or memory. for input in inputs { match input { BrilligInputs::Single(expr) => { - input_register_values.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?.into()); } BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values - let memory_pointer = input_memory.len(); for expr in expr_arr.iter() { - input_memory.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?.into()); } - - // Push value of the array pointer as a register - input_register_values.push(Value::from(memory_pointer)); } } } // Instantiate a Brillig VM given the solved input registers and memory, along with the Brillig bytecode. - let input_registers = Registers::load(input_register_values); - let mut vm = VM::new( - input_registers, - input_memory, - code, - Vec::new(), - &blackbox_solver::StubbedBlackBoxSolver, - ); + let mut vm = VM::new(calldata, code, Vec::new(), &blackbox_solver::StubbedBlackBoxSolver); // Run the Brillig VM on these inputs, bytecode, etc! let vm_status = vm.process_opcodes(); @@ -1872,7 +1852,9 @@ fn execute_brillig( // It may be finished, in-progress, failed, or may be waiting for results of a foreign call. // If it's finished then we can omit the opcode and just write in the return values. match vm_status { - VMStatus::Finished => Some((vm.get_registers().clone(), vm.get_memory().to_vec())), + VMStatus::Finished { return_data_offset, return_data_size } => Some( + vm.get_memory()[return_data_offset..(return_data_offset + return_data_size)].to_vec(), + ), VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"), VMStatus::Failure { .. } => { // TODO: Return an error stating that the brillig function failed. diff --git a/noir/test_programs/execution_success/brillig_nested_arrays/src/main.nr b/noir/test_programs/execution_success/brillig_nested_arrays/src/main.nr index d0a60ac0a58..5a5657246a8 100644 --- a/noir/test_programs/execution_success/brillig_nested_arrays/src/main.nr +++ b/noir/test_programs/execution_success/brillig_nested_arrays/src/main.nr @@ -12,11 +12,19 @@ unconstrained fn access_nested(notes: [MyNote; 2], x: Field, y: Field) -> Field notes[x].array[y] + notes[y].array[x] + notes[x].plain + notes[y].header.params[x] } -unconstrained fn create_inside_brillig(x: Field, y: Field) { +unconstrained fn create_inside_brillig() -> [MyNote; 2] { let header = Header { params: [1, 2, 3] }; let note0 = MyNote { array: [1, 2], plain: 3, header }; let note1 = MyNote { array: [4, 5], plain: 6, header }; - assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); + [note0, note1] +} + +unconstrained fn assert_inside_brillig(notes: [MyNote; 2], x: Field, y: Field) { + assert(access_nested(notes, x, y) == (2 + 4 + 3 + 1)); +} + +unconstrained fn create_and_assert_inside_brillig(x: Field, y: Field) { + assert_inside_brillig(create_inside_brillig(), x, y); } fn main(x: Field, y: Field) { @@ -24,7 +32,10 @@ fn main(x: Field, y: Field) { let note0 = MyNote { array: [1, 2], plain: 3, header }; let note1 = MyNote { array: [4, 5], plain: 6, header }; - create_inside_brillig(x, y); assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); + + let notes = create_inside_brillig(); + assert_inside_brillig(notes, x, y); + create_and_assert_inside_brillig(x, y); } diff --git a/noir/tooling/debugger/src/context.rs b/noir/tooling/debugger/src/context.rs index 12b55708b15..8e5c1dacf2c 100644 --- a/noir/tooling/debugger/src/context.rs +++ b/noir/tooling/debugger/src/context.rs @@ -1,6 +1,6 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap}; -use acvm::brillig_vm::{brillig::Value, Registers}; +use acvm::brillig_vm::brillig::Value; use acvm::pwg::{ ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, }; @@ -352,16 +352,6 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { acir_index < opcodes.len() && matches!(opcodes[acir_index], Opcode::Brillig(..)) } - pub(super) fn get_brillig_registers(&self) -> Option<&Registers> { - self.brillig_solver.as_ref().map(|solver| solver.get_registers()) - } - - pub(super) fn set_brillig_register(&mut self, register_index: usize, value: FieldElement) { - if let Some(solver) = self.brillig_solver.as_mut() { - solver.set_register(register_index, value.into()); - } - } - pub(super) fn get_brillig_memory(&self) -> Option<&[Value]> { self.brillig_solver.as_ref().map(|solver| solver.get_memory()) } @@ -442,7 +432,7 @@ mod tests { }, blackbox_solver::StubbedBlackBoxSolver, brillig_vm::brillig::{ - BinaryFieldOp, Opcode as BrilligOpcode, RegisterIndex, RegisterOrMemory, + BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray, }, }; use nargo::{artifacts::debug::DebugArtifact, ops::DefaultForeignCallExecutor}; @@ -461,16 +451,21 @@ mod tests { })], outputs: vec![], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 1, + offset: 0, + }, BrilligOpcode::Const { - destination: RegisterIndex::from(1), + destination: MemoryAddress::from(1), value: Value::from(fe_0), }, BrilligOpcode::ForeignCall { function: "clear_mock".into(), destinations: vec![], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, ], predicate: None, }; @@ -495,7 +490,7 @@ mod tests { assert_eq!(context.get_current_opcode_location(), Some(OpcodeLocation::Acir(0))); - // execute the first Brillig opcode (const) + // Execute the first Brillig opcode (calldata copy) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( @@ -503,15 +498,15 @@ mod tests { Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) ); - // try to execute the second Brillig opcode (and resolve the foreign call) + // execute the second Brillig opcode (const) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( context.get_current_opcode_location(), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) ); - // retry the second Brillig opcode (foreign call should be finished) + // try to execute the third Brillig opcode (and resolve the foreign call) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( @@ -519,6 +514,14 @@ mod tests { Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) ); + // retry the third Brillig opcode (foreign call should be finished) + let result = context.step_into_opcode(); + assert!(matches!(result, DebugCommandResult::Ok)); + assert_eq!( + context.get_current_opcode_location(), + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 }) + ); + // last Brillig opcode let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Done)); @@ -547,13 +550,18 @@ mod tests { ], outputs: vec![BrilligOutputs::Simple(w_z)], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, BrilligOpcode::BinaryFieldOp { - destination: RegisterIndex::from(0), + destination: MemoryAddress::from(0), op: BinaryFieldOp::Add, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, ], predicate: None, }; @@ -611,14 +619,22 @@ mod tests { Opcode::Brillig(Brillig { inputs: vec![], outputs: vec![], - bytecode: vec![BrilligOpcode::Stop, BrilligOpcode::Stop, BrilligOpcode::Stop], + bytecode: vec![ + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + ], predicate: None, }), Opcode::MemoryInit { block_id: BlockId(0), init: vec![] }, Opcode::Brillig(Brillig { inputs: vec![], outputs: vec![], - bytecode: vec![BrilligOpcode::Stop, BrilligOpcode::Stop, BrilligOpcode::Stop], + bytecode: vec![ + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + ], predicate: None, }), Opcode::AssertZero(Expression::default()), diff --git a/noir/tooling/debugger/src/repl.rs b/noir/tooling/debugger/src/repl.rs index 40ee6efdb86..92224ab785a 100644 --- a/noir/tooling/debugger/src/repl.rs +++ b/noir/tooling/debugger/src/repl.rs @@ -251,37 +251,6 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { println!("_{} = {value}", index); } - pub fn show_brillig_registers(&self) { - if !self.context.is_executing_brillig() { - println!("Not executing a Brillig block"); - return; - } - - let Some(registers) = self.context.get_brillig_registers() else { - // this can happen when just entering the Brillig block since ACVM - // would have not initialized the Brillig VM yet; in fact, the - // Brillig code may be skipped altogether - println!("Brillig VM registers not available"); - return; - }; - - for (index, value) in registers.inner.iter().enumerate() { - println!("{index} = {}", value.to_field()); - } - } - - pub fn set_brillig_register(&mut self, index: usize, value: String) { - let Some(field_value) = FieldElement::try_from_str(&value) else { - println!("Invalid value: {value}"); - return; - }; - if !self.context.is_executing_brillig() { - println!("Not executing a Brillig block"); - return; - } - self.context.set_brillig_register(index, field_value); - } - pub fn show_brillig_memory(&self) { if !self.context.is_executing_brillig() { println!("Not executing a Brillig block"); @@ -445,26 +414,6 @@ pub fn run( } }, ) - .add( - "registers", - command! { - "show Brillig registers (valid when executing a Brillig block)", - () => || { - ref_context.borrow().show_brillig_registers(); - Ok(CommandStatus::Done) - } - }, - ) - .add( - "regset", - command! { - "update a Brillig register with the given value", - (index: usize, value: String) => |index, value| { - ref_context.borrow_mut().set_brillig_register(index, value); - Ok(CommandStatus::Done) - } - }, - ) .add( "memory", command! { diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index 7b7e4778689..7d5dbbf08e3 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -21,7 +21,7 @@ import { jest } from '@jest/globals'; import { expectsNumOfEncryptedLogsInTheLastBlockToBe, setup, setupPXEService } from './fixtures/utils.js'; -const TIMEOUT = 60_000; +const TIMEOUT = 90_000; describe('e2e_2_pxes', () => { jest.setTimeout(TIMEOUT); diff --git a/yarn-project/end-to-end/src/e2e_persistence.test.ts b/yarn-project/end-to-end/src/e2e_persistence.test.ts index 7750291f270..bd062053f53 100644 --- a/yarn-project/end-to-end/src/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/e2e_persistence.test.ts @@ -11,12 +11,15 @@ import { CompleteAddress, EthAddress, Fq, Fr } from '@aztec/circuits.js'; import { DeployL1Contracts } from '@aztec/ethereum'; import { TokenContract } from '@aztec/noir-contracts/Token'; +import { jest } from '@jest/globals'; import { mkdtemp } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; import { EndToEndContext, setup } from './fixtures/utils.js'; +jest.setTimeout(60_000); + describe('Aztec persistence', () => { /** * These tests check that the Aztec Node and PXE can be shutdown and restarted without losing data. diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/side_effect.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/side_effect.nr index f49b429a417..8d2d218d6a1 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/side_effect.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/side_effect.nr @@ -55,13 +55,6 @@ impl Deserialize<2> for SideEffect { } } -impl SideEffect { - pub fn is_empty(self) -> bool { - (self.value == 0) - & (self.counter == 0) - } -} - struct SideEffectLinkedToNoteHash{ value: Field, note_hash: Field, @@ -116,11 +109,3 @@ impl Deserialize<3> for SideEffectLinkedToNoteHash { } } -impl SideEffectLinkedToNoteHash{ - pub fn is_empty(self) -> bool { - (self.value == 0) - & (self.note_hash == 0) - & (self.counter == 0) - } -} - diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts index 8afe5f4197a..3a005017e91 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts @@ -365,7 +365,7 @@ describe('sequencer/solo_block_builder', () => { const [l2Block] = await builder.buildL2Block(globalVariables, txs, mockL1ToL2Messages); expect(l2Block.number).toEqual(blockNumber); - }, 10_000); + }, 30_000); it('builds a mixed L2 block', async () => { // Ensure that each transaction has unique (non-intersecting nullifier values)