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: Foreign calls compiling and basic print executed in nargo #1612

Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ result
*.pk
*.vk
**/Verifier.toml
**/target
40 changes: 36 additions & 4 deletions crates/nargo/src/ops/execute.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use acvm::pwg::{solve, PartialWitnessGeneratorStatus};
use acvm::acir::brillig_vm::ForeignCallResult;
use acvm::acir::circuit::Opcode;
use acvm::pwg::{solve, PartialWitnessGeneratorStatus, UnresolvedBrilligCall};
use acvm::PartialWitnessGenerator;
use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap, pwg::block::Blocks};

Expand All @@ -11,9 +13,39 @@ pub fn execute_circuit(
) -> Result<WitnessMap, NargoError> {
let mut blocks = Blocks::default();
let solver_status = solve(backend, &mut initial_witness, &mut blocks, circuit.opcodes)?;
if matches!(solver_status, PartialWitnessGeneratorStatus::RequiresOracleData { .. }) {
todo!("Add oracle support to nargo execute")
// TODO 1557

// TODO(#1615): Nargo only supports "oracle_print_impl" functions that print a singular value and nothing else
// expand this in a general logging refactor
if let PartialWitnessGeneratorStatus::RequiresOracleData {
unresolved_brillig_calls,
required_oracle_data,
unsolved_opcodes,
} = solver_status
{
if !required_oracle_data.is_empty() {
unreachable!("oracles are not supported by nargo execute")
}
for unresolved_brillig_call in unresolved_brillig_calls {
let UnresolvedBrilligCall { foreign_call_wait_info, mut brillig } =
unresolved_brillig_call;
let value = foreign_call_wait_info.inputs[0];

// Execute foreign call "oracle_print_impl"
println!("{:?}", value.to_field().to_hex());

// TODO(#1615): "oracle_print_impl" is just an identity func
brillig.foreign_call_results.push(ForeignCallResult { values: vec![value] });

let mut next_opcodes_for_solving = vec![Opcode::Brillig(brillig)];
next_opcodes_for_solving.extend_from_slice(&unsolved_opcodes[..]);

let solver_status =
solve(backend, &mut initial_witness, &mut blocks, next_opcodes_for_solving)?;
if matches!(solver_status, PartialWitnessGeneratorStatus::RequiresOracleData { .. }) {
todo!("Add multiple foreign call support to nargo execute")
// TODO 1557
}
}
}

Ok(initial_witness)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
x = "3"
x = "10"

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ fn main(x: Field) {


#[oracle(oracle_print_impl)]
unconstrained fn oracle_print(x : Field) -> Field {}
unconstrained fn oracle_print(_x : Field) {}
kevaundray marked this conversation as resolved.
Show resolved Hide resolved

unconstrained fn oracle_print_wrapper(x: Field) {
oracle_print(x);
Expand Down
20 changes: 18 additions & 2 deletions crates/noirc_evaluator/src/brillig/brillig_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ use crate::ssa_refactor::ir::{
};
use acvm::{
acir::brillig_vm::{
BinaryFieldOp, BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, Value as BrilligValue,
BinaryFieldOp, BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, RegisterValueOrArray,
Value as BrilligValue,
},
FieldElement,
};
use iter_extended::vecmap;
use std::collections::HashMap;

#[derive(Default)]
Expand Down Expand Up @@ -184,6 +186,21 @@ impl BrilligGen {
};
self.push_code(opcode);
}
Instruction::ForeignCall { func, arguments } => {
let result_ids = dfg.instruction_results(instruction_id);

let input_registers =
vecmap(arguments, |value_id| self.convert_ssa_value(*value_id, dfg));
let output_registers =
vecmap(result_ids, |value_id| self.convert_ssa_value(*value_id, dfg));

let opcode = BrilligOpcode::ForeignCall {
function: func.to_owned(),
destination: RegisterValueOrArray::RegisterIndex(output_registers[0]),
kevaundray marked this conversation as resolved.
Show resolved Hide resolved
input: RegisterValueOrArray::RegisterIndex(input_registers[0]),
};
self.push_code(opcode);
}
_ => todo!("ICE: Instruction not supported {instruction:?}"),
};
}
Expand Down Expand Up @@ -273,7 +290,6 @@ impl BrilligGen {
brillig.convert_ssa_function(func);

brillig.push_code(BrilligOpcode::Stop);

brillig.obj
}

Expand Down
2 changes: 1 addition & 1 deletion crates/noirc_evaluator/src/ssa/ssa_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ impl IrGenerator {
let function_node_id = self.context.get_or_create_opcode_node_id(opcode);
Ok(Value::Node(function_node_id))
}
Definition::Oracle(_) => unimplemented!("oracles not supported by deprecated SSA")
Definition::Oracle(_) => unimplemented!("oracles not supported by deprecated SSA"),
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ impl Context {

(vec![result_ids[0]], vec![result_acir_var])
}
_ => unreachable!("instruction cannot be converted to ACIR"),
};

// Map the results of the instructions to Acir variables
Expand Down Expand Up @@ -340,7 +341,9 @@ impl Context {
Value::NumericConstant { constant, .. } => self.acir_context.add_constant(*constant),
Value::Intrinsic(..) => todo!(),
Value::Function(..) => unreachable!("ICE: All functions should have been inlined"),
Value::ForeignFunction(_) => unimplemented!("Oracle calls directly in constrained functions are not yet available."),
Value::ForeignFunction(_) => unimplemented!(
"Oracle calls directly in constrained functions are not yet available."
),
Value::Instruction { .. } | Value::Param { .. } => {
unreachable!("ICE: Should have been in cache {value:?}")
}
Expand Down
4 changes: 2 additions & 2 deletions crates/noirc_evaluator/src/ssa_refactor/ir/dfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ impl DataFlowGraph {
}

/// Gets or creates a ValueId for the given FunctionId.
pub(crate) fn import_foreign_function(&mut self, function: &String) -> ValueId {
self.values.insert(Value::ForeignFunction(function.clone()))
pub(crate) fn import_foreign_function(&mut self, function: &str) -> ValueId {
self.values.insert(Value::ForeignFunction(function.to_owned()))
}

/// Gets or creates a ValueId for the given Intrinsic.
Expand Down
23 changes: 12 additions & 11 deletions crates/noirc_evaluator/src/ssa_refactor/ir/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,10 @@ pub(crate) enum Instruction {
/// Performs a function call with a list of its arguments.
Call { func: ValueId, arguments: Vec<ValueId> },

// TODO NEEDED?
// /// Performs an "oracle" - an external function call with a list of its arguments.
// /// These are generally unconstrained functions that provide some value lookup.
// /// They may result in constraints outside of the context known to Noir.
// ForeignCall { func: String, arguments: Vec<ValueId> },
/// Performs an "oracle" - an external function call with a list of its arguments.
/// These are generally unconstrained functions that provide some value lookup.
/// They may result in constraints outside of the context known to Noir.
ForeignCall { func: String, arguments: Vec<ValueId> },

/// Allocates a region of memory. Note that this is not concerned with
/// the type of memory, the type of element is determined when loading this memory.
Expand Down Expand Up @@ -129,7 +128,9 @@ impl Instruction {
InstructionResultType::Operand(*value)
}
Instruction::Constrain(_) | Instruction::Store { .. } => InstructionResultType::None,
Instruction::Load { .. } | Instruction::Call { .. } /*| Instruction::ForeignCall { .. }*/ => InstructionResultType::Unknown,
Instruction::Load { .. }
| Instruction::Call { .. }
| Instruction::ForeignCall { .. } => InstructionResultType::Unknown,
}
}

Expand Down Expand Up @@ -157,10 +158,10 @@ impl Instruction {
max_bit_size: *max_bit_size,
},
Instruction::Constrain(value) => Instruction::Constrain(f(*value)),
// Instruction::ForeignCall { func, arguments } => Instruction::ForeignCall {
// func: *func,
// arguments: vecmap(arguments.iter().copied(), f),
// },
Instruction::ForeignCall { func, arguments } => Instruction::ForeignCall {
func: func.to_owned(),
arguments: vecmap(arguments.iter().copied(), f),
},
Instruction::Call { func, arguments } => Instruction::Call {
func: f(*func),
arguments: vecmap(arguments.iter().copied(), f),
Expand Down Expand Up @@ -214,7 +215,7 @@ impl Instruction {
}
Instruction::Truncate { .. } => None,
Instruction::Call { .. } => None,
// Instruction::ForeignCall { .. } => None,
Instruction::ForeignCall { .. } => None,
Instruction::Allocate { .. } => None,
Instruction::Load { .. } => None,
Instruction::Store { .. } => None,
Expand Down
3 changes: 3 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ir/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ pub(crate) fn display_instruction(
Instruction::Call { func, arguments } => {
writeln!(f, "call {}({})", show(*func), value_list(function, arguments))
}
Instruction::ForeignCall { func, arguments } => {
writeln!(f, "call {}({})", func, value_list(function, arguments))
}
Instruction::Allocate { size } => writeln!(f, "alloc {size} fields"),
Instruction::Load { address } => writeln!(f, "load {}", show(*address)),
Instruction::Store { address, value } => {
Expand Down
4 changes: 3 additions & 1 deletion crates/noirc_evaluator/src/ssa_refactor/opt/inlining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ impl<'function> PerFunctionContext<'function> {
}
Value::Function(function) => self.context.builder.import_function(*function),
Value::Intrinsic(intrinsic) => self.context.builder.import_intrinsic_id(*intrinsic),
Value::ForeignFunction(function) => self.context.builder.import_foreign_function(function),
Value::ForeignFunction(function) => {
self.context.builder.import_foreign_function(function)
}
};

self.values.insert(id, new_value);
Expand Down
16 changes: 14 additions & 2 deletions crates/noirc_evaluator/src/ssa_refactor/ssa_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ impl FunctionBuilder {
self.insert_instruction(Instruction::Constrain(boolean), None);
}

/// Insert a call instruction a the end of the current block and return
/// Insert a call instruction at the end of the current block and return
/// the results of the call.
pub(crate) fn insert_call(
&mut self,
Expand All @@ -222,6 +222,18 @@ impl FunctionBuilder {
self.insert_instruction(Instruction::Call { func, arguments }, Some(result_types)).results()
}

/// Insert a foreign call instruction at the end of the current block and return
/// the results of the call.
pub(crate) fn insert_foreign_call(
&mut self,
func: String,
arguments: Vec<ValueId>,
result_types: Vec<Type>,
) -> &[ValueId] {
self.insert_instruction(Instruction::ForeignCall { func, arguments }, Some(result_types))
.results()
}

/// Terminates the current block with the given terminator instruction
fn terminate_block_with(&mut self, terminator: TerminatorInstruction) {
self.current_function.dfg.set_block_terminator(self.current_block, terminator);
Expand Down Expand Up @@ -265,7 +277,7 @@ impl FunctionBuilder {

/// Returns a ValueId pointing to the given oracle/foreign function or imports the oracle
/// into the current function if it was not already, and returns that ID.
pub(crate) fn import_foreign_function(&mut self, function: &String) -> ValueId {
pub(crate) fn import_foreign_function(&mut self, function: &str) -> ValueId {
self.current_function.dfg.import_foreign_function(function)
}

Expand Down
19 changes: 19 additions & 0 deletions crates/noirc_evaluator/src/ssa_refactor/ssa_gen/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,25 @@ impl<'a> FunctionContext<'a> {
reshaped_return_values
}

pub(super) fn insert_foreign_call(
&mut self,
function: String,
arguments: Vec<ValueId>,
result_type: &ast::Type,
) -> Values {
let result_types = Self::convert_type(result_type).flatten();
let results = self.builder.insert_foreign_call(function, arguments, result_types);

let mut i = 0;
let reshaped_return_values = Self::map_type(result_type, |_| {
let result = results[i].into();
i += 1;
result
});
assert_eq!(i, results.len());
reshaped_return_values
}

/// Create a const offset of an address for an array load or store
pub(super) fn make_offset(&mut self, mut address: ValueId, offset: u128) -> ValueId {
if offset != 0 {
Expand Down
9 changes: 7 additions & 2 deletions crates/noirc_evaluator/src/ssa_refactor/ssa_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,14 +345,19 @@ impl<'a> FunctionContext<'a> {
/// Generate SSA for a function call. Note that calls to built-in functions
/// and intrinsics are also represented by the function call instruction.
fn codegen_call(&mut self, call: &ast::Call) -> Values {
let function = self.codegen_non_tuple_expression(&call.func);

let arguments = call
.arguments
.iter()
.flat_map(|argument| self.codegen_expression(argument).into_value_list(self))
.collect();

if let ast::Expression::Ident(ident) = call.func.as_ref() {
if let ast::Definition::Oracle(func) = &ident.definition {
return self.insert_foreign_call(func.to_owned(), arguments, &call.return_type);
}
}

let function = self.codegen_non_tuple_expression(&call.func);
self.insert_call(function, arguments, &call.return_type)
}

Expand Down
5 changes: 3 additions & 2 deletions crates/noirc_frontend/src/hir/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,10 @@ impl<'a> Resolver<'a> {

fn intern_function(&mut self, func: NoirFunction, id: FuncId) -> (HirFunction, FuncMeta) {
let func_meta = self.extract_meta(&func, id);

let hir_func = match func.kind {
FunctionKind::Builtin | FunctionKind::LowLevel | FunctionKind::Oracle => HirFunction::empty(),
FunctionKind::Builtin | FunctionKind::LowLevel | FunctionKind::Oracle => {
HirFunction::empty()
}
FunctionKind::Normal => {
let expr_id = self.intern_block(func.def.body);
self.interner.push_expr_location(expr_id, func.def.span, self.file);
Expand Down
3 changes: 2 additions & 1 deletion crates/noirc_frontend/src/monomorphization/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use crate::{
stmt::{HirAssignStatement, HirLValue, HirLetStatement, HirPattern, HirStatement},
},
node_interner::{self, DefinitionKind, NodeInterner, StmtId},
CompTime, FunctionKind, TypeBinding, TypeBindings, token::Attribute,
token::Attribute,
CompTime, FunctionKind, TypeBinding, TypeBindings,
};

use self::ast::{Definition, FuncId, Function, LocalId, Program};
Expand Down