From 21b697bbf5f84779fdde0546529a723efa19f998 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Fri, 15 Mar 2024 22:55:17 +0000 Subject: [PATCH] feat: initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduces earthly as an alternative CI that hopes to eventually replace our current build-system. https://docs.earthly.dev/ is a build system that combines Makefiles and Dockerfiles. This is basically exactly what our system needed, IMO, and has some nice things figured out. Hope is to reduce complexity of working with the build system by a good chunk. Core changes: - we have a github actions CI that runs a single end to end test inside earthly for arm64 and x86_64 - new Earthfile's now mirror the Dockerfile's, notable differences: - we build our own foundry package for ARM support - we build our own wasi-sdk package for ARM support - grumpkin SRS is no longer generated on the spot, but downloaded like bn254 SRS - we don't inject any commit hashes for Noir as this would cause spurious rebuilds as any difference stops caching, instead we inject a content hash (to be revisited) Side changes: - since we build our own wasi-sdk 21 package, and it is clang18, some compilation workarounds - allow specifying a different nargo and acvm binary in build - small output tweaks --------- Co-authored-by: Charlie Lye Co-authored-by: Innokentii Sennovskii Co-authored-by: Cody Gunton Co-authored-by: Alex Gherghisan Co-authored-by: Mitchell Tracy Co-authored-by: Jan Beneš Co-authored-by: esau <152162806+sklppy88@users.noreply.github.com> Co-authored-by: Facundo Co-authored-by: josh crites Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: Álvaro Rodríguez Co-authored-by: Ilyas Ridhuan --- .aztec-sync-commit | 2 +- .github/scripts/wasm-bindgen-install.sh | 2 +- acvm-repo/acir/codegen/acir.cpp | 125 +- acvm-repo/brillig/src/opcodes.rs | 7 + acvm-repo/brillig_vm/src/arithmetic.rs | 9 + aztec_macros/src/lib.rs | 9 - aztec_macros/src/transforms/functions.rs | 16 +- aztec_macros/src/utils/errors.rs | 6 - .../brillig/brillig_gen/brillig_black_box.rs | 4 +- .../src/brillig/brillig_gen/brillig_block.rs | 310 ++--- .../brillig/brillig_gen/brillig_slice_ops.rs | 165 +-- .../noirc_evaluator/src/brillig/brillig_ir.rs | 1095 +---------------- .../brillig/brillig_ir/brillig_variable.rs | 9 +- .../src/brillig/brillig_ir/codegen_binary.rs | 29 + .../src/brillig/brillig_ir/codegen_calls.rs | 102 ++ .../brillig_ir/codegen_control_flow.rs | 123 ++ .../brillig/brillig_ir/codegen_intrinsic.rs | 89 ++ .../src/brillig/brillig_ir/codegen_memory.rs | 255 ++++ .../src/brillig/brillig_ir/codegen_stack.rs | 26 + .../src/brillig/brillig_ir/debug_show.rs | 161 +-- .../src/brillig/brillig_ir/entry_point.rs | 115 +- .../src/brillig/brillig_ir/instructions.rs | 533 ++++++++ .../src/brillig/brillig_ir/registers.rs | 28 +- .../src/hir/resolution/resolver.rs | 65 +- compiler/noirc_frontend/src/tests.rs | 12 + noir_stdlib/src/array.nr | 2 +- noir_stdlib/src/hash.nr | 101 +- .../brillig_nested_slices/Prover.toml | 2 - .../custom_entry_not_found/Prover.toml | 1 - .../dep_impl_primitive/Prover.toml | 1 - .../compile_failure/depend_on_bin/Prover.toml | 1 - .../div_by_zero_modulo/Prover.toml | 0 .../dup_trait_implementation_4/Prover.toml | 0 .../dup_trait_implementation_5/Prover.toml | 0 .../dup_trait_items_1/Prover.toml | 0 .../dup_trait_items_2/Prover.toml | 0 .../dup_trait_items_3/Prover.toml | 0 .../dup_trait_items_4/Prover.toml | 0 .../dup_trait_items_5/Prover.toml | 0 .../dup_trait_items_6/Prover.toml | 0 .../hashmap_load_factor/Prover.toml | 26 - .../negate_unsigned/Prover.toml | 0 .../orphaned_trait_impl/Prover.toml | 2 - .../slice_remove_failure/Prover.toml | 2 - .../crates/a/Prover.toml | 2 - .../crates/b/Prover.toml | 2 - .../assert_msg_runtime/Nargo.toml | 0 .../assert_msg_runtime/Prover.toml | 0 .../assert_msg_runtime/src/main.nr | 0 .../brillig_assert_fail/Nargo.toml | 0 .../brillig_assert_fail/Prover.toml | 0 .../brillig_assert_fail/src/main.nr | 0 .../brillig_assert_msg_runtime/Nargo.toml | 0 .../brillig_assert_msg_runtime/Prover.toml | 0 .../brillig_assert_msg_runtime/src/main.nr | 0 .../div_by_zero_constants/Nargo.toml | 0 .../div_by_zero_constants}/Prover.toml | 0 .../div_by_zero_constants/src/main.nr | 0 .../div_by_zero_modulo/Nargo.toml | 0 .../div_by_zero_modulo}/Prover.toml | 0 .../div_by_zero_modulo/src/main.nr | 0 .../div_by_zero_numerator_witness/Nargo.toml | 0 .../div_by_zero_numerator_witness/Prover.toml | 0 .../div_by_zero_numerator_witness/src/main.nr | 0 .../div_by_zero_witness/Nargo.toml | 0 .../div_by_zero_witness/Prover.toml | 0 .../div_by_zero_witness/src/main.nr | 0 .../dyn_index_fail_nested_array/Nargo.toml | 0 .../dyn_index_fail_nested_array/Prover.toml | 0 .../dyn_index_fail_nested_array/src/main.nr | 0 .../dynamic_index_failure/Nargo.toml | 0 .../dynamic_index_failure/Prover.toml | 0 .../dynamic_index_failure/src/main.nr | 0 .../option_expect/Nargo.toml | 0 .../option_expect/src/main.nr | 0 .../slice_access_failure/Nargo.toml | 0 .../slice_access_failure}/Prover.toml | 0 .../slice_access_failure/src/main.nr | 0 .../slice_insert_failure/Nargo.toml | 0 .../slice_insert_failure}/Prover.toml | 0 .../slice_insert_failure/src/main.nr | 0 .../slice_remove_failure/Nargo.toml | 0 .../slice_remove_failure}/Prover.toml | 0 .../slice_remove_failure/src/main.nr | 0 .../workspace_fail/Nargo.toml | 0 .../workspace_fail/crates/a/Nargo.toml | 0 .../workspace_fail/crates/a/Prover.toml | 0 .../workspace_fail/crates/a/src/main.nr | 0 .../workspace_fail/crates/b/Nargo.toml | 0 .../workspace_fail/crates/b/Prover.toml | 0 .../workspace_fail/crates/b/src/main.nr | 0 tooling/nargo_cli/Cargo.toml | 1 + tooling/nargo_cli/build.rs | 41 +- tooling/nargo_fmt/src/rewrite/imports.rs | 9 +- tooling/nargo_fmt/tests/expected/contract.nr | 2 +- .../nargo_fmt/tests/expected/import_braces.nr | 1 + tooling/nargo_fmt/tests/input/contract.nr | 2 +- .../nargo_fmt/tests/input/import_braces.nr | 1 + 98 files changed, 1942 insertions(+), 1554 deletions(-) create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs delete mode 100644 test_programs/compile_failure/brillig_nested_slices/Prover.toml delete mode 100644 test_programs/compile_failure/custom_entry_not_found/Prover.toml delete mode 100644 test_programs/compile_failure/dep_impl_primitive/Prover.toml delete mode 100644 test_programs/compile_failure/depend_on_bin/Prover.toml delete mode 100644 test_programs/compile_failure/div_by_zero_modulo/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_implementation_4/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_implementation_5/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_1/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_2/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_3/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_4/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_5/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_6/Prover.toml delete mode 100644 test_programs/compile_failure/hashmap_load_factor/Prover.toml delete mode 100644 test_programs/compile_failure/negate_unsigned/Prover.toml delete mode 100644 test_programs/compile_failure/orphaned_trait_impl/Prover.toml delete mode 100644 test_programs/compile_failure/slice_remove_failure/Prover.toml delete mode 100644 test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml delete mode 100644 test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml rename test_programs/{compile_failure => execution_failure}/assert_msg_runtime/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/assert_msg_runtime/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/assert_msg_runtime/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_fail/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_fail/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_fail/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_msg_runtime/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_msg_runtime/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_msg_runtime/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_constants/Nargo.toml (100%) rename test_programs/{compile_failure/cyclic_dep => execution_failure/div_by_zero_constants}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_constants/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_modulo/Nargo.toml (100%) rename test_programs/{compile_failure/div_by_zero_constants => execution_failure/div_by_zero_modulo}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_modulo/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_numerator_witness/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_numerator_witness/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_numerator_witness/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_witness/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_witness/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_witness/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/dyn_index_fail_nested_array/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/dyn_index_fail_nested_array/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/dyn_index_fail_nested_array/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/dynamic_index_failure/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/dynamic_index_failure/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/dynamic_index_failure/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/option_expect/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/option_expect/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/slice_access_failure/Nargo.toml (100%) rename test_programs/{compile_failure/radix_non_constant_length => execution_failure/slice_access_failure}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/slice_access_failure/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/slice_insert_failure/Nargo.toml (100%) rename test_programs/{compile_failure/slice_access_failure => execution_failure/slice_insert_failure}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/slice_insert_failure/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/slice_remove_failure/Nargo.toml (100%) rename test_programs/{compile_failure/slice_insert_failure => execution_failure/slice_remove_failure}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/slice_remove_failure/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/a/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/a/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/a/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/b/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/b/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/b/src/main.nr (100%) create mode 100644 tooling/nargo_fmt/tests/expected/import_braces.nr create mode 100644 tooling/nargo_fmt/tests/input/import_braces.nr diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 4d9320c79ba..3e79562bd07 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -aa90f6ed7bfae06bdf6990816d154bbd24993689 +8e75fe5c47250e860a4eae4dbf0973c503221720 diff --git a/.github/scripts/wasm-bindgen-install.sh b/.github/scripts/wasm-bindgen-install.sh index a548372ee2c..20908003693 100755 --- a/.github/scripts/wasm-bindgen-install.sh +++ b/.github/scripts/wasm-bindgen-install.sh @@ -6,7 +6,7 @@ cd $(dirname "$0") ./cargo-binstall-install.sh # Install wasm-bindgen-cli. -if [ "$(wasm-bindgen --version | cut -d' ' -f2)" != "0.2.86" ]; then +if [ "$(wasm-bindgen --version &> /dev/null | cut -d' ' -f2)" != "0.2.86" ]; then echo "Building wasm-bindgen..." cargo binstall wasm-bindgen-cli@0.2.86 --force --no-confirm fi diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index 0fc84d47a0f..6fdb62c5674 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -31,13 +31,31 @@ namespace Circuit { static Div bincodeDeserialize(std::vector); }; + struct IntegerDiv { + friend bool operator==(const IntegerDiv&, const IntegerDiv&); + std::vector bincodeSerialize() const; + static IntegerDiv bincodeDeserialize(std::vector); + }; + struct Equals { friend bool operator==(const Equals&, const Equals&); std::vector bincodeSerialize() const; static Equals bincodeDeserialize(std::vector); }; - std::variant value; + struct LessThan { + friend bool operator==(const LessThan&, const LessThan&); + std::vector bincodeSerialize() const; + static LessThan bincodeDeserialize(std::vector); + }; + + struct LessThanEquals { + friend bool operator==(const LessThanEquals&, const LessThanEquals&); + std::vector bincodeSerialize() const; + static LessThanEquals bincodeDeserialize(std::vector); + }; + + std::variant value; friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; @@ -1317,6 +1335,41 @@ Circuit::BinaryFieldOp::Div serde::Deserializable:: return obj; } +namespace Circuit { + + inline bool operator==(const BinaryFieldOp::IntegerDiv &lhs, const BinaryFieldOp::IntegerDiv &rhs) { + return true; + } + + inline std::vector BinaryFieldOp::IntegerDiv::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BinaryFieldOp::IntegerDiv BinaryFieldOp::IntegerDiv::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::BinaryFieldOp::IntegerDiv &obj, Serializer &serializer) { +} + +template <> +template +Circuit::BinaryFieldOp::IntegerDiv serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BinaryFieldOp::IntegerDiv obj; + return obj; +} + namespace Circuit { inline bool operator==(const BinaryFieldOp::Equals &lhs, const BinaryFieldOp::Equals &rhs) { @@ -1352,6 +1405,76 @@ Circuit::BinaryFieldOp::Equals serde::Deserializable BinaryFieldOp::LessThan::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BinaryFieldOp::LessThan BinaryFieldOp::LessThan::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::BinaryFieldOp::LessThan &obj, Serializer &serializer) { +} + +template <> +template +Circuit::BinaryFieldOp::LessThan serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BinaryFieldOp::LessThan obj; + return obj; +} + +namespace Circuit { + + inline bool operator==(const BinaryFieldOp::LessThanEquals &lhs, const BinaryFieldOp::LessThanEquals &rhs) { + return true; + } + + inline std::vector BinaryFieldOp::LessThanEquals::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BinaryFieldOp::LessThanEquals BinaryFieldOp::LessThanEquals::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::BinaryFieldOp::LessThanEquals &obj, Serializer &serializer) { +} + +template <> +template +Circuit::BinaryFieldOp::LessThanEquals serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BinaryFieldOp::LessThanEquals obj; + return obj; +} + namespace Circuit { inline bool operator==(const BinaryIntOp &lhs, const BinaryIntOp &rhs) { diff --git a/acvm-repo/brillig/src/opcodes.rs b/acvm-repo/brillig/src/opcodes.rs index 51df1f90941..ad45c23ac35 100644 --- a/acvm-repo/brillig/src/opcodes.rs +++ b/acvm-repo/brillig/src/opcodes.rs @@ -180,9 +180,16 @@ pub enum BinaryFieldOp { Add, Sub, Mul, + /// Field division Div, + /// Integer division + IntegerDiv, /// (==) equal Equals, + /// (<) Field less than + LessThan, + /// (<=) field less or equal + LessThanEquals, } /// Binary fixed-length integer expressions diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index 9d7b6fe8f02..3b8e8b6589b 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -15,7 +15,16 @@ pub(crate) fn evaluate_binary_field_op( BinaryFieldOp::Sub => a - b, BinaryFieldOp::Mul => a * b, BinaryFieldOp::Div => a / b, + BinaryFieldOp::IntegerDiv => { + let a_big = BigUint::from_bytes_be(&a.to_be_bytes()); + let b_big = BigUint::from_bytes_be(&b.to_be_bytes()); + + let result = a_big / b_big; + FieldElement::from_be_bytes_reduce(&result.to_bytes_be()) + } BinaryFieldOp::Equals => (a == b).into(), + BinaryFieldOp::LessThan => (a < b).into(), + BinaryFieldOp::LessThanEquals => (a <= b).into(), } } diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 1f3546cbb6a..e0100977eee 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -179,15 +179,6 @@ fn transform_module( crate_graph.root_file_id, )); } - - let constructor_defined = module.functions.iter().any(|func| func.name() == "constructor"); - if !constructor_defined { - let crate_graph = &context.crate_graph[crate_id]; - return Err(( - AztecMacroError::ContractConstructorMissing { span: Span::default() }, - crate_graph.root_file_id, - )); - } } Ok(has_transformed_module) diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index 09c11e173fe..4fc2792a2cc 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -48,10 +48,9 @@ pub fn transform_function( func.def.body.0.insert(0, init_check); } - // Add assertion for initialization arguments + // Add assertion for initialization arguments and sender if is_initializer { - let assert_init_args = create_assert_init_args(); - func.def.body.0.insert(0, assert_init_args); + func.def.body.0.insert(0, create_assert_initializer()); } // Add access to the storage struct @@ -211,18 +210,19 @@ fn create_internal_check(fname: &str) -> Statement { ))) } -/// Creates a call to assert_initialization_args_match_address_preimage to ensure -/// the initialization arguments used in the init call match the address preimage. +/// Creates a call to assert_initialization_matches_address_preimage to be inserted +/// in the initializer. Checks that the args and sender to the initializer match the +/// commitments from the address preimage. /// /// ```noir -/// assert_initialization_args_match_address_preimage(context); +/// assert_initialization_matches_address_preimage(context); /// ``` -fn create_assert_init_args() -> Statement { +fn create_assert_initializer() -> Statement { make_statement(StatementKind::Expression(call( variable_path(chained_dep!( "aztec", "initializer", - "assert_initialization_args_match_address_preimage" + "assert_initialization_matches_address_preimage" )), vec![variable("context")], ))) diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs index 63892b58af9..199473baec6 100644 --- a/aztec_macros/src/utils/errors.rs +++ b/aztec_macros/src/utils/errors.rs @@ -7,7 +7,6 @@ use super::constants::MAX_CONTRACT_PRIVATE_FUNCTIONS; pub enum AztecMacroError { AztecDepNotFound, ContractHasTooManyPrivateFunctions { span: Span }, - ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, @@ -29,11 +28,6 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, - AztecMacroError::ContractConstructorMissing { span } => MacroError { - primary_message: "Contract must have a constructor function".to_owned(), - secondary_message: None, - span: Some(span), - }, AztecMacroError::UnsupportedFunctionArgumentType { span, typ } => MacroError { primary_message: format!("Provided parameter type `{typ:?}` is not supported in Aztec contract interface"), secondary_message: None, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index d542240a40c..36e5c99a2ca 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -167,7 +167,7 @@ pub(crate) fn convert_black_box_call( ) = (function_arguments, function_results) { let message_hash = convert_array_or_vector(brillig_context, message, bb_func); - let signature = brillig_context.array_to_vector(signature); + let signature = brillig_context.array_to_vector_instruction(signature); brillig_context.black_box_op_instruction(BlackBoxOp::SchnorrVerify { public_key_x: public_key_x.address, public_key_y: public_key_y.address, @@ -368,7 +368,7 @@ fn convert_array_or_vector( bb_func: &BlackBoxFunc, ) -> BrilligVector { match array_or_vector { - BrilligVariable::BrilligArray(array) => brillig_context.array_to_vector(array), + BrilligVariable::BrilligArray(array) => brillig_context.array_to_vector_instruction(array), BrilligVariable::BrilligVector(vector) => *vector, _ => unreachable!( "ICE: {} expected an array or a vector, but got {:?}", diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 938a80e87d1..5b6610df7b4 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -16,7 +16,7 @@ use crate::ssa::ir::{ types::{NumericType, Type}, value::{Value, ValueId}, }; -use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, ValueOrArray}; +use acvm::acir::brillig::{MemoryAddress, ValueOrArray}; use acvm::brillig_vm::brillig::HeapVector; use acvm::FieldElement; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; @@ -155,7 +155,7 @@ impl<'block> BrilligBlock<'block> { return_variable.extract_registers() }) .collect(); - self.brillig_context.return_instruction(&return_registers); + self.brillig_context.codegen_return(&return_registers); } } } @@ -297,16 +297,15 @@ impl<'block> BrilligBlock<'block> { Type::Reference(element) => match *element { Type::Array(..) => { self.brillig_context - .allocate_array_reference_instruction(address_register.address); + .codegen_allocate_array_reference(address_register.address); } Type::Slice(..) => { self.brillig_context - .allocate_vector_reference_instruction(address_register.address); + .codegen_allocate_vector_reference(address_register.address); } _ => { - self.brillig_context.allocate_single_addr_reference_instruction( - address_register.address, - ); + self.brillig_context + .codegen_allocate_single_addr_reference(address_register.address); } }, _ => { @@ -318,8 +317,7 @@ impl<'block> BrilligBlock<'block> { let address_var = self.convert_ssa_single_addr_value(*address, dfg); let source_variable = self.convert_ssa_value(*value, dfg); - self.brillig_context - .store_variable_instruction(address_var.address, source_variable); + self.brillig_context.codegen_store_variable(address_var.address, source_variable); } Instruction::Load { address } => { let target_variable = self.variables.define_variable( @@ -332,7 +330,7 @@ impl<'block> BrilligBlock<'block> { let address_variable = self.convert_ssa_single_addr_value(*address, dfg); self.brillig_context - .load_variable_instruction(target_variable, address_variable.address); + .codegen_load_variable(target_variable, address_variable.address); } Instruction::Not(value) => { let condition_register = self.convert_ssa_single_addr_value(*value, dfg); @@ -374,16 +372,16 @@ impl<'block> BrilligBlock<'block> { 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); + self.brillig_context.increase_free_memory_pointer_instruction(*size); // Update the dynamic slice length maintained in SSA 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); - self.brillig_context.usize_op_in_place( + self.brillig_context.codegen_usize_op_in_place( len_index, - BinaryIntOp::UnsignedDiv, + BrilligBinaryOp::UnsignedDiv, element_size, ); } else { @@ -495,7 +493,7 @@ impl<'block> BrilligBlock<'block> { // Update the user-facing slice length self.brillig_context.mov_instruction(target_len.address, limb_count.address); - self.brillig_context.radix_instruction( + self.brillig_context.codegen_to_radix( source, target_vector, radix, @@ -524,18 +522,18 @@ impl<'block> BrilligBlock<'block> { dfg, ) { BrilligVariable::BrilligArray(array) => { - self.brillig_context.array_to_vector(&array) + self.brillig_context.array_to_vector_instruction(&array) } BrilligVariable::BrilligVector(vector) => vector, BrilligVariable::SingleAddr(..) => unreachable!("ICE: ToBits on non-array"), }; - let radix = self.brillig_context.make_constant(2_usize.into(), 32); + let radix = self.brillig_context.make_constant_instruction(2_usize.into(), 32); // Update the user-facing slice length self.brillig_context.mov_instruction(target_len.address, limb_count.address); - self.brillig_context.radix_instruction( + self.brillig_context.codegen_to_radix( source, target_vector, radix, @@ -558,7 +556,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); let source_register = self.convert_ssa_single_addr_value(*value, dfg); - self.brillig_context.truncate_instruction( + self.brillig_context.codegen_truncate( destination_register, source_register, *bit_size, @@ -633,20 +631,19 @@ impl<'block> BrilligBlock<'block> { // Create a field constant with the max let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); - let right = self.brillig_context.make_constant( + let right = self.brillig_context.make_constant_instruction( FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), FieldElement::max_num_bits(), ); // Check if lte max - let brillig_binary_op = BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals); let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); self.brillig_context.binary_instruction( left, right, condition, - brillig_binary_op, + BrilligBinaryOp::LessThanEquals, ); self.brillig_context.constrain_instruction(condition, assert_message.clone()); @@ -661,7 +658,11 @@ impl<'block> BrilligBlock<'block> { | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, other => unreachable!("ICE: increment rc on non-array: {other:?}"), }; - self.brillig_context.usize_op_in_place(rc_register, BinaryIntOp::Add, 1); + self.brillig_context.codegen_usize_op_in_place( + rc_register, + BrilligBinaryOp::Add, + 1, + ); } Instruction::DecrementRc { value } => { let rc_register = match self.convert_ssa_value(*value, dfg) { @@ -669,7 +670,11 @@ impl<'block> BrilligBlock<'block> { | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, other => unreachable!("ICE: decrement rc on non-array: {other:?}"), }; - self.brillig_context.usize_op_in_place(rc_register, BinaryIntOp::Sub, 1); + self.brillig_context.codegen_usize_op_in_place( + rc_register, + BrilligBinaryOp::Sub, + 1, + ); } Instruction::EnableSideEffects { .. } => { todo!("enable_side_effects not supported by brillig") @@ -711,7 +716,7 @@ impl<'block> BrilligBlock<'block> { let saved_registers = self .brillig_context - .pre_call_save_registers_prep_args(&argument_registers, &variables_to_save); + .codegen_pre_call_save_registers_prep_args(&argument_registers, &variables_to_save); // We don't save and restore constants, so we dump them before a external call since the callee might use the registers where they are allocated. self.variables.dump_constants(); @@ -745,7 +750,7 @@ impl<'block> BrilligBlock<'block> { // puts the returns into the returned_registers and restores saved_registers self.brillig_context - .post_call_prep_returns_load_registers(&returned_registers, &saved_registers); + .codegen_post_call_prep_returns_load_registers(&returned_registers, &saved_registers); } fn validate_array_index( @@ -755,7 +760,7 @@ impl<'block> BrilligBlock<'block> { ) { let (size_as_register, should_deallocate_size) = match array_variable { BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { - (self.brillig_context.make_usize_constant(size.into()), true) + (self.brillig_context.make_usize_constant_instruction(size.into()), true) } BrilligVariable::BrilligVector(BrilligVector { size, .. }) => { (SingleAddrVariable::new_usize(size), false) @@ -765,11 +770,11 @@ impl<'block> BrilligBlock<'block> { let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( index_register.address, size_as_register.address, condition.address, - BinaryIntOp::LessThan, + BrilligBinaryOp::LessThan, ); self.brillig_context @@ -789,7 +794,7 @@ impl<'block> BrilligBlock<'block> { ) { match destination_variable { BrilligVariable::SingleAddr(destination_register) => { - self.brillig_context.array_get( + self.brillig_context.codegen_array_get( array_pointer, index_var, destination_register.address, @@ -797,8 +802,8 @@ impl<'block> BrilligBlock<'block> { } BrilligVariable::BrilligArray(..) | BrilligVariable::BrilligVector(..) => { let reference = self.brillig_context.allocate_register(); - self.brillig_context.array_get(array_pointer, index_var, reference); - self.brillig_context.load_variable_instruction(destination_variable, reference); + self.brillig_context.codegen_array_get(array_pointer, index_var, reference); + self.brillig_context.codegen_load_variable(destination_variable, reference); self.brillig_context.deallocate_register(reference); } } @@ -828,7 +833,7 @@ impl<'block> BrilligBlock<'block> { let (source_pointer, source_size_as_register) = match source_variable { BrilligVariable::BrilligArray(BrilligArray { size, pointer, rc: _ }) => { let source_size_register = self.brillig_context.allocate_register(); - self.brillig_context.usize_const(source_size_register, size.into()); + self.brillig_context.usize_const_instruction(source_size_register, size.into()); (pointer, source_size_register) } BrilligVariable::BrilligVector(BrilligVector { size, pointer, rc: _ }) => { @@ -840,23 +845,23 @@ impl<'block> BrilligBlock<'block> { }; // Here we want to compare the reference count against 1. - let one = self.brillig_context.make_usize_constant(1_usize.into()); + let one = self.brillig_context.make_usize_constant_instruction(1_usize.into()); let condition = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( reference_count, one.address, condition, - BinaryIntOp::Equals, + BrilligBinaryOp::Equals, ); - self.brillig_context.branch_instruction(condition, |ctx, cond| { + self.brillig_context.codegen_branch(condition, |ctx, cond| { if cond { // Reference count is 1, we can mutate the array directly ctx.mov_instruction(destination_pointer, source_pointer); } else { // First issue a array copy to the destination - ctx.allocate_array_instruction(destination_pointer, source_size_as_register); + ctx.codegen_allocate_array(destination_pointer, source_size_as_register); - ctx.copy_array_instruction( + ctx.codegen_copy_array( source_pointer, destination_pointer, SingleAddrVariable::new( @@ -869,7 +874,7 @@ impl<'block> BrilligBlock<'block> { match destination_variable { BrilligVariable::BrilligArray(BrilligArray { rc: target_rc, .. }) => { - self.brillig_context.usize_const(target_rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_rc, 1_usize.into()); } BrilligVariable::BrilligVector(BrilligVector { size: target_size, @@ -877,7 +882,7 @@ impl<'block> BrilligBlock<'block> { .. }) => { self.brillig_context.mov_instruction(target_size, source_size_as_register); - self.brillig_context.usize_const(target_rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_rc, 1_usize.into()); } _ => unreachable!("ICE: array set on non-array"), } @@ -901,20 +906,20 @@ impl<'block> BrilligBlock<'block> { ) { match value_variable { BrilligVariable::SingleAddr(value_variable) => { - ctx.array_set(destination_pointer, index_register, value_variable.address); + ctx.codegen_array_set(destination_pointer, index_register, value_variable.address); } BrilligVariable::BrilligArray(_) => { 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); + ctx.codegen_allocate_array_reference(reference); + ctx.codegen_store_variable(reference, value_variable); + ctx.codegen_array_set(destination_pointer, index_register, reference); ctx.deallocate_register(reference); } BrilligVariable::BrilligVector(_) => { let reference = ctx.allocate_register(); - ctx.allocate_vector_reference_instruction(reference); - ctx.store_variable_instruction(reference, value_variable); - ctx.array_set(destination_pointer, index_register, reference); + ctx.codegen_allocate_vector_reference(reference); + ctx.codegen_store_variable(reference, value_variable); + ctx.codegen_array_set(destination_pointer, index_register, reference); ctx.deallocate_register(reference); } } @@ -972,7 +977,12 @@ impl<'block> BrilligBlock<'block> { self.convert_ssa_value(*arg, dfg) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Add); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Add, + ); self.slice_push_back_operation(target_vector, source_vector, &item_values); } @@ -998,7 +1008,12 @@ impl<'block> BrilligBlock<'block> { self.convert_ssa_value(*arg, dfg) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Add); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Add, + ); self.slice_push_front_operation(target_vector, source_vector, &item_values); } @@ -1031,7 +1046,12 @@ impl<'block> BrilligBlock<'block> { ) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Sub); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Sub, + ); self.slice_pop_back_operation(target_vector, source_vector, &pop_variables); } @@ -1063,7 +1083,12 @@ impl<'block> BrilligBlock<'block> { ); let target_vector = target_variable.extract_vector(); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Sub); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Sub, + ); self.slice_pop_front_operation(target_vector, source_vector, &pop_variables); } @@ -1092,20 +1117,26 @@ impl<'block> BrilligBlock<'block> { // https://github.com/noir-lang/noir/issues/1889#issuecomment-1668048587 let user_index = self.convert_ssa_single_addr_value(arguments[2], dfg); - let converted_index = self.brillig_context.make_usize_constant(element_size.into()); + let converted_index = + self.brillig_context.make_usize_constant_instruction(element_size.into()); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( converted_index.address, user_index.address, converted_index.address, - BinaryIntOp::Mul, + BrilligBinaryOp::Mul, ); let items = vecmap(&arguments[3..element_size + 3], |arg| { self.convert_ssa_value(*arg, dfg) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Add); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Add, + ); self.slice_insert_operation(target_vector, source_vector, converted_index, &items); self.brillig_context.deallocate_single_addr(converted_index); @@ -1135,12 +1166,13 @@ impl<'block> BrilligBlock<'block> { // https://github.com/noir-lang/noir/issues/1889#issuecomment-1668048587 let user_index = self.convert_ssa_single_addr_value(arguments[2], dfg); - let converted_index = self.brillig_context.make_usize_constant(element_size.into()); - self.brillig_context.memory_op( + let converted_index = + self.brillig_context.make_usize_constant_instruction(element_size.into()); + self.brillig_context.memory_op_instruction( converted_index.address, user_index.address, converted_index.address, - BinaryIntOp::Mul, + BrilligBinaryOp::Mul, ); let removed_items = vecmap(&results[2..element_size + 2], |result| { @@ -1152,7 +1184,12 @@ impl<'block> BrilligBlock<'block> { ) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Sub); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Sub, + ); self.slice_remove_operation( target_vector, @@ -1181,12 +1218,12 @@ impl<'block> BrilligBlock<'block> { target_len: MemoryAddress, source_value: ValueId, dfg: &DataFlowGraph, - binary_op: BinaryIntOp, + binary_op: BrilligBinaryOp, ) { let source_len_variable = self.convert_ssa_value(source_value, dfg); let source_len = source_len_variable.extract_single_addr(); - self.brillig_context.usize_op(source_len.address, target_len, binary_op, 1); + self.brillig_context.codegen_usize_op(source_len.address, target_len, binary_op, 1); } /// Converts an SSA cast to a sequence of Brillig opcodes. @@ -1227,15 +1264,14 @@ impl<'block> BrilligBlock<'block> { result: SingleAddrVariable, is_signed: bool, ) { - let (op, bit_size) = if let BrilligBinaryOp::Integer(op) = binary_operation { - // Bit size is checked at compile time to be equal for left and right - (op, left.bit_size) - } else { + let bit_size = left.bit_size; + + if bit_size == FieldElement::max_num_bits() { return; - }; + } - match (op, is_signed) { - (BinaryIntOp::Add, false) => { + match (binary_operation, is_signed) { + (BrilligBinaryOp::Add, false) => { let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); // Check that lhs <= result @@ -1243,7 +1279,7 @@ impl<'block> BrilligBlock<'block> { left, result, condition, - BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals), + BrilligBinaryOp::LessThanEquals, ); self.brillig_context.constrain_instruction( condition, @@ -1251,7 +1287,7 @@ impl<'block> BrilligBlock<'block> { ); self.brillig_context.deallocate_single_addr(condition); } - (BinaryIntOp::Sub, false) => { + (BrilligBinaryOp::Sub, false) => { let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); // Check that rhs <= lhs @@ -1259,7 +1295,7 @@ impl<'block> BrilligBlock<'block> { right, left, condition, - BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals), + BrilligBinaryOp::LessThanEquals, ); self.brillig_context.constrain_instruction( condition, @@ -1267,19 +1303,20 @@ impl<'block> BrilligBlock<'block> { ); self.brillig_context.deallocate_single_addr(condition); } - (BinaryIntOp::Mul, false) => { + (BrilligBinaryOp::Mul, false) => { // Multiplication overflow is only possible for bit sizes > 1 if bit_size > 1 { let is_right_zero = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); - let zero = self.brillig_context.make_constant(0_usize.into(), bit_size); + let zero = + self.brillig_context.make_constant_instruction(0_usize.into(), bit_size); self.brillig_context.binary_instruction( zero, right, is_right_zero, - BrilligBinaryOp::Integer(BinaryIntOp::Equals), + BrilligBinaryOp::Equals, ); - self.brillig_context.if_not_instruction(is_right_zero.address, |ctx| { + self.brillig_context.codegen_if_not(is_right_zero.address, |ctx| { let condition = SingleAddrVariable::new(ctx.allocate_register(), 1); let division = SingleAddrVariable::new(ctx.allocate_register(), bit_size); // Check that result / rhs == lhs @@ -1287,14 +1324,9 @@ impl<'block> BrilligBlock<'block> { result, right, division, - BrilligBinaryOp::Integer(BinaryIntOp::UnsignedDiv), - ); - ctx.binary_instruction( - division, - left, - condition, - BrilligBinaryOp::Integer(BinaryIntOp::Equals), + BrilligBinaryOp::UnsignedDiv, ); + ctx.binary_instruction(division, left, condition, BrilligBinaryOp::Equals); ctx.constrain_instruction( condition, Some("attempt to multiply with overflow".to_string()), @@ -1345,17 +1377,21 @@ impl<'block> BrilligBlock<'block> { // Initialize the variable let pointer = match new_variable { BrilligVariable::BrilligArray(brillig_array) => { + self.brillig_context.codegen_allocate_fixed_length_array( + brillig_array.pointer, + array.len(), + ); self.brillig_context - .allocate_fixed_length_array(brillig_array.pointer, array.len()); - self.brillig_context.usize_const(brillig_array.rc, 1_usize.into()); + .usize_const_instruction(brillig_array.rc, 1_usize.into()); brillig_array.pointer } BrilligVariable::BrilligVector(vector) => { - self.brillig_context.usize_const(vector.size, array.len().into()); self.brillig_context - .allocate_array_instruction(vector.pointer, vector.size); - self.brillig_context.usize_const(vector.rc, 1_usize.into()); + .usize_const_instruction(vector.size, array.len().into()); + self.brillig_context + .codegen_allocate_array(vector.pointer, vector.size); + self.brillig_context.usize_const_instruction(vector.rc, 1_usize.into()); vector.pointer } @@ -1368,16 +1404,16 @@ impl<'block> BrilligBlock<'block> { // Allocate a register for the iterator let iterator_register = - self.brillig_context.make_usize_constant(0_usize.into()); + self.brillig_context.make_usize_constant_instruction(0_usize.into()); for element_id in array.iter() { let element_variable = self.convert_ssa_value(*element_id, dfg); // Store the item in memory self.store_variable_in_array(pointer, iterator_register, element_variable); // Increment the iterator - self.brillig_context.usize_op_in_place( + self.brillig_context.codegen_usize_op_in_place( iterator_register.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, 1, ); } @@ -1439,8 +1475,8 @@ impl<'block> BrilligBlock<'block> { dfg, ); let array = variable.extract_array(); - self.brillig_context.allocate_fixed_length_array(array.pointer, array.size); - self.brillig_context.usize_const(array.rc, 1_usize.into()); + self.brillig_context.codegen_allocate_fixed_length_array(array.pointer, array.size); + self.brillig_context.usize_const_instruction(array.rc, 1_usize.into()); variable } @@ -1456,8 +1492,8 @@ impl<'block> BrilligBlock<'block> { // Set the pointer to the current stack frame // The stack pointer will then be updated by the caller of this method // once the external call is resolved and the array size is known - self.brillig_context.set_array_pointer(vector.pointer); - self.brillig_context.usize_const(vector.rc, 1_usize.into()); + self.brillig_context.load_free_memory_pointer_instruction(vector.pointer); + self.brillig_context.usize_const_instruction(vector.rc, 1_usize.into()); variable } @@ -1481,13 +1517,14 @@ impl<'block> BrilligBlock<'block> { match array_variable { BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { - self.brillig_context.usize_const(result_register, (size / element_size).into()); + self.brillig_context + .usize_const_instruction(result_register, (size / element_size).into()); } BrilligVariable::BrilligVector(BrilligVector { size, .. }) => { - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( size, result_register, - BinaryIntOp::UnsignedDiv, + BrilligBinaryOp::UnsignedDiv, element_size, ); } @@ -1525,66 +1562,41 @@ pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type } } -/// Convert an SSA binary operation into: -/// - Brillig Binary Integer Op, if it is a integer type -/// - Brillig Binary Field Op, if it is a field type pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( ssa_op: BinaryOp, typ: &Type, ) -> (BrilligBinaryOp, bool) { - // First get the bit size and whether its a signed integer, if it is a numeric type - // if it is not,then we return None, indicating that - // it is a Field. - let bit_size_signedness = match typ { + let (is_field, is_signed) = match typ { Type::Numeric(numeric_type) => match numeric_type { - NumericType::Signed { bit_size } => Some((bit_size, true)), - NumericType::Unsigned { bit_size } => Some((bit_size, false)), - NumericType::NativeField => None, + NumericType::Signed { .. } => (false, true), + NumericType::Unsigned { .. } => (false, false), + NumericType::NativeField => (true, false), }, _ => unreachable!("only numeric types are allowed in binary operations. References are handled separately"), }; - fn binary_op_to_field_op(op: BinaryOp) -> BrilligBinaryOp { - match op { - BinaryOp::Add => BrilligBinaryOp::Field(BinaryFieldOp::Add), - BinaryOp::Sub => BrilligBinaryOp::Field(BinaryFieldOp::Sub), - BinaryOp::Mul => BrilligBinaryOp::Field(BinaryFieldOp::Mul), - BinaryOp::Div => BrilligBinaryOp::Field(BinaryFieldOp::Div), - BinaryOp::Eq => BrilligBinaryOp::Field(BinaryFieldOp::Equals), - _ => unreachable!( - "Field type cannot be used with {op}. This should have been caught by the frontend" - ), - } - } - - fn binary_op_to_int_op(op: BinaryOp, is_signed: bool) -> BrilligBinaryOp { - let operation = match op { - BinaryOp::Add => BinaryIntOp::Add, - BinaryOp::Sub => BinaryIntOp::Sub, - BinaryOp::Mul => BinaryIntOp::Mul, - BinaryOp::Div => { - if is_signed { - BinaryIntOp::SignedDiv - } else { - BinaryIntOp::UnsignedDiv - } + let brillig_binary_op = match ssa_op { + BinaryOp::Add => BrilligBinaryOp::Add, + BinaryOp::Sub => BrilligBinaryOp::Sub, + BinaryOp::Mul => BrilligBinaryOp::Mul, + BinaryOp::Div => { + if is_field { + BrilligBinaryOp::FieldDiv + } else if is_signed { + BrilligBinaryOp::SignedDiv + } else { + BrilligBinaryOp::UnsignedDiv } - BinaryOp::Mod => return BrilligBinaryOp::Modulo { is_signed_integer: is_signed }, - BinaryOp::Eq => BinaryIntOp::Equals, - BinaryOp::Lt => BinaryIntOp::LessThan, - BinaryOp::And => BinaryIntOp::And, - BinaryOp::Or => BinaryIntOp::Or, - BinaryOp::Xor => BinaryIntOp::Xor, - BinaryOp::Shl => BinaryIntOp::Shl, - BinaryOp::Shr => BinaryIntOp::Shr, - }; - - BrilligBinaryOp::Integer(operation) - } - - // If bit size is available then it is a binary integer operation - match bit_size_signedness { - Some((_, is_signed)) => (binary_op_to_int_op(ssa_op, is_signed), is_signed), - None => (binary_op_to_field_op(ssa_op), false), - } + } + BinaryOp::Mod => BrilligBinaryOp::Modulo { is_signed_integer: is_signed }, + BinaryOp::Eq => BrilligBinaryOp::Equals, + BinaryOp::Lt => BrilligBinaryOp::LessThan, + BinaryOp::And => BrilligBinaryOp::And, + BinaryOp::Or => BrilligBinaryOp::Or, + BinaryOp::Xor => BrilligBinaryOp::Xor, + BinaryOp::Shl => BrilligBinaryOp::Shl, + BinaryOp::Shr => BrilligBinaryOp::Shr, + }; + + (brillig_binary_op, is_signed) } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 969f95cff20..98dd17ce080 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,7 +1,6 @@ -use acvm::brillig_vm::brillig::BinaryIntOp; - -use crate::brillig::brillig_ir::brillig_variable::{ - BrilligVariable, BrilligVector, SingleAddrVariable, +use crate::brillig::brillig_ir::{ + brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, + BrilligBinaryOp, }; use super::brillig_block::BrilligBlock; @@ -14,30 +13,30 @@ impl<'block> BrilligBlock<'block> { variables_to_insert: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by variables_to_insert.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Add, + BrilligBinaryOp::Add, variables_to_insert.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we copy the source vector into the target vector - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, target_vector.pointer, SingleAddrVariable::new_usize(source_vector.size), ); for (index, variable) in variables_to_insert.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(index.into()); - self.brillig_context.memory_op( + let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); + self.brillig_context.memory_op_instruction( target_index.address, source_vector.size, target_index.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.store_variable_in_array(target_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); @@ -51,27 +50,27 @@ impl<'block> BrilligBlock<'block> { variables_to_insert: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by variables_to_insert.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Add, + BrilligBinaryOp::Add, variables_to_insert.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we offset the target pointer by variables_to_insert.len() let destination_copy_pointer = self.brillig_context.allocate_register(); - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( target_vector.pointer, destination_copy_pointer, - BinaryIntOp::Add, + BrilligBinaryOp::Add, variables_to_insert.len(), ); // Now we copy the source vector into the target vector starting at index variables_to_insert.len() - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, destination_copy_pointer, SingleAddrVariable::new_usize(source_vector.size), @@ -79,7 +78,7 @@ impl<'block> BrilligBlock<'block> { // Then we write the items to insert at the start for (index, variable) in variables_to_insert.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(index.into()); + let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); self.store_variable_in_array(target_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); } @@ -94,34 +93,34 @@ impl<'block> BrilligBlock<'block> { removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, removed_items.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we offset the source pointer by removed_items.len() let source_copy_pointer = self.brillig_context.allocate_register(); - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.pointer, source_copy_pointer, - BinaryIntOp::Add, + BrilligBinaryOp::Add, removed_items.len(), ); // Now we copy the source vector starting at index removed_items.len() into the target vector - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_copy_pointer, target_vector.pointer, SingleAddrVariable::new_usize(target_vector.size), ); for (index, variable) in removed_items.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(index.into()); + let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); } @@ -136,30 +135,30 @@ impl<'block> BrilligBlock<'block> { removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, removed_items.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we copy all elements except the last items into the target vector - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, target_vector.pointer, SingleAddrVariable::new_usize(target_vector.size), ); for (index, variable) in removed_items.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(index.into()); - self.brillig_context.memory_op( + let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); + self.brillig_context.memory_op_instruction( target_index.address, target_vector.size, target_index.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); @@ -174,18 +173,18 @@ impl<'block> BrilligBlock<'block> { items: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by items.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Add, + BrilligBinaryOp::Add, items.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Copy the elements to the left of the index - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, target_vector.pointer, index, @@ -193,38 +192,38 @@ impl<'block> BrilligBlock<'block> { // Compute the source pointer just at the index let source_pointer_at_index = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( source_vector.pointer, index.address, source_pointer_at_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); // Compute the target pointer after the inserted elements let target_pointer_after_index = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( target_vector.pointer, index.address, target_pointer_after_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); - self.brillig_context.usize_op_in_place( + self.brillig_context.codegen_usize_op_in_place( target_pointer_after_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, items.len(), ); // Compute the number of elements to the right of the index let item_count = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( source_vector.size, index.address, item_count, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, ); // Copy the elements to the right of the index - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_pointer_at_index, target_pointer_after_index, SingleAddrVariable::new_usize(item_count), @@ -232,12 +231,13 @@ impl<'block> BrilligBlock<'block> { // Write the items to insert starting at the index for (subitem_index, variable) in items.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(subitem_index.into()); - self.brillig_context.memory_op( + let target_index = + self.brillig_context.make_usize_constant_instruction(subitem_index.into()); + self.brillig_context.memory_op_instruction( target_index.address, index.address, target_index.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.store_variable_in_array(target_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); @@ -256,18 +256,18 @@ impl<'block> BrilligBlock<'block> { removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, removed_items.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Copy the elements to the left of the index - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, target_vector.pointer, index, @@ -275,39 +275,43 @@ impl<'block> BrilligBlock<'block> { // Compute the source pointer after the removed items let source_pointer_after_index = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( source_vector.pointer, index.address, source_pointer_after_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); - self.brillig_context.usize_op_in_place( + self.brillig_context.codegen_usize_op_in_place( source_pointer_after_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, removed_items.len(), ); // Compute the target pointer at the index let target_pointer_at_index = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( target_vector.pointer, index.address, target_pointer_at_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); // Compute the number of elements to the right of the index let item_count = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( source_vector.size, index.address, item_count, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, + ); + self.brillig_context.codegen_usize_op_in_place( + item_count, + BrilligBinaryOp::Sub, + removed_items.len(), ); - self.brillig_context.usize_op_in_place(item_count, BinaryIntOp::Sub, removed_items.len()); // Copy the elements to the right of the index - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_pointer_after_index, target_pointer_at_index, SingleAddrVariable::new_usize(item_count), @@ -315,12 +319,13 @@ impl<'block> BrilligBlock<'block> { // Get the removed items for (subitem_index, variable) in removed_items.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(subitem_index.into()); - self.brillig_context.memory_op( + let target_index = + self.brillig_context.make_usize_constant_instruction(subitem_index.into()); + self.brillig_context.memory_op_instruction( target_index.address, index.address, target_index.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); @@ -338,7 +343,7 @@ impl<'block> BrilligBlock<'block> { match source_variable { BrilligVariable::BrilligVector(source_vector) => source_vector, BrilligVariable::BrilligArray(source_array) => { - self.brillig_context.array_to_vector(&source_array) + self.brillig_context.array_to_vector_instruction(&source_array) } _ => unreachable!("ICE: unsupported slice push back source {:?}", source_variable), } @@ -425,7 +430,7 @@ mod tests { }; // Cast the source array to a vector - let source_vector = context.array_to_vector(&array_variable); + let source_vector = context.array_to_vector_instruction(&array_variable); // Allocate the results let target_vector = BrilligVector { @@ -450,7 +455,7 @@ mod tests { ); } - context.return_instruction(&[target_vector.pointer, target_vector.rc]); + context.codegen_return(&[target_vector.pointer, target_vector.rc]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let (vm, return_data_offset, return_data_size) = @@ -518,7 +523,7 @@ mod tests { }; // Cast the source array to a vector - let source_vector = context.array_to_vector(&array_variable); + let source_vector = context.array_to_vector_instruction(&array_variable); // Allocate the results let target_vector = BrilligVector { @@ -547,7 +552,7 @@ mod tests { ); } - context.return_instruction(&[ + context.codegen_return(&[ target_vector.pointer, target_vector.rc, removed_item.address, @@ -620,7 +625,7 @@ mod tests { ); // Cast the source array to a vector - let source_vector = context.array_to_vector(&array_variable); + let source_vector = context.array_to_vector_instruction(&array_variable); // Allocate the results let target_vector = BrilligVector { @@ -638,7 +643,7 @@ mod tests { &[BrilligVariable::SingleAddr(item_to_insert)], ); - context.return_instruction(&[target_vector.pointer, target_vector.rc]); + context.codegen_return(&[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; @@ -741,7 +746,7 @@ mod tests { ); // Cast the source array to a vector - let source_vector = context.array_to_vector(&array_variable); + let source_vector = context.array_to_vector_instruction(&array_variable); // Allocate the results let target_vector = BrilligVector { @@ -763,7 +768,7 @@ mod tests { &[BrilligVariable::SingleAddr(removed_item)], ); - context.return_instruction(&[ + context.codegen_return(&[ target_vector.pointer, target_vector.size, removed_item.address, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 2a96965171b..9138f57083a 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -4,28 +4,30 @@ //! `brillig_gen` is therefore the module which combines both //! ssa types and types in this module. //! A similar paradigm can be seen with the `acir_ir` module. +//! +//! The brillig ir provides instructions and codegens. +//! The instructions are low level operations that are printed via debug_show. +//! They should emit few opcodes. Codegens on the other hand orchestrate the +//! low level instructions to emit the desired high level operation. pub(crate) mod artifact; pub(crate) mod brillig_variable; pub(crate) mod debug_show; pub(crate) mod registers; +mod codegen_binary; +mod codegen_calls; +mod codegen_control_flow; +mod codegen_intrinsic; +mod codegen_memory; +mod codegen_stack; mod entry_point; +mod instructions; -use crate::ssa::ir::dfg::CallStack; +pub(crate) use instructions::BrilligBinaryOp; -use self::{ - artifact::{BrilligArtifact, UnresolvedJumpLocation}, - brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, - registers::BrilligRegistersContext, -}; -use acvm::{ - acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, MemoryAddress, Opcode as BrilligOpcode, Value, - ValueOrArray, - }, - brillig_vm::brillig::HeapValueType, - FieldElement, -}; +use self::{artifact::BrilligArtifact, registers::BrilligRegistersContext}; +use crate::ssa::ir::dfg::CallStack; +use acvm::acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}; use debug_show::DebugShow; /// The Brillig VM does not apply a limit to the memory address space, @@ -35,8 +37,8 @@ pub(crate) const BRILLIG_MEMORY_ADDRESSING_BIT_SIZE: u32 = 64; // Registers reserved in runtime for special purposes. pub(crate) enum ReservedRegisters { - /// This register stores the stack pointer. Allocations must be done after this pointer. - StackPointer = 0, + /// This register stores the free memory pointer. Allocations must be done after this pointer. + FreeMemoryPointer = 0, /// This register stores the previous stack pointer. The registers of the caller are stored here. PreviousStackPointer = 1, } @@ -53,9 +55,9 @@ impl ReservedRegisters { Self::NUM_RESERVED_REGISTERS } - /// Returns the stack pointer register. This will get used to allocate memory in runtime. - pub(crate) fn stack_pointer() -> MemoryAddress { - MemoryAddress::from(ReservedRegisters::StackPointer as usize) + /// Returns the free memory pointer register. This will get used to allocate memory in runtime. + pub(crate) fn free_memory_pointer() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::FreeMemoryPointer as usize) } /// Returns the previous stack pointer register. This will be used to restore the registers after a fn call. @@ -99,12 +101,8 @@ impl BrilligContext { } } - pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { - self.registers = BrilligRegistersContext::from_preallocated_registers(allocated_registers); - } - /// Adds a brillig instruction to the brillig byte code - pub(crate) fn push_opcode(&mut self, opcode: BrilligOpcode) { + fn push_opcode(&mut self, opcode: BrilligOpcode) { self.obj.push_opcode(opcode); } @@ -113,1054 +111,24 @@ impl BrilligContext { self.obj } - /// Allocates an array of size `size` and stores the pointer to the array - /// in `pointer_register` - pub(crate) fn allocate_fixed_length_array( - &mut self, - pointer_register: MemoryAddress, - size: usize, - ) { - // debug_show handled by allocate_array_instruction - let size_register = self.make_usize_constant(size.into()); - self.allocate_array_instruction(pointer_register, size_register.address); - self.deallocate_single_addr(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: 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: MemoryAddress) { - self.debug_show.mov_instruction(pointer_register, ReservedRegisters::stack_pointer()); - self.push_opcode(BrilligOpcode::Mov { - destination: pointer_register, - source: ReservedRegisters::stack_pointer(), - }); - } - - pub(crate) fn update_stack_pointer(&mut self, size_register: MemoryAddress) { - self.memory_op( - ReservedRegisters::stack_pointer(), - size_register, - ReservedRegisters::stack_pointer(), - BinaryIntOp::Add, - ); - } - - /// Allocates a variable in memory and stores the - /// pointer to the array in `pointer_register` - fn allocate_variable_reference_instruction( - &mut self, - pointer_register: MemoryAddress, - size: usize, - ) { - self.debug_show.allocate_instruction(pointer_register); - // A variable can be stored in up to three values, so we reserve three values for that. - let size_register = self.make_usize_constant(size.into()); - self.push_opcode(BrilligOpcode::Mov { - destination: pointer_register, - source: ReservedRegisters::stack_pointer(), - }); - self.memory_op( - ReservedRegisters::stack_pointer(), - size_register.address, - ReservedRegisters::stack_pointer(), - BinaryIntOp::Add, - ); - self.deallocate_single_addr(size_register); - } - - pub(crate) fn allocate_single_addr_reference_instruction( - &mut self, - pointer_register: MemoryAddress, - ) { - self.allocate_variable_reference_instruction(pointer_register, 1); - } - - pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: MemoryAddress) { - self.allocate_variable_reference_instruction( - pointer_register, - BrilligArray::registers_count(), - ); - } - - pub(crate) fn allocate_vector_reference_instruction( - &mut self, - pointer_register: MemoryAddress, - ) { - self.allocate_variable_reference_instruction( - pointer_register, - BrilligVector::registers_count(), - ); - } - - /// Gets the value in the array at index `index` and stores it in `result` - pub(crate) fn array_get( - &mut self, - array_ptr: MemoryAddress, - index: SingleAddrVariable, - result: MemoryAddress, - ) { - assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); - self.debug_show.array_get(array_ptr, index.address, result); - // Computes array_ptr + index, ie array[index] - let index_of_element_in_memory = self.allocate_register(); - self.memory_op(array_ptr, index.address, index_of_element_in_memory, BinaryIntOp::Add); - self.load_instruction(result, index_of_element_in_memory); - // Free up temporary register - self.deallocate_register(index_of_element_in_memory); - } - - /// Sets the item in the array at index `index` to `value` - pub(crate) fn array_set( - &mut self, - array_ptr: MemoryAddress, - index: SingleAddrVariable, - value: MemoryAddress, - ) { - assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); - self.debug_show.array_set(array_ptr, index.address, value); - // Computes array_ptr + index, ie array[index] - let index_of_element_in_memory = self.allocate_register(); - self.binary_instruction( - SingleAddrVariable::new_usize(array_ptr), - index, - SingleAddrVariable::new_usize(index_of_element_in_memory), - BrilligBinaryOp::Integer(BinaryIntOp::Add), - ); - - self.store_instruction(index_of_element_in_memory, value); - // Free up temporary register - self.deallocate_register(index_of_element_in_memory); - } - - /// Copies the values of an array pointed by source with length stored in `num_elements_register` - /// Into the array pointed by destination - pub(crate) fn copy_array_instruction( - &mut self, - source_pointer: MemoryAddress, - destination_pointer: MemoryAddress, - num_elements_variable: SingleAddrVariable, - ) { - assert!(num_elements_variable.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); - self.debug_show.copy_array_instruction( - source_pointer, - destination_pointer, - num_elements_variable.address, - ); - - let value_register = self.allocate_register(); - - self.loop_instruction(num_elements_variable.address, |ctx, iterator| { - ctx.array_get(source_pointer, iterator, value_register); - ctx.array_set(destination_pointer, iterator, value_register); - }); - - self.deallocate_register(value_register); - } - - /// 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: MemoryAddress, on_iteration: F) - where - F: FnOnce(&mut BrilligContext, SingleAddrVariable), - { - let iterator_register = self.make_usize_constant(0_u128.into()); - - let (loop_section, loop_label) = self.reserve_next_section_label(); - self.enter_section(loop_section); - - // Loop body - - // Check if iterator < iteration_count - let iterator_less_than_iterations = - SingleAddrVariable { address: self.allocate_register(), bit_size: 1 }; - - self.memory_op( - iterator_register.address, - iteration_count, - iterator_less_than_iterations.address, - BinaryIntOp::LessThan, - ); - - let (exit_loop_section, exit_loop_label) = self.reserve_next_section_label(); - - self.not_instruction(iterator_less_than_iterations, iterator_less_than_iterations); - - self.jump_if_instruction(iterator_less_than_iterations.address, exit_loop_label); - - // Call the on iteration function - on_iteration(self, iterator_register); - - // Increment the iterator register - self.usize_op_in_place(iterator_register.address, BinaryIntOp::Add, 1); - - self.jump_instruction(loop_label); - - // Exit the loop - self.enter_section(exit_loop_section); - - // Deallocate our temporary registers - self.deallocate_single_addr(iterator_less_than_iterations); - self.deallocate_single_addr(iterator_register); - } - - /// This instruction will issue an if-then branch that will check if the condition is true - /// and if so, perform the instructions given in `f(self, true)` and otherwise perform the - /// instructions given in `f(self, false)`. A boolean is passed instead of two separate - /// functions to allow the given function to mutably alias its environment. - pub(crate) fn branch_instruction( - &mut self, - condition: MemoryAddress, - mut f: impl FnMut(&mut BrilligContext, bool), - ) { - // Reserve 3 sections - let (then_section, then_label) = self.reserve_next_section_label(); - let (otherwise_section, otherwise_label) = self.reserve_next_section_label(); - let (end_section, end_label) = self.reserve_next_section_label(); - - self.jump_if_instruction(condition, then_label.clone()); - self.jump_instruction(otherwise_label.clone()); - - self.enter_section(then_section); - f(self, true); - self.jump_instruction(end_label.clone()); - - self.enter_section(otherwise_section); - f(self, false); - self.jump_instruction(end_label.clone()); - - self.enter_section(end_section); - } - - /// This instruction issues a branch that jumps over the code generated by the given function if the condition is truthy - pub(crate) fn if_not_instruction( - &mut self, - condition: MemoryAddress, - f: impl FnOnce(&mut BrilligContext), - ) { - let (end_section, end_label) = self.reserve_next_section_label(); - - self.jump_if_instruction(condition, end_label.clone()); - - f(self); - - self.enter_section(end_section); - } - - /// Adds a label to the next opcode - pub(crate) fn enter_context(&mut self, label: T) { - self.debug_show.enter_context(label.to_string()); - self.context_label = label.to_string(); - self.section_label = 0; - // Add a context label to the next opcode - self.obj.add_label_at_position(label.to_string(), self.obj.index_of_next_opcode()); - // Add a section label to the next opcode - self.obj - .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); - } - - /// Enter the given section - fn enter_section(&mut self, section: usize) { - self.section_label = section; - self.obj - .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); - } - - /// Create, reserve, and return a new section label. - fn reserve_next_section_label(&mut self) -> (usize, String) { - let section = self.next_section; - self.next_section += 1; - (section, self.compute_section_label(section)) - } - - /// Internal function used to compute the section labels - fn compute_section_label(&self, section: usize) -> String { - format!("{}-{}", self.context_label, section) - } - - /// Returns the current section label - fn current_section_label(&self) -> String { - self.compute_section_label(self.section_label) - } - - /// Adds a unresolved `Jump` instruction to the bytecode. - pub(crate) fn jump_instruction(&mut self, target_label: T) { - self.debug_show.jump_instruction(target_label.to_string()); - self.add_unresolved_jump(BrilligOpcode::Jump { location: 0 }, target_label.to_string()); - } - - /// Adds a unresolved `JumpIf` instruction to the bytecode. - pub(crate) fn jump_if_instruction( - &mut self, - condition: MemoryAddress, - target_label: T, - ) { - self.debug_show.jump_if_instruction(condition, target_label.to_string()); - self.add_unresolved_jump( - BrilligOpcode::JumpIf { condition, location: 0 }, - target_label.to_string(), - ); - } - - /// Adds a unresolved `Jump` instruction to the bytecode. - fn add_unresolved_jump( - &mut self, - jmp_instruction: BrilligOpcode, - destination: UnresolvedJumpLocation, - ) { - self.obj.add_unresolved_jump(jmp_instruction, destination); - } - - /// Allocates an unused register. - pub(crate) fn allocate_register(&mut self) -> MemoryAddress { - self.registers.allocate_register() - } - - /// Push a register to the deallocation list, ready for reuse. - pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { - self.registers.deallocate_register(register_index); - } - - /// Deallocates the address where the single address variable is stored - pub(crate) fn deallocate_single_addr(&mut self, var: SingleAddrVariable) { - self.deallocate_register(var.address); - } -} - -impl BrilligContext { - /// Emits brillig bytecode to jump to a trap condition if `condition` - /// is false. - pub(crate) fn constrain_instruction( - &mut self, - condition: SingleAddrVariable, - assert_message: Option, - ) { - assert!(condition.bit_size == 1); - self.debug_show.constrain_instruction(condition.address); - let (next_section, next_label) = self.reserve_next_section_label(); - self.add_unresolved_jump( - BrilligOpcode::JumpIf { condition: condition.address, location: 0 }, - next_label, - ); - self.push_opcode(BrilligOpcode::Trap); - if let Some(assert_message) = assert_message { - self.obj.add_assert_message_to_last_opcode(assert_message); - } - self.enter_section(next_section); - } - - /// Processes a return instruction. - /// - /// For Brillig, the return is implicit, since there is no explicit return instruction. - /// The caller will take `N` values from the Register starting at register index 0. - /// `N` indicates the number of return values expected. - /// - /// 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: &[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()); - - for (destination_index, return_register) in return_registers.iter().enumerate() { - // In case we have fewer return registers than indices to write to, ensure we've allocated this register - let destination_register = ReservedRegisters::user_register_index(destination_index); - self.registers.ensure_register_is_allocated(destination_register); - sources.push(*return_register); - destinations.push(destination_register); - } - destinations - .iter() - .for_each(|destination| self.registers.ensure_register_is_allocated(*destination)); - self.mov_registers_to_registers_instruction(sources, destinations); - self.stop_instruction(); - } - - /// This function moves values from a set of registers to another set of registers. - /// 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, - ) { - let new_sources: Vec<_> = sources - .iter() - .map(|source| { - let new_source = self.allocate_register(); - self.mov_instruction(new_source, *source); - new_source - }) - .collect(); - for (new_source, destination) in new_sources.iter().zip(destinations.iter()) { - self.mov_instruction(*destination, *new_source); - self.deallocate_register(*new_source); - } - } - - /// Emits a `mov` instruction. - /// - /// Copies the value at `source` into `destination` - 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 }); - } - - /// Cast truncates the value to the given bit size and converts the type of the value in memory to that bit size. - pub(crate) fn cast_instruction( - &mut self, - destination: SingleAddrVariable, - source: SingleAddrVariable, - ) { - self.debug_show.cast_instruction(destination.address, source.address, destination.bit_size); - self.push_opcode(BrilligOpcode::Cast { - destination: destination.address, - source: source.address, - bit_size: destination.bit_size, - }); - } - - fn binary_result_bit_size(operation: BrilligBinaryOp, arguments_bit_size: u32) -> u32 { - match operation { - BrilligBinaryOp::Field(BinaryFieldOp::Equals) - | BrilligBinaryOp::Integer(BinaryIntOp::Equals) - | BrilligBinaryOp::Integer(BinaryIntOp::LessThan) - | BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals) => 1, - _ => arguments_bit_size, - } - } - - /// Processes a binary instruction according `operation`. - /// - /// This method will compute lhs rhs - /// and store the result in the `result` register. - pub(crate) fn binary_instruction( - &mut self, - lhs: SingleAddrVariable, - rhs: SingleAddrVariable, - result: SingleAddrVariable, - operation: BrilligBinaryOp, - ) { - assert!( - lhs.bit_size == rhs.bit_size, - "Not equal bit size for lhs and rhs: lhs {}, rhs {}", - lhs.bit_size, - rhs.bit_size - ); - let expected_result_bit_size = - BrilligContext::binary_result_bit_size(operation, lhs.bit_size); - assert!( - result.bit_size == expected_result_bit_size, - "Expected result bit size to be {}, got {} for operation {:?}", - expected_result_bit_size, - result.bit_size, - operation - ); - self.debug_show.binary_instruction(lhs.address, rhs.address, result.address, operation); - match operation { - BrilligBinaryOp::Field(op) => { - let opcode = BrilligOpcode::BinaryFieldOp { - op, - destination: result.address, - lhs: lhs.address, - rhs: rhs.address, - }; - self.push_opcode(opcode); - } - BrilligBinaryOp::Integer(op) => { - let opcode = BrilligOpcode::BinaryIntOp { - op, - destination: result.address, - bit_size: lhs.bit_size, - lhs: lhs.address, - rhs: rhs.address, - }; - self.push_opcode(opcode); - } - BrilligBinaryOp::Modulo { is_signed_integer } => { - self.modulo_instruction(result, lhs, rhs, is_signed_integer); - } - } - } - - /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) { - self.debug_show.const_instruction(result.address, constant); - - self.push_opcode(BrilligOpcode::Const { - destination: result.address, - value: constant, - bit_size: result.bit_size, - }); - } - - pub(crate) fn usize_const(&mut self, result: MemoryAddress, constant: Value) { - self.const_instruction(SingleAddrVariable::new_usize(result), constant); - } - - /// Processes a not instruction. - /// - /// Not is computed using a subtraction operation as there is no native not instruction - /// in Brillig. - pub(crate) fn not_instruction( - &mut self, - input: SingleAddrVariable, - result: SingleAddrVariable, - ) { - self.debug_show.not_instruction(input.address, input.bit_size, result.address); - // Compile !x as ((-1) - x) - let u_max = FieldElement::from(2_i128).pow(&FieldElement::from(input.bit_size as i128)) - - FieldElement::one(); - let max = self.make_constant(Value::from(u_max), input.bit_size); - - let opcode = BrilligOpcode::BinaryIntOp { - destination: result.address, - op: BinaryIntOp::Sub, - bit_size: input.bit_size, - lhs: max.address, - rhs: input.address, - }; - self.push_opcode(opcode); - self.deallocate_single_addr(max); - } - - /// Processes a foreign call instruction. - /// - /// Note: the function being called is external and will - /// not be linked during brillig generation. - pub(crate) fn foreign_call_instruction( - &mut self, - func_name: String, - inputs: &[ValueOrArray], - input_value_types: &[HeapValueType], - outputs: &[ValueOrArray], - output_value_types: &[HeapValueType], - ) { - assert!(inputs.len() == input_value_types.len()); - assert!(outputs.len() == output_value_types.len()); - self.debug_show.foreign_call_instruction(func_name.clone(), inputs, outputs); - let opcode = BrilligOpcode::ForeignCall { - function: func_name, - destinations: outputs.to_vec(), - destination_value_types: output_value_types.to_vec(), - inputs: inputs.to_vec(), - input_value_types: input_value_types.to_vec(), - }; - self.push_opcode(opcode); - } - - /// Emits a load instruction - pub(crate) fn load_instruction( - &mut self, - destination: MemoryAddress, - source_pointer: MemoryAddress, - ) { - self.debug_show.load_instruction(destination, source_pointer); - self.push_opcode(BrilligOpcode::Load { destination, source_pointer }); - } - - /// Loads a variable stored previously - pub(crate) fn load_variable_instruction( - &mut self, - destination: BrilligVariable, - variable_pointer: MemoryAddress, - ) { - match destination { - BrilligVariable::SingleAddr(single_addr) => { - self.load_instruction(single_addr.address, variable_pointer); - } - BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { - self.load_instruction(pointer, variable_pointer); - - let rc_pointer = self.allocate_register(); - self.mov_instruction(rc_pointer, variable_pointer); - self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 1_usize); - - self.load_instruction(rc, rc_pointer); - self.deallocate_register(rc_pointer); - } - BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { - self.load_instruction(pointer, variable_pointer); - - let size_pointer = self.allocate_register(); - self.mov_instruction(size_pointer, variable_pointer); - self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); - - self.load_instruction(size, size_pointer); - self.deallocate_register(size_pointer); - - let rc_pointer = self.allocate_register(); - self.mov_instruction(rc_pointer, variable_pointer); - self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 2_usize); - - self.load_instruction(rc, rc_pointer); - self.deallocate_register(rc_pointer); - } - } - } - - /// Emits a store instruction - pub(crate) fn store_instruction( - &mut self, - destination_pointer: MemoryAddress, - source: MemoryAddress, - ) { - self.debug_show.store_instruction(destination_pointer, source); - self.push_opcode(BrilligOpcode::Store { destination_pointer, source }); - } - - /// Stores a variable by saving its registers to memory - pub(crate) fn store_variable_instruction( - &mut self, - variable_pointer: MemoryAddress, - source: BrilligVariable, - ) { - match source { - BrilligVariable::SingleAddr(single_addr) => { - self.store_instruction(variable_pointer, single_addr.address); - } - BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { - self.store_instruction(variable_pointer, pointer); - - 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); - self.deallocate_register(rc_pointer); - } - BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { - self.store_instruction(variable_pointer, pointer); - - let size_pointer = self.allocate_register(); - self.mov_instruction(size_pointer, variable_pointer); - self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); - self.store_instruction(size_pointer, size); - - 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); - - self.deallocate_register(size_pointer); - self.deallocate_register(rc_pointer); - } - } - } - - /// Emits a truncate instruction. - /// - /// Note: Truncation is used as an optimization in the SSA IR - /// for the ACIR generation pass; ACIR gen does not overflow - /// on every integer operation since it would be in-efficient. - /// Instead truncation instructions are emitted as to when a - /// truncation should be done. - /// For Brillig, all integer operations will overflow as its cheap. - /// We currently use cast to truncate: we cast to the required bit size - /// and back to the original bit size. - pub(crate) fn truncate_instruction( - &mut self, - destination_of_truncated_value: SingleAddrVariable, - value_to_truncate: SingleAddrVariable, - bit_size: u32, - ) { - self.debug_show.truncate_instruction( - destination_of_truncated_value.address, - value_to_truncate.address, - bit_size, - ); - assert!( - bit_size <= value_to_truncate.bit_size, - "tried to truncate to a bit size {} greater than the variable size {}", - bit_size, - value_to_truncate.bit_size - ); - - // We cast back and forth to ensure that the value is truncated. - let intermediate_register = - SingleAddrVariable { address: self.allocate_register(), bit_size }; - self.cast_instruction(intermediate_register, value_to_truncate); - self.cast_instruction(destination_of_truncated_value, intermediate_register); - self.deallocate_register(intermediate_register.address); - } - - /// Emits a stop instruction - pub(crate) fn stop_instruction(&mut self) { - self.debug_show.stop_instruction(); - 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, bit_size: u32) -> SingleAddrVariable { - let var = SingleAddrVariable::new(self.allocate_register(), bit_size); - self.const_instruction(var, constant); - var - } - - /// Returns a register which holds the value of an usize constant - pub(crate) fn make_usize_constant(&mut self, constant: Value) -> SingleAddrVariable { - let register = self.allocate_register(); - self.usize_const(register, constant); - SingleAddrVariable::new_usize(register) - } - - /// Computes left % right by emitting the necessary Brillig opcodes. - /// - /// This is done by using the following formula: - /// - /// a % b = a - (b * (a / b)) - /// - /// Brillig does not have an explicit modulo operation, - /// so we must emit multiple opcodes and process it differently - /// to other binary instructions. - pub(crate) fn modulo_instruction( - &mut self, - result: SingleAddrVariable, - left: SingleAddrVariable, - right: SingleAddrVariable, - signed: bool, - ) { - // no debug_show, shown in binary instruction - let scratch_register_i = self.allocate_register(); - let scratch_register_j = self.allocate_register(); - - assert!( - left.bit_size == right.bit_size, - "Not equal bitsize: lhs {}, rhs {}", - left.bit_size, - right.bit_size - ); - let bit_size = left.bit_size; - // i = left / right - self.push_opcode(BrilligOpcode::BinaryIntOp { - op: match signed { - true => BinaryIntOp::SignedDiv, - false => BinaryIntOp::UnsignedDiv, - }, - destination: scratch_register_i, - bit_size, - lhs: left.address, - rhs: right.address, - }); - - // j = i * right - self.push_opcode(BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Mul, - destination: scratch_register_j, - bit_size, - lhs: scratch_register_i, - rhs: right.address, - }); - - // result_register = left - j - self.push_opcode(BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Sub, - destination: result.address, - bit_size, - lhs: left.address, - rhs: scratch_register_j, - }); - // Free scratch registers - self.deallocate_register(scratch_register_i); - self.deallocate_register(scratch_register_j); - } - - /// Adds a unresolved external `Call` instruction to the bytecode. - /// This calls into another function compiled into this brillig artifact. - pub(crate) fn add_external_call_instruction(&mut self, func_label: T) { - self.debug_show.add_external_call_instruction(func_label.to_string()); - self.obj.add_unresolved_external_call( - BrilligOpcode::Call { location: 0 }, - func_label.to_string(), - ); - } - - /// Returns the i'th register after the reserved ones - 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 { - // Save all of the used registers at this point in memory - // because the function call will/may overwrite them. - // - // Note that here it is important that the stack pointer register is at register 0, - // as after the first register save we add to the pointer. - let mut used_registers: Vec<_> = - vars.iter().flat_map(|var| var.extract_registers()).collect(); - - // Also dump the previous stack pointer - used_registers.push(ReservedRegisters::previous_stack_pointer()); - for register in used_registers.iter() { - self.store_instruction(ReservedRegisters::stack_pointer(), *register); - // Add one to our stack pointer - self.usize_op_in_place(ReservedRegisters::stack_pointer(), BinaryIntOp::Add, 1); - } - - // Store the location of our registers in the previous stack pointer - self.mov_instruction( - ReservedRegisters::previous_stack_pointer(), - ReservedRegisters::stack_pointer(), - ); - used_registers - } - - /// Loads all of the registers that have been save by save_all_used_registers. - 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 - let iterator_register = self.allocate_register(); - self.mov_instruction(iterator_register, ReservedRegisters::previous_stack_pointer()); - - for register in used_registers.iter().rev() { - // Subtract one from our stack pointer - self.usize_op_in_place(iterator_register, BinaryIntOp::Sub, 1); - self.load_instruction(*register, iterator_register); - } - } - - /// Utility method to perform a binary instruction with a constant value in place - pub(crate) fn usize_op_in_place( - &mut self, - destination: MemoryAddress, - op: BinaryIntOp, - constant: usize, - ) { - self.usize_op(destination, destination, op, constant); - } - - /// Utility method to perform a binary instruction with a constant value - pub(crate) fn usize_op( - &mut self, - operand: MemoryAddress, - destination: MemoryAddress, - op: BinaryIntOp, - constant: usize, - ) { - let const_register = self.make_usize_constant(Value::from(constant)); - self.memory_op(operand, const_register.address, destination, op); - // Mark as no longer used for this purpose, frees for reuse - self.deallocate_single_addr(const_register); - } - - /// Utility method to perform a binary instruction with a memory address - pub(crate) fn memory_op( - &mut self, - lhs: MemoryAddress, - rhs: MemoryAddress, - destination: MemoryAddress, - op: BinaryIntOp, - ) { - self.binary_instruction( - SingleAddrVariable::new_usize(lhs), - SingleAddrVariable::new_usize(rhs), - SingleAddrVariable::new( - destination, - BrilligContext::binary_result_bit_size( - BrilligBinaryOp::Integer(op), - BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - ), - ), - BrilligBinaryOp::Integer(op), - ); - } - - // Used before a call instruction. - // Save all the registers we have used to the stack. - // Move argument values to the front of the register indices. - pub(crate) fn pre_call_save_registers_prep_args( - &mut self, - arguments: &[MemoryAddress], - variables_to_save: &[BrilligVariable], - ) -> Vec { - // Save all the registers we have used to the stack. - let saved_registers = self.save_registers_of_vars(variables_to_save); - - // Move argument values to the front of the registers - // - // This means that the arguments will be in the first `n` registers after - // the number of reserved registers. - let (sources, destinations): (Vec<_>, Vec<_>) = - arguments.iter().enumerate().map(|(i, argument)| (*argument, self.register(i))).unzip(); - destinations - .iter() - .for_each(|destination| self.registers.ensure_register_is_allocated(*destination)); - self.mov_registers_to_registers_instruction(sources, destinations); - saved_registers - } - - // Used after a call instruction. - // Move return values to the front of the register indices. - // 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: &[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 - let (sources, destinations): (Vec<_>, Vec<_>) = result_registers - .iter() - .enumerate() - .map(|(i, result_register)| (self.register(i), *result_register)) - .unzip(); - sources.iter().for_each(|source| self.registers.ensure_register_is_allocated(*source)); - self.mov_registers_to_registers_instruction(sources, destinations); - - // Restore all the same registers we have, in exact reverse order. - // Note that we have allocated some registers above, which we will not be handling here, - // only restoring registers that were used prior to the call finishing. - // After the call instruction, the stack frame pointer should be back to where we left off, - // so we do our instructions in reverse order. - self.load_all_saved_registers(saved_registers); - } - - /// Utility method to transform a HeapArray to a HeapVector by making a runtime constant with the size. - pub(crate) fn array_to_vector(&mut self, array: &BrilligArray) -> BrilligVector { - let size_register = self.make_usize_constant(array.size.into()); - BrilligVector { size: size_register.address, pointer: array.pointer, rc: array.rc } - } - - /// Issues a blackbox operation. - pub(crate) fn black_box_op_instruction(&mut self, op: BlackBoxOp) { - self.debug_show.black_box_op_instruction(&op); - self.push_opcode(BrilligOpcode::BlackBox(op)); - } - - /// Issues a to_radix instruction. This instruction will write the modulus of the source register - /// And the radix register limb_count times to the target vector. - pub(crate) fn radix_instruction( - &mut self, - source_field: SingleAddrVariable, - target_vector: BrilligVector, - radix: SingleAddrVariable, - limb_count: SingleAddrVariable, - big_endian: bool, - ) { - assert!(source_field.bit_size == FieldElement::max_num_bits()); - assert!(radix.bit_size == 32); - assert!(limb_count.bit_size == 32); - let radix_as_field = - SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); - self.cast_instruction(radix_as_field, radix); - - self.cast_instruction(SingleAddrVariable::new_usize(target_vector.size), limb_count); - self.usize_const(target_vector.rc, 1_usize.into()); - self.allocate_array_instruction(target_vector.pointer, target_vector.size); - - let shifted_field = - SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); - self.mov_instruction(shifted_field.address, source_field.address); - - let modulus_field = - SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); - - self.loop_instruction(target_vector.size, |ctx, iterator_register| { - // Compute the modulus - ctx.modulo_instruction(modulus_field, shifted_field, radix_as_field, false); - // Write it - ctx.array_set(target_vector.pointer, iterator_register, modulus_field.address); - // Integer div the field - ctx.binary_instruction( - shifted_field, - radix_as_field, - shifted_field, - BrilligBinaryOp::Integer(BinaryIntOp::UnsignedDiv), - ); - }); - - // Deallocate our temporary registers - self.deallocate_single_addr(shifted_field); - self.deallocate_single_addr(modulus_field); - self.deallocate_single_addr(radix_as_field); - - if big_endian { - self.reverse_vector_in_place_instruction(target_vector); - } - } - - /// This instruction will reverse the order of the elements in a vector. - pub(crate) fn reverse_vector_in_place_instruction(&mut self, vector: BrilligVector) { - let iteration_count = self.allocate_register(); - self.usize_op(vector.size, iteration_count, BinaryIntOp::UnsignedDiv, 2); - - let start_value_register = self.allocate_register(); - let index_at_end_of_array = self.allocate_register(); - let end_value_register = self.allocate_register(); - - self.loop_instruction(iteration_count, |ctx, iterator_register| { - // Load both values - ctx.array_get(vector.pointer, iterator_register, start_value_register); - - // The index at the end of array is size - 1 - iterator - ctx.mov_instruction(index_at_end_of_array, vector.size); - ctx.usize_op_in_place(index_at_end_of_array, BinaryIntOp::Sub, 1); - ctx.memory_op( - index_at_end_of_array, - iterator_register.address, - index_at_end_of_array, - BinaryIntOp::Sub, - ); - - ctx.array_get( - vector.pointer, - SingleAddrVariable::new_usize(index_at_end_of_array), - end_value_register, - ); - - // Write both values - ctx.array_set(vector.pointer, iterator_register, end_value_register); - ctx.array_set( - vector.pointer, - SingleAddrVariable::new_usize(index_at_end_of_array), - start_value_register, - ); - }); - - self.deallocate_register(iteration_count); - self.deallocate_register(start_value_register); - self.deallocate_register(end_value_register); - self.deallocate_register(index_at_end_of_array); - } - /// Sets a current call stack that the next pushed opcodes will be associated with. pub(crate) fn set_call_stack(&mut self, call_stack: CallStack) { self.obj.set_call_stack(call_stack); } } -/// Type to encapsulate the binary operation types in Brillig -#[derive(Clone, Copy, Debug)] -pub(crate) enum BrilligBinaryOp { - Field(BinaryFieldOp), - Integer(BinaryIntOp), - // Modulo operation requires more than one brillig opcode - Modulo { is_signed_integer: bool }, -} - #[cfg(test)] pub(crate) mod tests { use std::vec; use acvm::acir::brillig::{ - BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, - ValueOrArray, + ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, ValueOrArray, }; use acvm::brillig_vm::brillig::HeapValueType; use acvm::brillig_vm::{VMStatus, VM}; use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; - use crate::brillig::brillig_ir::BrilligContext; + use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use super::artifact::{BrilligParameter, GeneratedBrillig}; use super::{BrilligOpcode, ReservedRegisters}; @@ -1264,14 +232,14 @@ pub(crate) mod tests { // assert(the_sequence.len() == 12); // } let mut context = BrilligContext::new(true); - let r_stack = ReservedRegisters::stack_pointer(); + let r_stack = ReservedRegisters::free_memory_pointer(); // Start stack pointer at 0 - context.usize_const(r_stack, Value::from(ReservedRegisters::len() + 3)); + context.usize_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.usize_const(r_input_size, Value::from(12_usize)); + context.usize_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( @@ -1282,9 +250,14 @@ pub(crate) mod tests { &[HeapValueType::Vector { value_types: vec![HeapValueType::Simple] }], ); // push stack frame by r_returned_size - context.memory_op(r_stack, r_output_size, r_stack, BinaryIntOp::Add); + context.memory_op_instruction(r_stack, r_output_size, r_stack, BrilligBinaryOp::Add); // check r_input_size == r_output_size - context.memory_op(r_input_size, r_output_size, r_equality, BinaryIntOp::Equals); + context.memory_op_instruction( + r_input_size, + r_output_size, + r_equality, + BrilligBinaryOp::Equals, + ); // We push a JumpIf and Trap opcode directly as the constrain instruction // uses unresolved jumps which requires a block to be constructed in SSA and // we don't need this for Brillig IR tests diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index b94f8140ddd..b415421dd92 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -1,5 +1,6 @@ -use acvm::brillig_vm::brillig::{ - HeapArray, HeapValueType, HeapVector, MemoryAddress, ValueOrArray, +use acvm::{ + brillig_vm::brillig::{HeapArray, HeapValueType, HeapVector, MemoryAddress, ValueOrArray}, + FieldElement, }; use serde::{Deserialize, Serialize}; @@ -21,6 +22,10 @@ impl SingleAddrVariable { pub(crate) fn new_usize(address: MemoryAddress) -> Self { SingleAddrVariable { address, bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE } } + + pub(crate) fn new_field(address: MemoryAddress) -> Self { + SingleAddrVariable { address, bit_size: FieldElement::max_num_bits() } + } } /// The representation of a noir array in the Brillig IR diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs new file mode 100644 index 00000000000..248a304d820 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs @@ -0,0 +1,29 @@ +use acvm::acir::brillig::{MemoryAddress, Value}; + +use super::{instructions::BrilligBinaryOp, BrilligContext}; + +impl BrilligContext { + /// Utility method to perform a binary instruction with a constant value in place + pub(crate) fn codegen_usize_op_in_place( + &mut self, + destination: MemoryAddress, + op: BrilligBinaryOp, + constant: usize, + ) { + self.codegen_usize_op(destination, destination, op, constant); + } + + /// Utility method to perform a binary instruction with a constant value + pub(crate) fn codegen_usize_op( + &mut self, + operand: MemoryAddress, + destination: MemoryAddress, + op: BrilligBinaryOp, + constant: usize, + ) { + let const_register = self.make_usize_constant_instruction(Value::from(constant)); + self.memory_op_instruction(operand, const_register.address, destination, op); + // Mark as no longer used for this purpose, frees for reuse + self.deallocate_single_addr(const_register); + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs new file mode 100644 index 00000000000..db65849a6b8 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs @@ -0,0 +1,102 @@ +use acvm::acir::brillig::MemoryAddress; + +use super::{ + brillig_variable::BrilligVariable, BrilligBinaryOp, BrilligContext, ReservedRegisters, +}; + +impl BrilligContext { + /// Saves all of the registers that have been used up until this point. + fn codegen_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. + // + // Note that here it is important that the stack pointer register is at register 0, + // as after the first register save we add to the pointer. + let mut used_registers: Vec<_> = + vars.iter().flat_map(|var| var.extract_registers()).collect(); + + // Also dump the previous stack pointer + used_registers.push(ReservedRegisters::previous_stack_pointer()); + for register in used_registers.iter() { + self.store_instruction(ReservedRegisters::free_memory_pointer(), *register); + // Add one to our stack pointer + self.codegen_usize_op_in_place( + ReservedRegisters::free_memory_pointer(), + BrilligBinaryOp::Add, + 1, + ); + } + + // Store the location of our registers in the previous stack pointer + self.mov_instruction( + ReservedRegisters::previous_stack_pointer(), + ReservedRegisters::free_memory_pointer(), + ); + used_registers + } + + /// Loads all of the registers that have been save by save_all_used_registers. + fn codegen_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 + let iterator_register = self.allocate_register(); + self.mov_instruction(iterator_register, ReservedRegisters::previous_stack_pointer()); + + for register in used_registers.iter().rev() { + // Subtract one from our stack pointer + self.codegen_usize_op_in_place(iterator_register, BrilligBinaryOp::Sub, 1); + self.load_instruction(*register, iterator_register); + } + } + + // Used before a call instruction. + // Save all the registers we have used to the stack. + // Move argument values to the front of the register indices. + pub(crate) fn codegen_pre_call_save_registers_prep_args( + &mut self, + arguments: &[MemoryAddress], + variables_to_save: &[BrilligVariable], + ) -> Vec { + // Save all the registers we have used to the stack. + let saved_registers = self.codegen_save_registers_of_vars(variables_to_save); + + // Move argument values to the front of the registers + // + // This means that the arguments will be in the first `n` registers after + // the number of reserved registers. + let (sources, destinations): (Vec<_>, Vec<_>) = + arguments.iter().enumerate().map(|(i, argument)| (*argument, self.register(i))).unzip(); + destinations + .iter() + .for_each(|destination| self.registers.ensure_register_is_allocated(*destination)); + self.codegen_mov_registers_to_registers(sources, destinations); + saved_registers + } + + // Used after a call instruction. + // Move return values to the front of the register indices. + // Load all the registers we have previous saved in save_registers_prep_args. + pub(crate) fn codegen_post_call_prep_returns_load_registers( + &mut self, + 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 + let (sources, destinations): (Vec<_>, Vec<_>) = result_registers + .iter() + .enumerate() + .map(|(i, result_register)| (self.register(i), *result_register)) + .unzip(); + sources.iter().for_each(|source| self.registers.ensure_register_is_allocated(*source)); + self.codegen_mov_registers_to_registers(sources, destinations); + + // Restore all the same registers we have, in exact reverse order. + // Note that we have allocated some registers above, which we will not be handling here, + // only restoring registers that were used prior to the call finishing. + // After the call instruction, the stack frame pointer should be back to where we left off, + // so we do our instructions in reverse order. + self.codegen_load_all_saved_registers(saved_registers); + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs new file mode 100644 index 00000000000..49836033f31 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs @@ -0,0 +1,123 @@ +use acvm::acir::brillig::MemoryAddress; + +use super::{ + brillig_variable::SingleAddrVariable, BrilligBinaryOp, BrilligContext, ReservedRegisters, +}; + +impl BrilligContext { + /// Codegens a return from the current function. + /// + /// For Brillig, the return is implicit, since there is no explicit return instruction. + /// The caller will take `N` values from the Register starting at register index 0. + /// `N` indicates the number of return values expected. + /// + /// 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 codegen_return(&mut self, return_registers: &[MemoryAddress]) { + let mut sources = Vec::with_capacity(return_registers.len()); + let mut destinations = Vec::with_capacity(return_registers.len()); + + for (destination_index, return_register) in return_registers.iter().enumerate() { + // In case we have fewer return registers than indices to write to, ensure we've allocated this register + let destination_register = ReservedRegisters::user_register_index(destination_index); + self.registers.ensure_register_is_allocated(destination_register); + sources.push(*return_register); + destinations.push(destination_register); + } + destinations + .iter() + .for_each(|destination| self.registers.ensure_register_is_allocated(*destination)); + self.codegen_mov_registers_to_registers(sources, destinations); + self.stop_instruction(); + } + + /// This codegen 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 codegen_loop(&mut self, iteration_count: MemoryAddress, on_iteration: F) + where + F: FnOnce(&mut BrilligContext, SingleAddrVariable), + { + let iterator_register = self.make_usize_constant_instruction(0_u128.into()); + + let (loop_section, loop_label) = self.reserve_next_section_label(); + self.enter_section(loop_section); + + // Loop body + + // Check if iterator < iteration_count + let iterator_less_than_iterations = + SingleAddrVariable { address: self.allocate_register(), bit_size: 1 }; + + self.memory_op_instruction( + iterator_register.address, + iteration_count, + iterator_less_than_iterations.address, + BrilligBinaryOp::LessThan, + ); + + let (exit_loop_section, exit_loop_label) = self.reserve_next_section_label(); + + self.not_instruction(iterator_less_than_iterations, iterator_less_than_iterations); + + self.jump_if_instruction(iterator_less_than_iterations.address, exit_loop_label); + + // Call the on iteration function + on_iteration(self, iterator_register); + + // Increment the iterator register + self.codegen_usize_op_in_place(iterator_register.address, BrilligBinaryOp::Add, 1); + + self.jump_instruction(loop_label); + + // Exit the loop + self.enter_section(exit_loop_section); + + // Deallocate our temporary registers + self.deallocate_single_addr(iterator_less_than_iterations); + self.deallocate_single_addr(iterator_register); + } + + /// This codegen will issue an if-then branch that will check if the condition is true + /// and if so, perform the instructions given in `f(self, true)` and otherwise perform the + /// instructions given in `f(self, false)`. A boolean is passed instead of two separate + /// functions to allow the given function to mutably alias its environment. + pub(crate) fn codegen_branch( + &mut self, + condition: MemoryAddress, + mut f: impl FnMut(&mut BrilligContext, bool), + ) { + // Reserve 3 sections + let (then_section, then_label) = self.reserve_next_section_label(); + let (otherwise_section, otherwise_label) = self.reserve_next_section_label(); + let (end_section, end_label) = self.reserve_next_section_label(); + + self.jump_if_instruction(condition, then_label.clone()); + self.jump_instruction(otherwise_label.clone()); + + self.enter_section(then_section); + f(self, true); + self.jump_instruction(end_label.clone()); + + self.enter_section(otherwise_section); + f(self, false); + self.jump_instruction(end_label.clone()); + + self.enter_section(end_section); + } + + /// This codegen issues a branch that jumps over the code generated by the given function if the condition is truthy + pub(crate) fn codegen_if_not( + &mut self, + condition: MemoryAddress, + f: impl FnOnce(&mut BrilligContext), + ) { + let (end_section, end_label) = self.reserve_next_section_label(); + + self.jump_if_instruction(condition, end_label.clone()); + + f(self); + + self.enter_section(end_section); + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs new file mode 100644 index 00000000000..3d0c00d8a3d --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs @@ -0,0 +1,89 @@ +use acvm::FieldElement; + +use crate::brillig::brillig_ir::BrilligBinaryOp; + +use super::{ + brillig_variable::{BrilligVector, SingleAddrVariable}, + BrilligContext, +}; + +impl BrilligContext { + /// Codegens a truncation of a value to the given bit size + pub(crate) fn codegen_truncate( + &mut self, + destination_of_truncated_value: SingleAddrVariable, + value_to_truncate: SingleAddrVariable, + bit_size: u32, + ) { + assert!( + bit_size <= value_to_truncate.bit_size, + "tried to truncate to a bit size {} greater than the variable size {}", + bit_size, + value_to_truncate.bit_size + ); + + // We cast back and forth to ensure that the value is truncated. + let intermediate_register = + SingleAddrVariable { address: self.allocate_register(), bit_size }; + self.cast_instruction(intermediate_register, value_to_truncate); + self.cast_instruction(destination_of_truncated_value, intermediate_register); + self.deallocate_single_addr(intermediate_register); + } + + /// Issues a to_radix instruction. This instruction will write the modulus of the source register + /// And the radix register limb_count times to the target vector. + pub(crate) fn codegen_to_radix( + &mut self, + source_field: SingleAddrVariable, + target_vector: BrilligVector, + radix: SingleAddrVariable, + limb_count: SingleAddrVariable, + big_endian: bool, + ) { + assert!(source_field.bit_size == FieldElement::max_num_bits()); + assert!(radix.bit_size == 32); + assert!(limb_count.bit_size == 32); + let radix_as_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + self.cast_instruction(radix_as_field, radix); + + self.cast_instruction(SingleAddrVariable::new_usize(target_vector.size), limb_count); + self.usize_const_instruction(target_vector.rc, 1_usize.into()); + self.codegen_allocate_array(target_vector.pointer, target_vector.size); + + let shifted_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + self.mov_instruction(shifted_field.address, source_field.address); + + let modulus_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + + self.codegen_loop(target_vector.size, |ctx, iterator_register| { + // Compute the modulus + ctx.binary_instruction( + shifted_field, + radix_as_field, + modulus_field, + BrilligBinaryOp::Modulo { is_signed_integer: false }, + ); + // Write it + ctx.codegen_array_set(target_vector.pointer, iterator_register, modulus_field.address); + // Integer div the field + ctx.binary_instruction( + shifted_field, + radix_as_field, + shifted_field, + BrilligBinaryOp::UnsignedDiv, + ); + }); + + // Deallocate our temporary registers + self.deallocate_single_addr(shifted_field); + self.deallocate_single_addr(modulus_field); + self.deallocate_single_addr(radix_as_field); + + if big_endian { + self.codegen_reverse_vector_in_place(target_vector); + } + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs new file mode 100644 index 00000000000..15761113f51 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs @@ -0,0 +1,255 @@ +use acvm::acir::brillig::MemoryAddress; + +use crate::brillig::brillig_ir::BrilligBinaryOp; + +use super::{ + brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, + BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, +}; + +impl BrilligContext { + /// Allocates an array of size `size` and stores the pointer to the array + /// in `pointer_register` + pub(crate) fn codegen_allocate_fixed_length_array( + &mut self, + pointer_register: MemoryAddress, + size: usize, + ) { + let size_register = self.make_usize_constant_instruction(size.into()); + self.codegen_allocate_array(pointer_register, size_register.address); + self.deallocate_single_addr(size_register); + } + + /// Allocates an array of size contained in size_register and stores the + /// pointer to the array in `pointer_register` + pub(crate) fn codegen_allocate_array( + &mut self, + pointer_register: MemoryAddress, + size_register: MemoryAddress, + ) { + self.load_free_memory_pointer_instruction(pointer_register); + self.increase_free_memory_pointer_instruction(size_register); + } + + /// Allocates a variable in memory and stores the + /// pointer to the array in `pointer_register` + fn codegen_allocate_variable_reference( + &mut self, + pointer_register: MemoryAddress, + size: usize, + ) { + // A variable can be stored in up to three values, so we reserve three values for that. + let size_register = self.make_usize_constant_instruction(size.into()); + self.mov_instruction(pointer_register, ReservedRegisters::free_memory_pointer()); + self.memory_op_instruction( + ReservedRegisters::free_memory_pointer(), + size_register.address, + ReservedRegisters::free_memory_pointer(), + BrilligBinaryOp::Add, + ); + self.deallocate_single_addr(size_register); + } + + pub(crate) fn codegen_allocate_single_addr_reference( + &mut self, + pointer_register: MemoryAddress, + ) { + self.codegen_allocate_variable_reference(pointer_register, 1); + } + + pub(crate) fn codegen_allocate_array_reference(&mut self, pointer_register: MemoryAddress) { + self.codegen_allocate_variable_reference(pointer_register, BrilligArray::registers_count()); + } + + pub(crate) fn codegen_allocate_vector_reference(&mut self, pointer_register: MemoryAddress) { + self.codegen_allocate_variable_reference( + pointer_register, + BrilligVector::registers_count(), + ); + } + + /// Gets the value in the array at index `index` and stores it in `result` + pub(crate) fn codegen_array_get( + &mut self, + array_ptr: MemoryAddress, + index: SingleAddrVariable, + result: MemoryAddress, + ) { + assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + // Computes array_ptr + index, ie array[index] + let index_of_element_in_memory = self.allocate_register(); + self.memory_op_instruction( + array_ptr, + index.address, + index_of_element_in_memory, + BrilligBinaryOp::Add, + ); + self.load_instruction(result, index_of_element_in_memory); + // Free up temporary register + self.deallocate_register(index_of_element_in_memory); + } + + /// Sets the item in the array at index `index` to `value` + pub(crate) fn codegen_array_set( + &mut self, + array_ptr: MemoryAddress, + index: SingleAddrVariable, + value: MemoryAddress, + ) { + assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + // Computes array_ptr + index, ie array[index] + let index_of_element_in_memory = self.allocate_register(); + self.binary_instruction( + SingleAddrVariable::new_usize(array_ptr), + index, + SingleAddrVariable::new_usize(index_of_element_in_memory), + BrilligBinaryOp::Add, + ); + + self.store_instruction(index_of_element_in_memory, value); + // Free up temporary register + self.deallocate_register(index_of_element_in_memory); + } + + /// Copies the values of an array pointed by source with length stored in `num_elements_register` + /// Into the array pointed by destination + pub(crate) fn codegen_copy_array( + &mut self, + source_pointer: MemoryAddress, + destination_pointer: MemoryAddress, + num_elements_variable: SingleAddrVariable, + ) { + assert!(num_elements_variable.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + + let value_register = self.allocate_register(); + + self.codegen_loop(num_elements_variable.address, |ctx, iterator| { + ctx.codegen_array_get(source_pointer, iterator, value_register); + ctx.codegen_array_set(destination_pointer, iterator, value_register); + }); + + self.deallocate_register(value_register); + } + + /// Loads a variable stored previously + pub(crate) fn codegen_load_variable( + &mut self, + destination: BrilligVariable, + variable_pointer: MemoryAddress, + ) { + match destination { + BrilligVariable::SingleAddr(single_addr) => { + self.load_instruction(single_addr.address, variable_pointer); + } + BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { + self.load_instruction(pointer, variable_pointer); + + let rc_pointer = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.codegen_usize_op_in_place(rc_pointer, BrilligBinaryOp::Add, 1_usize); + + self.load_instruction(rc, rc_pointer); + self.deallocate_register(rc_pointer); + } + BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { + self.load_instruction(pointer, variable_pointer); + + let size_pointer = self.allocate_register(); + self.mov_instruction(size_pointer, variable_pointer); + self.codegen_usize_op_in_place(size_pointer, BrilligBinaryOp::Add, 1_usize); + + self.load_instruction(size, size_pointer); + self.deallocate_register(size_pointer); + + let rc_pointer = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.codegen_usize_op_in_place(rc_pointer, BrilligBinaryOp::Add, 2_usize); + + self.load_instruction(rc, rc_pointer); + self.deallocate_register(rc_pointer); + } + } + } + + /// Stores a variable by saving its registers to memory + pub(crate) fn codegen_store_variable( + &mut self, + variable_pointer: MemoryAddress, + source: BrilligVariable, + ) { + match source { + BrilligVariable::SingleAddr(single_addr) => { + self.store_instruction(variable_pointer, single_addr.address); + } + BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { + self.store_instruction(variable_pointer, pointer); + + let rc_pointer: MemoryAddress = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.codegen_usize_op_in_place(rc_pointer, BrilligBinaryOp::Add, 1_usize); + self.store_instruction(rc_pointer, rc); + self.deallocate_register(rc_pointer); + } + BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { + self.store_instruction(variable_pointer, pointer); + + let size_pointer = self.allocate_register(); + self.mov_instruction(size_pointer, variable_pointer); + self.codegen_usize_op_in_place(size_pointer, BrilligBinaryOp::Add, 1_usize); + self.store_instruction(size_pointer, size); + + let rc_pointer: MemoryAddress = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.codegen_usize_op_in_place(rc_pointer, BrilligBinaryOp::Add, 2_usize); + self.store_instruction(rc_pointer, rc); + + self.deallocate_register(size_pointer); + self.deallocate_register(rc_pointer); + } + } + } + + /// This instruction will reverse the order of the elements in a vector. + pub(crate) fn codegen_reverse_vector_in_place(&mut self, vector: BrilligVector) { + let iteration_count = self.allocate_register(); + self.codegen_usize_op(vector.size, iteration_count, BrilligBinaryOp::UnsignedDiv, 2); + + let start_value_register = self.allocate_register(); + let index_at_end_of_array = self.allocate_register(); + let end_value_register = self.allocate_register(); + + self.codegen_loop(iteration_count, |ctx, iterator_register| { + // Load both values + ctx.codegen_array_get(vector.pointer, iterator_register, start_value_register); + + // The index at the end of array is size - 1 - iterator + ctx.mov_instruction(index_at_end_of_array, vector.size); + ctx.codegen_usize_op_in_place(index_at_end_of_array, BrilligBinaryOp::Sub, 1); + ctx.memory_op_instruction( + index_at_end_of_array, + iterator_register.address, + index_at_end_of_array, + BrilligBinaryOp::Sub, + ); + + ctx.codegen_array_get( + vector.pointer, + SingleAddrVariable::new_usize(index_at_end_of_array), + end_value_register, + ); + + // Write both values + ctx.codegen_array_set(vector.pointer, iterator_register, end_value_register); + ctx.codegen_array_set( + vector.pointer, + SingleAddrVariable::new_usize(index_at_end_of_array), + start_value_register, + ); + }); + + self.deallocate_register(iteration_count); + self.deallocate_register(start_value_register); + self.deallocate_register(end_value_register); + self.deallocate_register(index_at_end_of_array); + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs new file mode 100644 index 00000000000..1c30f0f848f --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs @@ -0,0 +1,26 @@ +use acvm::acir::brillig::MemoryAddress; + +use super::BrilligContext; + +impl BrilligContext { + /// This function moves values from a set of registers to another set of registers. + /// It first moves all sources to new allocated registers to avoid overwriting. + pub(crate) fn codegen_mov_registers_to_registers( + &mut self, + sources: Vec, + destinations: Vec, + ) { + let new_sources: Vec<_> = sources + .iter() + .map(|source| { + let new_source = self.allocate_register(); + self.mov_instruction(new_source, *source); + new_source + }) + .collect(); + for (new_source, destination) in new_sources.iter().zip(destinations.iter()) { + self.mov_instruction(*destination, *new_source); + self.deallocate_register(*new_source); + } + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index e32ce6f6b92..36427e7efe3 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -2,10 +2,7 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::ReservedRegisters; -use acvm::acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, - ValueOrArray, -}; +use acvm::acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, ValueOrArray}; /// Trait for converting values into debug-friendly strings. trait DebugToString { @@ -26,8 +23,8 @@ default_to_string_impl! { str usize u32 } impl DebugToString for MemoryAddress { fn debug_to_string(&self) -> String { - if *self == ReservedRegisters::stack_pointer() { - "Stack".into() + if *self == ReservedRegisters::free_memory_pointer() { + "FreeMem".into() } else if *self == ReservedRegisters::previous_stack_pointer() { "PrevStack".into() } else { @@ -48,43 +45,23 @@ impl DebugToString for HeapVector { } } -impl DebugToString for BinaryFieldOp { - fn debug_to_string(&self) -> String { - match self { - BinaryFieldOp::Add => "f+".into(), - BinaryFieldOp::Sub => "f-".into(), - BinaryFieldOp::Mul => "f*".into(), - BinaryFieldOp::Div => "f/".into(), - BinaryFieldOp::Equals => "f==".into(), - } - } -} - -impl DebugToString for BinaryIntOp { - fn debug_to_string(&self) -> String { - match self { - BinaryIntOp::Add => "+".into(), - BinaryIntOp::Sub => "-".into(), - BinaryIntOp::Mul => "*".into(), - BinaryIntOp::Equals => "==".into(), - BinaryIntOp::SignedDiv => "/".into(), - BinaryIntOp::UnsignedDiv => "//".into(), - BinaryIntOp::LessThan => "<".into(), - BinaryIntOp::LessThanEquals => "<=".into(), - BinaryIntOp::And => "&&".into(), - BinaryIntOp::Or => "||".into(), - BinaryIntOp::Xor => "^".into(), - BinaryIntOp::Shl => "<<".into(), - BinaryIntOp::Shr => ">>".into(), - } - } -} - impl DebugToString for BrilligBinaryOp { fn debug_to_string(&self) -> String { match self { - BrilligBinaryOp::Field(op) => op.debug_to_string(), - BrilligBinaryOp::Integer(op) => op.debug_to_string(), + BrilligBinaryOp::Add => "+".into(), + BrilligBinaryOp::Sub => "-".into(), + BrilligBinaryOp::Mul => "*".into(), + BrilligBinaryOp::Equals => "==".into(), + BrilligBinaryOp::FieldDiv => "f/".into(), + BrilligBinaryOp::SignedDiv => "/".into(), + BrilligBinaryOp::UnsignedDiv => "//".into(), + BrilligBinaryOp::LessThan => "<".into(), + BrilligBinaryOp::LessThanEquals => "<=".into(), + BrilligBinaryOp::And => "&&".into(), + BrilligBinaryOp::Or => "||".into(), + BrilligBinaryOp::Xor => "^".into(), + BrilligBinaryOp::Shl => "<<".into(), + BrilligBinaryOp::Shr => ">>".into(), BrilligBinaryOp::Modulo { is_signed_integer } => { let op = if *is_signed_integer { "%" } else { "%%" }; op.into() @@ -143,17 +120,6 @@ impl DebugShow { debug_println!(self.enable_debug_trace, " ASSERT {} != 0", condition); } - /// Processes a return instruction. - pub(crate) fn return_instruction(&self, return_registers: &[MemoryAddress]) { - let registers_string = return_registers - .iter() - .map(MemoryAddress::debug_to_string) - .collect::>() - .join(", "); - - debug_println!(self.enable_debug_trace, " // return {};", registers_string); - } - /// Emits a `mov` instruction. pub(crate) fn mov_instruction(&self, destination: MemoryAddress, source: MemoryAddress) { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); @@ -240,64 +206,17 @@ impl DebugShow { debug_println!(self.enable_debug_trace, " STOP"); } - /// Debug function for allocate_array_instruction - pub(crate) fn allocate_array_instruction( + /// Emits a external stop instruction (returns data) + pub(crate) fn external_stop_instruction( &self, - pointer_register: MemoryAddress, - size_register: MemoryAddress, + return_data_offset: usize, + return_data_size: usize, ) { debug_println!( self.enable_debug_trace, - " ALLOCATE_ARRAY {} SIZE {}", - pointer_register, - size_register - ); - } - - /// Debug function for allocate_instruction - 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: MemoryAddress, - index: MemoryAddress, - result: MemoryAddress, - ) { - debug_println!( - self.enable_debug_trace, - " ARRAY_GET {}[{}] -> {}", - array_ptr, - index, - result - ); - } - - /// Debug function for array_set - pub(crate) fn array_set( - &self, - array_ptr: MemoryAddress, - index: MemoryAddress, - value: MemoryAddress, - ) { - debug_println!(self.enable_debug_trace, " ARRAY_SET {}[{}] = {}", array_ptr, index, value); - } - - /// Debug function for copy_array_instruction - pub(crate) fn copy_array_instruction( - &self, - source: MemoryAddress, - destination: MemoryAddress, - num_elements_register: MemoryAddress, - ) { - debug_println!( - self.enable_debug_trace, - " COPY_ARRAY {} -> {} ({} ELEMENTS)", - source, - destination, - num_elements_register + " EXT_STOP {}..{}", + return_data_offset, + return_data_offset + return_data_size ); } @@ -328,22 +247,6 @@ impl DebugShow { ); } - /// Debug function for cast_instruction - pub(crate) fn truncate_instruction( - &self, - destination: MemoryAddress, - source: MemoryAddress, - target_bit_size: u32, - ) { - debug_println!( - self.enable_debug_trace, - " TRUNCATE {} FROM {} TO {} BITS", - destination, - source, - target_bit_size - ); - } - /// Debug function for black_box_op pub(crate) fn black_box_op_instruction(&self, op: &BlackBoxOp) { match op { @@ -529,4 +432,20 @@ impl DebugShow { pub(crate) fn add_external_call_instruction(&self, func_label: String) { debug_println!(self.enable_debug_trace, " CALL {}", func_label); } + + /// Debug function for calldata_copy + pub(crate) fn calldata_copy_instruction( + &self, + destination: MemoryAddress, + calldata_size: usize, + offset: usize, + ) { + debug_println!( + self.enable_debug_trace, + " CALLDATA_COPY {} {}..{}", + destination, + offset, + offset + calldata_size + ); + } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 83440e4a51d..14c4ada8606 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -3,12 +3,9 @@ use super::{ brillig_variable::{BrilligArray, BrilligVariable, SingleAddrVariable}, debug_show::DebugShow, registers::BrilligRegistersContext, - BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, -}; -use acvm::{ - acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, - FieldElement, + BrilligBinaryOp, BrilligContext, ReservedRegisters, }; +use acvm::{acir::brillig::MemoryAddress, FieldElement}; pub(crate) const MAX_STACK_SIZE: usize = 1024; @@ -28,18 +25,18 @@ impl BrilligContext { debug_show: DebugShow::new(false), }; - context.entry_point_instruction(&arguments, &return_parameters); + context.codegen_entry_point(&arguments, &return_parameters); context.add_external_call_instruction(target_function); - context.exit_point_instruction(&arguments, &return_parameters); + context.codegen_exit_point(&arguments, &return_parameters); context.artifact() } /// Adds the instructions needed to handle entry point parameters /// The runtime will leave the parameters in calldata. /// Arrays will be passed flattened. - fn entry_point_instruction( + fn codegen_entry_point( &mut self, arguments: &[BrilligParameter], return_parameters: &[BrilligParameter], @@ -48,11 +45,10 @@ impl BrilligContext { 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(), - bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - }); + self.const_instruction( + SingleAddrVariable::new_usize(ReservedRegisters::free_memory_pointer()), + (MAX_STACK_SIZE + calldata_size + return_data_size).into(), + ); // Copy calldata self.copy_and_cast_calldata(arguments); @@ -75,8 +71,8 @@ impl BrilligContext { } BrilligParameter::Array(_, _) => { let pointer_to_the_array_in_calldata = - self.make_usize_constant(current_calldata_pointer.into()); - let rc_register = self.make_usize_constant(1_usize.into()); + self.make_usize_constant_instruction(current_calldata_pointer.into()); + let rc_register = self.make_usize_constant_instruction(1_usize.into()); let flattened_size = BrilligContext::flattened_size(argument); let var = BrilligVariable::BrilligArray(BrilligArray { pointer: pointer_to_the_array_in_calldata.address, @@ -111,11 +107,7 @@ impl BrilligContext { fn copy_and_cast_calldata(&mut self, arguments: &[BrilligParameter]) { let calldata_size = BrilligContext::flattened_tuple_size(arguments); - self.push_opcode(BrilligOpcode::CalldataCopy { - destination_address: MemoryAddress(MAX_STACK_SIZE), - size: calldata_size, - offset: 0, - }); + self.calldata_copy_instruction(MemoryAddress(MAX_STACK_SIZE), calldata_size, 0); fn flat_bit_sizes(param: &BrilligParameter) -> Box + '_> { match param { @@ -130,11 +122,10 @@ impl BrilligContext { for (i, bit_size) in arguments.iter().flat_map(flat_bit_sizes).enumerate() { // Calldatacopy tags everything with field type, so when downcast when necessary if bit_size < FieldElement::max_num_bits() { - self.push_opcode(BrilligOpcode::Cast { - destination: MemoryAddress(MAX_STACK_SIZE + i), - source: MemoryAddress(MAX_STACK_SIZE + i), - bit_size, - }); + self.cast_instruction( + SingleAddrVariable::new(MemoryAddress(MAX_STACK_SIZE + i), bit_size), + SingleAddrVariable::new_field(MemoryAddress(MAX_STACK_SIZE + i)), + ); } } } @@ -178,7 +169,7 @@ impl BrilligContext { let target_item_size = item_type.len(); let source_item_size = BrilligContext::flattened_tuple_size(item_type); - self.allocate_fixed_length_array( + self.codegen_allocate_fixed_length_array( deflattened_array_pointer, item_count * target_item_size, ); @@ -190,20 +181,22 @@ impl BrilligContext { let mut source_offset = 0; for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_usize_constant((source_item_base_index + source_offset).into()); + let source_index = self.make_usize_constant_instruction( + (source_item_base_index + source_offset).into(), + ); - let target_index = - self.make_usize_constant((target_item_base_index + subitem_index).into()); + let target_index = self.make_usize_constant_instruction( + (target_item_base_index + subitem_index).into(), + ); match subitem { BrilligParameter::SingleAddr(_) => { - self.array_get( + self.codegen_array_get( flattened_array_pointer, source_index, movement_register, ); - self.array_set( + self.codegen_array_set( deflattened_array_pointer, target_index, movement_register, @@ -216,11 +209,11 @@ impl BrilligContext { ) => { let nested_array_pointer = self.allocate_register(); self.mov_instruction(nested_array_pointer, flattened_array_pointer); - self.memory_op( + self.memory_op_instruction( nested_array_pointer, source_index.address, nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, + BrilligBinaryOp::Add, ); let deflattened_nested_array_pointer = self.deflatten_array( nested_array_item_type, @@ -229,17 +222,21 @@ impl BrilligContext { ); let reference = self.allocate_register(); let rc = self.allocate_register(); - self.usize_const(rc, 1_usize.into()); + self.usize_const_instruction(rc, 1_usize.into()); - self.allocate_array_reference_instruction(reference); + self.codegen_allocate_array_reference(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.codegen_store_variable(reference, array_variable); - self.array_set(deflattened_array_pointer, target_index, reference); + self.codegen_array_set( + deflattened_array_pointer, + target_index, + reference, + ); self.deallocate_register(nested_array_pointer); self.deallocate_register(reference); @@ -272,7 +269,7 @@ impl BrilligContext { /// 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( + fn codegen_exit_point( &mut self, arguments: &[BrilligParameter], return_parameters: &[BrilligParameter], @@ -318,7 +315,8 @@ impl BrilligContext { } BrilligParameter::Array(item_type, item_count) => { let returned_pointer = returned_variable.extract_array().pointer; - let pointer_to_return_data = self.make_usize_constant(return_data_index.into()); + let pointer_to_return_data = + self.make_usize_constant_instruction(return_data_index.into()); self.flatten_array( item_type, @@ -336,7 +334,7 @@ impl BrilligContext { } } - self.push_opcode(BrilligOpcode::Stop { return_data_offset, return_data_size }); + self.external_stop_instruction(return_data_offset, return_data_size); } // Flattens an array by recursively copying nested arrays and regular items. @@ -361,19 +359,21 @@ impl BrilligContext { let mut target_offset = 0; for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_usize_constant((source_item_base_index + subitem_index).into()); - let target_index = - self.make_usize_constant((target_item_base_index + target_offset).into()); + let source_index = self.make_usize_constant_instruction( + (source_item_base_index + subitem_index).into(), + ); + let target_index = self.make_usize_constant_instruction( + (target_item_base_index + target_offset).into(), + ); match subitem { BrilligParameter::SingleAddr(_) => { - self.array_get( + self.codegen_array_get( deflattened_array_pointer, source_index, movement_register, ); - self.array_set( + self.codegen_array_set( flattened_array_pointer, target_index, movement_register, @@ -385,7 +385,7 @@ impl BrilligContext { nested_array_item_count, ) => { let nested_array_reference = self.allocate_register(); - self.array_get( + self.codegen_array_get( deflattened_array_pointer, source_index, nested_array_reference, @@ -398,7 +398,7 @@ impl BrilligContext { rc: self.allocate_register(), }); - self.load_variable_instruction( + self.codegen_load_variable( nested_array_variable, nested_array_reference, ); @@ -410,11 +410,11 @@ impl BrilligContext { flattened_array_pointer, ); - self.memory_op( + self.memory_op_instruction( flattened_nested_array_pointer, target_index.address, flattened_nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.flatten_array( @@ -443,12 +443,9 @@ impl BrilligContext { self.deallocate_register(movement_register); } else { - let item_count = self.make_usize_constant((item_count * item_type.len()).into()); - self.copy_array_instruction( - deflattened_array_pointer, - flattened_array_pointer, - item_count, - ); + let item_count = + self.make_usize_constant_instruction((item_count * item_type.len()).into()); + self.codegen_copy_array(deflattened_array_pointer, flattened_array_pointer, item_count); self.deallocate_single_addr(item_count); } } @@ -493,7 +490,7 @@ mod tests { context.load_instruction(array_pointer, array_pointer); context.load_instruction(array_value, array_pointer); - context.return_instruction(&[array_value]); + context.codegen_return(&[array_value]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let (vm, return_data_offset, return_data_size) = @@ -531,7 +528,7 @@ mod tests { rc: context.allocate_register(), }; - context.return_instruction(&brillig_array.extract_registers()); + context.codegen_return(&brillig_array.extract_registers()); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let (vm, return_data_pointer, return_data_size) = diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs new file mode 100644 index 00000000000..bd4d30916be --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -0,0 +1,533 @@ +use acvm::{ + acir::brillig::{ + BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapValueType, MemoryAddress, + Opcode as BrilligOpcode, Value, ValueOrArray, + }, + FieldElement, +}; + +use super::{ + artifact::UnresolvedJumpLocation, + brillig_variable::{BrilligArray, BrilligVector, SingleAddrVariable}, + BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, +}; + +/// Low level instructions of the brillig IR, used by the brillig ir codegens and brillig_gen +/// Printed using debug_slow +impl BrilligContext { + /// Processes a binary instruction according `operation`. + /// + /// This method will compute lhs rhs + /// and store the result in the `result` register. + pub(crate) fn binary_instruction( + &mut self, + lhs: SingleAddrVariable, + rhs: SingleAddrVariable, + result: SingleAddrVariable, + operation: BrilligBinaryOp, + ) { + self.debug_show.binary_instruction(lhs.address, rhs.address, result.address, operation); + self.binary(lhs, rhs, result, operation); + } + + /// Processes a not instruction. + /// + /// Not is computed using a subtraction operation as there is no native not instruction + /// in Brillig. + pub(crate) fn not_instruction( + &mut self, + input: SingleAddrVariable, + result: SingleAddrVariable, + ) { + self.debug_show.not_instruction(input.address, input.bit_size, result.address); + // Compile !x as ((-1) - x) + let u_max = FieldElement::from(2_i128).pow(&FieldElement::from(input.bit_size as i128)) + - FieldElement::one(); + let max = self.make_constant(Value::from(u_max), input.bit_size); + + self.binary(max, input, result, BrilligBinaryOp::Sub); + self.deallocate_single_addr(max); + } + + /// Utility method to perform a binary instruction with a memory address + pub(crate) fn memory_op_instruction( + &mut self, + lhs: MemoryAddress, + rhs: MemoryAddress, + destination: MemoryAddress, + op: BrilligBinaryOp, + ) { + self.binary_instruction( + SingleAddrVariable::new_usize(lhs), + SingleAddrVariable::new_usize(rhs), + SingleAddrVariable::new( + destination, + BrilligContext::binary_result_bit_size(op, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE), + ), + op, + ); + } + + fn binary( + &mut self, + lhs: SingleAddrVariable, + rhs: SingleAddrVariable, + result: SingleAddrVariable, + operation: BrilligBinaryOp, + ) { + assert!( + lhs.bit_size == rhs.bit_size, + "Not equal bit size for lhs and rhs: lhs {}, rhs {}", + lhs.bit_size, + rhs.bit_size + ); + let is_field_op = lhs.bit_size == FieldElement::max_num_bits(); + let expected_result_bit_size = + BrilligContext::binary_result_bit_size(operation, lhs.bit_size); + assert!( + result.bit_size == expected_result_bit_size, + "Expected result bit size to be {}, got {} for operation {:?}", + expected_result_bit_size, + result.bit_size, + operation + ); + + if let BrilligBinaryOp::Modulo { is_signed_integer } = operation { + self.modulo(result, lhs, rhs, is_signed_integer); + } else if is_field_op { + self.push_opcode(BrilligOpcode::BinaryFieldOp { + op: operation.into(), + destination: result.address, + lhs: lhs.address, + rhs: rhs.address, + }); + } else { + self.push_opcode(BrilligOpcode::BinaryIntOp { + op: operation.into(), + destination: result.address, + bit_size: lhs.bit_size, + lhs: lhs.address, + rhs: rhs.address, + }); + } + } + + /// Computes left % right by emitting the necessary Brillig opcodes. + /// + /// This is done by using the following formula: + /// + /// a % b = a - (b * (a / b)) + /// + /// Brillig does not have an explicit modulo operation, + /// so we must emit multiple opcodes and process it differently + /// to other binary instructions. + fn modulo( + &mut self, + result: SingleAddrVariable, + left: SingleAddrVariable, + right: SingleAddrVariable, + signed: bool, + ) { + assert!( + left.bit_size == right.bit_size, + "Not equal bitsize: lhs {}, rhs {}", + left.bit_size, + right.bit_size + ); + let bit_size = left.bit_size; + + let scratch_var_i = SingleAddrVariable::new(self.allocate_register(), bit_size); + let scratch_var_j = SingleAddrVariable::new(self.allocate_register(), bit_size); + + // i = left / right + self.binary( + left, + right, + scratch_var_i, + match signed { + true => BrilligBinaryOp::SignedDiv, + false => BrilligBinaryOp::UnsignedDiv, + }, + ); + + // j = i * right + self.binary(scratch_var_i, right, scratch_var_j, BrilligBinaryOp::Mul); + + // result_register = left - j + self.binary(left, scratch_var_j, result, BrilligBinaryOp::Sub); + // Free scratch registers + self.deallocate_register(scratch_var_i.address); + self.deallocate_register(scratch_var_j.address); + } + + fn binary_result_bit_size(operation: BrilligBinaryOp, arguments_bit_size: u32) -> u32 { + match operation { + BrilligBinaryOp::Equals + | BrilligBinaryOp::LessThan + | BrilligBinaryOp::LessThanEquals => 1, + _ => arguments_bit_size, + } + } + + /// Processes a foreign call instruction. + /// + /// Note: the function being called is external and will + /// not be linked during brillig generation. + pub(crate) fn foreign_call_instruction( + &mut self, + func_name: String, + inputs: &[ValueOrArray], + input_value_types: &[HeapValueType], + outputs: &[ValueOrArray], + output_value_types: &[HeapValueType], + ) { + self.debug_show.foreign_call_instruction(func_name.clone(), inputs, outputs); + + assert!(inputs.len() == input_value_types.len()); + assert!(outputs.len() == output_value_types.len()); + + self.push_opcode(BrilligOpcode::ForeignCall { + function: func_name, + destinations: outputs.to_vec(), + destination_value_types: output_value_types.to_vec(), + inputs: inputs.to_vec(), + input_value_types: input_value_types.to_vec(), + }); + } + + /// Adds a unresolved external `Call` instruction to the bytecode. + /// This calls into another function compiled into this brillig artifact. + pub(crate) fn add_external_call_instruction(&mut self, func_label: T) { + self.debug_show.add_external_call_instruction(func_label.to_string()); + self.obj.add_unresolved_external_call( + BrilligOpcode::Call { location: 0 }, + func_label.to_string(), + ); + } + + /// Adds a unresolved `Jump` instruction to the bytecode. + pub(crate) fn jump_instruction(&mut self, target_label: T) { + self.debug_show.jump_instruction(target_label.to_string()); + self.add_unresolved_jump(BrilligOpcode::Jump { location: 0 }, target_label.to_string()); + } + + /// Adds a unresolved `JumpIf` instruction to the bytecode. + pub(crate) fn jump_if_instruction( + &mut self, + condition: MemoryAddress, + target_label: T, + ) { + self.debug_show.jump_if_instruction(condition, target_label.to_string()); + self.add_unresolved_jump( + BrilligOpcode::JumpIf { condition, location: 0 }, + target_label.to_string(), + ); + } + + /// Emits brillig bytecode to jump to a trap condition if `condition` + /// is false. + pub(crate) fn constrain_instruction( + &mut self, + condition: SingleAddrVariable, + assert_message: Option, + ) { + self.debug_show.constrain_instruction(condition.address); + + assert!(condition.bit_size == 1); + + let (next_section, next_label) = self.reserve_next_section_label(); + self.add_unresolved_jump( + BrilligOpcode::JumpIf { condition: condition.address, location: 0 }, + next_label, + ); + self.push_opcode(BrilligOpcode::Trap); + if let Some(assert_message) = assert_message { + self.obj.add_assert_message_to_last_opcode(assert_message); + } + self.enter_section(next_section); + } + + /// Adds a unresolved `Jump` to the bytecode. + fn add_unresolved_jump( + &mut self, + jmp_instruction: BrilligOpcode, + destination: UnresolvedJumpLocation, + ) { + self.obj.add_unresolved_jump(jmp_instruction, destination); + } + + /// Adds a label to the next opcode + pub(crate) fn enter_context(&mut self, label: T) { + self.debug_show.enter_context(label.to_string()); + self.context_label = label.to_string(); + self.section_label = 0; + // Add a context label to the next opcode + self.obj.add_label_at_position(label.to_string(), self.obj.index_of_next_opcode()); + // Add a section label to the next opcode + self.obj + .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); + } + + /// Enter the given section + pub(super) fn enter_section(&mut self, section: usize) { + self.section_label = section; + self.obj + .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); + } + + /// Create, reserve, and return a new section label. + pub(super) fn reserve_next_section_label(&mut self) -> (usize, String) { + let section = self.next_section; + self.next_section += 1; + (section, self.compute_section_label(section)) + } + + /// Internal function used to compute the section labels + fn compute_section_label(&self, section: usize) -> String { + format!("{}-{}", self.context_label, section) + } + + /// Returns the current section label + fn current_section_label(&self) -> String { + self.compute_section_label(self.section_label) + } + + /// Emits a stop instruction + pub(crate) fn stop_instruction(&mut self) { + self.debug_show.stop_instruction(); + self.push_opcode(BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }); + } + + /// Emits a external stop instruction (returns data) + pub(crate) fn external_stop_instruction( + &mut self, + return_data_offset: usize, + return_data_size: usize, + ) { + self.debug_show.external_stop_instruction(return_data_offset, return_data_size); + self.push_opcode(BrilligOpcode::Stop { return_data_offset, return_data_size }); + } + + /// Issues a blackbox operation. + pub(crate) fn black_box_op_instruction(&mut self, op: BlackBoxOp) { + self.debug_show.black_box_op_instruction(&op); + self.push_opcode(BrilligOpcode::BlackBox(op)); + } + + pub(crate) fn load_free_memory_pointer_instruction(&mut self, pointer_register: MemoryAddress) { + self.debug_show.mov_instruction(pointer_register, ReservedRegisters::free_memory_pointer()); + self.push_opcode(BrilligOpcode::Mov { + destination: pointer_register, + source: ReservedRegisters::free_memory_pointer(), + }); + } + + pub(crate) fn increase_free_memory_pointer_instruction( + &mut self, + size_register: MemoryAddress, + ) { + self.memory_op_instruction( + ReservedRegisters::free_memory_pointer(), + size_register, + ReservedRegisters::free_memory_pointer(), + BrilligBinaryOp::Add, + ); + } + + /// Emits a store instruction + pub(crate) fn store_instruction( + &mut self, + destination_pointer: MemoryAddress, + source: MemoryAddress, + ) { + self.debug_show.store_instruction(destination_pointer, source); + self.push_opcode(BrilligOpcode::Store { destination_pointer, source }); + } + + /// Utility method to transform a HeapArray to a HeapVector by making a runtime constant with the size. + pub(crate) fn array_to_vector_instruction(&mut self, array: &BrilligArray) -> BrilligVector { + let size_register = self.make_usize_constant_instruction(array.size.into()); + BrilligVector { size: size_register.address, pointer: array.pointer, rc: array.rc } + } + + /// Emits a load instruction + pub(crate) fn load_instruction( + &mut self, + destination: MemoryAddress, + source_pointer: MemoryAddress, + ) { + self.debug_show.load_instruction(destination, source_pointer); + self.push_opcode(BrilligOpcode::Load { destination, source_pointer }); + } + + /// Emits a `mov` instruction. + /// + /// Copies the value at `source` into `destination` + 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 }); + } + + /// Cast truncates the value to the given bit size and converts the type of the value in memory to that bit size. + pub(crate) fn cast_instruction( + &mut self, + destination: SingleAddrVariable, + source: SingleAddrVariable, + ) { + self.debug_show.cast_instruction(destination.address, source.address, destination.bit_size); + self.cast(destination, source); + } + + pub(crate) fn cast(&mut self, destination: SingleAddrVariable, source: SingleAddrVariable) { + self.push_opcode(BrilligOpcode::Cast { + destination: destination.address, + source: source.address, + bit_size: destination.bit_size, + }); + } + + /// Stores the value of `constant` in the `result` register + pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) { + self.debug_show.const_instruction(result.address, constant); + self.constant(result, constant); + } + + fn constant(&mut self, result: SingleAddrVariable, constant: Value) { + if result.bit_size > 128 && !constant.to_field().fits_in_u128() { + let high = Value::from(FieldElement::from_be_bytes_reduce( + constant + .to_field() + .to_be_bytes() + .get(0..16) + .expect("FieldElement::to_be_bytes() too short!"), + )); + let low = Value::from(constant.to_u128()); + let high_register = SingleAddrVariable::new(self.allocate_register(), 254); + let low_register = SingleAddrVariable::new(self.allocate_register(), 254); + let intermediate_register = SingleAddrVariable::new(self.allocate_register(), 254); + self.constant(high_register, high); + self.constant(low_register, low); + // I want to multiply high by 2^128, but I can't get that big constant in. + // So I'll multiply by 2^64 twice. + self.constant(intermediate_register, Value::from(1_u128 << 64)); + self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); + self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); + // Now we can add. + self.binary(high_register, low_register, intermediate_register, BrilligBinaryOp::Add); + self.cast(result, intermediate_register); + self.deallocate_single_addr(high_register); + self.deallocate_single_addr(low_register); + self.deallocate_single_addr(intermediate_register); + } else { + self.push_opcode(BrilligOpcode::Const { + destination: result.address, + value: constant, + bit_size: result.bit_size, + }); + } + } + + pub(crate) fn usize_const_instruction(&mut self, result: MemoryAddress, constant: Value) { + self.const_instruction(SingleAddrVariable::new_usize(result), constant); + } + + /// Returns a register which holds the value of a constant + pub(crate) fn make_constant_instruction( + &mut self, + constant: Value, + bit_size: u32, + ) -> SingleAddrVariable { + let var = SingleAddrVariable::new(self.allocate_register(), bit_size); + self.const_instruction(var, constant); + var + } + + fn make_constant(&mut self, constant: Value, bit_size: u32) -> SingleAddrVariable { + let var = SingleAddrVariable::new(self.allocate_register(), bit_size); + self.constant(var, constant); + var + } + + /// Returns a register which holds the value of an usize constant + pub(crate) fn make_usize_constant_instruction( + &mut self, + constant: Value, + ) -> SingleAddrVariable { + let register = self.allocate_register(); + self.usize_const_instruction(register, constant); + SingleAddrVariable::new_usize(register) + } + + pub(super) fn calldata_copy_instruction( + &mut self, + destination: MemoryAddress, + calldata_size: usize, + offset: usize, + ) { + self.debug_show.calldata_copy_instruction(destination, calldata_size, offset); + + self.push_opcode(BrilligOpcode::CalldataCopy { + destination_address: destination, + size: calldata_size, + offset, + }); + } +} + +/// Type to encapsulate the binary operation types in Brillig +#[derive(Clone, Copy, Debug)] +pub(crate) enum BrilligBinaryOp { + Add, + Sub, + Mul, + FieldDiv, + SignedDiv, + UnsignedDiv, + Equals, + LessThan, + LessThanEquals, + And, + Or, + Xor, + Shl, + Shr, + // Modulo operation requires more than one brillig opcode + Modulo { is_signed_integer: bool }, +} + +impl From for BinaryFieldOp { + fn from(operation: BrilligBinaryOp) -> BinaryFieldOp { + match operation { + BrilligBinaryOp::Add => BinaryFieldOp::Add, + BrilligBinaryOp::Sub => BinaryFieldOp::Sub, + BrilligBinaryOp::Mul => BinaryFieldOp::Mul, + BrilligBinaryOp::FieldDiv => BinaryFieldOp::Div, + BrilligBinaryOp::UnsignedDiv => BinaryFieldOp::IntegerDiv, + BrilligBinaryOp::Equals => BinaryFieldOp::Equals, + BrilligBinaryOp::LessThan => BinaryFieldOp::LessThan, + BrilligBinaryOp::LessThanEquals => BinaryFieldOp::LessThanEquals, + _ => panic!("Unsupported operation: {:?} on a field", operation), + } + } +} + +impl From for BinaryIntOp { + fn from(operation: BrilligBinaryOp) -> BinaryIntOp { + match operation { + BrilligBinaryOp::Add => BinaryIntOp::Add, + BrilligBinaryOp::Sub => BinaryIntOp::Sub, + BrilligBinaryOp::Mul => BinaryIntOp::Mul, + BrilligBinaryOp::UnsignedDiv => BinaryIntOp::UnsignedDiv, + BrilligBinaryOp::SignedDiv => BinaryIntOp::SignedDiv, + BrilligBinaryOp::Equals => BinaryIntOp::Equals, + BrilligBinaryOp::LessThan => BinaryIntOp::LessThan, + BrilligBinaryOp::LessThanEquals => BinaryIntOp::LessThanEquals, + BrilligBinaryOp::And => BinaryIntOp::And, + BrilligBinaryOp::Or => BinaryIntOp::Or, + BrilligBinaryOp::Xor => BinaryIntOp::Xor, + BrilligBinaryOp::Shl => BinaryIntOp::Shl, + BrilligBinaryOp::Shr => BinaryIntOp::Shr, + _ => panic!("Unsupported operation: {:?} on an integer", operation), + } + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index 8c0e36215a9..f756f06aa69 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -2,7 +2,7 @@ use acvm::acir::brillig::MemoryAddress; use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; -use super::ReservedRegisters; +use super::{brillig_variable::SingleAddrVariable, BrilligContext, ReservedRegisters}; /// Every brillig stack frame/call context has its own view of register space. /// This is maintained by copying these registers to the stack during calls and reading them back. @@ -81,3 +81,29 @@ impl BrilligRegistersContext { self.deallocated_registers.push(register_index); } } + +impl BrilligContext { + /// Returns the i'th register after the reserved ones + pub(crate) fn register(&self, i: usize) -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) + } + + /// Allocates an unused register. + pub(crate) fn allocate_register(&mut self) -> MemoryAddress { + self.registers.allocate_register() + } + + pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { + self.registers = BrilligRegistersContext::from_preallocated_registers(allocated_registers); + } + + /// Push a register to the deallocation list, ready for reuse. + pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { + self.registers.deallocate_register(register_index); + } + + /// Deallocates the address where the single address variable is stored + pub(crate) fn deallocate_single_addr(&mut self, var: SingleAddrVariable) { + self.deallocate_register(var.address); + } +} diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 00b1b443430..c33b83257b0 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -37,11 +37,11 @@ use crate::{ StatementKind, }; use crate::{ - ArrayLiteral, Distinctness, ForRange, FunctionDefinition, FunctionReturnType, Generics, - ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, Shared, - StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, - Visibility, ERROR_IDENT, + ArrayLiteral, BinaryOpKind, Distinctness, ForRange, FunctionDefinition, FunctionReturnType, + Generics, ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, + Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, + UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, + UnresolvedTypeExpression, Visibility, ERROR_IDENT, }; use fm::FileId; use iter_extended::vecmap; @@ -1943,10 +1943,65 @@ impl<'a> Resolver<'a> { rhs: ExprId, span: Span, ) -> Result> { + // Arbitrary amount of recursive calls to try before giving up + let fuel = 100; + self.try_eval_array_length_id_with_fuel(rhs, span, fuel) + } + + fn try_eval_array_length_id_with_fuel( + &self, + rhs: ExprId, + span: Span, + fuel: u32, + ) -> Result> { + if fuel == 0 { + // If we reach here, it is likely from evaluating cyclic globals. We expect an error to + // be issued for them after name resolution so issue no error now. + return Err(None); + } + match self.interner.expression(&rhs) { HirExpression::Literal(HirLiteral::Integer(int, false)) => { int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span })) } + HirExpression::Ident(ident) => { + let definition = self.interner.definition(ident.id); + match definition.kind { + DefinitionKind::Global(global_id) => { + let let_statement = self.interner.get_global_let_statement(global_id); + if let Some(let_statement) = let_statement { + let expression = let_statement.expression; + self.try_eval_array_length_id_with_fuel(expression, span, fuel - 1) + } else { + Err(Some(ResolverError::InvalidArrayLengthExpr { span })) + } + } + _ => Err(Some(ResolverError::InvalidArrayLengthExpr { span })), + } + } + HirExpression::Infix(infix) => { + let lhs = self.try_eval_array_length_id_with_fuel(infix.lhs, span, fuel - 1)?; + let rhs = self.try_eval_array_length_id_with_fuel(infix.rhs, span, fuel - 1)?; + + match infix.operator.kind { + BinaryOpKind::Add => Ok(lhs + rhs), + BinaryOpKind::Subtract => Ok(lhs - rhs), + BinaryOpKind::Multiply => Ok(lhs * rhs), + BinaryOpKind::Divide => Ok(lhs / rhs), + BinaryOpKind::Equal => Ok((lhs == rhs) as u128), + BinaryOpKind::NotEqual => Ok((lhs != rhs) as u128), + BinaryOpKind::Less => Ok((lhs < rhs) as u128), + BinaryOpKind::LessEqual => Ok((lhs <= rhs) as u128), + BinaryOpKind::Greater => Ok((lhs > rhs) as u128), + BinaryOpKind::GreaterEqual => Ok((lhs >= rhs) as u128), + BinaryOpKind::And => Ok(lhs & rhs), + BinaryOpKind::Or => Ok(lhs | rhs), + BinaryOpKind::Xor => Ok(lhs ^ rhs), + BinaryOpKind::ShiftRight => Ok(lhs >> rhs), + BinaryOpKind::ShiftLeft => Ok(lhs << rhs), + BinaryOpKind::Modulo => Ok(lhs % rhs), + } + } _other => Err(Some(ResolverError::InvalidArrayLengthExpr { span })), } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 1158f4c229a..b8ed6fb73d2 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1214,6 +1214,18 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { assert_eq!(get_program_errors(src).len(), 0); } + #[test] + fn operators_in_global_used_in_type() { + let src = r#" + global ONE = 1; + global COUNT = ONE + 2; + fn main() { + let _array: [Field; COUNT] = [1, 2, 3]; + } + "#; + assert_eq!(get_program_errors(src).len(), 0); + } + // Regression for #4545 #[test] fn type_aliases_in_main() { diff --git a/noir_stdlib/src/array.nr b/noir_stdlib/src/array.nr index 3da4b649174..baa4bef50cc 100644 --- a/noir_stdlib/src/array.nr +++ b/noir_stdlib/src/array.nr @@ -1,4 +1,4 @@ -use crate::cmp::{Ord}; +use crate::cmp::Ord; // TODO: Once we fully move to the new SSA pass this module can be removed and replaced // by the methods in the `slice` module diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index fcf21436197..896dae15371 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -4,6 +4,7 @@ mod poseidon2; mod pedersen; use crate::default::Default; +use crate::uint128::U128; #[foreign(sha256)] // docs:start:sha256 @@ -120,10 +121,102 @@ where } } -// TODO: add implementations for the remainder of primitive types. -impl Hash for Field{ +impl Hash for Field { fn hash(self, state: &mut H) where H: Hasher{ - let input: [Field] = [self]; - H::write(state, input); + H::write(state, [self]); + } +} + +impl Hash for u8 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for u32 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for u64 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for i8 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for i32 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for i64 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for bool { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for () { + fn hash(_self: Self, _state: &mut H) where H: Hasher {} +} + +impl Hash for U128 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self.lo as Field, self.hi as Field]); + } +} + +impl Hash for [T; N] where T: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + for elem in self { + elem.hash(state); + } + } +} + +impl Hash for (A, B) where A: Hash, B: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.0.hash(state); + self.1.hash(state); + } +} + +impl Hash for (A, B, C) where A: Hash, B: Hash, C: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + } +} + +impl Hash for (A, B, C, D) where A: Hash, B: Hash, C: Hash, D: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + self.3.hash(state); + } +} + +impl Hash for (A, B, C, D, E) where A: Hash, B: Hash, C: Hash, D: Hash, E: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + self.3.hash(state); + self.4.hash(state); } } diff --git a/test_programs/compile_failure/brillig_nested_slices/Prover.toml b/test_programs/compile_failure/brillig_nested_slices/Prover.toml deleted file mode 100644 index c52564de922..00000000000 --- a/test_programs/compile_failure/brillig_nested_slices/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -a = "5" -b = "10" diff --git a/test_programs/compile_failure/custom_entry_not_found/Prover.toml b/test_programs/compile_failure/custom_entry_not_found/Prover.toml deleted file mode 100644 index 4dd6b405159..00000000000 --- a/test_programs/compile_failure/custom_entry_not_found/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = "1" diff --git a/test_programs/compile_failure/dep_impl_primitive/Prover.toml b/test_programs/compile_failure/dep_impl_primitive/Prover.toml deleted file mode 100644 index 7d4290a117a..00000000000 --- a/test_programs/compile_failure/dep_impl_primitive/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = 1 diff --git a/test_programs/compile_failure/depend_on_bin/Prover.toml b/test_programs/compile_failure/depend_on_bin/Prover.toml deleted file mode 100644 index 7d4290a117a..00000000000 --- a/test_programs/compile_failure/depend_on_bin/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = 1 diff --git a/test_programs/compile_failure/div_by_zero_modulo/Prover.toml b/test_programs/compile_failure/div_by_zero_modulo/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_implementation_4/Prover.toml b/test_programs/compile_failure/dup_trait_implementation_4/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_implementation_5/Prover.toml b/test_programs/compile_failure/dup_trait_implementation_5/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_1/Prover.toml b/test_programs/compile_failure/dup_trait_items_1/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_2/Prover.toml b/test_programs/compile_failure/dup_trait_items_2/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_3/Prover.toml b/test_programs/compile_failure/dup_trait_items_3/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_4/Prover.toml b/test_programs/compile_failure/dup_trait_items_4/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_5/Prover.toml b/test_programs/compile_failure/dup_trait_items_5/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_6/Prover.toml b/test_programs/compile_failure/dup_trait_items_6/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/hashmap_load_factor/Prover.toml b/test_programs/compile_failure/hashmap_load_factor/Prover.toml deleted file mode 100644 index e54319c61e9..00000000000 --- a/test_programs/compile_failure/hashmap_load_factor/Prover.toml +++ /dev/null @@ -1,26 +0,0 @@ -# Expected 6 key-value entries for hashmap capacity of 8. -# These must be distinct (both key-to-key, and value-to-value) for correct testing. - -[[input]] -key = 2 -value = 17 - -[[input]] -key = 3 -value = 19 - -[[input]] -key = 5 -value = 23 - -[[input]] -key = 7 -value = 29 - -[[input]] -key = 11 -value = 31 - -[[input]] -key = 41 -value = 43 \ No newline at end of file diff --git a/test_programs/compile_failure/negate_unsigned/Prover.toml b/test_programs/compile_failure/negate_unsigned/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/orphaned_trait_impl/Prover.toml b/test_programs/compile_failure/orphaned_trait_impl/Prover.toml deleted file mode 100644 index 2c1854573a4..00000000000 --- a/test_programs/compile_failure/orphaned_trait_impl/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = 1 -y = 2 diff --git a/test_programs/compile_failure/slice_remove_failure/Prover.toml b/test_programs/compile_failure/slice_remove_failure/Prover.toml deleted file mode 100644 index f28f2f8cc48..00000000000 --- a/test_programs/compile_failure/slice_remove_failure/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "5" -y = "10" diff --git a/test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml b/test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml deleted file mode 100644 index 465ef562de4..00000000000 --- a/test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "1" -y = "1" diff --git a/test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml b/test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml deleted file mode 100644 index a0397e89477..00000000000 --- a/test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "1" -y = "0" diff --git a/test_programs/compile_failure/assert_msg_runtime/Nargo.toml b/test_programs/execution_failure/assert_msg_runtime/Nargo.toml similarity index 100% rename from test_programs/compile_failure/assert_msg_runtime/Nargo.toml rename to test_programs/execution_failure/assert_msg_runtime/Nargo.toml diff --git a/test_programs/compile_failure/assert_msg_runtime/Prover.toml b/test_programs/execution_failure/assert_msg_runtime/Prover.toml similarity index 100% rename from test_programs/compile_failure/assert_msg_runtime/Prover.toml rename to test_programs/execution_failure/assert_msg_runtime/Prover.toml diff --git a/test_programs/compile_failure/assert_msg_runtime/src/main.nr b/test_programs/execution_failure/assert_msg_runtime/src/main.nr similarity index 100% rename from test_programs/compile_failure/assert_msg_runtime/src/main.nr rename to test_programs/execution_failure/assert_msg_runtime/src/main.nr diff --git a/test_programs/compile_failure/brillig_assert_fail/Nargo.toml b/test_programs/execution_failure/brillig_assert_fail/Nargo.toml similarity index 100% rename from test_programs/compile_failure/brillig_assert_fail/Nargo.toml rename to test_programs/execution_failure/brillig_assert_fail/Nargo.toml diff --git a/test_programs/compile_failure/brillig_assert_fail/Prover.toml b/test_programs/execution_failure/brillig_assert_fail/Prover.toml similarity index 100% rename from test_programs/compile_failure/brillig_assert_fail/Prover.toml rename to test_programs/execution_failure/brillig_assert_fail/Prover.toml diff --git a/test_programs/compile_failure/brillig_assert_fail/src/main.nr b/test_programs/execution_failure/brillig_assert_fail/src/main.nr similarity index 100% rename from test_programs/compile_failure/brillig_assert_fail/src/main.nr rename to test_programs/execution_failure/brillig_assert_fail/src/main.nr diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/Nargo.toml b/test_programs/execution_failure/brillig_assert_msg_runtime/Nargo.toml similarity index 100% rename from test_programs/compile_failure/brillig_assert_msg_runtime/Nargo.toml rename to test_programs/execution_failure/brillig_assert_msg_runtime/Nargo.toml diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml b/test_programs/execution_failure/brillig_assert_msg_runtime/Prover.toml similarity index 100% rename from test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml rename to test_programs/execution_failure/brillig_assert_msg_runtime/Prover.toml diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr b/test_programs/execution_failure/brillig_assert_msg_runtime/src/main.nr similarity index 100% rename from test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr rename to test_programs/execution_failure/brillig_assert_msg_runtime/src/main.nr diff --git a/test_programs/compile_failure/div_by_zero_constants/Nargo.toml b/test_programs/execution_failure/div_by_zero_constants/Nargo.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_constants/Nargo.toml rename to test_programs/execution_failure/div_by_zero_constants/Nargo.toml diff --git a/test_programs/compile_failure/cyclic_dep/Prover.toml b/test_programs/execution_failure/div_by_zero_constants/Prover.toml similarity index 100% rename from test_programs/compile_failure/cyclic_dep/Prover.toml rename to test_programs/execution_failure/div_by_zero_constants/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_constants/src/main.nr b/test_programs/execution_failure/div_by_zero_constants/src/main.nr similarity index 100% rename from test_programs/compile_failure/div_by_zero_constants/src/main.nr rename to test_programs/execution_failure/div_by_zero_constants/src/main.nr diff --git a/test_programs/compile_failure/div_by_zero_modulo/Nargo.toml b/test_programs/execution_failure/div_by_zero_modulo/Nargo.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_modulo/Nargo.toml rename to test_programs/execution_failure/div_by_zero_modulo/Nargo.toml diff --git a/test_programs/compile_failure/div_by_zero_constants/Prover.toml b/test_programs/execution_failure/div_by_zero_modulo/Prover.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_constants/Prover.toml rename to test_programs/execution_failure/div_by_zero_modulo/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_modulo/src/main.nr b/test_programs/execution_failure/div_by_zero_modulo/src/main.nr similarity index 100% rename from test_programs/compile_failure/div_by_zero_modulo/src/main.nr rename to test_programs/execution_failure/div_by_zero_modulo/src/main.nr diff --git a/test_programs/compile_failure/div_by_zero_numerator_witness/Nargo.toml b/test_programs/execution_failure/div_by_zero_numerator_witness/Nargo.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_numerator_witness/Nargo.toml rename to test_programs/execution_failure/div_by_zero_numerator_witness/Nargo.toml diff --git a/test_programs/compile_failure/div_by_zero_numerator_witness/Prover.toml b/test_programs/execution_failure/div_by_zero_numerator_witness/Prover.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_numerator_witness/Prover.toml rename to test_programs/execution_failure/div_by_zero_numerator_witness/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_numerator_witness/src/main.nr b/test_programs/execution_failure/div_by_zero_numerator_witness/src/main.nr similarity index 100% rename from test_programs/compile_failure/div_by_zero_numerator_witness/src/main.nr rename to test_programs/execution_failure/div_by_zero_numerator_witness/src/main.nr diff --git a/test_programs/compile_failure/div_by_zero_witness/Nargo.toml b/test_programs/execution_failure/div_by_zero_witness/Nargo.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_witness/Nargo.toml rename to test_programs/execution_failure/div_by_zero_witness/Nargo.toml diff --git a/test_programs/compile_failure/div_by_zero_witness/Prover.toml b/test_programs/execution_failure/div_by_zero_witness/Prover.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_witness/Prover.toml rename to test_programs/execution_failure/div_by_zero_witness/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_witness/src/main.nr b/test_programs/execution_failure/div_by_zero_witness/src/main.nr similarity index 100% rename from test_programs/compile_failure/div_by_zero_witness/src/main.nr rename to test_programs/execution_failure/div_by_zero_witness/src/main.nr diff --git a/test_programs/compile_failure/dyn_index_fail_nested_array/Nargo.toml b/test_programs/execution_failure/dyn_index_fail_nested_array/Nargo.toml similarity index 100% rename from test_programs/compile_failure/dyn_index_fail_nested_array/Nargo.toml rename to test_programs/execution_failure/dyn_index_fail_nested_array/Nargo.toml diff --git a/test_programs/compile_failure/dyn_index_fail_nested_array/Prover.toml b/test_programs/execution_failure/dyn_index_fail_nested_array/Prover.toml similarity index 100% rename from test_programs/compile_failure/dyn_index_fail_nested_array/Prover.toml rename to test_programs/execution_failure/dyn_index_fail_nested_array/Prover.toml diff --git a/test_programs/compile_failure/dyn_index_fail_nested_array/src/main.nr b/test_programs/execution_failure/dyn_index_fail_nested_array/src/main.nr similarity index 100% rename from test_programs/compile_failure/dyn_index_fail_nested_array/src/main.nr rename to test_programs/execution_failure/dyn_index_fail_nested_array/src/main.nr diff --git a/test_programs/compile_failure/dynamic_index_failure/Nargo.toml b/test_programs/execution_failure/dynamic_index_failure/Nargo.toml similarity index 100% rename from test_programs/compile_failure/dynamic_index_failure/Nargo.toml rename to test_programs/execution_failure/dynamic_index_failure/Nargo.toml diff --git a/test_programs/compile_failure/dynamic_index_failure/Prover.toml b/test_programs/execution_failure/dynamic_index_failure/Prover.toml similarity index 100% rename from test_programs/compile_failure/dynamic_index_failure/Prover.toml rename to test_programs/execution_failure/dynamic_index_failure/Prover.toml diff --git a/test_programs/compile_failure/dynamic_index_failure/src/main.nr b/test_programs/execution_failure/dynamic_index_failure/src/main.nr similarity index 100% rename from test_programs/compile_failure/dynamic_index_failure/src/main.nr rename to test_programs/execution_failure/dynamic_index_failure/src/main.nr diff --git a/test_programs/compile_failure/option_expect/Nargo.toml b/test_programs/execution_failure/option_expect/Nargo.toml similarity index 100% rename from test_programs/compile_failure/option_expect/Nargo.toml rename to test_programs/execution_failure/option_expect/Nargo.toml diff --git a/test_programs/compile_failure/option_expect/src/main.nr b/test_programs/execution_failure/option_expect/src/main.nr similarity index 100% rename from test_programs/compile_failure/option_expect/src/main.nr rename to test_programs/execution_failure/option_expect/src/main.nr diff --git a/test_programs/compile_failure/slice_access_failure/Nargo.toml b/test_programs/execution_failure/slice_access_failure/Nargo.toml similarity index 100% rename from test_programs/compile_failure/slice_access_failure/Nargo.toml rename to test_programs/execution_failure/slice_access_failure/Nargo.toml diff --git a/test_programs/compile_failure/radix_non_constant_length/Prover.toml b/test_programs/execution_failure/slice_access_failure/Prover.toml similarity index 100% rename from test_programs/compile_failure/radix_non_constant_length/Prover.toml rename to test_programs/execution_failure/slice_access_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_access_failure/src/main.nr b/test_programs/execution_failure/slice_access_failure/src/main.nr similarity index 100% rename from test_programs/compile_failure/slice_access_failure/src/main.nr rename to test_programs/execution_failure/slice_access_failure/src/main.nr diff --git a/test_programs/compile_failure/slice_insert_failure/Nargo.toml b/test_programs/execution_failure/slice_insert_failure/Nargo.toml similarity index 100% rename from test_programs/compile_failure/slice_insert_failure/Nargo.toml rename to test_programs/execution_failure/slice_insert_failure/Nargo.toml diff --git a/test_programs/compile_failure/slice_access_failure/Prover.toml b/test_programs/execution_failure/slice_insert_failure/Prover.toml similarity index 100% rename from test_programs/compile_failure/slice_access_failure/Prover.toml rename to test_programs/execution_failure/slice_insert_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_insert_failure/src/main.nr b/test_programs/execution_failure/slice_insert_failure/src/main.nr similarity index 100% rename from test_programs/compile_failure/slice_insert_failure/src/main.nr rename to test_programs/execution_failure/slice_insert_failure/src/main.nr diff --git a/test_programs/compile_failure/slice_remove_failure/Nargo.toml b/test_programs/execution_failure/slice_remove_failure/Nargo.toml similarity index 100% rename from test_programs/compile_failure/slice_remove_failure/Nargo.toml rename to test_programs/execution_failure/slice_remove_failure/Nargo.toml diff --git a/test_programs/compile_failure/slice_insert_failure/Prover.toml b/test_programs/execution_failure/slice_remove_failure/Prover.toml similarity index 100% rename from test_programs/compile_failure/slice_insert_failure/Prover.toml rename to test_programs/execution_failure/slice_remove_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_remove_failure/src/main.nr b/test_programs/execution_failure/slice_remove_failure/src/main.nr similarity index 100% rename from test_programs/compile_failure/slice_remove_failure/src/main.nr rename to test_programs/execution_failure/slice_remove_failure/src/main.nr diff --git a/test_programs/compile_failure/workspace_fail/Nargo.toml b/test_programs/execution_failure/workspace_fail/Nargo.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/Nargo.toml rename to test_programs/execution_failure/workspace_fail/Nargo.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/a/Nargo.toml b/test_programs/execution_failure/workspace_fail/crates/a/Nargo.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/a/Nargo.toml rename to test_programs/execution_failure/workspace_fail/crates/a/Nargo.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/a/Prover.toml b/test_programs/execution_failure/workspace_fail/crates/a/Prover.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/a/Prover.toml rename to test_programs/execution_failure/workspace_fail/crates/a/Prover.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/a/src/main.nr b/test_programs/execution_failure/workspace_fail/crates/a/src/main.nr similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/a/src/main.nr rename to test_programs/execution_failure/workspace_fail/crates/a/src/main.nr diff --git a/test_programs/compile_failure/workspace_fail/crates/b/Nargo.toml b/test_programs/execution_failure/workspace_fail/crates/b/Nargo.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/b/Nargo.toml rename to test_programs/execution_failure/workspace_fail/crates/b/Nargo.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/b/Prover.toml b/test_programs/execution_failure/workspace_fail/crates/b/Prover.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/b/Prover.toml rename to test_programs/execution_failure/workspace_fail/crates/b/Prover.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/b/src/main.nr b/test_programs/execution_failure/workspace_fail/crates/b/src/main.nr similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/b/src/main.nr rename to test_programs/execution_failure/workspace_fail/crates/b/src/main.nr diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml index f92a2421b0f..1629ae86edb 100644 --- a/tooling/nargo_cli/Cargo.toml +++ b/tooling/nargo_cli/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "nargo_cli" description = "Noir's package manager" +default-run = "nargo" version.workspace = true authors.workspace = true edition.workspace = true diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 1ca12b75dfb..f68ccbfd50e 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -41,6 +41,7 @@ fn main() { println!("cargo:rerun-if-changed={}", test_dir.as_os_str().to_str().unwrap()); generate_execution_success_tests(&mut test_file, &test_dir); + generate_execution_failure_tests(&mut test_file, &test_dir); generate_noir_test_success_tests(&mut test_file, &test_dir); generate_noir_test_failure_tests(&mut test_file, &test_dir); generate_compile_success_empty_tests(&mut test_file, &test_dir); @@ -86,6 +87,44 @@ fn execution_success_{test_name}() {{ } } +fn generate_execution_failure_tests(test_file: &mut File, test_data_dir: &Path) { + let test_sub_dir = "execution_failure"; + let test_data_dir = test_data_dir.join(test_sub_dir); + + let test_case_dirs = + fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); + + for test_dir in test_case_dirs { + let test_name = + test_dir.file_name().into_string().expect("Directory can't be converted to string"); + if test_name.contains('-') { + panic!( + "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" + ); + }; + let test_dir = &test_dir.path(); + + write!( + test_file, + r#" +#[test] +fn execution_failure_{test_name}() {{ + let test_program_dir = PathBuf::from("{test_dir}"); + + let mut cmd = Command::cargo_bin("nargo").unwrap(); + cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); + cmd.arg("--program-dir").arg(test_program_dir); + cmd.arg("execute").arg("--force"); + + cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); +}} + "#, + test_dir = test_dir.display(), + ) + .expect("Could not write templated test file."); + } +} + fn generate_noir_test_success_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "noir_test_success"; let test_data_dir = test_data_dir.join(test_sub_dir); @@ -281,7 +320,7 @@ fn compile_failure_{test_name}() {{ let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force"); + cmd.arg("compile").arg("--force"); cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); }} diff --git a/tooling/nargo_fmt/src/rewrite/imports.rs b/tooling/nargo_fmt/src/rewrite/imports.rs index 2788f778140..55eb259bcdd 100644 --- a/tooling/nargo_fmt/src/rewrite/imports.rs +++ b/tooling/nargo_fmt/src/rewrite/imports.rs @@ -102,7 +102,14 @@ impl UseTree { let mut iter = self.path.iter().peekable(); while let Some(segment) = iter.next() { - let segment_str = segment.rewrite(visitor, shape); + let mut segment_str = segment.rewrite(visitor, shape); + if segment_str.contains('{') + && !segment_str.contains(',') + && !segment_str.contains("::") + { + let empty = ""; + segment_str = segment_str.replace(['{', '}'], empty); + } result.push_str(&segment_str); if iter.peek().is_some() { diff --git a/tooling/nargo_fmt/tests/expected/contract.nr b/tooling/nargo_fmt/tests/expected/contract.nr index c5b19a686d2..97a6ebd6b77 100644 --- a/tooling/nargo_fmt/tests/expected/contract.nr +++ b/tooling/nargo_fmt/tests/expected/contract.nr @@ -11,7 +11,7 @@ contract Benchmarking { context::Context, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - types::address::{AztecAddress} + types::address::AztecAddress }; struct Storage { diff --git a/tooling/nargo_fmt/tests/expected/import_braces.nr b/tooling/nargo_fmt/tests/expected/import_braces.nr new file mode 100644 index 00000000000..49c9d09001e --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/import_braces.nr @@ -0,0 +1 @@ +use dep::std::hash::sha256; diff --git a/tooling/nargo_fmt/tests/input/contract.nr b/tooling/nargo_fmt/tests/input/contract.nr index c5b19a686d2..97a6ebd6b77 100644 --- a/tooling/nargo_fmt/tests/input/contract.nr +++ b/tooling/nargo_fmt/tests/input/contract.nr @@ -11,7 +11,7 @@ contract Benchmarking { context::Context, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - types::address::{AztecAddress} + types::address::AztecAddress }; struct Storage { diff --git a/tooling/nargo_fmt/tests/input/import_braces.nr b/tooling/nargo_fmt/tests/input/import_braces.nr new file mode 100644 index 00000000000..88c7e9562a8 --- /dev/null +++ b/tooling/nargo_fmt/tests/input/import_braces.nr @@ -0,0 +1 @@ +use dep::std::hash::{sha256}; \ No newline at end of file