Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ inline void signextend(StackTop stack) noexcept
}
}

inline void clz(StackTop stack) noexcept
{
auto& x = stack.top();
x = intx::clz(x);
}

inline void lt(StackTop stack) noexcept
{
const auto& x = stack.pop();
Expand Down
1 change: 1 addition & 0 deletions lib/evmone/instructions_opcodes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ enum Opcode : uint8_t
OP_MULMOD = 0x09,
OP_EXP = 0x0a,
OP_SIGNEXTEND = 0x0b,
OP_CLZ = 0x0c,

OP_LT = 0x10,
OP_GT = 0x11,
Expand Down
2 changes: 2 additions & 0 deletions lib/evmone/instructions_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ constexpr inline GasCostTable gas_costs = []() noexcept {
table[EVMC_PRAGUE] = table[EVMC_CANCUN];

table[EVMC_OSAKA] = table[EVMC_PRAGUE];
table[EVMC_OSAKA][OP_CLZ] = 3;

table[EVMC_EXPERIMENTAL] = table[EVMC_OSAKA];

Expand Down Expand Up @@ -266,6 +267,7 @@ constexpr inline std::array<Traits, 256> traits = []() noexcept {
table[OP_MULMOD] = {"MULMOD", 0, false, 3, -2, EVMC_FRONTIER, REV_EOF1};
table[OP_EXP] = {"EXP", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1};
table[OP_SIGNEXTEND] = {"SIGNEXTEND", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1};
table[OP_CLZ] = {"CLZ", 0, false, 1, 0, EVMC_OSAKA, REV_EOF1};

table[OP_LT] = {"LT", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1};
table[OP_GT] = {"GT", 0, false, 2, -1, EVMC_FRONTIER, REV_EOF1};
Expand Down
2 changes: 1 addition & 1 deletion lib/evmone/instructions_xmacro.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
ON_OPCODE_IDENTIFIER(OP_MULMOD, mulmod) \
ON_OPCODE_IDENTIFIER(OP_EXP, exp) \
ON_OPCODE_IDENTIFIER(OP_SIGNEXTEND, signextend) \
ON_OPCODE_UNDEFINED(0x0c) \
ON_OPCODE_IDENTIFIER(OP_CLZ, clz) \
ON_OPCODE_UNDEFINED(0x0d) \
ON_OPCODE_UNDEFINED(0x0e) \
ON_OPCODE_UNDEFINED(0x0f) \
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ target_sources(
evm_eip3860_initcode_test.cpp
evm_eip4844_blobhash_test.cpp
evm_eip7516_blobbasefee_test.cpp
evm_eip7939_clz_test.cpp
evm_eof_test.cpp
evm_eof_calls_test.cpp
evm_eof_function_test.cpp
Expand Down
88 changes: 88 additions & 0 deletions test/unittests/evm_eip7939_clz_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2024 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

/// This file contains EVM unit tests for EIP-7939 "CLZ instruction"
/// https://eips.ethereum.org/EIPS/eip-7939

#include "evm_fixture.hpp"

using namespace evmc::literals;
using namespace evmone::test;

TEST_P(evm, clz_pre_osaka)
{
rev = EVMC_PRAGUE;
const auto code = bytecode{OP_CLZ};

execute(code);
EXPECT_STATUS(EVMC_UNDEFINED_INSTRUCTION);
}

TEST_P(evm, clz_zero)
{
rev = EVMC_OSAKA;
execute(push(0) + OP_CLZ + ret_top());
EXPECT_GAS_USED(EVMC_SUCCESS, 21);
EXPECT_OUTPUT_INT(256);
}

TEST_P(evm, clz_one)
{
rev = EVMC_OSAKA;
execute(push(1) + OP_CLZ + ret_top());
EXPECT_GAS_USED(EVMC_SUCCESS, 21);
EXPECT_OUTPUT_INT(255);
}

TEST_P(evm, clz_msb_set)
{
rev = EVMC_OSAKA;
// Test with MSB set (0x8000...0000)
execute(push("0x8000000000000000000000000000000000000000000000000000000000000000") + OP_CLZ + ret_top());
EXPECT_GAS_USED(EVMC_SUCCESS, 21);
EXPECT_OUTPUT_INT(0);
}

TEST_P(evm, clz_second_msb_set)
{
rev = EVMC_OSAKA;
// Test with second MSB set (0x4000...0000)
execute(push("0x4000000000000000000000000000000000000000000000000000000000000000") + OP_CLZ + ret_top());
EXPECT_GAS_USED(EVMC_SUCCESS, 21);
EXPECT_OUTPUT_INT(1);
}

TEST_P(evm, clz_bit_128_set)
{
rev = EVMC_OSAKA;
// Test with bit 128 set (0x0000...0001 followed by 16 zeros)
execute(push("0x0000000000000000000000000000000100000000000000000000000000000000") + OP_CLZ + ret_top());
EXPECT_GAS_USED(EVMC_SUCCESS, 21);
EXPECT_OUTPUT_INT(127);
}

TEST_P(evm, clz_bit_64_set)
{
rev = EVMC_OSAKA;
// Test with bit 64 set (0x0000...0000 followed by 0x10000...0000)
execute(push("0x0000000000000000000000000000000000000000000000010000000000000000") + OP_CLZ + ret_top());
EXPECT_GAS_USED(EVMC_SUCCESS, 21);
EXPECT_OUTPUT_INT(191);
}

TEST_P(evm, clz_bit_63_set)
{
rev = EVMC_OSAKA;
// Test with bit 63 set (0x0000...0000 followed by 0x8000...0000)
execute(push("0x0000000000000000000000000000000000000000000000008000000000000000") + OP_CLZ + ret_top());
EXPECT_GAS_USED(EVMC_SUCCESS, 21);
EXPECT_OUTPUT_INT(192);
}

TEST_P(evm, clz_stack_underflow)
{
rev = EVMC_OSAKA;
execute(OP_CLZ);
EXPECT_STATUS(EVMC_STACK_UNDERFLOW);
}
1 change: 1 addition & 0 deletions test/unittests/instructions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ constexpr bool instruction_only_in_evmone(evmc_revision rev, Opcode op) noexcept
case OP_EOFCREATE:
case OP_TXCREATE:
case OP_RETURNCODE:
case OP_CLZ:
return true;
default:
return false;
Expand Down