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

chore: Embed a file map into CompiledProgram/CompiledContract #2666

Merged
merged 5 commits into from
Sep 12, 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
2 changes: 1 addition & 1 deletion compiler/noirc_driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ noirc_abi.workspace = true
acvm.workspace = true
fm.workspace = true
serde.workspace = true
base64.workspace = true
base64.workspace = true
11 changes: 9 additions & 2 deletions compiler/noirc_driver/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use crate::program::{deserialize_circuit, serialize_circuit};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

use acvm::acir::circuit::Circuit;
use fm::FileId;
use noirc_abi::Abi;
use noirc_errors::debug_info::DebugInfo;
use serde::{Deserialize, Serialize};

use super::debug::DebugFile;
use crate::program::{deserialize_circuit, serialize_circuit};

/// Describes the types of smart contract functions that are allowed.
/// Unlike the similar enum in noirc_frontend, 'open' and 'unconstrained'
Expand All @@ -28,6 +33,8 @@ pub struct CompiledContract {
/// Each of the contract's functions are compiled into a separate `CompiledProgram`
/// stored in this `Vector`.
pub functions: Vec<ContractFunction>,

pub file_map: BTreeMap<FileId, DebugFile>,
}

/// Each function in the contract will be compiled
Expand Down
45 changes: 45 additions & 0 deletions compiler/noirc_driver/src/debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use fm::{FileId, FileManager};
use noirc_errors::debug_info::DebugInfo;
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, BTreeSet},
path::PathBuf,
};

/// For a given file, we store the source code and the path to the file
/// so consumers of the debug artifact can reconstruct the original source code structure.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DebugFile {
pub source: String,
pub path: PathBuf,
}

pub(crate) fn filter_relevant_files(
debug_symbols: &[DebugInfo],
file_manager: &FileManager,
) -> BTreeMap<FileId, DebugFile> {
let files_with_debug_symbols: BTreeSet<FileId> = debug_symbols
.iter()
.flat_map(|function_symbols| {
function_symbols
.locations
.values()
.filter_map(|call_stack| call_stack.last().map(|location| location.file))
})
.collect();

let mut file_map = BTreeMap::new();

for file_id in files_with_debug_symbols {
let file_source = file_manager.fetch_file(file_id).source();

file_map.insert(
file_id,
DebugFile {
source: file_source.to_string(),
path: file_manager.path(file_id).to_path_buf(),
},
);
}
file_map
}
12 changes: 10 additions & 2 deletions compiler/noirc_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#![warn(clippy::semicolon_if_nothing_returned)]

use clap::Args;
use debug::filter_relevant_files;
use fm::FileId;
use noirc_abi::{AbiParameter, AbiType};
use noirc_errors::{CustomDiagnostic, FileDiagnostic};
Expand All @@ -17,9 +18,11 @@
use std::path::Path;

mod contract;
mod debug;
mod program;

pub use contract::{CompiledContract, ContractFunction, ContractFunctionType};
pub use debug::DebugFile;
pub use program::CompiledProgram;

const STD_CRATE_NAME: &str = "std";
Expand Down Expand Up @@ -158,7 +161,7 @@
let compiled_program = compile_no_check(context, options, main)?;

if options.print_acir {
println!("Compiled ACIR for main (unoptimized):");

Check warning on line 164 in compiler/noirc_driver/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (unoptimized)
println!("{}", compiled_program.circuit);
}

Expand Down Expand Up @@ -192,7 +195,7 @@
for compiled_contract in &compiled_contracts {
for contract_function in &compiled_contract.functions {
println!(
"Compiled ACIR for {}::{} (unoptimized):",

Check warning on line 198 in compiler/noirc_driver/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (unoptimized)
compiled_contract.name, contract_function.name
);
println!("{}", contract_function.bytecode);
Expand Down Expand Up @@ -248,7 +251,10 @@
}

if errors.is_empty() {
Ok(CompiledContract { name: contract.name, functions })
let debug_infos: Vec<_> = functions.iter().map(|function| function.debug.clone()).collect();
let file_map = filter_relevant_files(&debug_infos, &context.file_manager);

Ok(CompiledContract { name: contract.name, functions, file_map })
} else {
Err(errors)
}
Expand All @@ -269,5 +275,7 @@
let (circuit, debug, abi) =
create_circuit(context, program, options.show_ssa, options.show_brillig)?;

Ok(CompiledProgram { circuit, debug, abi })
let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager);

Ok(CompiledProgram { circuit, debug, abi, file_map })
}
6 changes: 6 additions & 0 deletions compiler/noirc_driver/src/program.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
use std::collections::BTreeMap;

use acvm::acir::circuit::Circuit;
use fm::FileId;

use base64::Engine;
use noirc_errors::debug_info::DebugInfo;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use super::debug::DebugFile;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CompiledProgram {
#[serde(serialize_with = "serialize_circuit", deserialize_with = "deserialize_circuit")]
pub circuit: Circuit,
pub abi: noirc_abi::Abi,
pub debug: DebugInfo,
pub file_map: BTreeMap<FileId, DebugFile>,
}

pub(crate) fn serialize_circuit<S>(circuit: &Circuit, s: S) -> Result<S::Ok, S::Error>
Expand Down
10 changes: 1 addition & 9 deletions tooling/nargo/src/artifacts/debug.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
use codespan_reporting::files::{Error, Files, SimpleFile};
use noirc_driver::DebugFile;
use noirc_errors::debug_info::DebugInfo;
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, BTreeSet},
ops::Range,
path::PathBuf,
};

use fm::{FileId, FileManager, PathString};

/// For a given file, we store the source code and the path to the file
/// so consumers of the debug artifact can reconstruct the original source code structure.
#[derive(Debug, Serialize, Deserialize)]
pub struct DebugFile {
pub source: String,
pub path: PathBuf,
}

/// A Debug Artifact stores, for a given program, the debug info for every function
/// along with a map of file Id to the source code so locations in debug info can be mapped to source code they point to.
#[derive(Debug, Serialize, Deserialize)]
Expand Down
2 changes: 1 addition & 1 deletion tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ fn smart_contract_for_package(
let preprocessed_program = if circuit_build_path.exists() {
read_program_from_file(circuit_build_path)?
} else {
let (program, _) =
let program =
compile_bin_package(package, compile_options, np_language, &is_opcode_supported)?;

PreprocessedProgram {
Expand Down
79 changes: 36 additions & 43 deletions tooling/nargo_cli/src/cli/compile_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,46 +72,40 @@ pub(crate) fn run(
.partition(|package| package.is_binary());

// Compile all of the packages in parallel.
let program_results: Vec<(FileManager, CompilationResult<(CompiledProgram, DebugArtifact)>)> =
binary_packages
.par_iter()
.map(|package| {
compile_program(package, &args.compile_options, np_language, &is_opcode_supported)
})
.collect();
#[allow(clippy::type_complexity)]
let contract_results: Vec<(
FileManager,
CompilationResult<Vec<(CompiledContract, DebugArtifact)>>,
)> = contract_packages
let program_results: Vec<(FileManager, CompilationResult<CompiledProgram>)> = binary_packages
.par_iter()
.map(|package| {
compile_contracts(package, &args.compile_options, np_language, &is_opcode_supported)
compile_program(package, &args.compile_options, np_language, &is_opcode_supported)
})
.collect();
let contract_results: Vec<(FileManager, CompilationResult<Vec<CompiledContract>>)> =
contract_packages
.par_iter()
.map(|package| {
compile_contracts(package, &args.compile_options, np_language, &is_opcode_supported)
})
.collect();

// Report any warnings/errors which were encountered during compilation.
let compiled_programs: Vec<(CompiledProgram, DebugArtifact)> = program_results
let compiled_programs: Vec<CompiledProgram> = program_results
.into_iter()
.map(|(file_manager, compilation_result)| {
report_errors(compilation_result, &file_manager, args.compile_options.deny_warnings)
})
.collect::<Result<_, _>>()?;
let compiled_contracts: Vec<Vec<(CompiledContract, DebugArtifact)>> = contract_results
let compiled_contracts: Vec<Vec<CompiledContract>> = contract_results
.into_iter()
.map(|(file_manager, compilation_result)| {
report_errors(compilation_result, &file_manager, args.compile_options.deny_warnings)
})
.collect::<Result<_, _>>()?;

// Save build artifacts to disk.
for (package, (program, debug_artifact)) in binary_packages.into_iter().zip(compiled_programs) {
save_program(debug_artifact, program, package, &circuit_dir, args.output_debug);
for (package, program) in binary_packages.into_iter().zip(compiled_programs) {
save_program(program, package, &circuit_dir, args.output_debug);
}
for (package, contracts_with_debug_artifacts) in
contract_packages.into_iter().zip(compiled_contracts)
{
save_contracts(contracts_with_debug_artifacts, package, &circuit_dir, args.output_debug);
for (package, compiled_contracts) in contract_packages.into_iter().zip(compiled_contracts) {
save_contracts(compiled_contracts, package, &circuit_dir, args.output_debug);
}

Ok(())
Expand All @@ -122,26 +116,25 @@ pub(crate) fn compile_bin_package(
compile_options: &CompileOptions,
np_language: Language,
is_opcode_supported: &impl Fn(&Opcode) -> bool,
) -> Result<(CompiledProgram, DebugArtifact), CliError> {
) -> Result<CompiledProgram, CliError> {
if package.is_library() {
return Err(CompileError::LibraryCrate(package.name.clone()).into());
}

let (file_manager, compilation_result) =
compile_program(package, compile_options, np_language, &is_opcode_supported);

let (program, debug_artifact) =
report_errors(compilation_result, &file_manager, compile_options.deny_warnings)?;
let program = report_errors(compilation_result, &file_manager, compile_options.deny_warnings)?;

Ok((program, debug_artifact))
Ok(program)
}

pub(crate) fn compile_contract_package(
package: &Package,
compile_options: &CompileOptions,
np_language: Language,
is_opcode_supported: &impl Fn(&Opcode) -> bool,
) -> Result<Vec<(CompiledContract, DebugArtifact)>, CliError> {
) -> Result<Vec<CompiledContract>, CliError> {
let (file_manager, compilation_result) =
compile_contracts(package, compile_options, np_language, &is_opcode_supported);
let contracts_with_debug_artifacts =
Expand All @@ -154,7 +147,7 @@ fn compile_program(
compile_options: &CompileOptions,
np_language: Language,
is_opcode_supported: &impl Fn(&Opcode) -> bool,
) -> (FileManager, CompilationResult<(CompiledProgram, DebugArtifact)>) {
) -> (FileManager, CompilationResult<CompiledProgram>) {
let (mut context, crate_id) = prepare_package(package);

let (program, warnings) =
Expand All @@ -170,18 +163,15 @@ fn compile_program(
nargo::ops::optimize_program(program, np_language, &is_opcode_supported)
.expect("Backend does not support an opcode that is in the IR");

let debug_artifact =
DebugArtifact::new(vec![optimized_program.debug.clone()], &context.file_manager);

(context.file_manager, Ok(((optimized_program, debug_artifact), warnings)))
(context.file_manager, Ok((optimized_program, warnings)))
}

fn compile_contracts(
package: &Package,
compile_options: &CompileOptions,
np_language: Language,
is_opcode_supported: &impl Fn(&Opcode) -> bool,
) -> (FileManager, CompilationResult<Vec<(CompiledContract, DebugArtifact)>>) {
) -> (FileManager, CompilationResult<Vec<CompiledContract>>) {
let (mut context, crate_id) = prepare_package(package);
let (contracts, warnings) =
match noirc_driver::compile_contracts(&mut context, crate_id, compile_options) {
Expand All @@ -196,18 +186,10 @@ fn compile_contracts(
})
.expect("Backend does not support an opcode that is in the IR");

let contracts_with_debug_artifacts = vecmap(optimized_contracts, |contract| {
let debug_infos = vecmap(&contract.functions, |func| func.debug.clone());
let debug_artifact = DebugArtifact::new(debug_infos, &context.file_manager);

(contract, debug_artifact)
});

(context.file_manager, Ok((contracts_with_debug_artifacts, warnings)))
(context.file_manager, Ok((optimized_contracts, warnings)))
}

fn save_program(
debug_artifact: DebugArtifact,
program: CompiledProgram,
package: &Package,
circuit_dir: &Path,
Expand All @@ -222,13 +204,15 @@ fn save_program(
save_program_to_file(&preprocessed_program, &package.name, circuit_dir);

if output_debug {
let debug_artifact =
DebugArtifact { debug_symbols: vec![program.debug], file_map: program.file_map };
let circuit_name: String = (&package.name).into();
save_debug_artifact_to_file(&debug_artifact, &circuit_name, circuit_dir);
}
}

fn save_contracts(
contracts: Vec<(CompiledContract, DebugArtifact)>,
contracts: Vec<CompiledContract>,
package: &Package,
circuit_dir: &Path,
output_debug: bool,
Expand All @@ -238,7 +222,16 @@ fn save_contracts(
// are compiled via nargo-core and then the PreprocessedContract is constructed here.
// This is due to EACH function needing it's own CRS, PKey, and VKey from the backend.
let preprocessed_contracts: Vec<(PreprocessedContract, DebugArtifact)> =
vecmap(contracts, |(contract, debug_artifact)| {
vecmap(contracts, |contract| {
let debug_artifact = DebugArtifact {
debug_symbols: contract
.functions
.iter()
.map(|function| function.debug.clone())
.collect(),
file_map: contract.file_map,
};

let preprocessed_functions =
vecmap(contract.functions, |func| PreprocessedContractFunction {
name: func.name,
Expand Down
14 changes: 5 additions & 9 deletions tooling/nargo_cli/src/cli/execute_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,11 @@ pub(crate) fn run(

let (np_language, is_opcode_supported) = backend.get_backend_info()?;
for package in &workspace {
let (compiled_program, debug_artifact) =
let compiled_program =
compile_bin_package(package, &args.compile_options, np_language, &is_opcode_supported)?;

let (return_value, solved_witness) = execute_program_and_decode(
compiled_program,
debug_artifact,
package,
&args.prover_name,
)?;
let (return_value, solved_witness) =
execute_program_and_decode(compiled_program, package, &args.prover_name)?;

println!("[{}] Circuit witness successfully solved", package.name);
if let Some(return_value) = return_value {
Expand All @@ -81,11 +77,11 @@ pub(crate) fn run(

fn execute_program_and_decode(
program: CompiledProgram,
debug_artifact: DebugArtifact,
package: &Package,
prover_name: &str,
) -> Result<(Option<InputValue>, WitnessMap), CliError> {
let CompiledProgram { abi, circuit, .. } = program;
let CompiledProgram { abi, circuit, debug, file_map } = program;
let debug_artifact = DebugArtifact { debug_symbols: vec![debug], file_map };

// Parse the initial witness values from Prover.toml
let (inputs_map, _) =
Expand Down
Loading