From 26cab2e00aa9c31f81190ecee7106b02dca10ada Mon Sep 17 00:00:00 2001 From: Eytan Singher Date: Sun, 12 Apr 2026 12:26:42 +0300 Subject: [PATCH] perf: box Variable.info, BlockEnd::Match, and Statement::EnumConstruct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Box three large/rare lowering IR fields to reduce container sizes: - Variable.info: TypeInfo (~160 bytes) → Box (8 bytes inline) - BlockEnd::Match { info }: MatchInfo → Box - Statement::EnumConstruct: StatementEnumConstruct → Box<...> Empirically selected from testing all 5 candidates individually and in combinations. This 3-combo gives the best ERC20 Starknet results: -2.8% total allocations, -2.3% peak memory. fib.cairo is neutral. Co-Authored-By: Claude Opus 4.6 (1M context) --- Cargo.lock | 87 ++++++++++++++++++- Cargo.toml | 1 + crates/bin/cairo-compile/Cargo.toml | 2 + crates/bin/cairo-compile/src/main.rs | 9 +- crates/bin/starknet-compile/Cargo.toml | 2 + crates/bin/starknet-compile/src/main.rs | 9 +- .../src/add_withdraw_gas/mod.rs | 8 +- crates/cairo-lang-lowering/src/cache/mod.rs | 16 ++-- .../cairo-lang-lowering/src/concretize/mod.rs | 4 +- crates/cairo-lang-lowering/src/db.rs | 2 +- crates/cairo-lang-lowering/src/destructs.rs | 12 ++- .../cairo-lang-lowering/src/implicits/mod.rs | 2 +- .../src/lower/block_builder.rs | 4 +- .../src/lower/flow_control/lower_graph.rs | 4 +- .../src/lower/generators.rs | 4 +- crates/cairo-lang-lowering/src/objects.rs | 12 +-- .../src/optimizations/branch_inversion.rs | 19 ++-- .../src/optimizations/const_folding.rs | 13 +-- .../src/optimizations/dedup_blocks.rs | 35 +++----- .../src/optimizations/early_unsafe_panic.rs | 12 ++- .../src/optimizations/gas_redeposit.rs | 10 +-- .../src/optimizations/match_optimizer.rs | 7 +- .../src/optimizations/return_optimization.rs | 7 +- .../src/optimizations/scrub_units.rs | 13 ++- .../src/optimizations/trim_unreachable.rs | 4 +- crates/cairo-lang-lowering/src/panic/mod.rs | 24 ++--- .../cairo-lang-lowering/src/specialization.rs | 15 ++-- crates/cairo-lang-lowering/src/utils.rs | 16 ++-- .../src/block_generator.rs | 2 +- 29 files changed, 227 insertions(+), 128 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d628ae80452..fae09405bc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + [[package]] name = "adler2" version = "2.0.1" @@ -341,6 +350,21 @@ dependencies = [ "fs_extra", ] +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + [[package]] name = "base64" version = "0.22.1" @@ -462,6 +486,7 @@ dependencies = [ "cairo-lang-lowering", "cairo-lang-utils", "clap", + "dhat", "log", "mimalloc", "tracing", @@ -1691,6 +1716,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "dhat" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cd11d84628e233de0ce467de10b8633f4ddaecafadefc86e13b84b8739b827" +dependencies = [ + "backtrace", + "lazy_static", + "mintex", + "parking_lot", + "rustc-hash 1.1.0", + "serde", + "serde_json", + "thousands", +] + [[package]] name = "diff" version = "0.1.13" @@ -2125,6 +2166,12 @@ dependencies = [ "wasip3", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "glob" version = "0.3.3" @@ -2852,6 +2899,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mintex" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536" + [[package]] name = "mio" version = "1.1.1" @@ -2975,6 +3028,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -3330,7 +3392,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.1.1", "rustls", "socket2", "thiserror 2.0.18", @@ -3351,7 +3413,7 @@ dependencies = [ "lru-slab", "rand 0.9.2", "ring", - "rustc-hash", + "rustc-hash 2.1.1", "rustls", "rustls-pki-types", "slab", @@ -3671,6 +3733,18 @@ dependencies = [ "num-traits", ] +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -3784,7 +3858,7 @@ dependencies = [ "parking_lot", "portable-atomic", "rayon", - "rustc-hash", + "rustc-hash 2.1.1", "salsa-macro-rules", "salsa-macros", "smallvec", @@ -4097,6 +4171,7 @@ dependencies = [ "cairo-lang-starknet", "cairo-lang-starknet-classes", "clap", + "dhat", "mimalloc", ] @@ -4405,6 +4480,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "thousands" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" + [[package]] name = "thread_local" version = "1.1.9" diff --git a/Cargo.toml b/Cargo.toml index 90fbfc39f81..8b348f9610e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,7 @@ itertools = { version = "0.14.0", default-features = false } keccak = "0.1.5" lalrpop-util = { version = "0.23.1", features = ["lexer"] } log = "0.4.27" +dhat = "0.3" mimalloc = { version = "0.1.48" } num-bigint = { version = "0.4.6", default-features = false } num-integer = "0.1.46" diff --git a/crates/bin/cairo-compile/Cargo.toml b/crates/bin/cairo-compile/Cargo.toml index bd25191ea8b..6838f9ca670 100644 --- a/crates/bin/cairo-compile/Cargo.toml +++ b/crates/bin/cairo-compile/Cargo.toml @@ -9,6 +9,7 @@ description = "Cairo compiler executable for the Cairo programming language" [dependencies] anyhow.workspace = true clap.workspace = true +dhat = { workspace = true, optional = true } log.workspace = true mimalloc = { workspace = true, optional = true } tracing.workspace = true @@ -18,4 +19,5 @@ cairo-lang-lowering = { path = "../../cairo-lang-lowering", version = "=2.16.1" cairo-lang-utils = { path = "../../cairo-lang-utils", version = "=2.16.1", features = ["tracing"] } [features] +dhat = ["dep:dhat"] mimalloc = ["dep:mimalloc"] diff --git a/crates/bin/cairo-compile/src/main.rs b/crates/bin/cairo-compile/src/main.rs index 97d92afd9ec..8376a0eff98 100644 --- a/crates/bin/cairo-compile/src/main.rs +++ b/crates/bin/cairo-compile/src/main.rs @@ -7,10 +7,14 @@ use cairo_lang_compiler::{CompilerConfig, compile_cairo_project_at_path}; use cairo_lang_utils::logging::init_logging; use clap::Parser; -#[cfg(feature = "mimalloc")] +#[cfg(all(feature = "mimalloc", not(feature = "dhat")))] #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; +#[cfg(feature = "dhat")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + /// Options for the `inlining-strategy` arguments. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, clap::ValueEnum)] pub enum InliningStrategy { @@ -51,6 +55,9 @@ struct Args { } fn main() -> anyhow::Result<()> { + #[cfg(feature = "dhat")] + let _profiler = dhat::Profiler::new_heap(); + init_logging(tracing::Level::ERROR); log::info!("Starting Cairo compilation."); diff --git a/crates/bin/starknet-compile/Cargo.toml b/crates/bin/starknet-compile/Cargo.toml index 9f52fdc3161..e51de5e4d02 100644 --- a/crates/bin/starknet-compile/Cargo.toml +++ b/crates/bin/starknet-compile/Cargo.toml @@ -9,6 +9,7 @@ description = "Compiler executable for the Cairo programming language with the S [dependencies] anyhow.workspace = true clap.workspace = true +dhat = { workspace = true, optional = true } mimalloc = { workspace = true, optional = true } cairo-lang-compiler = { path = "../../cairo-lang-compiler", version = "=2.16.1" } @@ -16,4 +17,5 @@ cairo-lang-starknet = { path = "../../cairo-lang-starknet", version = "=2.16.1" cairo-lang-starknet-classes = { path = "../../cairo-lang-starknet-classes", version = "=2.16.1" } [features] +dhat = ["dep:dhat"] mimalloc = ["dep:mimalloc"] diff --git a/crates/bin/starknet-compile/src/main.rs b/crates/bin/starknet-compile/src/main.rs index 9ac7dbe4e6d..ae1e41e2c74 100644 --- a/crates/bin/starknet-compile/src/main.rs +++ b/crates/bin/starknet-compile/src/main.rs @@ -9,10 +9,14 @@ use cairo_lang_starknet::compile::starknet_compile; use cairo_lang_starknet_classes::allowed_libfuncs::ListSelector; use clap::Parser; -#[cfg(feature = "mimalloc")] +#[cfg(all(feature = "mimalloc", not(feature = "dhat")))] #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; +#[cfg(feature = "dhat")] +#[global_allocator] +static ALLOC: dhat::Alloc = dhat::Alloc; + /// Compiles the specified contract from a Cairo project into a contract class file. /// Exits with 0/1 if the compilation succeeds/fails. #[derive(Parser, Debug)] @@ -43,6 +47,9 @@ struct Args { } fn main() -> anyhow::Result<()> { + #[cfg(feature = "dhat")] + let _profiler = dhat::Profiler::new_heap(); + let args = Args::parse(); // Check if args.path is a file or a directory. diff --git a/crates/cairo-lang-lowering/src/add_withdraw_gas/mod.rs b/crates/cairo-lang-lowering/src/add_withdraw_gas/mod.rs index ca29fc742b0..62b9d12d279 100644 --- a/crates/cairo-lang-lowering/src/add_withdraw_gas/mod.rs +++ b/crates/cairo-lang-lowering/src/add_withdraw_gas/mod.rs @@ -49,7 +49,7 @@ fn add_withdraw_gas_to_function<'db>( let new_root_block = Block { statements: vec![], end: BlockEnd::Match { - info: MatchInfo::Extern(MatchExternInfo { + info: Box::new(MatchInfo::Extern(MatchExternInfo { function: get_function_id( db, core_submodule(db, SmolStrId::from(db, "gas")), @@ -77,7 +77,7 @@ fn add_withdraw_gas_to_function<'db>( }, ], location, - }), + })), }, }; @@ -126,12 +126,12 @@ fn create_panic_block<'db>( is_specialization_base_call: false, })], end: BlockEnd::Match { - info: MatchInfo::Enum(MatchEnumInfo { + info: Box::new(MatchInfo::Enum(MatchEnumInfo { concrete_enum_id: *never_enum_id, input: VarUsage { var_id: never_var, location }, arms: vec![], location, - }), + })), }, }) } diff --git a/crates/cairo-lang-lowering/src/cache/mod.rs b/crates/cairo-lang-lowering/src/cache/mod.rs index c5a6e4d6a7a..d913b098ce8 100644 --- a/crates/cairo-lang-lowering/src/cache/mod.rs +++ b/crates/cairo-lang-lowering/src/cache/mod.rs @@ -545,7 +545,7 @@ struct VariableCached { } impl VariableCached { fn new<'db>(variable: Variable<'db>, ctx: &mut CacheSavingContext<'db>) -> Self { - let TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl } = variable.info; + let TypeInfo { droppable, copyable, destruct_impl, panic_destruct_impl } = *variable.info; Self { droppable: droppable .map(|impl_id| ImplIdCached::new(impl_id, &mut ctx.semantic_ctx)) @@ -567,7 +567,7 @@ impl VariableCached { Variable { ty: self.ty.get_embedded(&ctx.semantic_loading_data), location: self.location.embed(ctx), - info: TypeInfo { + info: Box::new(TypeInfo { droppable: self .droppable .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data)) @@ -584,7 +584,7 @@ impl VariableCached { .panic_destruct_impl .map(|impl_id| impl_id.get_embedded(&ctx.semantic_loading_data)) .ok_or(InferenceError::Reported(skip_diagnostic())), - }, + }), } } } @@ -665,7 +665,7 @@ impl BlockEndCached { } BlockEnd::NotSet => BlockEndCached::NotSet, BlockEnd::Match { info } => { - BlockEndCached::Match { info: MatchInfoCached::new(info, ctx) } + BlockEndCached::Match { info: MatchInfoCached::new(*info, ctx) } } } } @@ -680,7 +680,7 @@ impl BlockEndCached { BlockEnd::Goto(BlockId(block_id), remapping.embed(ctx)) } BlockEndCached::NotSet => BlockEnd::NotSet, - BlockEndCached::Match { info } => BlockEnd::Match { info: info.embed(ctx) }, + BlockEndCached::Match { info } => BlockEnd::Match { info: Box::new(info.embed(ctx)) }, } } } @@ -918,7 +918,7 @@ impl StatementCached { StatementCached::StructDestructure(StatementStructDestructureCached::new(stmt, ctx)) } Statement::EnumConstruct(stmt) => { - StatementCached::EnumConstruct(StatementEnumConstructCached::new(stmt, ctx)) + StatementCached::EnumConstruct(StatementEnumConstructCached::new(*stmt, ctx)) } Statement::Snapshot(stmt) => { StatementCached::Snapshot(StatementSnapshotCached::new(stmt, ctx)) @@ -940,7 +940,9 @@ impl StatementCached { StatementCached::StructDestructure(stmt) => { Statement::StructDestructure(stmt.embed(ctx)) } - StatementCached::EnumConstruct(stmt) => Statement::EnumConstruct(stmt.embed(ctx)), + StatementCached::EnumConstruct(stmt) => { + Statement::EnumConstruct(Box::new(stmt.embed(ctx))) + } StatementCached::Snapshot(stmt) => Statement::Snapshot(stmt.embed(ctx)), StatementCached::Desnap(stmt) => Statement::Desnap(stmt.embed(ctx)), StatementCached::IntoBox(stmt) => Statement::IntoBox(stmt.embed(ctx)), diff --git a/crates/cairo-lang-lowering/src/concretize/mod.rs b/crates/cairo-lang-lowering/src/concretize/mod.rs index 8e823179bc0..a50d008355b 100644 --- a/crates/cairo-lang-lowering/src/concretize/mod.rs +++ b/crates/cairo-lang-lowering/src/concretize/mod.rs @@ -41,7 +41,7 @@ pub fn concretize_lowered<'db>( // Substitute all types. for (_, var) in lowered.variables.iter_mut() { var.ty = substitution.substitute(db, var.ty)?; - let TypeInfo { destruct_impl, panic_destruct_impl, .. } = &mut var.info; + let TypeInfo { destruct_impl, panic_destruct_impl, .. } = &mut *var.info; for impl_id in [destruct_impl, panic_destruct_impl].into_iter().flatten() { *impl_id = substitution.substitute(db, *impl_id)?; } @@ -68,7 +68,7 @@ pub fn concretize_lowered<'db>( } } if let BlockEnd::Match { info } = &mut block.end { - for MatchArm { arm_selector: selector, .. } in match info { + for MatchArm { arm_selector: selector, .. } in match &mut **info { crate::MatchInfo::Enum(s) => s.arms.iter_mut(), crate::MatchInfo::Extern(s) => { s.function = concretize_function(db, substitution, s.function)?; diff --git a/crates/cairo-lang-lowering/src/db.rs b/crates/cairo-lang-lowering/src/db.rs index e2c760c49e8..74a2f33fe40 100644 --- a/crates/cairo-lang-lowering/src/db.rs +++ b/crates/cairo-lang-lowering/src/db.rs @@ -539,7 +539,7 @@ pub(crate) fn get_direct_callees<'db>( BlockEnd::Goto(next, _) => stack.push(*next), BlockEnd::Match { info } => { let mut arms = info.arms().iter(); - if let MatchInfo::Extern(s) = info { + if let MatchInfo::Extern(s) = &**info { direct_callees.push(s.function); if DependencyType::Cost == dependency_type && withdraw_gas_fns.contains(&s.function) diff --git a/crates/cairo-lang-lowering/src/destructs.rs b/crates/cairo-lang-lowering/src/destructs.rs index a811895dfc8..9d652129a3a 100644 --- a/crates/cairo-lang-lowering/src/destructs.rs +++ b/crates/cairo-lang-lowering/src/destructs.rs @@ -426,13 +426,17 @@ pub fn add_destructs<'db>( match destruct_type { AddDestructFlowType::Plain => { let block = &mut lowered.blocks[BlockId(block_id)]; - block - .statements - .splice(statement_idx..statement_idx, stmts.into_iter().map(Statement::Call)); + block.statements.splice( + statement_idx..statement_idx, + stmts.into_iter().map(Statement::Call), + ); } AddDestructFlowType::PanicPostMatch => { let block = &mut lowered.blocks[BlockId(match_block_id)]; - let BlockEnd::Match { info: MatchInfo::Enum(info) } = &mut block.end else { + let BlockEnd::Match { info } = &mut block.end else { + unreachable!(); + }; + let MatchInfo::Enum(info) = &mut **info else { unreachable!(); }; diff --git a/crates/cairo-lang-lowering/src/implicits/mod.rs b/crates/cairo-lang-lowering/src/implicits/mod.rs index 36f399e12c6..2fa01b4a575 100644 --- a/crates/cairo-lang-lowering/src/implicits/mod.rs +++ b/crates/cairo-lang-lowering/src/implicits/mod.rs @@ -191,7 +191,7 @@ fn lower_function_blocks_implicits<'db>( } BlockEnd::Match { info } => { blocks_to_visit.extend(info.arms().iter().rev().map(|a| a.block_id)); - match info { + match &mut **info { MatchInfo::Enum(_) | MatchInfo::Value(_) => { for MatchArm { arm_selector: _, block_id, var_ids: _ } in info.arms() { assert!( diff --git a/crates/cairo-lang-lowering/src/lower/block_builder.rs b/crates/cairo-lang-lowering/src/lower/block_builder.rs index 527caa45ea2..7e60f03d40b 100644 --- a/crates/cairo-lang-lowering/src/lower/block_builder.rs +++ b/crates/cairo-lang-lowering/src/lower/block_builder.rs @@ -254,7 +254,7 @@ impl<'db> BlockBuilder<'db> { /// Ends a block with an unreachable match. pub fn unreachable_match(self, ctx: &mut LoweringContext<'db, '_>, match_info: MatchInfo<'db>) { - self.finalize(ctx, BlockEnd::Match { info: match_info }); + self.finalize(ctx, BlockEnd::Match { info: Box::new(match_info) }); } /// Ends a block with Panic. @@ -316,7 +316,7 @@ impl<'db> BlockBuilder<'db> { return Err(LoweringFlowError::Match(match_info)); }; let prev_scope = std::mem::replace(self, new_scope); - prev_scope.finalize(ctx, BlockEnd::Match { info: match_info }); + prev_scope.finalize(ctx, BlockEnd::Match { info: Box::new(match_info) }); Ok(merged_expr) } diff --git a/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph.rs b/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph.rs index d299d4502d0..edc1e17b9e5 100644 --- a/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph.rs +++ b/crates/cairo-lang-lowering/src/lower/flow_control/lower_graph.rs @@ -202,7 +202,7 @@ impl<'mt, 'db, 'a> LowerGraphContext<'db, 'mt, 'a> { if id == self.effective_root { self.result = Some((builder, LowerGraphResult::Match(info))); } else { - builder.finalize(self.ctx, BlockEnd::Match { info }); + builder.finalize(self.ctx, BlockEnd::Match { info: Box::new(info) }); } } @@ -257,7 +257,7 @@ impl<'mt, 'db, 'a> LowerGraphContext<'db, 'mt, 'a> { if let Some((new_builder, lowered_expr)) = merge_sealed_block_builders(self.ctx, self.sealed_blocks, self.location) { - builder.finalize(self.ctx, BlockEnd::Match { info: match_info }); + builder.finalize(self.ctx, BlockEnd::Match { info: Box::new(match_info) }); (Ok(lowered_expr), new_builder) } else { (Err(LoweringFlowError::Match(match_info)), builder) diff --git a/crates/cairo-lang-lowering/src/lower/generators.rs b/crates/cairo-lang-lowering/src/lower/generators.rs index d11c2607446..83132279f49 100644 --- a/crates/cairo-lang-lowering/src/lower/generators.rs +++ b/crates/cairo-lang-lowering/src/lower/generators.rs @@ -127,11 +127,11 @@ impl<'db> EnumConstruct<'db> { )) .intern(ctx.db); let output = ctx.new_var(VarRequest { ty, location: self.location }); - builder.push_statement(Statement::EnumConstruct(StatementEnumConstruct { + builder.push_statement(Statement::EnumConstruct(Box::new(StatementEnumConstruct { variant: self.variant, input: self.input, output, - })); + }))); VarUsage { var_id: output, location: self.location } } } diff --git a/crates/cairo-lang-lowering/src/objects.rs b/crates/cairo-lang-lowering/src/objects.rs index 2120128831d..3f6bbb966b4 100644 --- a/crates/cairo-lang-lowering/src/objects.rs +++ b/crates/cairo-lang-lowering/src/objects.rs @@ -229,7 +229,7 @@ pub enum BlockEnd<'db> { /// This block ends with a jump to a different block. Goto(BlockId, VarRemapping<'db>), Match { - info: MatchInfo<'db>, + info: Box>, }, } @@ -253,7 +253,7 @@ pub struct Variable<'db> { /// Location of the variable. pub location: LocationId<'db>, /// The semantic type info of the variable. - pub info: TypeInfo<'db>, + pub info: Box>, } impl<'db> DebugWithDb<'db> for Variable<'db> { @@ -271,7 +271,7 @@ impl<'db> Variable<'db> { ty: semantic::TypeId<'db>, location: LocationId<'db>, ) -> Self { - Self { ty, location, info: db.type_info(ctx, ty) } + Self { ty, location, info: Box::new(db.type_info(ctx, ty)) } } /// Returns a new variable with the type, with info calculated with the default context. @@ -283,7 +283,7 @@ impl<'db> Variable<'db> { Self { ty, location, - info: TypeInfo { + info: Box::new(TypeInfo { copyable: db.copyable(ty), droppable: db.droppable(ty), destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer( @@ -292,7 +292,7 @@ impl<'db> Variable<'db> { panic_destruct_impl: Err(InferenceError::Ambiguity(Ambiguity::WillNotInfer( concrete_panic_destruct_trait(db, ty), ))), - }, + }), } } } @@ -311,7 +311,7 @@ pub enum Statement<'db> { StructDestructure(StatementStructDestructure<'db>), // Enums. - EnumConstruct(StatementEnumConstruct<'db>), + EnumConstruct(Box>), Snapshot(StatementSnapshot<'db>), Desnap(StatementDesnap<'db>), diff --git a/crates/cairo-lang-lowering/src/optimizations/branch_inversion.rs b/crates/cairo-lang-lowering/src/optimizations/branch_inversion.rs index dfbb60afe78..c5e39cfcd78 100644 --- a/crates/cairo-lang-lowering/src/optimizations/branch_inversion.rs +++ b/crates/cairo-lang-lowering/src/optimizations/branch_inversion.rs @@ -8,7 +8,7 @@ use cairo_lang_utils::Intern; use salsa::Database; use crate::ids::FunctionLongId; -use crate::{BlockEnd, Lowered, MatchInfo, Statement, StatementCall}; +use crate::{BlockEnd, Lowered, MatchInfo, Statement}; /// Performs branch inversion optimization on a lowered function. /// @@ -34,20 +34,19 @@ pub fn branch_inversion(db: &dyn Database, lowered: &mut Lowered<'_>) { .intern(db); for block in lowered.blocks.iter_mut() { - if let BlockEnd::Match { info: MatchInfo::Enum(info) } = &mut block.end + if let BlockEnd::Match { info } = &mut block.end + && let MatchInfo::Enum(info) = &mut **info && let Some(negated_condition) = block .statements .iter() .rev() .filter_map(|stmt| match stmt { - Statement::Call(StatementCall { - function, - inputs, - outputs, - with_coupon: false, - .. - }) if function == &bool_not_func_id && outputs[..] == [info.input.var_id] => { - Some(inputs[0]) + Statement::Call(stmt) + if stmt.function == bool_not_func_id + && !stmt.with_coupon + && stmt.outputs[..] == [info.input.var_id] => + { + Some(stmt.inputs[0]) } _ => None, }) diff --git a/crates/cairo-lang-lowering/src/optimizations/const_folding.rs b/crates/cairo-lang-lowering/src/optimizations/const_folding.rs index 46980a110e2..67f6b3a43f2 100644 --- a/crates/cairo-lang-lowering/src/optimizations/const_folding.rs +++ b/crates/cairo-lang-lowering/src/optimizations/const_folding.rs @@ -331,7 +331,8 @@ impl<'db, 'mt> ConstFoldingContext<'db, 'mt> { } } } - Statement::EnumConstruct(StatementEnumConstruct { variant, input, output }) => { + Statement::EnumConstruct(stmt) => { + let StatementEnumConstruct { variant, input, output } = &**stmt; let value = if let Some(info) = self.var_info.get(&input.var_id) { if let VarInfo::Const(val) = info.as_ref() { VarInfo::Const(ConstValue::Enum(*variant, *val).intern(self.db)) @@ -395,7 +396,7 @@ impl<'db, 'mt> ConstFoldingContext<'db, 'mt> { } BlockEnd::Match { info } => { self.maybe_replace_inputs(info.inputs_mut()); - match info { + match &mut **info { MatchInfo::Enum(info) => { if let Some(updated_end) = self.handle_enum_block_end(info, statements) { block.end = updated_end; @@ -1019,7 +1020,7 @@ impl<'db, 'mt> ConstFoldingContext<'db, 'mt> { ); let unused_nz_var = self.variables.alloc(unused_nz_var); return Some(BlockEnd::Match { - info: MatchInfo::Extern(MatchExternInfo { + info: Box::new(MatchInfo::Extern(MatchExternInfo { function, inputs: vec![nz_input], arms: vec![ @@ -1039,7 +1040,7 @@ impl<'db, 'mt> ConstFoldingContext<'db, 'mt> { }, ], location: info.location, - }), + })), }); } Some(BlockEnd::Goto( @@ -1115,12 +1116,12 @@ impl<'db, 'mt> ConstFoldingContext<'db, 'mt> { is_specialization_base_call: false, })); return Some(BlockEnd::Match { - info: MatchInfo::Enum(MatchEnumInfo { + info: Box::new(MatchInfo::Enum(MatchEnumInfo { concrete_enum_id: *concrete_enum_id, input: VarUsage { var_id: result, location: info.location }, arms: core::mem::take(&mut info.arms), location: info.location, - }), + })), }); } } diff --git a/crates/cairo-lang-lowering/src/optimizations/dedup_blocks.rs b/crates/cairo-lang-lowering/src/optimizations/dedup_blocks.rs index 00ac147dc13..0fe089e5b9d 100644 --- a/crates/cairo-lang-lowering/src/optimizations/dedup_blocks.rs +++ b/crates/cairo-lang-lowering/src/optimizations/dedup_blocks.rs @@ -11,9 +11,9 @@ use itertools::{Itertools, zip_eq}; use crate::ids::FunctionId; use crate::utils::{Rebuilder, RebuilderEx}; use crate::{ - Block, BlockEnd, BlockId, Lowered, Statement, StatementCall, StatementConst, StatementDesnap, - StatementEnumConstruct, StatementIntoBox, StatementSnapshot, StatementStructConstruct, - StatementStructDestructure, StatementUnbox, VarRemapping, VarUsage, VariableArena, VariableId, + Block, BlockEnd, BlockId, Lowered, Statement, StatementConst, StatementDesnap, + StatementIntoBox, StatementSnapshot, StatementStructConstruct, StatementStructDestructure, + StatementUnbox, VarRemapping, VarUsage, VariableArena, VariableId, }; /// A canonic representation of a block (used to find duplicated blocks). @@ -128,18 +128,11 @@ impl<'db, 'a> CanonicBlockBuilder<'db, 'a> { output: self.handle_output(output), boxed: *boxed, }, - Statement::Call(StatementCall { - function, - inputs, - with_coupon, - outputs, - location: _, - is_specialization_base_call: _, - }) => CanonicStatement::Call { - function: *function, - inputs: inputs.iter().map(|input| self.handle_input(input)).collect(), - with_coupon: *with_coupon, - outputs: outputs.iter().map(|output| self.handle_output(output)).collect(), + Statement::Call(stmt) => CanonicStatement::Call { + function: stmt.function, + inputs: stmt.inputs.iter().map(|input| self.handle_input(input)).collect(), + with_coupon: stmt.with_coupon, + outputs: stmt.outputs.iter().map(|output| self.handle_output(output)).collect(), }, Statement::StructConstruct(StatementStructConstruct { inputs, output }) => { CanonicStatement::StructConstruct { @@ -153,13 +146,11 @@ impl<'db, 'a> CanonicBlockBuilder<'db, 'a> { outputs: outputs.iter().map(|output| self.handle_output(output)).collect(), } } - Statement::EnumConstruct(StatementEnumConstruct { variant, input, output }) => { - CanonicStatement::EnumConstruct { - variant: *variant, - input: self.handle_input(input), - output: self.handle_output(output), - } - } + Statement::EnumConstruct(stmt) => CanonicStatement::EnumConstruct { + variant: stmt.variant, + input: self.handle_input(&stmt.input), + output: self.handle_output(&stmt.output), + }, Statement::Snapshot(StatementSnapshot { input, outputs }) => { CanonicStatement::Snapshot { input: self.handle_input(input), diff --git a/crates/cairo-lang-lowering/src/optimizations/early_unsafe_panic.rs b/crates/cairo-lang-lowering/src/optimizations/early_unsafe_panic.rs index 003fd113417..cd6f3c09023 100644 --- a/crates/cairo-lang-lowering/src/optimizations/early_unsafe_panic.rs +++ b/crates/cairo-lang-lowering/src/optimizations/early_unsafe_panic.rs @@ -12,9 +12,7 @@ use salsa::Database; use crate::analysis::core::StatementLocation; use crate::analysis::{DataflowAnalyzer, DataflowBackAnalysis, Direction, Edge}; use crate::ids::{LocationId, SemanticFunctionIdEx}; -use crate::{ - Block, BlockEnd, BlockId, Lowered, MatchExternInfo, MatchInfo, Statement, StatementCall, -}; +use crate::{Block, BlockEnd, BlockId, Lowered, MatchExternInfo, MatchInfo, Statement}; /// Adds an early unsafe_panic when we detect that `return` is unreachable from a certain point in /// the code. This step is needed to avoid issues with undroppable references in Sierra to CASM. @@ -49,12 +47,12 @@ pub fn early_unsafe_panic<'db>(db: &'db dyn Database, lowered: &mut Lowered<'db> block.statements.truncate(statement_idx); block.end = BlockEnd::Match { - info: MatchInfo::Extern(MatchExternInfo { + info: Box::new(MatchInfo::Extern(MatchExternInfo { arms: vec![], location, function: panic_func_id, inputs: vec![], - }), + })), } } } @@ -72,8 +70,8 @@ pub struct UnsafePanicContext<'db> { impl<'db> UnsafePanicContext<'db> { /// Returns true if the statement has side effects. pub fn has_side_effects(&self, stmt: &Statement<'db>) -> bool { - if let Statement::Call(StatementCall { function, .. }) = stmt { - let Some((extern_fn, _gargs)) = function.get_extern(self.db) else { + if let Statement::Call(stmt) = stmt { + let Some((extern_fn, _gargs)) = stmt.function.get_extern(self.db) else { return false; }; diff --git a/crates/cairo-lang-lowering/src/optimizations/gas_redeposit.rs b/crates/cairo-lang-lowering/src/optimizations/gas_redeposit.rs index 1d2e206f4a7..466a279ae3d 100644 --- a/crates/cairo-lang-lowering/src/optimizations/gas_redeposit.rs +++ b/crates/cairo-lang-lowering/src/optimizations/gas_redeposit.rs @@ -13,10 +13,7 @@ use crate::analysis::{DataflowAnalyzer, DataflowBackAnalysis, Direction, Edge}; use crate::ids::{ConcreteFunctionWithBodyId, LocationId, SemanticFunctionIdEx}; use crate::implicits::FunctionImplicitsTrait; use crate::panic::PanicSignatureInfo; -use crate::{ - BlockEnd, BlockId, Lowered, Statement, StatementCall, StatementEnumConstruct, VarUsage, - VariableId, -}; +use crate::{BlockEnd, BlockId, Lowered, Statement, StatementCall, VarUsage, VariableId}; /// Adds redeposit gas actions. /// @@ -144,12 +141,11 @@ impl<'db, 'a> DataflowAnalyzer<'db, 'a> for GasRedepositContext<'db> { return; }; - let Statement::EnumConstruct(StatementEnumConstruct { variant, input: _, output }) = stmt - else { + let Statement::EnumConstruct(stmt) = stmt else { return; }; - if *output == var_id && *variant == self.err_variant { + if stmt.output == var_id && stmt.variant == self.err_variant { *info = RedepositState::Unnecessary; } } diff --git a/crates/cairo-lang-lowering/src/optimizations/match_optimizer.rs b/crates/cairo-lang-lowering/src/optimizations/match_optimizer.rs index 9d3d9c5c21b..0ba54df2a6c 100644 --- a/crates/cairo-lang-lowering/src/optimizations/match_optimizer.rs +++ b/crates/cairo-lang-lowering/src/optimizations/match_optimizer.rs @@ -134,11 +134,12 @@ pub fn optimize_matches<'db>(lowered: &mut Lowered<'db>) { } let block = &mut lowered.blocks[fix.match_block]; - let BlockEnd::Match { info: MatchInfo::Enum(MatchEnumInfo { arms, location, .. }) } = - &mut block.end - else { + let BlockEnd::Match { info } = &mut block.end else { unreachable!("match block should end with a match."); }; + let MatchInfo::Enum(MatchEnumInfo { arms, location, .. }) = &mut **info else { + unreachable!("match block should end with an enum match."); + }; let arm = arms.get_mut(fix.arm_idx).unwrap(); if fix.target_block != arm.block_id { diff --git a/crates/cairo-lang-lowering/src/optimizations/return_optimization.rs b/crates/cairo-lang-lowering/src/optimizations/return_optimization.rs index 6d70819d0cf..1a1d8b5d2d4 100644 --- a/crates/cairo-lang-lowering/src/optimizations/return_optimization.rs +++ b/crates/cairo-lang-lowering/src/optimizations/return_optimization.rs @@ -105,9 +105,9 @@ impl<'db, 'a> EarlyReturnContext<'db, 'a> { ty, self.location, )); - self.statements.push(Statement::EnumConstruct( + self.statements.push(Statement::EnumConstruct(Box::new( StatementEnumConstruct { variant: *variant, input, output }, - )); + ))); output }); res.push(VarUsage { var_id: output, location: self.location }); @@ -511,7 +511,8 @@ impl<'db, 'a> Analyzer<'db, 'a> for ReturnOptimizerContext<'db, 'a> { } Statement::StructDestructure(stmt) => info.apply_deconstruct(self, stmt), - Statement::EnumConstruct(StatementEnumConstruct { variant, input, output }) => { + Statement::EnumConstruct(stmt) => { + let StatementEnumConstruct { variant, input, output } = &**stmt; info.replace( *output, ValueInfo::EnumConstruct { diff --git a/crates/cairo-lang-lowering/src/optimizations/scrub_units.rs b/crates/cairo-lang-lowering/src/optimizations/scrub_units.rs index 60b2fc0d02f..c8946744d4b 100644 --- a/crates/cairo-lang-lowering/src/optimizations/scrub_units.rs +++ b/crates/cairo-lang-lowering/src/optimizations/scrub_units.rs @@ -5,10 +5,7 @@ mod test; use cairo_lang_semantic::corelib; use salsa::Database; -use crate::{ - BlockEnd, Lowered, Statement, StatementCall, StatementStructConstruct, - StatementStructDestructure, -}; +use crate::{BlockEnd, Lowered, Statement, StatementStructConstruct, StatementStructDestructure}; /// Removes unit values from returns and call statements. pub fn scrub_units(db: &dyn Database, lowered: &mut Lowered<'_>) { @@ -21,17 +18,17 @@ pub fn scrub_units(db: &dyn Database, lowered: &mut Lowered<'_>) { let mut fixes = vec![]; for block in lowered.blocks.iter_mut() { for (idx, stmt) in block.statements.iter_mut().enumerate() { - let Statement::Call(StatementCall { function, outputs, .. }) = stmt else { + let Statement::Call(stmt_call) = stmt else { continue; }; // Unit scrubbing is only valid for user functions. - if function.body(db).unwrap().is_none() { + if stmt_call.function.body(db).unwrap().is_none() { continue; } - if lowered.variables[*outputs.last().unwrap()].ty == unit_ty { - fixes.push((idx, outputs.pop().unwrap())); + if lowered.variables[*stmt_call.outputs.last().unwrap()].ty == unit_ty { + fixes.push((idx, stmt_call.outputs.pop().unwrap())); } } diff --git a/crates/cairo-lang-lowering/src/optimizations/trim_unreachable.rs b/crates/cairo-lang-lowering/src/optimizations/trim_unreachable.rs index 159edfdaba6..dcc8f86ddcc 100644 --- a/crates/cairo-lang-lowering/src/optimizations/trim_unreachable.rs +++ b/crates/cairo-lang-lowering/src/optimizations/trim_unreachable.rs @@ -57,12 +57,12 @@ pub fn trim_unreachable<'db>(db: &'db dyn Database, lowered: &mut Lowered<'db>) block.statements.truncate(0); block.end = BlockEnd::Match { - info: MatchInfo::Enum(MatchEnumInfo { + info: Box::new(MatchInfo::Enum(MatchEnumInfo { concrete_enum_id: *concrete_enum_id, input: VarUsage { var_id: output, location }, arms: vec![], location, - }), + })), } } } diff --git a/crates/cairo-lang-lowering/src/panic/mod.rs b/crates/cairo-lang-lowering/src/panic/mod.rs index f6b4d360342..dc44cbf9859 100644 --- a/crates/cairo-lang-lowering/src/panic/mod.rs +++ b/crates/cairo-lang-lowering/src/panic/mod.rs @@ -156,12 +156,12 @@ fn lower_unsafe_panic<'db>( } block.end = BlockEnd::Match { - info: MatchInfo::Extern(MatchExternInfo { + info: Box::new(MatchInfo::Extern(MatchExternInfo { arms: vec![], location: err_data.location, function: panic_func_id, inputs: vec![], - }), + })), } } } @@ -378,7 +378,7 @@ impl<'db> PanicBlockLoweringContext<'db> { }); let cur_block_end = BlockEnd::Match { - info: MatchInfo::Enum(MatchEnumInfo { + info: Box::new(MatchInfo::Enum(MatchEnumInfo { concrete_enum_id: callee_info.ok_variant.concrete_enum_id, input: VarUsage { var_id: panic_result_var, location }, arms: vec![ @@ -394,7 +394,7 @@ impl<'db> PanicBlockLoweringContext<'db> { }, ], location, - }), + })), }; Ok((cur_block_end, Some(block_continuation))) @@ -411,11 +411,13 @@ impl<'db> PanicBlockLoweringContext<'db> { err_data.var_id } else { let output = self.new_var(ty, location); - self.statements.push(Statement::EnumConstruct(StatementEnumConstruct { - variant: self.ctx.panic_info.err_variant, - input: err_data, - output, - })); + self.statements.push(Statement::EnumConstruct(Box::new( + StatementEnumConstruct { + variant: self.ctx.panic_info.err_variant, + input: err_data, + output, + }, + ))); output }; BlockEnd::Return(vec![VarUsage { var_id: output, location }], location) @@ -431,11 +433,11 @@ impl<'db> PanicBlockLoweringContext<'db> { // Wrap with PanicResult::Ok. let ty = self.ctx.panic_info.actual_return_ty; let output = self.new_var(ty, location); - self.statements.push(Statement::EnumConstruct(StatementEnumConstruct { + self.statements.push(Statement::EnumConstruct(Box::new(StatementEnumConstruct { variant: self.ctx.panic_info.ok_variant, input: VarUsage { var_id: tupled_res, location }, output, - })); + }))); BlockEnd::Return(vec![VarUsage { var_id: output, location }], location) } BlockEnd::NotSet => unreachable!(), diff --git a/crates/cairo-lang-lowering/src/specialization.rs b/crates/cairo-lang-lowering/src/specialization.rs index 93031010285..487a226d7eb 100644 --- a/crates/cairo-lang-lowering/src/specialization.rs +++ b/crates/cairo-lang-lowering/src/specialization.rs @@ -282,11 +282,16 @@ pub fn specialized_function_lowered<'db>( })); } SpecializationArgBuildingState::BuildEnum { variant, payload } => { - statements.push(Statement::EnumConstruct(StatementEnumConstructObj { - variant, - input: VarUsage { var_id: payload, location: variables[payload].location }, - output: var_id, - })); + statements.push(Statement::EnumConstruct(Box::new( + StatementEnumConstructObj { + variant, + input: VarUsage { + var_id: payload, + location: variables[payload].location, + }, + output: var_id, + }, + ))); } } } diff --git a/crates/cairo-lang-lowering/src/utils.rs b/crates/cairo-lang-lowering/src/utils.rs index 05ea4245f1f..59087c762e7 100644 --- a/crates/cairo-lang-lowering/src/utils.rs +++ b/crates/cairo-lang-lowering/src/utils.rs @@ -74,11 +74,13 @@ pub trait RebuilderEx<'db>: Rebuilder<'db> { outputs: stmt.outputs.iter().map(|v| self.map_var_id(*v)).collect(), }) } - Statement::EnumConstruct(stmt) => Statement::EnumConstruct(StatementEnumConstruct { - variant: stmt.variant, - input: self.map_var_usage(stmt.input), - output: self.map_var_id(stmt.output), - }), + Statement::EnumConstruct(stmt) => { + Statement::EnumConstruct(Box::new(StatementEnumConstruct { + variant: stmt.variant, + input: self.map_var_usage(stmt.input), + output: self.map_var_id(stmt.output), + })) + } Statement::Snapshot(stmt) => Statement::Snapshot(StatementSnapshot::new( self.map_var_usage(stmt.input), self.map_var_id(stmt.original()), @@ -125,7 +127,7 @@ pub trait RebuilderEx<'db>: Rebuilder<'db> { } BlockEnd::NotSet => unreachable!(), BlockEnd::Match { info } => BlockEnd::Match { - info: match info { + info: Box::new(match &**info { MatchInfo::Extern(stmt) => MatchInfo::Extern(MatchExternInfo { function: stmt.function, inputs: stmt.inputs.iter().map(|v| self.map_var_usage(*v)).collect(), @@ -180,7 +182,7 @@ pub trait RebuilderEx<'db>: Rebuilder<'db> { .collect(), location: self.map_location(stmt.location), }), - }, + }), }, }; self.transform_end(&mut end); diff --git a/crates/cairo-lang-sierra-generator/src/block_generator.rs b/crates/cairo-lang-sierra-generator/src/block_generator.rs index 0e3fc6e6418..404dfe55bf1 100644 --- a/crates/cairo-lang-sierra-generator/src/block_generator.rs +++ b/crates/cairo-lang-sierra-generator/src/block_generator.rs @@ -187,7 +187,7 @@ fn generate_block_code<'db>( context.maybe_set_cairo_location(Some(*statement_cairo_location)); - match info { + match &**info { lowering::MatchInfo::Extern(s) => { generate_match_extern_code(context, block_gen_stack, s, &statement_location) }