From e572def9878c5ac26ed31a7a27725915fb51c3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Mon, 23 Sep 2024 14:22:35 +0000 Subject: [PATCH 1/3] Add a `BorrowedLocal` to reduce local bloat --- clar2wasm/src/wasm_generator.rs | 49 ++++++++++++++++++++++++++++---- clar2wasm/src/words/sequences.rs | 8 ++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/clar2wasm/src/wasm_generator.rs b/clar2wasm/src/wasm_generator.rs index 41387631..d16000c4 100644 --- a/clar2wasm/src/wasm_generator.rs +++ b/clar2wasm/src/wasm_generator.rs @@ -1,5 +1,8 @@ use std::borrow::BorrowMut; -use std::collections::HashMap; +use std::collections::{HashMap, hash_map::Entry}; +use std::ops::Deref; +use std::rc::Rc; +use std::cell::RefCell; use clarity::vm::analysis::ContractAnalysis; use clarity::vm::diagnostic::DiagnosableError; @@ -24,6 +27,7 @@ use crate::wasm_utils::{ owned_ordered_tuple_signature, }; use crate::words; +use crate::debug::DebugExt; // First free position after data directly defined in standard.wat pub const END_OF_STANDARD_DATA: u32 = 1352; @@ -60,6 +64,7 @@ pub struct WasmGenerator { /// Size of the maximum extra work space required by the stdlib functions /// to be available on the stack. max_work_space: u32, + local_pool: Rc>>>, } #[derive(Debug, Clone, Default)] @@ -268,6 +273,28 @@ fn get_global(module: &Module, name: &str) -> Result { }) } +pub(crate) struct BorrowedLocal { + id: LocalId, + ty: ValType, + pool: Rc>>> +} + +impl Drop for BorrowedLocal { + fn drop(&mut self) { + match (*self.pool).borrow_mut().entry(self.ty) { + Entry::Occupied(mut list) => list.get_mut().push(self.id), + Entry::Vacant(e) => { e.insert(vec![self.id]);} + } + } +} + +impl Deref for BorrowedLocal { + type Target = LocalId; + fn deref(&self) -> &Self::Target { + &self.id + } +} + impl WasmGenerator { pub fn new(contract_analysis: ContractAnalysis) -> Result { let standard_lib_wasm: &[u8] = include_bytes!("standard/standard.wasm"); @@ -292,6 +319,7 @@ impl WasmGenerator { max_work_space: 0, datavars_types: HashMap::new(), maps_types: HashMap::new(), + local_pool: Rc::new(RefCell::new(HashMap::new())), }) } @@ -882,6 +910,15 @@ impl WasmGenerator { (offset, size) } + pub(crate) fn borrow_local(&mut self, ty: ValType) -> BorrowedLocal { + let reuse = (*self.local_pool).borrow_mut().get_mut(&ty).and_then(Vec::pop); + BorrowedLocal { + id: reuse.unwrap_or_else(|| self.module.locals.add(ty)), + ty: ty, + pool: self.local_pool.clone() + } + } + /// Write the value that is on the top of the data stack, which has type /// `ty`, to the memory, at offset stored in local variable, /// `offset_local`, plus constant offset `offset`. Returns the number of @@ -898,17 +935,17 @@ impl WasmGenerator { TypeSignature::IntType | TypeSignature::UIntType => { // Data stack: TOP | High | Low | ... // Save the high/low to locals. - let high = self.module.locals.add(ValType::I64); - let low = self.module.locals.add(ValType::I64); - builder.local_set(high).local_set(low); + let high = self.borrow_local(ValType::I64); + let low = self.borrow_local(ValType::I64); + builder.local_set(*high).local_set(*low); // Store the high/low to memory. - builder.local_get(offset_local).local_get(low).store( + builder.local_get(offset_local).local_get(*low).store( memory, StoreKind::I64 { atomic: false }, MemArg { align: 8, offset }, ); - builder.local_get(offset_local).local_get(high).store( + builder.local_get(offset_local).local_get(*high).store( memory, StoreKind::I64 { atomic: false }, MemArg { diff --git a/clar2wasm/src/words/sequences.rs b/clar2wasm/src/words/sequences.rs index d8c74268..aef6d66e 100644 --- a/clar2wasm/src/words/sequences.rs +++ b/clar2wasm/src/words/sequences.rs @@ -2039,6 +2039,14 @@ mod tests { crosscheck_compare_only(snippet); } + #[test] + fn test_large_list() { + let n = 50000 / 2 + 1; + crosscheck_compare_only( + &format!("(list {})", "9922 ".repeat(n)), + ); + } + // // Module with tests that should only be executed // when running Clarity::V2 or Clarity::v3. From 0b9a739fb785013acdbb830d3baa6be49d6595ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Mon, 23 Sep 2024 14:48:40 +0000 Subject: [PATCH 2/3] rustfmt, clippy, invalid import --- clar2wasm/src/wasm_generator.rs | 56 ++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/clar2wasm/src/wasm_generator.rs b/clar2wasm/src/wasm_generator.rs index d16000c4..bce6202e 100644 --- a/clar2wasm/src/wasm_generator.rs +++ b/clar2wasm/src/wasm_generator.rs @@ -1,8 +1,8 @@ use std::borrow::BorrowMut; -use std::collections::{HashMap, hash_map::Entry}; +use std::cell::RefCell; +use std::collections::{hash_map::Entry, HashMap}; use std::ops::Deref; use std::rc::Rc; -use std::cell::RefCell; use clarity::vm::analysis::ContractAnalysis; use clarity::vm::diagnostic::DiagnosableError; @@ -27,7 +27,6 @@ use crate::wasm_utils::{ owned_ordered_tuple_signature, }; use crate::words; -use crate::debug::DebugExt; // First free position after data directly defined in standard.wat pub const END_OF_STANDARD_DATA: u32 = 1352; @@ -64,7 +63,7 @@ pub struct WasmGenerator { /// Size of the maximum extra work space required by the stdlib functions /// to be available on the stack. max_work_space: u32, - local_pool: Rc>>>, + local_pool: Rc>>>, } #[derive(Debug, Clone, Default)] @@ -274,25 +273,27 @@ fn get_global(module: &Module, name: &str) -> Result { } pub(crate) struct BorrowedLocal { - id: LocalId, - ty: ValType, - pool: Rc>>> + id: LocalId, + ty: ValType, + pool: Rc>>>, } impl Drop for BorrowedLocal { - fn drop(&mut self) { - match (*self.pool).borrow_mut().entry(self.ty) { - Entry::Occupied(mut list) => list.get_mut().push(self.id), - Entry::Vacant(e) => { e.insert(vec![self.id]);} - } - } + fn drop(&mut self) { + match (*self.pool).borrow_mut().entry(self.ty) { + Entry::Occupied(mut list) => list.get_mut().push(self.id), + Entry::Vacant(e) => { + e.insert(vec![self.id]); + } + } + } } impl Deref for BorrowedLocal { - type Target = LocalId; - fn deref(&self) -> &Self::Target { - &self.id - } + type Target = LocalId; + fn deref(&self) -> &Self::Target { + &self.id + } } impl WasmGenerator { @@ -319,7 +320,7 @@ impl WasmGenerator { max_work_space: 0, datavars_types: HashMap::new(), maps_types: HashMap::new(), - local_pool: Rc::new(RefCell::new(HashMap::new())), + local_pool: Rc::new(RefCell::new(HashMap::new())), }) } @@ -910,14 +911,17 @@ impl WasmGenerator { (offset, size) } - pub(crate) fn borrow_local(&mut self, ty: ValType) -> BorrowedLocal { - let reuse = (*self.local_pool).borrow_mut().get_mut(&ty).and_then(Vec::pop); - BorrowedLocal { - id: reuse.unwrap_or_else(|| self.module.locals.add(ty)), - ty: ty, - pool: self.local_pool.clone() - } - } + pub(crate) fn borrow_local(&mut self, ty: ValType) -> BorrowedLocal { + let reuse = (*self.local_pool) + .borrow_mut() + .get_mut(&ty) + .and_then(Vec::pop); + BorrowedLocal { + id: reuse.unwrap_or_else(|| self.module.locals.add(ty)), + ty, + pool: self.local_pool.clone(), + } + } /// Write the value that is on the top of the data stack, which has type /// `ty`, to the memory, at offset stored in local variable, From 4aca6b73dccc03eb9280b1d94cb7bc70e64deec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristoffer=20Str=C3=B6m?= Date: Mon, 23 Sep 2024 15:09:24 +0000 Subject: [PATCH 3/3] rustfmt --- clar2wasm/src/wasm_generator.rs | 3 ++- clar2wasm/src/words/sequences.rs | 12 +++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/clar2wasm/src/wasm_generator.rs b/clar2wasm/src/wasm_generator.rs index bce6202e..fd6868df 100644 --- a/clar2wasm/src/wasm_generator.rs +++ b/clar2wasm/src/wasm_generator.rs @@ -1,6 +1,7 @@ use std::borrow::BorrowMut; use std::cell::RefCell; -use std::collections::{hash_map::Entry, HashMap}; +use std::collections::hash_map::Entry; +use std::collections::HashMap; use std::ops::Deref; use std::rc::Rc; diff --git a/clar2wasm/src/words/sequences.rs b/clar2wasm/src/words/sequences.rs index aef6d66e..672501c5 100644 --- a/clar2wasm/src/words/sequences.rs +++ b/clar2wasm/src/words/sequences.rs @@ -2039,13 +2039,11 @@ mod tests { crosscheck_compare_only(snippet); } - #[test] - fn test_large_list() { - let n = 50000 / 2 + 1; - crosscheck_compare_only( - &format!("(list {})", "9922 ".repeat(n)), - ); - } + #[test] + fn test_large_list() { + let n = 50000 / 2 + 1; + crosscheck_compare_only(&format!("(list {})", "9922 ".repeat(n))); + } // // Module with tests that should only be executed