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 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
6 changes: 5 additions & 1 deletion compiler/noirc_driver/src/abi_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use std::collections::BTreeMap;

use acvm::acir::native_types::Witness;
use iter_extended::{btree_map, vecmap};
use noirc_abi::{Abi, AbiParameter, AbiType};
use noirc_abi::{Abi, AbiParameter, AbiReturnType, AbiType};
use noirc_frontend::{
hir::Context,
hir_def::{function::Param, stmt::HirPattern},
node_interner::{FuncId, NodeInterner},
Visibility,
};
use std::ops::Range;

Expand All @@ -17,9 +18,12 @@ pub(super) fn gen_abi(
func_id: &FuncId,
input_witnesses: Vec<Witness>,
return_witnesses: Vec<Witness>,
return_visibility: Visibility,
) -> Abi {
let (parameters, return_type) = compute_function_abi(context, func_id);
let param_witnesses = param_witnesses_from_abi_param(&parameters, input_witnesses);
let return_type = return_type
.map(|typ| AbiReturnType { abi_type: typ, visibility: return_visibility.into() });
Abi { parameters, return_type, param_witnesses, return_witnesses }
}

Expand Down
5 changes: 3 additions & 2 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,12 @@ pub fn compile_no_check(
if !force_compile && hashes_match {
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)?;

let abi = abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses);
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);

Ok(CompiledProgram {
Expand Down
24 changes: 23 additions & 1 deletion compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::collections::HashSet;
use std::fmt::Debug;

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 @@ -199,6 +202,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 @@ -226,6 +230,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 @@ -892,10 +898,26 @@ impl Context {
dfg: &DataFlowGraph,
) -> Result<AcirValue, RuntimeError> {
let (array_id, _, block_id) = self.check_array_is_initialized(array, dfg)?;

let results = dfg.instruction_results(instruction);
let res_typ = dfg.type_of_value(results[0]);

// 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) {
// TODO: the block_id of call-data must be notified to the backend
// TODO: should we do the same for return-data?
let type_size = res_typ.flattened_size();
let type_size =
self.acir_context.add_constant(FieldElement::from(type_size as i128));
let offset = self.acir_context.mul_var(var_index, type_size)?;
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(offset, bus_index)?;
return self.array_get(instruction, call_data, new_index, dfg);
}
}

let value = if !res_typ.contains_slice_element() {
self.array_get_value(&res_typ, block_id, &mut var_index, &[])?
} else {
Expand Down
144 changes: 144 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,144 @@
use std::rc::Rc;

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

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(),
}
}

/// Generates a boolean vector telling which (ssa) parameter from the given function signature
/// are tagged with databus visibility
pub(crate) fn is_databus(main_signature: &FunctionSignature) -> Vec<bool> {
let mut params_is_databus = Vec::new();

for param in &main_signature.0 {
let is_databus = match param.2 {
noirc_frontend::Visibility::Public | noirc_frontend::Visibility::Private => false,
noirc_frontend::Visibility::DataBus => true,
};
let len = param.1.field_count() as usize;
params_is_databus.extend(vec![is_databus; len]);
}
params_is_databus
}
}

#[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 of the 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 @@ -67,8 +67,11 @@ impl Ssa {
// The pass is also currently only setup to handle a function with a single flattened block.
// For complex Brillig functions we can expect this pass to panic.
if function.runtime() == RuntimeType::Acir {
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));
guipublic marked this conversation as resolved.
Show resolved Hide resolved
}
}
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
Loading