Skip to content

Commit

Permalink
Merge branch 'master' into gd/issue_6875
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench authored Jan 6, 2025
2 parents c200300 + ab8807d commit 41d721e
Show file tree
Hide file tree
Showing 28 changed files with 303 additions and 512 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,10 +590,17 @@ pub fn compile_no_check(
cached_program: Option<CompiledProgram>,
force_compile: bool,
) -> Result<CompiledProgram, CompileError> {
let force_unconstrained = options.force_brillig;

let program = if options.instrument_debug {
monomorphize_debug(main_function, &mut context.def_interner, &context.debug_instrumenter)?
monomorphize_debug(
main_function,
&mut context.def_interner,
&context.debug_instrumenter,
force_unconstrained,
)?
} else {
monomorphize(main_function, &mut context.def_interner)?
monomorphize(main_function, &mut context.def_interner, force_unconstrained)?
};

if options.show_monomorphized {
Expand Down Expand Up @@ -632,7 +639,6 @@ pub fn compile_no_check(
}
},
enable_brillig_logging: options.show_brillig,
force_brillig_output: options.force_brillig,
print_codegen_timings: options.benchmark_codegen,
expression_width: if options.bounded_codegen {
options.expression_width.unwrap_or(DEFAULT_EXPRESSION_WIDTH)
Expand Down
3 changes: 2 additions & 1 deletion compiler/noirc_evaluator/src/acir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,8 @@ impl<'a> Context<'a> {
unreachable!("Expected all load instructions to be removed before acir_gen")
}
Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } => {
// Do nothing. Only Brillig needs to worry about reference counted arrays
// Only Brillig needs to worry about reference counted arrays
unreachable!("Expected all Rc instructions to be removed before acir_gen")
}
Instruction::RangeCheck { value, max_bit_size, assert_message } => {
let acir_var = self.convert_numeric_value(*value, dfg)?;
Expand Down
29 changes: 9 additions & 20 deletions compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@ pub struct SsaEvaluatorOptions {

pub enable_brillig_logging: bool,

/// Force Brillig output (for step debugging)
pub force_brillig_output: bool,

/// Pretty print benchmark times of each code generation pass
pub print_codegen_timings: bool,

Expand Down Expand Up @@ -99,7 +96,6 @@ pub(crate) fn optimize_into_acir(
let builder = SsaBuilder::new(
program,
options.ssa_logging.clone(),
options.force_brillig_output,
options.print_codegen_timings,
&options.emit_ssa,
)?;
Expand Down Expand Up @@ -154,15 +150,16 @@ pub(crate) fn optimize_into_acir(
/// Run all SSA passes.
fn optimize_all(builder: SsaBuilder, options: &SsaEvaluatorOptions) -> Result<Ssa, RuntimeError> {
Ok(builder
.run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions")
.run_pass(Ssa::defunctionalize, "Defunctionalization")
.run_pass(Ssa::remove_paired_rc, "Removing Paired rc_inc & rc_decs")
.run_pass(Ssa::separate_runtime, "Runtime Separation")
.run_pass(Ssa::resolve_is_unconstrained, "Resolving IsUnconstrained")
.run_pass(|ssa| ssa.inline_functions(options.inliner_aggressiveness), "Inlining (1st)")
// Run mem2reg with the CFG separated into blocks
.run_pass(Ssa::mem2reg, "Mem2Reg (1st)")
.run_pass(Ssa::simplify_cfg, "Simplifying (1st)")
.run_pass(Ssa::as_slice_optimization, "`as_slice` optimization")
.run_pass(Ssa::remove_unreachable_functions, "Removing Unreachable Functions")
.try_run_pass(
Ssa::evaluate_static_assert_and_assert_constant,
"`static_assert` and `assert_constant`",
Expand Down Expand Up @@ -275,19 +272,12 @@ pub fn create_program(
(generated_acirs, generated_brillig, brillig_function_names, error_types),
ssa_level_warnings,
) = optimize_into_acir(program, options)?;
if options.force_brillig_output {
assert_eq!(
generated_acirs.len(),
1,
"Only the main ACIR is expected when forcing Brillig output"
);
} else {
assert_eq!(
generated_acirs.len(),
func_sigs.len(),
"The generated ACIRs should match the supplied function signatures"
);
}

assert_eq!(
generated_acirs.len(),
func_sigs.len(),
"The generated ACIRs should match the supplied function signatures"
);

let error_types = error_types
.into_iter()
Expand Down Expand Up @@ -450,11 +440,10 @@ impl SsaBuilder {
fn new(
program: Program,
ssa_logging: SsaLogging,
force_brillig_runtime: bool,
print_codegen_timings: bool,
emit_ssa: &Option<PathBuf>,
) -> Result<SsaBuilder, RuntimeError> {
let ssa = ssa_gen::generate_ssa(program, force_brillig_runtime)?;
let ssa = ssa_gen::generate_ssa(program)?;
if let Some(emit_ssa) = emit_ssa {
let mut emit_ssa_dir = emit_ssa.clone();
// We expect the full package artifact path to be passed in here,
Expand Down
44 changes: 24 additions & 20 deletions compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,29 +483,33 @@ impl FunctionBuilder {
///
/// Returns whether a reference count instruction was issued.
fn update_array_reference_count(&mut self, value: ValueId, increment: bool) -> bool {
match self.type_of_value(value) {
Type::Numeric(_) => false,
Type::Function => false,
Type::Reference(element) => {
if element.contains_an_array() {
let reference = value;
let value = self.insert_load(reference, element.as_ref().clone());
self.update_array_reference_count(value, increment);
true
} else {
false
if self.current_function.runtime().is_brillig() {
match self.type_of_value(value) {
Type::Numeric(_) => false,
Type::Function => false,
Type::Reference(element) => {
if element.contains_an_array() {
let reference = value;
let value = self.insert_load(reference, element.as_ref().clone());
self.update_array_reference_count(value, increment);
true
} else {
false
}
}
}
Type::Array(..) | Type::Slice(..) => {
// If there are nested arrays or slices, we wait until ArrayGet
// is issued to increment the count of that array.
if increment {
self.insert_inc_rc(value);
} else {
self.insert_dec_rc(value);
Type::Array(..) | Type::Slice(..) => {
// If there are nested arrays or slices, we wait until ArrayGet
// is issued to increment the count of that array.
if increment {
self.insert_inc_rc(value);
} else {
self.insert_dec_rc(value);
}
true
}
true
}
} else {
false
}
}

Expand Down
34 changes: 31 additions & 3 deletions compiler/noirc_evaluator/src/ssa/ir/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::ssa::{function_builder::data_bus::DataBus, ir::instruction::SimplifyR
use super::{
basic_block::{BasicBlock, BasicBlockId},
call_stack::{CallStack, CallStackHelper, CallStackId},
function::FunctionId,
function::{FunctionId, RuntimeType},
instruction::{
Instruction, InstructionId, InstructionResultType, Intrinsic, TerminatorInstruction,
},
Expand All @@ -26,8 +26,13 @@ use serde_with::DisplayFromStr;
/// owning most data in a function and handing out Ids to this data that can be
/// shared without worrying about ownership.
#[serde_as]
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub(crate) struct DataFlowGraph {
/// Runtime of the [Function] that owns this [DataFlowGraph].
/// This might change during the `runtime_separation` pass where
/// ACIR functions are cloned as Brillig functions.
runtime: RuntimeType,

/// All of the instructions in a function
instructions: DenseMap<Instruction>,

Expand Down Expand Up @@ -100,6 +105,16 @@ pub(crate) struct DataFlowGraph {
}

impl DataFlowGraph {
/// Runtime type of the function.
pub(crate) fn runtime(&self) -> RuntimeType {
self.runtime
}

/// Set runtime type of the function.
pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) {
self.runtime = runtime;
}

/// Creates a new basic block with no parameters.
/// After being created, the block is unreachable in the current function
/// until another block is made to jump to it.
Expand Down Expand Up @@ -164,6 +179,11 @@ impl DataFlowGraph {
id
}

/// Check if the function runtime would simply ignore this instruction.
pub(crate) fn is_handled_by_runtime(&self, instruction: &Instruction) -> bool {
!(self.runtime().is_acir() && instruction.is_brillig_only())
}

fn insert_instruction_without_simplification(
&mut self,
instruction_data: Instruction,
Expand All @@ -184,6 +204,10 @@ impl DataFlowGraph {
ctrl_typevars: Option<Vec<Type>>,
call_stack: CallStackId,
) -> InsertInstructionResult {
if !self.is_handled_by_runtime(&instruction_data) {
return InsertInstructionResult::InstructionRemoved;
}

let id = self.insert_instruction_without_simplification(
instruction_data,
block,
Expand All @@ -194,14 +218,18 @@ impl DataFlowGraph {
InsertInstructionResult::Results(id, self.instruction_results(id))
}

/// Inserts a new instruction at the end of the given block and returns its results
/// Simplifies a new instruction and inserts it at the end of the given block and returns its results.
/// If the instruction is not handled by the current runtime, `InstructionRemoved` is returned.
pub(crate) fn insert_instruction_and_results(
&mut self,
instruction: Instruction,
block: BasicBlockId,
ctrl_typevars: Option<Vec<Type>>,
call_stack: CallStackId,
) -> InsertInstructionResult {
if !self.is_handled_by_runtime(&instruction) {
return InsertInstructionResult::InstructionRemoved;
}
match instruction.simplify(self, block, ctrl_typevars.clone(), call_stack) {
SimplifyResult::SimplifiedTo(simplification) => {
InsertInstructionResult::SimplifiedTo(simplification)
Expand Down
18 changes: 11 additions & 7 deletions compiler/noirc_evaluator/src/ssa/ir/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ impl RuntimeType {
}
}

impl Default for RuntimeType {
fn default() -> Self {
RuntimeType::Acir(InlineType::default())
}
}

/// A function holds a list of instructions.
/// These instructions are further grouped into Basic blocks
///
Expand All @@ -72,8 +78,6 @@ pub(crate) struct Function {

id: FunctionId,

runtime: RuntimeType,

/// The DataFlowGraph holds the majority of data pertaining to the function
/// including its blocks, instructions, and values.
pub(crate) dfg: DataFlowGraph,
Expand All @@ -86,20 +90,20 @@ impl Function {
pub(crate) fn new(name: String, id: FunctionId) -> Self {
let mut dfg = DataFlowGraph::default();
let entry_block = dfg.make_block();
Self { name, id, entry_block, dfg, runtime: RuntimeType::Acir(InlineType::default()) }
Self { name, id, entry_block, dfg }
}

/// Creates a new function as a clone of the one passed in with the passed in id.
pub(crate) fn clone_with_id(id: FunctionId, another: &Function) -> Self {
let dfg = another.dfg.clone();
let entry_block = another.entry_block;
Self { name: another.name.clone(), id, entry_block, dfg, runtime: another.runtime }
Self { name: another.name.clone(), id, entry_block, dfg }
}

/// Takes the signature (function name & runtime) from a function but does not copy the body.
pub(crate) fn clone_signature(id: FunctionId, another: &Function) -> Self {
let mut new_function = Function::new(another.name.clone(), id);
new_function.runtime = another.runtime;
new_function.set_runtime(another.runtime());
new_function
}

Expand All @@ -116,12 +120,12 @@ impl Function {

/// Runtime type of the function.
pub(crate) fn runtime(&self) -> RuntimeType {
self.runtime
self.dfg.runtime()
}

/// Set runtime type of the function.
pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) {
self.runtime = runtime;
self.dfg.set_runtime(runtime);
}

pub(crate) fn is_no_predicates(&self) -> bool {
Expand Down
6 changes: 6 additions & 0 deletions compiler/noirc_evaluator/src/ssa/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,12 @@ impl Instruction {
Instruction::Noop => Remove,
}
}

/// Some instructions are only to be used in Brillig and should be eliminated
/// after runtime separation, never to be be reintroduced in an ACIR runtime.
pub(crate) fn is_brillig_only(&self) -> bool {
matches!(self, Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. })
}
}

/// Given a chain of operations like:
Expand Down
5 changes: 2 additions & 3 deletions compiler/noirc_evaluator/src/ssa/ir/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,8 @@ fn value(function: &Function, id: ValueId) -> String {
}
Value::Function(id) => id.to_string(),
Value::Intrinsic(intrinsic) => intrinsic.to_string(),
Value::Param { .. } | Value::Instruction { .. } | Value::ForeignFunction(_) => {
id.to_string()
}
Value::ForeignFunction(function) => function.clone(),
Value::Param { .. } | Value::Instruction { .. } => id.to_string(),
}
}

Expand Down
8 changes: 4 additions & 4 deletions compiler/noirc_evaluator/src/ssa/ir/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ pub(crate) type ValueId = Id<Value>;
pub(crate) enum Value {
/// This value was created due to an instruction
///
/// instruction -- This is the instruction which defined it
/// typ -- This is the `Type` of the instruction
/// position -- Returns the position in the results
/// vector that this `Value` is located.
/// * `instruction`: This is the instruction which defined it
/// * `typ`: This is the `Type` of the instruction
/// * `position`: Returns the position in the results vector that this `Value` is located.
///
/// Example, if you add two numbers together, then the resulting
/// value would have position `0`, the typ would be the type
/// of the operands, and the instruction would map to an add instruction.
Expand Down
8 changes: 6 additions & 2 deletions compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -830,9 +830,12 @@ fn simplify(dfg: &DataFlowGraph, lhs: ValueId, rhs: ValueId) -> Option<(ValueId,
mod test {
use std::sync::Arc;

use noirc_frontend::monomorphization::ast::InlineType;

use crate::ssa::{
function_builder::FunctionBuilder,
ir::{
function::RuntimeType,
map::Id,
types::{NumericType, Type},
},
Expand Down Expand Up @@ -1153,6 +1156,7 @@ mod test {

// Compiling main
let mut builder = FunctionBuilder::new("main".into(), main_id);
builder.set_runtime(RuntimeType::Brillig(InlineType::default()));
let v0 = builder.add_parameter(Type::unsigned(64));
let zero = builder.numeric_constant(0u128, NumericType::unsigned(64));
let typ = Type::Array(Arc::new(vec![Type::unsigned(64)]), 25);
Expand Down Expand Up @@ -1550,7 +1554,7 @@ mod test {
fn deduplicates_side_effecting_intrinsics() {
let src = "
// After EnableSideEffectsIf removal:
acir(inline) fn main f0 {
brillig(inline) fn main f0 {
b0(v0: Field, v1: Field, v2: u1):
v4 = call is_unconstrained() -> u1
v7 = call to_be_radix(v0, u32 256) -> [u8; 1] // `a.to_be_radix(256)`;
Expand All @@ -1567,7 +1571,7 @@ mod test {
";
let ssa = Ssa::from_str(src).unwrap();
let expected = "
acir(inline) fn main f0 {
brillig(inline) fn main f0 {
b0(v0: Field, v1: Field, v2: u1):
v4 = call is_unconstrained() -> u1
v7 = call to_be_radix(v0, u32 256) -> [u8; 1]
Expand Down
Loading

0 comments on commit 41d721e

Please sign in to comment.