Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion EXTERNAL_NOIR_LIBRARIES.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ libraries:
critical: false
noir_json_parser:
repo: noir-lang/noir_json_parser
timeout: 12
timeout: 100
critical: false
sha256:
repo: noir-lang/sha256
Expand Down
11 changes: 8 additions & 3 deletions compiler/noirc_evaluator/src/ssa/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub mod value;

use value::Value;

struct Interpreter<'ssa, W> {
pub(crate) struct Interpreter<'ssa, W> {
/// Contains each function called with `main` (or the first called function if
/// the interpreter was manually invoked on a different function) at
/// the front of the Vec.
Expand All @@ -49,6 +49,8 @@ struct Interpreter<'ssa, W> {
pub struct InterpreterOptions {
/// If true, the interpreter will trace its execution.
pub trace: bool,
/// If true, the interpreter treats all foreign function calls (e.g., `print`) as unknown
pub no_foreign_calls: bool,
}

struct CallContext {
Expand Down Expand Up @@ -88,7 +90,7 @@ impl Ssa {
self.interpret_function(self.main_id, args, options, output)
}

fn interpret_function<W: Write>(
pub fn interpret_function<W: Write>(
&self,
function: FunctionId,
args: Vec<Value>,
Expand All @@ -102,7 +104,7 @@ impl Ssa {
}

impl<'ssa, W: Write> Interpreter<'ssa, W> {
fn new(ssa: &'ssa Ssa, options: InterpreterOptions, output: W) -> Self {
pub(crate) fn new(ssa: &'ssa Ssa, options: InterpreterOptions, output: W) -> Self {
let call_stack = vec![CallContext::global_context()];
Self { ssa, call_stack, side_effects_enabled: true, options, output }
}
Expand Down Expand Up @@ -752,6 +754,9 @@ impl<'ssa, W: Write> Interpreter<'ssa, W> {
Value::Intrinsic(intrinsic) => {
self.call_intrinsic(intrinsic, argument_ids, results)?
}
Value::ForeignFunction(name) if self.options.no_foreign_calls => {
return Err(InterpreterError::UnknownForeignFunctionCall { name });
}
Value::ForeignFunction(name) if name == "print" => self.call_print(arguments)?,
Value::ForeignFunction(name) => {
return Err(InterpreterError::UnknownForeignFunctionCall { name });
Expand Down
76 changes: 23 additions & 53 deletions compiler/noirc_evaluator/src/ssa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::{

use crate::{
acir::ssa::Artifacts,
brillig::{Brillig, BrilligOptions},
brillig::BrilligOptions,
errors::{RuntimeError, SsaReport},
};
use acvm::{
Expand Down Expand Up @@ -102,9 +102,6 @@ pub struct SsaEvaluatorOptions {
pub struct ArtifactsAndWarnings(pub Artifacts, pub Vec<SsaReport>);

/// The default SSA optimization pipeline.
///
/// After these passes everything is ready for execution, which is
/// something we take can advantage of in the [secondary_passes].
pub fn primary_passes(options: &SsaEvaluatorOptions) -> Vec<SsaPass> {
vec![
SsaPass::new(Ssa::expand_signed_checks, "expand signed checks"),
Expand Down Expand Up @@ -191,6 +188,14 @@ pub fn primary_passes(options: &SsaEvaluatorOptions) -> Vec<SsaPass> {
// A function can be potentially unreachable post-DIE if all calls to that function were removed.
.and_then(Ssa::remove_unreachable_functions),
SsaPass::new(Ssa::checked_to_unchecked, "Checked to unchecked"),
SsaPass::new(Ssa::fold_constants_using_brillig, "Constant Fold Brillig Calls"),
SsaPass::new(Ssa::remove_unreachable_instructions, "Remove Unreachable Instructions")
// It could happen that we inlined all calls to a given brillig function.
// In that case it's unused so we can remove it. This is what we check next.
.and_then(Ssa::remove_unreachable_functions),
SsaPass::new(Ssa::dead_instruction_elimination, "Dead Instruction Elimination")
// A function can be potentially unreachable post-DIE if all calls to that function were removed.
.and_then(Ssa::remove_unreachable_functions),
SsaPass::new_try(
Ssa::verify_no_dynamic_indices_to_references,
"Verifying no dynamic array indices to reference value elements",
Expand All @@ -204,20 +209,6 @@ pub fn primary_passes(options: &SsaEvaluatorOptions) -> Vec<SsaPass> {
]
}

/// The second SSA pipeline, in which we take the Brillig functions compiled after
/// the primary pipeline, and execute the ones with all-constant arguments,
/// to replace the calls with the return value.
pub fn secondary_passes(brillig: &Brillig) -> Vec<SsaPass> {
vec![
SsaPass::new(move |ssa| ssa.fold_constants_with_brillig(brillig), "Inlining Brillig Calls"),
SsaPass::new(Ssa::remove_unreachable_instructions, "Remove Unreachable Instructions")
// It could happen that we inlined all calls to a given brillig function.
// In that case it's unused so we can remove it. This is what we check next.
.and_then(Ssa::remove_unreachable_functions),
SsaPass::new(Ssa::dead_instruction_elimination_acir, "Dead Instruction Elimination - ACIR"),
]
}

/// For testing purposes we want a list of the minimum number of SSA passes that should
/// return the same result as the full pipeline.
///
Expand Down Expand Up @@ -249,37 +240,25 @@ pub fn minimal_passes() -> Vec<SsaPass<'static>> {
/// An ACIR program is made up of both ACIR functions
/// and Brillig functions for unconstrained execution.
///
/// The `primary` SSA passes are applied on the initial SSA.
/// Then we compile the Brillig functions, and use the output
/// to run a `secondary` pass, which can use the Brillig
/// artifacts to do constant folding.
///
/// See the [primary_passes] and [secondary_passes] for
/// the default implementations.
pub fn optimize_ssa_builder_into_acir<S>(
/// See the [primary_passes] for the default implementation.
pub fn optimize_ssa_builder_into_acir(
builder: SsaBuilder,
options: &SsaEvaluatorOptions,
primary: &[SsaPass],
secondary: S,
) -> Result<ArtifactsAndWarnings, RuntimeError>
where
S: for<'b> Fn(&'b Brillig) -> Vec<SsaPass<'b>>,
{
) -> Result<ArtifactsAndWarnings, RuntimeError> {
let ssa_gen_span = span!(Level::TRACE, "ssa_generation");
let ssa_gen_span_guard = ssa_gen_span.enter();
let builder = builder.with_skip_passes(options.skip_passes.clone()).run_passes(primary)?;

drop(ssa_gen_span_guard);

let ssa = builder.ssa();
let brillig = time("SSA to Brillig", options.print_codegen_timings, || {
ssa.to_brillig(&options.brillig_options)
builder.ssa().to_brillig(&options.brillig_options)
});

let ssa_gen_span_guard = ssa_gen_span.enter();

let mut ssa = builder.run_passes(&secondary(&brillig))?.finish();

let mut ssa = builder.finish();
let mut ssa_level_warnings = vec![];
if !options.skip_underconstrained_check {
ssa_level_warnings.extend(time(
Expand Down Expand Up @@ -320,18 +299,13 @@ where
/// to run a `secondary` pass, which can use the Brillig
/// artifacts to do constant folding.
///
/// See the [primary_passes] and [secondary_passes] for
/// the default implementations.
pub fn optimize_into_acir<S>(
/// See the [primary_passes] for the default implementation.
pub fn optimize_into_acir(
program: Program,
options: &SsaEvaluatorOptions,
primary: &[SsaPass],
secondary: S,
files: Option<&fm::FileManager>,
) -> Result<ArtifactsAndWarnings, RuntimeError>
where
S: for<'b> Fn(&'b Brillig) -> Vec<SsaPass<'b>>,
{
) -> Result<ArtifactsAndWarnings, RuntimeError> {
let builder = SsaBuilder::from_program(
program,
options.ssa_logging.clone(),
Expand All @@ -340,7 +314,7 @@ where
files,
)?;

optimize_ssa_builder_into_acir(builder, options, primary, secondary)
optimize_ssa_builder_into_acir(builder, options, primary)
}

/// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Program].
Expand All @@ -352,7 +326,7 @@ pub fn create_program(
options: &SsaEvaluatorOptions,
files: Option<&fm::FileManager>,
) -> Result<SsaProgramArtifact, RuntimeError> {
create_program_with_passes(program, options, &primary_passes(options), secondary_passes, files)
create_program_with_passes(program, options, &primary_passes(options), files)
}

/// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Program] using the minimum amount of SSA passes.
Expand All @@ -372,30 +346,26 @@ pub fn create_program_with_minimal_passes(
func.name
);
}
create_program_with_passes(program, options, &minimal_passes(), |_| vec![], Some(files))
create_program_with_passes(program, options, &minimal_passes(), Some(files))
}

/// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Program] by running it through
/// `primary` and `secondary` SSA passes.
#[tracing::instrument(level = "trace", skip_all)]
pub fn create_program_with_passes<S>(
pub fn create_program_with_passes(
program: Program,
options: &SsaEvaluatorOptions,
primary: &[SsaPass],
secondary: S,
files: Option<&fm::FileManager>,
) -> Result<SsaProgramArtifact, RuntimeError>
where
S: for<'b> Fn(&'b Brillig) -> Vec<SsaPass<'b>>,
{
) -> Result<SsaProgramArtifact, RuntimeError> {
let debug_variables = program.debug_variables.clone();
let debug_types = program.debug_types.clone();
let debug_functions = program.debug_functions.clone();

let arg_size_and_visibilities: Vec<Vec<(u32, Visibility)>> =
program.function_signatures.iter().map(resolve_function_signature).collect();

let artifacts = optimize_into_acir(program, options, primary, secondary, files)?;
let artifacts = optimize_into_acir(program, options, primary, files)?;

Ok(combine_artifacts(
artifacts,
Expand Down
Loading
Loading