Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: data bus #3508

Merged
merged 24 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 3 additions & 1 deletion compiler/noirc_evaluator/src/ssa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub fn create_circuit(
enable_brillig_logging: bool,
) -> Result<(Circuit, DebugInfo, Abi, Vec<SsaReport>), RuntimeError> {
let func_sig = program.main_function_signature.clone();
let return_visibility = program.return_visibility;
let mut generated_acir =
optimize_into_acir(program, enable_ssa_logging, enable_brillig_logging)?;
let opcodes = generated_acir.take_opcodes();
Expand All @@ -101,7 +102,8 @@ pub fn create_circuit(
..
} = generated_acir;

let abi = gen_abi(context, func_sig, input_witnesses, return_witnesses.clone());
let abi =
gen_abi(context, func_sig, input_witnesses, return_witnesses.clone(), return_visibility);
let public_abi = abi.clone().public_abi();

let public_parameters = PublicInputs(tree_to_set(&public_abi.param_witnesses));
Expand Down
5 changes: 4 additions & 1 deletion compiler/noirc_evaluator/src/ssa/abi_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use noirc_frontend::{
stmt::HirPattern,
},
node_interner::NodeInterner,
Visibility,
};
use std::ops::Range;

Expand Down Expand Up @@ -41,10 +42,12 @@ pub(crate) fn gen_abi(
func_sig: FunctionSignature,
input_witnesses: Vec<Range<Witness>>,
return_witnesses: Vec<Witness>,
return_visibility: Visibility,
) -> Abi {
let (parameters, return_type) = func_sig;
let parameters = into_abi_params(context, parameters);
let return_type = return_type.map(|typ| AbiType::from_type(context, &typ));
let return_type =
return_type.map(|typ| (AbiType::from_type(context, &typ), return_visibility.into()));
let param_witnesses = param_witnesses_from_abi_param(&parameters, input_witnesses);
Abi { parameters, return_type, param_witnesses, return_witnesses }
}
Expand Down
17 changes: 16 additions & 1 deletion compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fmt::Debug;
use std::ops::RangeInclusive;

use self::acir_ir::acir_variable::{AcirContext, AcirType, AcirVar};
use super::function_builder::data_bus::DataBus;
use super::ir::dfg::CallStack;
use super::{
ir::{
Expand Down Expand Up @@ -90,6 +91,8 @@ struct Context {
/// Maps SSA array values to their slice size and any nested slices internal to the parent slice.
/// This enables us to maintain the slice structure of a slice when performing an array get.
slice_sizes: HashMap<Id<Value>, Vec<usize>>,

data_bus: DataBus,
}

#[derive(Clone)]
Expand Down Expand Up @@ -187,6 +190,7 @@ impl Context {
internal_mem_block_lengths: HashMap::default(),
max_block_id: 0,
slice_sizes: HashMap::default(),
data_bus: DataBus::default(),
}
}

Expand Down Expand Up @@ -214,6 +218,8 @@ impl Context {
let dfg = &main_func.dfg;
let entry_block = &dfg[main_func.entry_block()];
let input_witness = self.convert_ssa_block_params(entry_block.parameters(), dfg)?;

self.data_bus = dfg.data_bus.to_owned();
let mut warnings = Vec::new();
for instruction_id in entry_block.instructions() {
warnings.extend(self.convert_ssa_instruction(
Expand Down Expand Up @@ -878,7 +884,16 @@ impl Context {
dfg: &DataFlowGraph,
) -> Result<AcirValue, RuntimeError> {
let (array_id, _, block_id) = self.check_array_is_initialized(array, dfg)?;

// Get operations to call-data parameters are replaced by a get to the call-data-bus array
if let Some(call_data) = self.data_bus.call_data {
if self.data_bus.call_data_map.contains_key(&array_id) {
let bus_index = self.acir_context.add_constant(FieldElement::from(
self.data_bus.call_data_map[&array_id] as i128,
));
let new_index = self.acir_context.add_var(var_index, bus_index)?;
return self.array_get(instruction, call_data, new_index, dfg);
}
}
let results = dfg.instruction_results(instruction);
let res_typ = dfg.type_of_value(results[0]);

Expand Down
127 changes: 127 additions & 0 deletions compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use std::rc::Rc;

use crate::ssa::ir::{types::Type, value::ValueId};
use acvm::FieldElement;
use fxhash::FxHashMap as HashMap;

use super::FunctionBuilder;

/// Used to create a data bus, which is an array of private inputs
/// replacing public inputs
pub(crate) struct DataBusBuilder {
pub(crate) values: im::Vector<ValueId>,
index: usize,
pub(crate) map: HashMap<ValueId, usize>,
pub(crate) databus: Option<ValueId>,
}

impl DataBusBuilder {
pub(crate) fn new() -> DataBusBuilder {
DataBusBuilder {
index: 0,
map: HashMap::default(),
databus: None,
values: im::Vector::new(),
}
}
}

#[derive(Clone, Default, Debug)]
pub(crate) struct DataBus {
pub(crate) call_data: Option<ValueId>,
pub(crate) call_data_map: HashMap<ValueId, usize>,
pub(crate) return_data: Option<ValueId>,
}

impl DataBus {
/// Updates the databus values with the provided function
pub(crate) fn map_values(&self, mut f: impl FnMut(ValueId) -> ValueId) -> DataBus {
let mut call_data_map = HashMap::default();
for (k, v) in self.call_data_map.iter() {
call_data_map.insert(f(*k), *v);
}
DataBus {
call_data: self.call_data.map(&mut f),
call_data_map,
return_data: self.return_data.map(&mut f),
}
}

/// Construct a databus from call_data and return_data data bus builders
pub(crate) fn get_data_bus(call_data: DataBusBuilder, return_data: DataBusBuilder) -> DataBus {
DataBus {
call_data: call_data.databus,
call_data_map: call_data.map,
return_data: return_data.databus,
}
}
}

impl FunctionBuilder {
/// Insert a value into a data bus builder
fn add_to_data_bus(&mut self, value: ValueId, databus: &mut DataBusBuilder) {
assert!(databus.databus.is_none(), "initialising finalized call data");
let typ = self.current_function.dfg[value].get_type().clone();
match typ {
Type::Numeric(_) => {
databus.values.push_back(value);
databus.index += 1;
}
Type::Reference => unreachable!(),
Type::Array(typ, len) => {
assert!(typ.len() == 1, "unsupported composite type");
databus.map.insert(value, databus.index);
for i in 0..len {
// load each element of the array
let index = self
.current_function
.dfg
.make_constant(FieldElement::from(i as i128), Type::field());
let element = self.insert_array_get(value, index, typ[0].clone());
self.add_to_data_bus(element, databus);
}
}
Type::Slice(_) => unreachable!(),
Type::Function => unreachable!(),
}
}

/// Create a data bus builder from a list of values
pub(crate) fn initialize_data_bus(
&mut self,
values: &[ValueId],
mut databus: DataBusBuilder,
) -> DataBusBuilder {
for value in values {
self.add_to_data_bus(*value, &mut databus);
}
let len = databus.values.len();

let array = if len > 0 {
let array =
self.array_constant(databus.values, Type::Array(Rc::new(vec![Type::field()]), len));
Some(array)
} else {
None
};

DataBusBuilder { index: 0, map: databus.map, databus: array, values: im::Vector::new() }
}

/// Generate the data bus for call-data, based on the parameters of the entry block
/// and a boolean vector telling which ones are call-data
pub(crate) fn call_data_bus(&mut self, is_params_databus: Vec<bool>) -> DataBusBuilder {
//filter parameters to first block that have call-data visilibity
let first_block = self.current_function.entry_block();
let params = self.current_function.dfg[first_block].parameters();
let mut databus_param = Vec::new();
for (param, is_databus) in params.iter().zip(is_params_databus) {
if is_databus {
databus_param.push(param.to_owned());
}
}
// create the call-data-bus from the filtered list
let call_data = DataBusBuilder::new();
self.initialize_data_bus(&databus_param, call_data)
}
}
2 changes: 2 additions & 0 deletions compiler/noirc_evaluator/src/ssa/function_builder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub(crate) mod data_bus;

use std::{borrow::Cow, rc::Rc};

use acvm::FieldElement;
Expand Down
4 changes: 3 additions & 1 deletion compiler/noirc_evaluator/src/ssa/ir/dfg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::borrow::Cow;

use crate::ssa::ir::instruction::SimplifyResult;
use crate::ssa::{function_builder::data_bus::DataBus, ir::instruction::SimplifyResult};

use super::{
basic_block::{BasicBlock, BasicBlockId},
Expand Down Expand Up @@ -80,6 +80,8 @@ pub(crate) struct DataFlowGraph {
/// Instructions inserted by internal SSA passes that don't correspond to user code
/// may not have a corresponding location.
locations: HashMap<InstructionId, CallStack>,

pub(crate) data_bus: DataBus,
}

pub(crate) type CallStack = im::Vector<Location>;
Expand Down
4 changes: 4 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/die.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ impl Ssa {
/// of its instructions are needed elsewhere.
fn dead_instruction_elimination(function: &mut Function) {
let mut context = Context::default();
if let Some(call_data) = function.dfg.data_bus.call_data {
context.mark_used_instruction_results(&function.dfg, call_data);
}

let blocks = PostOrder::with_function(function);

for block in blocks.as_slice() {
Expand Down
3 changes: 3 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/fill_internal_slices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ use fxhash::FxHashMap as HashMap;
impl Ssa {
pub(crate) fn fill_internal_slices(mut self) -> Ssa {
for function in self.functions.values_mut() {
let databus = function.dfg.data_bus.clone();
let mut context = Context::new(function);
context.process_blocks();
// update the databus with the new array instructions
function.dfg.data_bus = databus.map_values(|t| context.inserter.resolve(t));
}
self
}
Expand Down
6 changes: 5 additions & 1 deletion compiler/noirc_evaluator/src/ssa/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,15 @@ impl InlineContext {

context.blocks.insert(context.source_function.entry_block(), entry_block);
context.inline_blocks(ssa);
// translate databus values
let databus = entry_point.dfg.data_bus.map_values(|t| context.translate_value(t));

// Finally, we should have 1 function left representing the inlined version of the target function.
let mut new_ssa = self.builder.finish();
assert_eq!(new_ssa.functions.len(), 1);
new_ssa.functions.pop_first().unwrap().1
let mut new_func = new_ssa.functions.pop_first().unwrap().1;
new_func.dfg.data_bus = databus;
new_func
}

/// Inlines a function into the current function and returns the translated return values
Expand Down
6 changes: 6 additions & 0 deletions compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ impl Ssa {
let mut context = PerFunctionContext::new(function);
context.mem2reg();
context.remove_instructions();
context.update_data_bus();
}
self
}
Expand Down Expand Up @@ -362,6 +363,11 @@ impl<'f> PerFunctionContext<'f> {
}
}

fn update_data_bus(&mut self) {
let databus = self.inserter.function.dfg.data_bus.clone();
self.inserter.function.dfg.data_bus = databus.map_values(|t| self.inserter.resolve(t));
}

fn handle_terminator(&mut self, block: BasicBlockId, references: &mut Block) {
self.inserter.map_terminator_in_place(block);

Expand Down
Loading