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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ doctest = false
anyhow = "1.0"
thiserror = "2.0"
semver = "1.0"
serde = { version = "1.0", "features" = [ "derive" ] }
serde = { version = "1.0", features = [ "derive" ] }
num = "0.4"
itertools = "0.14"
indexmap = { version = "2.10", features = ["serde"] }

zkevm_opcode_defs = "=0.150.6"

Expand Down
4 changes: 2 additions & 2 deletions src/context/function/block/evmla_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
#[derive(Debug, Clone)]
pub struct EVMLAData {
/// The initial hashes of the allowed stack states.
pub stack_hashes: Vec<[u8; era_compiler_common::BYTE_LENGTH_FIELD]>,
pub stack_hashes: Vec<u64>,
}

impl EVMLAData {
///
/// A shortcut constructor.
///
pub fn new(stack_hashes: Vec<[u8; era_compiler_common::BYTE_LENGTH_FIELD]>) -> Self {
pub fn new(stack_hashes: Vec<u64>) -> Self {
Self { stack_hashes }
}
}
6 changes: 1 addition & 5 deletions src/context/traits/evmla_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,5 @@ pub trait IEVMLAFunction<'ctx> {
///
/// If there is only one block, it is returned unconditionally.
///
fn find_block(
&self,
key: &BlockKey,
stack_hash: &[u8; era_compiler_common::BYTE_LENGTH_FIELD],
) -> anyhow::Result<Block<'ctx>>;
fn find_block(&self, key: &BlockKey, stack_hash: &u64) -> anyhow::Result<Block<'ctx>>;
}
6 changes: 1 addition & 5 deletions src/eravm/context/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,7 @@ impl<'ctx> Function<'ctx> {
}

impl<'ctx> IEVMLAFunction<'ctx> for Function<'ctx> {
fn find_block(
&self,
key: &BlockKey,
stack_hash: &[u8; era_compiler_common::BYTE_LENGTH_FIELD],
) -> anyhow::Result<Block<'ctx>> {
fn find_block(&self, key: &BlockKey, stack_hash: &u64) -> anyhow::Result<Block<'ctx>> {
let evmla_data = self.evmla();

if evmla_data
Expand Down
6 changes: 1 addition & 5 deletions src/evm/context/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,7 @@ impl<'ctx> Function<'ctx> {
}

impl<'ctx> IEVMLAFunction<'ctx> for Function<'ctx> {
fn find_block(
&self,
key: &BlockKey,
stack_hash: &[u8; era_compiler_common::BYTE_LENGTH_FIELD],
) -> anyhow::Result<Block<'ctx>> {
fn find_block(&self, key: &BlockKey, stack_hash: &u64) -> anyhow::Result<Block<'ctx>> {
let evmla_data = self.evmla();

if evmla_data
Expand Down
32 changes: 31 additions & 1 deletion src/evm/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::context::r#loop::Loop;
use crate::context::IContext;
use crate::debug_config::DebugConfig;
use crate::evm::build::Build as EVMBuild;
use crate::evm::profiler::Profiler;
use crate::evm::warning::Warning;
use crate::optimizer::settings::Settings as OptimizerSettings;
use crate::optimizer::Optimizer;
Expand Down Expand Up @@ -121,10 +122,17 @@ impl<'ctx> Context<'ctx> {
output_assembly: bool,
output_bytecode: bool,
is_size_fallback: bool,
profiler: &mut Profiler,
) -> anyhow::Result<EVMBuild> {
let module_clone = self.module.clone();
let contract_path = self.module.get_name().to_str().expect("Always valid");

let run_init_verify = profiler.start_evm_translation_unit(
contract_path,
self.code_segment,
"InitVerify",
self.optimizer.settings(),
);
let target_machine = TargetMachine::new(
era_compiler_common::Target::EVM,
self.optimizer.settings(),
Expand Down Expand Up @@ -158,7 +166,14 @@ impl<'ctx> Context<'ctx> {
self.code_segment,
)
})?;
run_init_verify.borrow_mut().finish();

let run_optimize_verify = profiler.start_evm_translation_unit(
contract_path,
self.code_segment,
"OptimizeVerify",
self.optimizer.settings(),
);
self.optimizer
.run(&target_machine, self.module())
.map_err(|error| anyhow::anyhow!("{} code optimizing: {error}", self.code_segment))?;
Expand All @@ -176,8 +191,15 @@ impl<'ctx> Context<'ctx> {
self.code_segment,
)
})?;
run_optimize_verify.borrow_mut().finish();

let assembly_buffer = if output_assembly || self.debug_config.is_some() {
let run_emit_llvm_assembly = profiler.start_evm_translation_unit(
contract_path,
self.code_segment,
"EmitLLVMAssembly",
self.optimizer.settings(),
);
let assembly_buffer = target_machine
.write_to_memory_buffer(self.module(), inkwell::targets::FileType::Assembly)
.map_err(|error| anyhow::anyhow!("assembly emitting: {error}"))?;
Expand All @@ -193,6 +215,7 @@ impl<'ctx> Context<'ctx> {
)?;
}

run_emit_llvm_assembly.borrow_mut().finish();
Some(assembly_buffer)
} else {
None
Expand All @@ -201,11 +224,18 @@ impl<'ctx> Context<'ctx> {
.map(|assembly_buffer| String::from_utf8_lossy(assembly_buffer.as_slice()).to_string());

if output_bytecode {
let run_emit_bytecode = profiler.start_evm_translation_unit(
contract_path,
self.code_segment,
"EmitBytecode",
self.optimizer.settings(),
);
let bytecode_buffer = target_machine
.write_to_memory_buffer(self.module(), inkwell::targets::FileType::Object)
.map_err(|error| {
anyhow::anyhow!("{} bytecode emitting: {error}", self.code_segment)
})?;
run_emit_bytecode.borrow_mut().finish();

let immutables = match self.code_segment {
era_compiler_common::CodeSegment::Deploy => None,
Expand Down Expand Up @@ -242,7 +272,7 @@ impl<'ctx> Context<'ctx> {
for function in self.module.get_functions() {
Function::set_size_attributes(self.llvm, function);
}
return self.build(output_assembly, output_bytecode, true);
return self.build(output_assembly, output_bytecode, true, profiler);
} else {
warnings.push(match self.code_segment {
era_compiler_common::CodeSegment::Deploy => Warning::DeployCodeSize {
Expand Down
1 change: 1 addition & 0 deletions src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod build;
pub mod r#const;
pub mod context;
pub mod instructions;
pub mod profiler;
pub mod warning;

use std::collections::BTreeMap;
Expand Down
93 changes: 93 additions & 0 deletions src/evm/profiler/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//!
//! Compiler pipeline profiler.
//!

pub mod run;

use std::cell::RefCell;
use std::rc::Rc;

use indexmap::IndexMap;

use crate::optimizer::settings::Settings as OptimizerSettings;

use self::run::Run;

///
/// Compiler pipeline profiler.
///
#[derive(Debug, Default)]
pub struct Profiler {
/// Indexed map of timing entries.
pub timings: IndexMap<String, Rc<RefCell<Run>>>,
}

impl Profiler {
///
/// Starts a new run for a generic part of the pipeline.
///
pub fn start_pipeline_element(&mut self, description: &str) -> Rc<RefCell<Run>> {
let run_name = description.to_owned();
assert!(
!self.timings.contains_key(run_name.as_str()),
"Translation unit run `{run_name}` already exists"
);

self.start_run(run_name)
}

///
/// Starts a new run for an EVM translation unit.
///
pub fn start_evm_translation_unit(
&mut self,
full_path: &str,
code_segment: era_compiler_common::CodeSegment,
description: &str,
optimizer_settings: &OptimizerSettings,
) -> Rc<RefCell<Run>> {
let spill_area_description = format!(
"SpillArea({})",
optimizer_settings.spill_area_size().unwrap_or_default()
);
let run_name = format!(
"{full_path}:{code_segment}/{description}/{optimizer_settings}/{spill_area_description}",
);
assert!(
!self.timings.contains_key(run_name.as_str()),
"Translation unit run `{run_name}` already exists"
);

self.start_run(run_name)
}

///
/// Returns a serializeable vector of the profiler runs.
///
pub fn to_vec(&self) -> Vec<(String, u64)> {
self.timings
.iter()
.map(|(name, run)| {
let run = run.borrow();
(
name.clone(),
run.duration.expect("Always exists").as_millis() as u64,
)
})
.collect()
}

///
/// Starts a new run with the given name.
///
fn start_run(&mut self, name: String) -> Rc<RefCell<Run>> {
assert!(
!self.timings.contains_key(name.as_str()),
"Run `{name}` already exists"
);

let run = Rc::new(RefCell::new(Run::default()));
self.timings.insert(name, run.clone());
run
}
}
52 changes: 52 additions & 0 deletions src/evm/profiler/run.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//!
//! Compiler pipeline profiler run.
//!

use std::time::Duration;
use std::time::Instant;

///
/// Compiler pipeline profiler run.
///
#[derive(Debug)]
pub struct Run {
/// Start time.
pub start_time: Instant,
/// Recorded duration.
pub duration: Option<Duration>,
}

impl Default for Run {
fn default() -> Self {
Run {
start_time: Instant::now(),
duration: None,
}
}
}

impl Run {
///
/// Records the duration of the run.
///
pub fn finish(&mut self) {
assert!(
self.duration.is_none(),
"Duration has already been recorded"
);

self.duration = Some(self.start_time.elapsed());
}
}

impl std::fmt::Display for Run {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let duration = self
.duration
.as_ref()
.expect("Duration has not been recorded yet");

write!(f, "{duration:?}")?;
Ok(())
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ pub use self::evm::instructions::return_data as evm_return_data;
pub use self::evm::instructions::storage as evm_storage;
pub use self::evm::link as evm_link;
pub use self::evm::minimal_deploy_code as evm_minimal_deploy_code;
pub use self::evm::profiler::run::Run as EVMProfilerRun;
pub use self::evm::profiler::Profiler as EVMProfiler;
pub use self::evm::r#const as evm_const;
pub use self::evm::warning::Warning as EVMWarning;
pub use self::evm::DummyLLVMWritable as EVMDummyLLVMWritable;
Expand Down
Loading