Skip to content
Merged
2 changes: 1 addition & 1 deletion .noir-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
d44f882be094bf492b1742370fd3896b0c371f59
24d26c05705fabca81b19d789203ebb6fc22ff32
18 changes: 18 additions & 0 deletions noir/noir-repo/compiler/noirc_evaluator/src/ssa/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,19 +475,37 @@ impl<'function> PerFunctionContext<'function> {
/// Inline each instruction in the given block into the function being inlined into.
/// This may recurse if it finds another function to inline if a call instruction is within this block.
fn inline_block_instructions(&mut self, ssa: &Ssa, block_id: BasicBlockId) {
let mut side_effects_enabled: Option<ValueId> = None;

let block = &self.source_function.dfg[block_id];
for id in block.instructions() {
match &self.source_function.dfg[*id] {
Instruction::Call { func, arguments } => match self.get_function(*func) {
Some(func_id) => {
if self.should_inline_call(ssa, func_id) {
self.inline_function(ssa, *id, func_id, arguments);

// This is only relevant during handling functions with `InlineType::NoPredicates` as these
// can pollute the function they're being inlined into with `Instruction::EnabledSideEffects`,
// resulting in predicates not being applied properly.
//
// Note that this doesn't cover the case in which there exists an `Instruction::EnabledSideEffects`
// within the function being inlined whilst the source function has not encountered one yet.
// In practice this isn't an issue as the last `Instruction::EnabledSideEffects` in the
// function being inlined will be to turn off predicates rather than to create one.
if let Some(condition) = side_effects_enabled {
self.context.builder.insert_enable_side_effects_if(condition);
}
} else {
self.push_instruction(*id);
}
}
None => self.push_instruction(*id),
},
Instruction::EnableSideEffects { condition } => {
side_effects_enabled = Some(self.translate_value(*condition));
self.push_instruction(*id);
}
_ => self.push_instruction(*id),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::ssa::{
dfg::DataFlowGraph,
function::Function,
instruction::{BinaryOp, Instruction, Intrinsic},
types::Type,
value::Value,
},
ssa_gen::Ssa,
Expand Down Expand Up @@ -62,6 +63,7 @@ impl Context {
) {
let instructions = function.dfg[block].take_instructions();

let mut active_condition = function.dfg.make_constant(FieldElement::one(), Type::bool());
let mut last_side_effects_enabled_instruction = None;

let mut new_instructions = Vec::with_capacity(instructions.len());
Expand All @@ -72,19 +74,26 @@ impl Context {
// instructions with side effects then we can drop the instruction we're holding and
// continue with the new `Instruction::EnableSideEffects`.
if let Instruction::EnableSideEffects { condition } = instruction {
// If this instruction isn't changing the currently active condition then we can ignore it.
if active_condition == *condition {
continue;
}

// If we're seeing an `enable_side_effects u1 1` then we want to insert it immediately.
// This is because we want to maximize the effect it will have.
if function
let condition_is_one = function
.dfg
.get_numeric_constant(*condition)
.map_or(false, |condition| condition.is_one())
{
.map_or(false, |condition| condition.is_one());
if condition_is_one {
new_instructions.push(instruction_id);
last_side_effects_enabled_instruction = None;
active_condition = *condition;
continue;
}

last_side_effects_enabled_instruction = Some(instruction_id);
active_condition = *condition;
continue;
}

Expand Down Expand Up @@ -172,3 +181,88 @@ impl Context {
}
}
}

#[cfg(test)]
mod test {

use crate::ssa::{
function_builder::FunctionBuilder,
ir::{
instruction::{BinaryOp, Instruction},
map::Id,
types::Type,
},
};

#[test]
fn remove_chains_of_same_condition() {
// acir(inline) fn main f0 {
// b0(v0: Field):
// enable_side_effects u1 1
// v4 = mul v0, Field 2
// enable_side_effects u1 1
// v5 = mul v0, Field 2
// enable_side_effects u1 1
// v6 = mul v0, Field 2
// enable_side_effects u1 1
// v7 = mul v0, Field 2
// enable_side_effects u1 1
// (no terminator instruction)
// }
//
// After constructing this IR, we run constant folding which should replace the second cast
// with a reference to the results to the first. This then allows us to optimize away
// the constrain instruction as both inputs are known to be equal.
//
// The first cast instruction is retained and will be removed in the dead instruction elimination pass.
let main_id = Id::test_new(0);

// Compiling main
let mut builder = FunctionBuilder::new("main".into(), main_id);
let v0 = builder.add_parameter(Type::field());

let two = builder.numeric_constant(2u128, Type::field());

let one = builder.numeric_constant(1u128, Type::bool());

builder.insert_enable_side_effects_if(one);
builder.insert_binary(v0, BinaryOp::Mul, two);
builder.insert_enable_side_effects_if(one);
builder.insert_binary(v0, BinaryOp::Mul, two);
builder.insert_enable_side_effects_if(one);
builder.insert_binary(v0, BinaryOp::Mul, two);
builder.insert_enable_side_effects_if(one);
builder.insert_binary(v0, BinaryOp::Mul, two);
builder.insert_enable_side_effects_if(one);

let ssa = builder.finish();

println!("{ssa}");

let main = ssa.main();
let instructions = main.dfg[main.entry_block()].instructions();
assert_eq!(instructions.len(), 9);

// Expected output:
//
// acir(inline) fn main f0 {
// b0(v0: Field):
// v3 = mul v0, Field 2
// v4 = mul v0, Field 2
// v5 = mul v0, Field 2
// v6 = mul v0, Field 2
// (no terminator instruction)
// }
let ssa = ssa.remove_enable_side_effects();

println!("{ssa}");

let main = ssa.main();
let instructions = main.dfg[main.entry_block()].instructions();

assert_eq!(instructions.len(), 4);
for instruction in instructions.iter().take(4) {
assert_eq!(&main.dfg[*instruction], &Instruction::binary(BinaryOp::Mul, v0, two));
}
}
}
2 changes: 2 additions & 0 deletions noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,8 @@ impl<'context> Elaborator<'context> {
self.elaborate_comptime_global(global_id);
}

self.interner.add_definition_location(ReferenceId::Global(global_id));

self.local_module = old_module;
self.file = old_file;
self.current_item = old_item;
Expand Down
15 changes: 12 additions & 3 deletions noir/noir-repo/compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,6 @@ impl<'context> Elaborator<'context> {
};

if let Some(unresolved_span) = typ.span {
let reference =
ReferenceId::Variable(Location::new(unresolved_span, self.file), is_self_type_name);

match resolved_type {
Type::Struct(ref struct_type, _) => {
// Record the location of the type reference
Expand All @@ -167,11 +164,19 @@ impl<'context> Elaborator<'context> {

if !is_synthetic {
let referenced = ReferenceId::Struct(struct_type.borrow().id);
let reference = ReferenceId::Variable(
Location::new(unresolved_span, self.file),
is_self_type_name,
);
self.interner.add_reference(referenced, reference);
}
}
Type::Alias(ref alias_type, _) => {
let referenced = ReferenceId::Alias(alias_type.borrow().id);
let reference = ReferenceId::Variable(
Location::new(unresolved_span, self.file),
is_self_type_name,
);
self.interner.add_reference(referenced, reference);
}
_ => (),
Expand Down Expand Up @@ -364,6 +369,10 @@ impl<'context> Elaborator<'context> {
self.interner.add_global_dependency(current_item, id);
}

let referenced = ReferenceId::Global(id);
let reference = ReferenceId::Variable(Location::new(path.span(), self.file), false);
self.interner.add_reference(referenced, reference);

Some(Type::Constant(self.eval_global_as_array_length(id, path)))
}
_ => None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,25 +511,18 @@ fn add_import_reference(
return;
}

match def_id {
crate::macros_api::ModuleDefId::FunctionId(func_id) => {
let variable = ReferenceId::Variable(Location::new(name.span(), file_id), false);
interner.add_reference(ReferenceId::Function(func_id), variable);
}
crate::macros_api::ModuleDefId::TypeId(struct_id) => {
let variable = ReferenceId::Variable(Location::new(name.span(), file_id), false);
interner.add_reference(ReferenceId::Struct(struct_id), variable);
}
crate::macros_api::ModuleDefId::TraitId(trait_id) => {
let variable = ReferenceId::Variable(Location::new(name.span(), file_id), false);
interner.add_reference(ReferenceId::Trait(trait_id), variable);
}
let referenced = match def_id {
crate::macros_api::ModuleDefId::ModuleId(module_id) => ReferenceId::Module(module_id),
crate::macros_api::ModuleDefId::FunctionId(func_id) => ReferenceId::Function(func_id),
crate::macros_api::ModuleDefId::TypeId(struct_id) => ReferenceId::Struct(struct_id),
crate::macros_api::ModuleDefId::TraitId(trait_id) => ReferenceId::Trait(trait_id),
crate::macros_api::ModuleDefId::TypeAliasId(type_alias_id) => {
let variable = ReferenceId::Variable(Location::new(name.span(), file_id), false);
interner.add_reference(ReferenceId::Alias(type_alias_id), variable);
ReferenceId::Alias(type_alias_id)
}
_ => (),
}
crate::macros_api::ModuleDefId::GlobalId(global_id) => ReferenceId::Global(global_id),
};
let reference = ReferenceId::Variable(Location::new(name.span(), file_id), false);
interner.add_reference(referenced, reference);
}

fn inject_prelude(
Expand Down
30 changes: 11 additions & 19 deletions noir/noir-repo/compiler/noirc_frontend/src/locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,26 +117,18 @@ impl NodeInterner {
let node_index = self.location_indices.get_node_from_location(location)?;

let reference_node = self.reference_graph[node_index];
let found_locations: Vec<Location> = match reference_node {
ReferenceId::Global(_) | ReferenceId::Module(_) => todo!(),
ReferenceId::Function(_)
| ReferenceId::Struct(_)
| ReferenceId::Trait(_)
| ReferenceId::Alias(_) => self.find_all_references_for_index(
node_index,
include_referenced,
include_self_type_name,
),

ReferenceId::Variable(_, _) => {
let referenced_node_index = self.referenced_index(node_index)?;
self.find_all_references_for_index(
referenced_node_index,
include_referenced,
include_self_type_name,
)
}
let referenced_node_index = if let ReferenceId::Variable(_, _) = reference_node {
self.referenced_index(node_index)?
} else {
node_index
};

let found_locations = self.find_all_references_for_index(
referenced_node_index,
include_referenced,
include_self_type_name,
);

Some(found_locations)
}

Expand Down
2 changes: 2 additions & 0 deletions noir/noir-repo/noir_stdlib/src/hash/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub fn blake3<let N: u32>(input: [u8; N]) -> [u8; 32]
// docs:end:blake3
{}

#[no_predicates]
// docs:start:pedersen_commitment
pub fn pedersen_commitment<let N: u32>(input: [Field; N]) -> EmbeddedCurvePoint {
// docs:end:pedersen_commitment
Expand All @@ -46,6 +47,7 @@ fn pedersen_commitment_with_separator_noir<let N: u32>(input: [Field; N], separa
EmbeddedCurvePoint { x: values[0], y: values[1], is_infinite: values[2] as bool }
}

#[no_predicates]
pub fn pedersen_commitment_with_separator<let N: u32>(input: [Field; N], separator: u32) -> EmbeddedCurvePoint {
let values = __pedersen_commitment_with_separator(input, separator);
EmbeddedCurvePoint { x: values[0], y: values[1], is_infinite: false }
Expand Down
Loading