Skip to content

Commit

Permalink
feat: Foreign calls compiling and basic print executed in nargo (#1612)
Browse files Browse the repository at this point in the history
* get foreign calls compiling and basic print executed in nargo

* cargo clipy and cargo fmt

* missing

* Update crates/noirc_evaluator/src/brillig/brillig_gen.rs

* add issue num for logging

---------

Co-authored-by: kevaundray <kevtheappdev@gmail.com>
  • Loading branch information
vezenovm and kevaundray authored Jun 8, 2023
1 parent 828c116 commit 6d83b22
Show file tree
Hide file tree
Showing 16 changed files with 127 additions and 31 deletions.
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) {}

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]),
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

0 comments on commit 6d83b22

Please sign in to comment.