Skip to content
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
16 changes: 15 additions & 1 deletion .github/workflows/test-js-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ jobs:
run: yarn workspace @noir-lang/noir_wasm test:browser

test-noir-codegen:
needs: [build-acvm-js, build-noirc-abi]
needs: [build-acvm-js, build-noirc-abi, build-nargo]
name: noir_codegen
runs-on: ubuntu-latest
timeout-minutes: 30
Expand All @@ -328,6 +328,12 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Download nargo binary
uses: actions/download-artifact@v3
with:
name: nargo
path: ./nargo

- name: Download acvm_js package artifact
uses: actions/download-artifact@v3
with:
Expand All @@ -339,6 +345,14 @@ jobs:
with:
name: noirc_abi_wasm
path: ./tooling/noirc_abi_wasm

- name: Set nargo on PATH
run: |
nargo_binary="${{ github.workspace }}/nargo/nargo"
chmod +x $nargo_binary
echo "$(dirname $nargo_binary)" >> $GITHUB_PATH
export PATH="$PATH:$(dirname $nargo_binary)"
nargo -V

- name: Install Yarn dependencies
uses: ./.github/actions/setup
Expand Down
23 changes: 23 additions & 0 deletions compiler/noirc_frontend/src/hir/def_map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,29 @@ impl CrateDefMap {
})
})
}

/// Go through all modules in this crate, and find all functions in
/// each module with the #[export] attribute
pub fn get_all_exported_functions<'a>(
&'a self,
interner: &'a NodeInterner,
) -> impl Iterator<Item = FuncId> + 'a {
self.modules.iter().flat_map(|(_, module)| {
module.value_definitions().filter_map(|id| {
if let Some(func_id) = id.as_function() {
let attributes = interner.function_attributes(&func_id);
if attributes.secondary.contains(&SecondaryAttribute::Export) {
Some(func_id)
} else {
None
}
} else {
None
}
})
})
}

/// Go through all modules in this crate, find all `contract ... { ... }` declarations,
/// and collect them all into a Vec.
pub fn get_all_contracts(&self, interner: &NodeInterner) -> Vec<Contract> {
Expand Down
13 changes: 13 additions & 0 deletions compiler/noirc_frontend/src/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,19 @@ impl Context<'_> {
.collect()
}

pub fn get_all_exported_functions_in_crate(&self, crate_id: &CrateId) -> Vec<(String, FuncId)> {
let interner = &self.def_interner;
let def_map = self.def_map(crate_id).expect("The local crate should be analyzed already");

def_map
.get_all_exported_functions(interner)
.map(|function_id| {
let function_name = self.function_name(&function_id).to_owned();
(function_name, function_id)
})
.collect()
}

/// Return a Vec of all `contract` declarations in the source code and the functions they contain
pub fn get_all_contracts(&self, crate_id: &CrateId) -> Vec<Contract> {
self.def_map(crate_id)
Expand Down
5 changes: 4 additions & 1 deletion compiler/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ impl Attribute {
Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod)
}
["event"] => Attribute::Secondary(SecondaryAttribute::Event),
["export"] => Attribute::Secondary(SecondaryAttribute::Export),
["deprecated", name] => {
if !name.starts_with('"') && !name.ends_with('"') {
return Err(LexerErrorKind::MalformedFuncAttribute {
Expand Down Expand Up @@ -588,6 +589,7 @@ pub enum SecondaryAttribute {
// the entry point.
ContractLibraryMethod,
Event,
Export,
Field(String),
Custom(String),
}
Expand All @@ -602,6 +604,7 @@ impl fmt::Display for SecondaryAttribute {
SecondaryAttribute::Custom(ref k) => write!(f, "#[{k}]"),
SecondaryAttribute::ContractLibraryMethod => write!(f, "#[contract_library_method]"),
SecondaryAttribute::Event => write!(f, "#[event]"),
SecondaryAttribute::Export => write!(f, "#[export]"),
SecondaryAttribute::Field(ref k) => write!(f, "#[field({k})]"),
}
}
Expand All @@ -625,7 +628,7 @@ impl AsRef<str> for SecondaryAttribute {
SecondaryAttribute::Deprecated(None) => "",
SecondaryAttribute::Custom(string) | SecondaryAttribute::Field(string) => string,
SecondaryAttribute::ContractLibraryMethod => "",
SecondaryAttribute::Event => "",
SecondaryAttribute::Event | SecondaryAttribute::Export => "",
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"bincode",
"bindgen",
"bitand",
"bitxor",
"bitor",
"bitxor",
"blackbox",
"bridgekeeper",
"brillig",
Expand All @@ -29,6 +29,7 @@
"chumsky",
"clippy",
"codegen",
"codegenned",
"codegens",
"Codespaces",
"codespan",
Expand Down
2 changes: 2 additions & 0 deletions tooling/nargo/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub const PROOFS_DIR: &str = "proofs";
pub const SRC_DIR: &str = "src";
/// The directory to store circuits' serialized ACIR representations.
pub const TARGET_DIR: &str = "target";
/// The directory to store serialized ACIR representations of exported library functions.
pub const EXPORT_DIR: &str = "export";

// Files
/// The file from which Nargo pulls prover inputs
Expand Down
6 changes: 5 additions & 1 deletion tooling/nargo/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{
};

use crate::{
constants::{CONTRACT_DIR, PROOFS_DIR, TARGET_DIR},
constants::{CONTRACT_DIR, EXPORT_DIR, PROOFS_DIR, TARGET_DIR},
package::Package,
};

Expand Down Expand Up @@ -40,6 +40,10 @@ impl Workspace {
pub fn target_directory_path(&self) -> PathBuf {
self.root_dir.join(TARGET_DIR)
}

pub fn export_directory_path(&self) -> PathBuf {
self.root_dir.join(EXPORT_DIR)
}
}

pub enum IntoIter<'a, T> {
Expand Down
2 changes: 1 addition & 1 deletion tooling/nargo_cli/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ fn compile_contract(
Ok((optimized_contract, warnings))
}

fn save_program(
pub(super) fn save_program(
program: CompiledProgram,
package: &Package,
circuit_dir: &Path,
Expand Down
120 changes: 120 additions & 0 deletions tooling/nargo_cli/src/cli/export_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
use nargo::errors::CompileError;
use noirc_errors::FileDiagnostic;
use rayon::prelude::*;

use fm::FileManager;
use iter_extended::try_vecmap;
use nargo::insert_all_files_for_workspace_into_file_manager;
use nargo::package::Package;
use nargo::prepare_package;
use nargo::workspace::Workspace;
use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection};
use noirc_driver::{
compile_no_check, file_manager_with_stdlib, CompileOptions, CompiledProgram,
NOIR_ARTIFACT_VERSION_STRING,
};

use noirc_frontend::graph::CrateName;

use clap::Args;

use crate::backends::Backend;
use crate::errors::CliError;

use super::check_cmd::check_crate_and_report_errors;

use super::compile_cmd::report_errors;
use super::fs::program::save_program_to_file;
use super::NargoConfig;

/// Exports functions marked with #[export] attribute
#[derive(Debug, Clone, Args)]
pub(crate) struct ExportCommand {
/// The name of the package to compile
#[clap(long, conflicts_with = "workspace")]
package: Option<CrateName>,

/// Compile all packages in the workspace
#[clap(long, conflicts_with = "package")]
workspace: bool,

#[clap(flatten)]
compile_options: CompileOptions,
}

pub(crate) fn run(
_backend: &Backend,
args: ExportCommand,
config: NargoConfig,
) -> Result<(), CliError> {
let toml_path = get_package_manifest(&config.program_dir)?;
let default_selection =
if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll };
let selection = args.package.map_or(default_selection, PackageSelection::Selected);

let workspace = resolve_workspace_from_toml(
&toml_path,
selection,
Some(NOIR_ARTIFACT_VERSION_STRING.to_owned()),
)?;

let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir);
insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager);

let library_packages: Vec<_> =
workspace.into_iter().filter(|package| package.is_library()).collect();

library_packages
.par_iter()
.map(|package| {
compile_exported_functions(
&workspace_file_manager,
&workspace,
package,
&args.compile_options,
)
})
.collect()
}

fn compile_exported_functions(
file_manager: &FileManager,
workspace: &Workspace,
package: &Package,
compile_options: &CompileOptions,
) -> Result<(), CliError> {
let (mut context, crate_id) = prepare_package(file_manager, package);
check_crate_and_report_errors(
&mut context,
crate_id,
compile_options.deny_warnings,
compile_options.disable_macros,
compile_options.silence_warnings,
)?;

let exported_functions = context.get_all_exported_functions_in_crate(&crate_id);

let exported_programs = try_vecmap(
exported_functions,
|(function_name, function_id)| -> Result<(String, CompiledProgram), CompileError> {
// TODO: We should to refactor how to deal with compilation errors to avoid this.
let program = compile_no_check(&context, compile_options, function_id, None, false)
.map_err(|error| vec![FileDiagnostic::from(error)]);

let program = report_errors(
program.map(|program| (program, Vec::new())),
file_manager,
compile_options.deny_warnings,
compile_options.silence_warnings,
)?;

Ok((function_name, program))
},
)?;

let export_dir = workspace.export_directory_path();
for (function_name, program) in exported_programs {
save_program_to_file(&program.into(), &function_name.parse().unwrap(), &export_dir);
}
Ok(())
}
4 changes: 4 additions & 0 deletions tooling/nargo_cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod compile_cmd;
mod dap_cmd;
mod debug_cmd;
mod execute_cmd;
mod export_cmd;
mod fmt_cmd;
mod info_cmd;
mod init_cmd;
Expand Down Expand Up @@ -69,6 +70,8 @@ enum NargoCommand {
Init(init_cmd::InitCommand),
Execute(execute_cmd::ExecuteCommand),
#[command(hide = true)] // Hidden while the feature is being built out
Export(export_cmd::ExportCommand),
#[command(hide = true)] // Hidden while the feature is being built out
Debug(debug_cmd::DebugCommand),
Prove(prove_cmd::ProveCommand),
Verify(verify_cmd::VerifyCommand),
Expand Down Expand Up @@ -109,6 +112,7 @@ pub(crate) fn start_cli() -> eyre::Result<()> {
NargoCommand::Compile(args) => compile_cmd::run(&backend, args, config),
NargoCommand::Debug(args) => debug_cmd::run(&backend, args, config),
NargoCommand::Execute(args) => execute_cmd::run(&backend, args, config),
NargoCommand::Export(args) => export_cmd::run(&backend, args, config),
NargoCommand::Prove(args) => prove_cmd::run(&backend, args, config),
NargoCommand::Verify(args) => verify_cmd::run(&backend, args, config),
NargoCommand::Test(args) => test_cmd::run(&backend, args, config),
Expand Down
1 change: 0 additions & 1 deletion tooling/noir_codegen/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
crs
lib

!test/*/target
test/codegen
4 changes: 2 additions & 2 deletions tooling/noir_codegen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@
"dev": "tsc-multi --watch",
"build": "tsc",
"test": "yarn test:codegen && yarn test:node && yarn test:clean",
"test:codegen": "tsx src/main.ts ./test/assert_lt/target/** --out-dir ./test/codegen",
"test:codegen": "nargo export --program-dir=./test/test_lib && tsx src/main.ts ./test/test_lib/export/** --out-dir ./test/codegen",
"test:node": "mocha --timeout 25000 --exit --config ./.mocharc.json",
"test:clean": "rm -rf ./test/codegen",
"test:clean": "rm -rf ./test/codegen ./test/test_lib/export",
"prettier": "prettier 'src/**/*.ts'",
"prettier:fix": "prettier --write 'src/**/*.ts' 'test/**/*.ts'",
"lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0",
Expand Down
1 change: 0 additions & 1 deletion tooling/noir_codegen/test/assert_lt/target/assert_lt.json

This file was deleted.

8 changes: 5 additions & 3 deletions tooling/noir_codegen/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { expect } from 'chai';
import { assert_lt, MyStruct, u64, ForeignCallHandler } from './codegen/index.js';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore File is codegenned at test time.
import { exported_function_foo, MyStruct, u64, ForeignCallHandler } from './codegen/index.js';

it('codegens a callable function', async () => {
const my_struct = { foo: true, bar: ['12345', '12345', '12345'] };

const [sum, constant, struct]: [u64, u64, MyStruct] = await assert_lt(
const [sum, constant, struct]: [u64, u64, MyStruct] = await exported_function_foo(
'2',
'3',
[0, 0, 0, 0, 0],
Expand Down Expand Up @@ -35,7 +37,7 @@ it('allows passing a custom foreign call handler', async () => {

const my_struct = { foo: true, bar: ['12345', '12345', '12345'] };

const [sum, constant, struct]: [u64, u64, MyStruct] = await assert_lt(
const [sum, constant, struct]: [u64, u64, MyStruct] = await exported_function_foo(
'2',
'3',
[0, 0, 0, 0, 0],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "assert_lt"
type = "bin"
name = "test_lib"
type = "lib"
authors = [""]
[dependencies]
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ struct NestedStruct {
baz: u64
}

fn main(
x: u64,
y: pub u64,
array: [u8; 5],
my_struct: NestedStruct,
string: str<5>
) -> pub (u64, u64, MyStruct) {
#[export]
fn exported_function_foo(x: u64, y: u64, array: [u8; 5], my_struct: NestedStruct, string: str<5>) -> (u64, u64, MyStruct) {
assert(array.len() == 5);
assert(my_struct.foo.foo);
assert(string == "12345");
Expand All @@ -24,3 +19,8 @@ fn main(
assert(x < y);
(x + y, 3, my_struct.foo)
}

#[export]
fn exported_function_bar(my_struct: NestedStruct) -> (u64) {
my_struct.baz
}