6969/// - `SELFDESTRUCT`
7070/// - `MCOPY`
7171/// - `DIFFICULTY`
72+ /// - `CLZ`
7273pub 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) ]
262281mod 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