Skip to content

Commit 1fc3353

Browse files
authored
Merge pull request #3 from openSVM/openSVM_lessvm_issue_2_cd25dcd3
LessVM Codebase (Run ID: openSVM_lessvm_issue_2_cd25dcd3)
2 parents 4de5056 + 4ac68b8 commit 1fc3353

File tree

3 files changed

+215
-59
lines changed

3 files changed

+215
-59
lines changed

lessvm-solana/src/vm/core.rs

Lines changed: 175 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,14 @@ impl<'a> VM<'a> {
8484
accounts: &'a [AccountInfo<'a>],
8585
_instruction_data: &'a [u8],
8686
) -> Self {
87-
let data_structures = DataStructureStore {
88-
btrees: vec![None; 8],
89-
tries: vec![None; 8],
90-
graphs: vec![None; 8],
91-
ohlcvs: vec![None; 8],
92-
hypergraphs: vec![None; 8],
93-
};
9487
Self {
9588
pc: 0,
9689
gas: Gas::new(200_000),
9790
stack: Stack::new(),
9891
memory: Memory::new(),
9992
accounts: AccountsView { accounts, current: 0 },
10093
program_id,
101-
data_structures,
94+
data_structures: DataStructureStore::new(),
10295
reentrancy_guard: ReentrancyGuard::new(),
10396
tracer: Box::new(DefaultTracer),
10497
}
@@ -146,8 +139,21 @@ impl<'a> VM<'a> {
146139
return Err(VMError::StackUnderflow);
147140
}
148141

149-
let values = _mm256_loadu_si256(self.stack.as_simd_ptr()? as *const __m256i);
150-
let result = _mm256_add_epi64(values, values);
142+
// Get two vectors from the stack (4 values each)
143+
let values1 = _mm256_loadu_si256(self.stack.as_simd_ptr()? as *const __m256i);
144+
145+
// Pop the first 4 values and get the next 4 values
146+
self.stack.pop()?;
147+
self.stack.pop()?;
148+
self.stack.pop()?;
149+
self.stack.pop()?;
150+
151+
let values2 = _mm256_loadu_si256(self.stack.as_simd_ptr()? as *const __m256i);
152+
153+
// Add the two vectors
154+
let result = _mm256_add_epi64(values1, values2);
155+
156+
// Store the result back to the stack
151157
_mm256_storeu_si256(self.stack.as_simd_mut_ptr()? as *mut __m256i, result);
152158
Ok(())
153159
}
@@ -236,6 +242,67 @@ impl<'a> VM<'a> {
236242
}
237243
self.stack.push(Value(div as u64))?;
238244
},
245+
OpCode::Mod => {
246+
let b = self.stack.pop()?;
247+
let a = self.stack.pop()?;
248+
if b.0 == 0 {
249+
return Err(VMError::DivisionByZero.into());
250+
}
251+
let result = Value(a.0 % b.0);
252+
self.stack.push(result)?;
253+
},
254+
OpCode::Exp => {
255+
let exponent = self.stack.pop()?.0;
256+
let base = self.stack.pop()?.0;
257+
258+
// Handle potential overflow
259+
if exponent > 64 && base > 1 {
260+
return Err(VMError::ArithmeticOverflow.into());
261+
}
262+
263+
let mut result = 1u64;
264+
let mut base_power = base;
265+
let mut exp = exponent;
266+
267+
// Fast exponentiation algorithm
268+
while exp > 0 {
269+
if exp & 1 == 1 {
270+
result = result.checked_mul(base_power)
271+
.ok_or(VMError::ArithmeticOverflow)?;
272+
}
273+
base_power = base_power.checked_mul(base_power)
274+
.ok_or(VMError::ArithmeticOverflow)?;
275+
exp >>= 1;
276+
}
277+
278+
self.stack.push(Value(result))?;
279+
},
280+
OpCode::SignExtend => {
281+
let byte_num = self.stack.pop()?.0 as usize;
282+
let value = self.stack.pop()?.0;
283+
284+
if byte_num >= 8 {
285+
// No change if byte_num is out of range
286+
self.stack.push(Value(value))?;
287+
return Ok(());
288+
}
289+
290+
let bit_position = (byte_num + 1) * 8 - 1;
291+
let mask = 1u64 << bit_position;
292+
let sign_bit = value & mask;
293+
294+
let result = if sign_bit != 0 {
295+
// If sign bit is 1, set all higher bits to 1
296+
let higher_bits_mask = !((1u64 << (bit_position + 1)) - 1);
297+
value | higher_bits_mask
298+
} else {
299+
// If sign bit is 0, clear all higher bits
300+
let lower_bits_mask = (1u64 << (bit_position + 1)) - 1;
301+
value & lower_bits_mask
302+
};
303+
304+
self.stack.push(Value(result))?;
305+
},
239306

240307
// Memory Operations
241308
OpCode::Load => {
@@ -266,6 +333,16 @@ impl<'a> VM<'a> {
266333
OpCode::Msize => {
267334
self.stack.push(Value(self.memory.size() as u64))?;
268335
},
336+
OpCode::Mload8 => {
337+
let offset = self.stack.pop()?.0 as usize;
338+
let value = self.memory.load8(offset)? as u64;
339+
self.stack.push(Value(value))?;
340+
},
341+
OpCode::Mstore8 => {
342+
let offset = self.stack.pop()?.0 as usize;
343+
let value = self.stack.pop()?.0 as u8;
344+
self.memory.store8(offset, value)?;
345+
},
269346

270347
// Control Flow
271348
OpCode::Jump => {
@@ -454,9 +531,7 @@ impl<'a> VM<'a> {
454531
OpCode::BTreeClear => {
455532
let id = self.stack.pop()?.0 as usize;
456533

457-
if id >= self.data_structures.btrees.len() {
458-
return Err(VMError::InvalidDataStructureOperation.into());
459-
}
534+
self.data_structures.ensure_capacity(DataStructureType::BTreeMap, id);
460535

461536
if let Some(btree) = &mut self.data_structures.btrees[id] {
462537
btree.clear();
@@ -468,9 +543,7 @@ impl<'a> VM<'a> {
468543
// Trie operations
469544
OpCode::TrieCreate => {
470545
let id = self.stack.pop()?.0 as usize;
471-
if id >= self.data_structures.tries.len() {
472-
return Err(VMError::InvalidDataStructureOperation.into());
473-
}
546+
self.data_structures.ensure_capacity(DataStructureType::Trie, id);
474547
self.data_structures.tries[id] = Some(TrieDS::new());
475548
},
476549
OpCode::TrieInsert => {
@@ -480,9 +553,7 @@ impl<'a> VM<'a> {
480553
let key_ptr = self.stack.pop()?.0 as usize;
481554
let id = self.stack.pop()?.0 as usize;
482555

483-
if id >= self.data_structures.tries.len() {
484-
return Err(VMError::InvalidDataStructureOperation.into());
485-
}
556+
self.data_structures.ensure_capacity(DataStructureType::Trie, id);
486557

487558
// Read key from memory
488559
let key = self.memory.load(key_ptr, key_len)?;
@@ -504,9 +575,7 @@ impl<'a> VM<'a> {
504575
return Err(VMError::InvalidDataStructureOperation.into());
505576
}
506577

507-
if id >= self.data_structures.tries.len() {
508-
return Err(VMError::InvalidDataStructureOperation.into());
509-
}
578+
self.data_structures.ensure_capacity(DataStructureType::Trie, id);
510579

511580
// Read key from memory with bounds check
512581
let key = match self.memory.load(key_ptr, key_len) {
@@ -534,9 +603,7 @@ impl<'a> VM<'a> {
534603
return Err(VMError::InvalidDataStructureOperation.into());
535604
}
536605

537-
if id >= self.data_structures.tries.len() {
538-
return Err(VMError::InvalidDataStructureOperation.into());
539-
}
606+
self.data_structures.ensure_capacity(DataStructureType::Trie, id);
540607

541608
// Read key from memory with bounds check
542609
let key = match self.memory.load(key_ptr, key_len) {
@@ -554,9 +621,7 @@ impl<'a> VM<'a> {
554621
OpCode::TrieClear => {
555622
let id = self.stack.pop()?.0 as usize;
556623

557-
if id >= self.data_structures.tries.len() {
558-
return Err(VMError::InvalidDataStructureOperation.into());
559-
}
624+
self.data_structures.ensure_capacity(DataStructureType::Trie, id);
560625

561626
if let Some(trie) = &mut self.data_structures.tries[id] {
562627
trie.clear();
@@ -595,21 +660,97 @@ impl<'a> VM<'a> {
595660
// OHLCV operations
596661
OpCode::OhlcvCreate => {
597662
let id = self.stack.pop()?.0 as usize;
598-
if id >= self.data_structures.ohlcvs.len() {
599-
return Err(VMError::InvalidDataStructureOperation.into());
600-
}
663+
self.data_structures.ensure_capacity(DataStructureType::OHLCV, id);
601664
self.data_structures.ohlcvs[id] = Some(OHLCVDS::new());
602665
},
603666

604667
// Hypergraph operations
605668
OpCode::HyperCreate => {
606669
let id = self.stack.pop()?.0 as usize;
607-
if id >= self.data_structures.hypergraphs.len() {
608-
return Err(VMError::InvalidDataStructureOperation.into());
609-
}
670+
self.data_structures.ensure_capacity(DataStructureType::Hypergraph, id);
610671
self.data_structures.hypergraphs[id] = Some(HypergraphDS::new());
611672
},
612673

674+
// Bitwise Operations
675+
OpCode::And => {
676+
let b = self.stack.pop()?.0;
677+
let a = self.stack.pop()?.0;
678+
self.stack.push(Value(a & b))?;
679+
},
680+
OpCode::Or => {
681+
let b = self.stack.pop()?.0;
682+
let a = self.stack.pop()?.0;
683+
self.stack.push(Value(a | b))?;
684+
},
685+
OpCode::Xor => {
686+
let b = self.stack.pop()?.0;
687+
let a = self.stack.pop()?.0;
688+
self.stack.push(Value(a ^ b))?;
689+
},
690+
OpCode::Not => {
691+
let a = self.stack.pop()?.0;
692+
self.stack.push(Value(!a))?;
693+
},
694+
OpCode::Byte => {
695+
let byte_index = self.stack.pop()?.0 as usize;
696+
let value = self.stack.pop()?.0;
697+
698+
let result = if byte_index >= 32 {
699+
0
700+
} else {
701+
// Extract the specified byte
702+
let shift = (31 - byte_index) * 8;
703+
(value >> shift) & 0xFF
704+
};
705+
706+
self.stack.push(Value(result))?;
707+
},
708+
OpCode::Shl => {
709+
let shift = self.stack.pop()?.0;
710+
let value = self.stack.pop()?.0;
711+
712+
// Avoid undefined behavior with large shifts
713+
let result = if shift >= 64 {
714+
0
715+
} else {
716+
value << shift
717+
};
718+
719+
self.stack.push(Value(result))?;
720+
},
721+
OpCode::Shr => {
722+
let shift = self.stack.pop()?.0;
723+
let value = self.stack.pop()?.0;
724+
725+
// Avoid undefined behavior with large shifts
726+
let result = if shift >= 64 {
727+
0
728+
} else {
729+
value >> shift
730+
};
731+
732+
self.stack.push(Value(result))?;
733+
},
734+
OpCode::Sar => {
735+
let shift = self.stack.pop()?.0;
736+
let value = self.stack.pop()?.0;
737+
738+
// Arithmetic right shift (sign-extending)
739+
let result = if shift >= 64 {
740+
if (value as i64) < 0 {
741+
u64::MAX // All 1s for negative numbers
742+
} else {
743+
0 // All 0s for positive numbers
744+
}
745+
} else {
746+
// Rust doesn't have arithmetic right shift for unsigned types
747+
// Convert to signed, shift, then back to unsigned
748+
((value as i64) >> shift) as u64
749+
};
750+
751+
self.stack.push(Value(result))?;
752+
},
753+
613754
OpCode::Halt => {
614755
break;
615756
},

lessvm-solana/src/vm/data_structures.rs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,8 @@ use std::collections::{BTreeMap as StdBTreeMap, HashMap, HashSet};
1212
use std::collections::VecDeque;
1313
use solana_program::msg;
1414

15-
// Constants for data structure IDs and maximum sizes
15+
// Constants for data structure IDs
1616
pub const MAX_DATA_STRUCTURES: usize = 32;
17-
pub const MAX_GRAPH_NODES: usize = 1024;
18-
pub const MAX_TRIE_NODES: usize = 1024;
19-
pub const MAX_BTREE_NODES: usize = 1024;
20-
pub const MAX_OHLCV_ENTRIES: usize = 1024;
2117

2218
/// The type of data structure
2319
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -136,10 +132,6 @@ impl TrieDS {
136132
}
137133

138134
pub fn insert(&mut self, key: &[u8], value: Value) -> Result<(), VMError> {
139-
if self.nodes.len() >= MAX_TRIE_NODES {
140-
return Err(VMError::StackOverflow); // Reuse error for memory limit
141-
}
142-
143135
let mut current_node = self.root;
144136

145137
for &byte in key {
@@ -330,10 +322,6 @@ impl OHLCVDS {
330322
}
331323

332324
pub fn add_entry(&mut self, entry: OHLCVEntry) -> Result<(), VMError> {
333-
if self.data.len() >= MAX_OHLCV_ENTRIES {
334-
return Err(VMError::StackOverflow); // Reuse error for memory limit
335-
}
336-
337325
// Typically OHLCV data should be sorted by timestamp
338326
// Find the right position to insert
339327
let pos = self.data.binary_search_by_key(&entry.timestamp, |e| e.timestamp)
@@ -413,10 +401,6 @@ impl HypergraphDS {
413401
}
414402

415403
pub fn add_node(&mut self, node_id: u64, value: u64) -> Result<(), VMError> {
416-
if self.node_values.len() >= MAX_GRAPH_NODES {
417-
return Err(VMError::StackOverflow); // Reuse error for memory limit
418-
}
419-
420404
self.node_values.insert(node_id, value);
421405
Ok(())
422406
}

0 commit comments

Comments
 (0)