Skip to content

Commit

Permalink
feat(acir_gen): Fold attribute at compile-time and initial non inline…
Browse files Browse the repository at this point in the history
…d ACIR (#5341)

Partially resolves noir-lang/noir#4428 but not
fully. Need ACVM execution work still.

This does the initial codegen work for having multiple ACIR functions. 

It does a few things:
1. Adds a new `InlineType` that is now part of `RuntimeType::Acir`. We
do not care about `RuntimeType::Brillig` as we do not inline any of
those calls.
2. Fetch the appropriate function signatures for all possible `Circuit`
functions inside of a `Program`. Previously we were just returning the
main function signature. This is needed to accurately set up the
`private_parameters` and `public_parameters` fields in a circuit.
3. Iterates over all SSA functions, determines which are entry points
and codegens them
4. Codegens the newly added `Call` opcode from
#4773

---------

Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
Co-authored-by: Tom French <tom@tomfren.ch>
Co-authored-by: jfecher <jfecher11@gmail.com>
  • Loading branch information
4 people authored Mar 29, 2024
1 parent 9ffe457 commit a979150
Show file tree
Hide file tree
Showing 38 changed files with 869 additions and 199 deletions.
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,7 @@ workflows:
<<: *defaults
- noir-packages-tests:
requires:
- bb-js
- noir-ecr-manifest
- noir-packages
<<: *defaults
Expand Down
4 changes: 2 additions & 2 deletions noir/noir-repo/acvm-repo/acir/src/circuit/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ impl std::fmt::Display for Opcode {
}
Opcode::Call { id, inputs, outputs } => {
write!(f, "CALL func {}: ", id)?;
writeln!(f, "inputs: {:?}", inputs)?;
writeln!(f, "outputs: {:?}", outputs)
write!(f, "inputs: {:?}, ", inputs)?;
write!(f, "outputs: {:?}", outputs)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ pub(super) fn transform_internal(
new_acir_opcode_positions.push(acir_opcode_positions[index]);
transformed_opcodes.push(opcode);
}
Opcode::Call { .. } => todo!("Handle Call opcodes in the ACVM"),
Opcode::Call { .. } => {
// `Call` does not write values to the `WitnessMap`
// A separate ACIR function should have its own respective `WitnessMap`
new_acir_opcode_positions.push(acir_opcode_positions[index]);
transformed_opcodes.push(opcode);
}
}
}

Expand Down
19 changes: 12 additions & 7 deletions noir/noir-repo/compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
#![warn(unreachable_pub)]
#![warn(clippy::semicolon_if_nothing_returned)]

use acvm::acir::circuit::{ExpressionWidth, Program};
use acvm::acir::circuit::ExpressionWidth;
use clap::Args;
use fm::{FileId, FileManager};
use iter_extended::vecmap;
use noirc_abi::{AbiParameter, AbiType, ContractEvent};
use noirc_errors::{CustomDiagnostic, FileDiagnostic};
use noirc_evaluator::create_circuit;
use noirc_evaluator::create_program;
use noirc_evaluator::errors::RuntimeError;
use noirc_frontend::debug::build_debug_crate_file;
use noirc_frontend::graph::{CrateId, CrateName};
Expand Down Expand Up @@ -478,18 +478,23 @@ pub fn compile_no_check(
return Ok(cached_program.expect("cache must exist for hashes to match"));
}
let visibility = program.return_visibility;
let (circuit, debug, input_witnesses, return_witnesses, warnings) =
create_circuit(program, options.show_ssa, options.show_brillig, options.force_brillig)?;

let (program, debug, warnings, input_witnesses, return_witnesses) =
create_program(program, options.show_ssa, options.show_brillig, options.force_brillig)?;

let abi =
abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses, visibility);
let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager);
let file_map = filter_relevant_files(&debug, &context.file_manager);

Ok(CompiledProgram {
hash,
// TODO(https://github.com/noir-lang/noir/issues/4428)
program: Program { functions: vec![circuit] },
debug,
program,
// TODO(https://github.com/noir-lang/noir/issues/4428)
// Debug info is only relevant for errors at execution time which is not yet supported
// The CompileProgram `debug` field is used in multiple places and is better
// left to be updated once execution of multiple ACIR functions is enabled
debug: debug[0].clone(),
abi,
file_map,
noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,9 @@ mod tests {
use crate::ssa::ssa_gen::Ssa;

fn create_test_environment() -> (Ssa, FunctionContext, BrilligContext) {
let builder =
FunctionBuilder::new("main".to_string(), Id::test_new(0), RuntimeType::Brillig);
let mut builder = FunctionBuilder::new("main".to_string(), Id::test_new(0));
builder.set_runtime(RuntimeType::Brillig);

let ssa = builder.finish();
let mut brillig_context = create_context();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ mod test {
// }

let main_id = Id::test_new(1);
let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Brillig);
let mut builder = FunctionBuilder::new("main".into(), main_id);
builder.set_runtime(RuntimeType::Brillig);

let b1 = builder.insert_block();
let b2 = builder.insert_block();
Expand Down Expand Up @@ -425,7 +426,8 @@ mod test {
// }

let main_id = Id::test_new(1);
let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Brillig);
let mut builder = FunctionBuilder::new("main".into(), main_id);
builder.set_runtime(RuntimeType::Brillig);

let b1 = builder.insert_block();
let b2 = builder.insert_block();
Expand Down
2 changes: 1 addition & 1 deletion noir/noir-repo/compiler/noirc_evaluator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ pub mod ssa;

pub mod brillig;

pub use ssa::create_circuit;
pub use ssa::create_program;
75 changes: 64 additions & 11 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ use crate::{
errors::{RuntimeError, SsaReport},
};
use acvm::acir::{
circuit::{Circuit, ExpressionWidth, PublicInputs},
circuit::{Circuit, ExpressionWidth, Program as AcirProgram, PublicInputs},
native_types::Witness,
};

use noirc_errors::debug_info::DebugInfo;
use noirc_errors::debug_info::{DebugFunctions, DebugInfo, DebugTypes, DebugVariables};

use noirc_frontend::{
hir_def::function::FunctionSignature, monomorphization::ast::Program, Visibility,
Expand All @@ -41,7 +41,7 @@ pub(crate) fn optimize_into_acir(
print_ssa_passes: bool,
print_brillig_trace: bool,
force_brillig_output: bool,
) -> Result<GeneratedAcir, RuntimeError> {
) -> Result<Vec<GeneratedAcir>, RuntimeError> {
let abi_distinctness = program.return_distinctness;

let ssa_gen_span = span!(Level::TRACE, "ssa_generation");
Expand Down Expand Up @@ -70,31 +70,82 @@ pub(crate) fn optimize_into_acir(

let last_array_uses = ssa.find_last_array_uses();

ssa.into_acir(brillig, abi_distinctness, &last_array_uses)
ssa.into_acir(&brillig, abi_distinctness, &last_array_uses)
}

/// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Circuit].
/// Compiles the [`Program`] into [`ACIR``][acvm::acir::circuit::Program].
///
/// The output ACIR is is backend-agnostic and so must go through a transformation pass before usage in proof generation.
#[allow(clippy::type_complexity)]
#[tracing::instrument(level = "trace", skip_all)]
pub fn create_circuit(
pub fn create_program(
program: Program,
enable_ssa_logging: bool,
enable_brillig_logging: bool,
force_brillig_output: bool,
) -> Result<(Circuit, DebugInfo, Vec<Witness>, Vec<Witness>, Vec<SsaReport>), RuntimeError> {
) -> Result<(AcirProgram, Vec<DebugInfo>, Vec<SsaReport>, Vec<Witness>, Vec<Witness>), RuntimeError>
{
let debug_variables = program.debug_variables.clone();
let debug_types = program.debug_types.clone();
let debug_functions = program.debug_functions.clone();
let func_sig = program.main_function_signature.clone();

let func_sigs = program.function_signatures.clone();

let recursive = program.recursive;
let mut generated_acir = optimize_into_acir(
let generated_acirs = optimize_into_acir(
program,
enable_ssa_logging,
enable_brillig_logging,
force_brillig_output,
)?;
assert_eq!(
generated_acirs.len(),
func_sigs.len(),
"The generated ACIRs should match the supplied function signatures"
);

let mut functions = vec![];
let mut debug_infos = vec![];
let mut warning_infos = vec![];
let mut main_input_witnesses = Vec::new();
let mut main_return_witnesses = Vec::new();
// For setting up the ABI we need separately specify main's input and return witnesses
let mut is_main = true;
for (acir, func_sig) in generated_acirs.into_iter().zip(func_sigs) {
let (circuit, debug_info, warnings, input_witnesses, return_witnesses) =
convert_generated_acir_into_circuit(
acir,
func_sig,
recursive,
// TODO: get rid of these clones
debug_variables.clone(),
debug_functions.clone(),
debug_types.clone(),
);
functions.push(circuit);
debug_infos.push(debug_info);
warning_infos.extend(warnings);
if is_main {
// main_input_witness = circuit.re
main_input_witnesses = input_witnesses;
main_return_witnesses = return_witnesses;
}
is_main = false;
}

let program = AcirProgram { functions };

Ok((program, debug_infos, warning_infos, main_input_witnesses, main_return_witnesses))
}

fn convert_generated_acir_into_circuit(
mut generated_acir: GeneratedAcir,
func_sig: FunctionSignature,
recursive: bool,
debug_variables: DebugVariables,
debug_functions: DebugFunctions,
debug_types: DebugTypes,
) -> (Circuit, DebugInfo, Vec<SsaReport>, Vec<Witness>, Vec<Witness>) {
let opcodes = generated_acir.take_opcodes();
let current_witness_index = generated_acir.current_witness_index().0;
let GeneratedAcir {
Expand All @@ -106,6 +157,8 @@ pub fn create_circuit(
..
} = generated_acir;

let locations = locations.clone();

let (public_parameter_witnesses, private_parameters) =
split_public_and_private_inputs(&func_sig, &input_witnesses);

Expand All @@ -119,7 +172,7 @@ pub fn create_circuit(
private_parameters,
public_parameters,
return_values,
assert_messages: assert_messages.into_iter().collect(),
assert_messages: assert_messages.clone().into_iter().collect(),
recursive,
};

Expand All @@ -135,7 +188,7 @@ pub fn create_circuit(
let (optimized_circuit, transformation_map) = acvm::compiler::optimize(circuit);
debug_info.update_acir(transformation_map);

Ok((optimized_circuit, debug_info, input_witnesses, return_witnesses, warnings))
(optimized_circuit, debug_info, warnings, input_witnesses, return_witnesses)
}

// Takes each function argument and partitions the circuit's inputs witnesses according to its visibility.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1757,6 +1757,30 @@ impl AcirContext {
}
Ok(())
}

pub(crate) fn call_acir_function(
&mut self,
id: u32,
inputs: Vec<AcirValue>,
output_count: usize,
) -> Result<Vec<AcirVar>, RuntimeError> {
let inputs = self.prepare_inputs_for_black_box_func_call(inputs)?;
let inputs = inputs
.iter()
.flat_map(|input| vecmap(input, |input| input.witness))
.collect::<Vec<_>>();
let outputs = vecmap(0..output_count, |_| self.acir_ir.next_witness_index());

// Convert `Witness` values which are now constrained to be the output of the
// ACIR function call into `AcirVar`s.
// Similar to black box functions, we do not apply range information on the output of the function.
// See issue https://github.com/noir-lang/noir/issues/1439
let results =
vecmap(&outputs, |witness_index| self.add_data(AcirVarData::Witness(*witness_index)));

self.acir_ir.push_opcode(Opcode::Call { id, inputs, outputs });
Ok(results)
}
}

/// Enum representing the possible values that a
Expand Down
Loading

0 comments on commit a979150

Please sign in to comment.