Skip to content

Commit d6b77e2

Browse files
authored
enable eip7939 opcode CLZ (#64)
* enable eip7939 opcode CLZ * update * update * add clz test * fix error
1 parent 205ec3e commit d6b77e2

File tree

1 file changed

+82
-2
lines changed

1 file changed

+82
-2
lines changed

src/instructions.rs

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ where
6969
/// - `SELFDESTRUCT`
7070
/// - `MCOPY`
7171
/// - `DIFFICULTY`
72+
/// - `CLZ`
7273
pub fn make_scroll_instruction_table<WIRE: InterpreterTypes, HOST: ScrollContextTr>(
7374
) -> InstructionTable<WIRE, HOST> {
7475
let mut table = instruction_table::<WIRE, HOST>();
@@ -82,6 +83,7 @@ pub fn make_scroll_instruction_table<WIRE: InterpreterTypes, HOST: ScrollContext
8283
table[opcode::SELFDESTRUCT as usize] = Instruction::new(selfdestruct::<WIRE, HOST>, 0);
8384
table[opcode::MCOPY as usize] = Instruction::new(mcopy::<WIRE, HOST>, 0);
8485
table[opcode::DIFFICULTY as usize] = Instruction::new(difficulty::<WIRE, HOST>, 2);
86+
table[opcode::CLZ as usize] = Instruction::new(clz::<WIRE, HOST>, 5);
8587

8688
table
8789
}
@@ -245,6 +247,23 @@ pub fn difficulty<WIRE: InterpreterTypes, H: Host + ?Sized>(
245247
push!(context.interpreter, DIFFICULTY);
246248
}
247249

250+
/// Implements the CLZ instruction
251+
///
252+
/// EIP-7939 count leading zeros.
253+
fn clz<WIRE: InterpreterTypes, H: ScrollContextTr>(context: InstructionContext<'_, H, WIRE>) {
254+
let host = context.host;
255+
let interpreter = context.interpreter;
256+
if !host.cfg().spec().is_enabled_in(ScrollSpecId::GALILEO) {
257+
interpreter.halt(InstructionResult::NotActivated);
258+
return;
259+
}
260+
261+
popn_top!([], op1, interpreter);
262+
263+
let leading_zeros = op1.leading_zeros();
264+
*op1 = U256::from(leading_zeros);
265+
}
266+
248267
// HELPER FUNCTIONS
249268
// ================================================================================================
250269

@@ -260,7 +279,7 @@ fn compute_block_hash(chain_id: u64, block_number: u64) -> U256 {
260279

261280
#[cfg(test)]
262281
mod tests {
263-
use super::{compute_block_hash, make_scroll_instruction_table};
282+
use super::{clz, compute_block_hash, make_scroll_instruction_table};
264283
use crate::{
265284
builder::{DefaultScrollContext, ScrollContext},
266285
instructions::HISTORY_STORAGE_ADDRESS,
@@ -270,7 +289,7 @@ mod tests {
270289
use revm::{
271290
bytecode::{opcode::*, Bytecode},
272291
database::{EmptyDB, InMemoryDB},
273-
interpreter::Interpreter,
292+
interpreter::{push, InstructionContext, Interpreter},
274293
primitives::{Bytes, U256},
275294
DatabaseRef,
276295
};
@@ -361,4 +380,65 @@ mod tests {
361380
let actual_gas_used = interpreter.gas.used();
362381
assert_eq!(actual_gas_used, expected_gas_used);
363382
}
383+
384+
#[test]
385+
fn test_clz() {
386+
use revm::primitives::uint;
387+
388+
let spec = GALILEO;
389+
let db = EmptyDB::new();
390+
let mut scroll_context = ScrollContext::scroll().with_db(InMemoryDB::new(db));
391+
scroll_context.modify_cfg(|cfg| cfg.spec = spec);
392+
393+
let mut interpreter = Interpreter::default();
394+
395+
struct TestCase {
396+
value: U256,
397+
expected: U256,
398+
}
399+
400+
uint! {
401+
let test_cases = [
402+
TestCase { value: 0x0_U256, expected: 256_U256 },
403+
TestCase { value: 0x1_U256, expected: 255_U256 },
404+
TestCase { value: 0x2_U256, expected: 254_U256 },
405+
TestCase { value: 0x3_U256, expected: 254_U256 },
406+
TestCase { value: 0x4_U256, expected: 253_U256 },
407+
TestCase { value: 0x7_U256, expected: 253_U256 },
408+
TestCase { value: 0x8_U256, expected: 252_U256 },
409+
TestCase { value: 0xff_U256, expected: 248_U256 },
410+
TestCase { value: 0x100_U256, expected: 247_U256 },
411+
TestCase { value: 0xffff_U256, expected: 240_U256 },
412+
TestCase {
413+
value: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_U256, // U256::MAX
414+
expected: 0_U256,
415+
},
416+
TestCase {
417+
value: 0x8000000000000000000000000000000000000000000000000000000000000000_U256, // 1 << 255
418+
expected: 0_U256,
419+
},
420+
TestCase { // Smallest value with 1 leading zero
421+
value: 0x4000000000000000000000000000000000000000000000000000000000000000_U256, // 1 << 254
422+
expected: 1_U256,
423+
},
424+
TestCase { // Value just below 1 << 255
425+
value: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_U256,
426+
expected: 1_U256,
427+
},
428+
];
429+
}
430+
431+
for test in test_cases {
432+
push!(interpreter, test.value);
433+
let context =
434+
InstructionContext { host: &mut scroll_context, interpreter: &mut interpreter };
435+
clz(context);
436+
let res = interpreter.stack.pop().unwrap();
437+
assert_eq!(
438+
res, test.expected,
439+
"CLZ for value {:#x} failed. Expected: {}, Got: {}",
440+
test.value, test.expected, res
441+
);
442+
}
443+
}
364444
}

0 commit comments

Comments
 (0)