Skip to content
Open
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
1 change: 1 addition & 0 deletions category/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ add_library(
"rlp/decode_error.hpp"
"rlp/encode.hpp"
# runtime
"runtime/bit.hpp"
"runtime/non_temporal_memory.hpp"
"runtime/uint256.hpp"
"runtime/uint256/intrinsics.hpp"
Expand Down
3 changes: 2 additions & 1 deletion category/core/int.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <category/core/config.hpp>
#include <category/core/hex.hpp>
#include <category/core/runtime/bit.hpp>
#include <category/core/runtime/uint128.hpp>
#include <category/core/runtime/uint256.hpp>

Expand Down Expand Up @@ -101,7 +102,7 @@ template <typename T>
return byteswap(x);
}
else if constexpr (std::unsigned_integral<T>) {
return std::byteswap(x);
return bit::byteswap(x);
}
else {
static_assert(sizeof(T) == 0, "bswap not supported for this type");
Expand Down
3 changes: 2 additions & 1 deletion category/core/rlp/encode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <category/core/assume.h>
#include <category/core/byte_string.hpp>
#include <category/core/endian.hpp>
#include <category/core/int.hpp>
#include <category/core/likely.h>
#include <category/core/rlp/config.hpp>
#include <category/core/runtime/unaligned.hpp>
Expand Down Expand Up @@ -50,7 +51,7 @@ namespace impl
n <<= lz_bytes * 8;
}

size_t const n_be = std::byteswap(n);
size_t const n_be = bswap(n);
MONAD_ASSUME(d.size() >= sizeof(size_t));
unaligned_store(d.data(), n_be);
return d.subspan((sizeof(size_t) - lz_bytes));
Expand Down
30 changes: 30 additions & 0 deletions category/core/runtime/bit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (C) 2025-26 Category Labs, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#pragma once

#include <bit>

// The scalar byte-order primitive, behind a name of our own so that the zkVM
// guest can substitute an implementation which does not call out to libgcc —
// see zkvm/category/core/runtime/bit.hpp, which open-codes the 4- and 8-byte
// cases for riscv64ima, a target with no rev8 instruction.
//
// Callers outside this directory want monad::bswap (category/core/int.hpp): it
// is the canonical entry point and also covers uint256_t and uint128_t.
namespace monad::bit
{
using std::byteswap;
}
3 changes: 2 additions & 1 deletion category/core/runtime/uint128.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <category/core/assert.h>
#include <category/core/config.hpp>
#include <category/core/runtime/bit.hpp>
#include <category/core/throw.hpp>

#include <algorithm>
Expand Down Expand Up @@ -209,7 +210,7 @@ operator>>(uint128_t const x, uint64_t const shift) noexcept

[[nodiscard]] constexpr uint128_t byteswap(uint128_t const x) noexcept
{
return {std::byteswap(x.hi), std::byteswap(x.lo)};
return {bit::byteswap(x.hi), bit::byteswap(x.lo)};
}

[[nodiscard]] inline std::string
Expand Down
9 changes: 5 additions & 4 deletions category/core/runtime/uint256.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <category/core/assert.h>
#include <category/core/config.hpp>
#include <category/core/likely.h>
#include <category/core/runtime/bit.hpp>
#include <category/core/runtime/uint128.hpp>
#include <category/core/runtime/uint256/intrinsics.hpp>
#include <category/core/runtime/uint256/portable.hpp>
Expand Down Expand Up @@ -1008,10 +1009,10 @@ inline uint256_t byte(uint256_t const &byte_index_256, uint256_t const &x)
[[gnu::always_inline]] constexpr uint256_t byteswap(uint256_t const &x) noexcept
{
return uint256_t{
std::byteswap(x[3]),
std::byteswap(x[2]),
std::byteswap(x[1]),
std::byteswap(x[0])};
bit::byteswap(x[3]),
bit::byteswap(x[2]),
bit::byteswap(x[1]),
bit::byteswap(x[0])};
}

consteval uint256_t operator""_u256(char const *s)
Expand Down
3 changes: 2 additions & 1 deletion category/execution/ethereum/precompiles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <category/core/address.hpp>
#include <category/core/byte_string.hpp>
#include <category/core/config.hpp>
#include <category/core/int.hpp>
#include <category/vm/evm/traits.hpp>

#include <evmc/evmc.h>
Expand Down Expand Up @@ -94,7 +95,7 @@ blake2bf_gas_cost_ethereum(byte_string_view const input)
static_assert(
std::endian::native == std::endian::little,
"blake2bf_gas_cost_ethereum only works on little-endian platforms");
return std::byteswap(rounds);
return bswap(rounds);
}

template <Traits traits>
Expand Down
16 changes: 6 additions & 10 deletions category/mpt/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <category/core/assert.h>
#include <category/core/byte_string.hpp>
#include <category/core/hex.hpp>
#include <category/core/int.hpp>
#include <category/core/runtime/unaligned.hpp>
#include <category/mpt/config.hpp>
#include <category/mpt/nibbles_view.hpp>
Expand Down Expand Up @@ -285,19 +286,14 @@ inline byte_string serialize_as_big_endian(UnsignedInteger n)
{
MONAD_ASSERT(N <= sizeof(UnsignedInteger));

// std::byteswap is C++23 only, using GCC intrinsic instead
if constexpr (std::endian::native != std::endian::big) {
if constexpr (sizeof(UnsignedInteger) <= 2) {
n = __builtin_bswap16(n);
}
else if constexpr (sizeof(UnsignedInteger) == 4) {
n = __builtin_bswap32(n);
}
else if constexpr (sizeof(UnsignedInteger) == 8) {
n = __builtin_bswap64(n);
// Wider than a word: swap and slice the low 8 bytes, which is all N can
// select given the assert above.
if constexpr (sizeof(UnsignedInteger) > 8) {
return serialize_as_big_endian<N>(static_cast<uint64_t>(n));
}
else {
return serialize_as_big_endian<N>(static_cast<uint64_t>(n));
n = bswap(n);
}
}
auto arr =
Expand Down
5 changes: 3 additions & 2 deletions category/vm/interpreter/push.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#pragma once

#include <category/core/int.hpp>
#include <category/core/runtime/uint256.hpp>
#include <category/core/runtime/unaligned.hpp>
#include <category/vm/evm/opcodes.hpp>
Expand Down Expand Up @@ -56,7 +57,7 @@ namespace monad::vm::interpreter
[[gnu::always_inline]] inline subword_t
read_unaligned(uint8_t const *const ptr)
{
return std::byteswap(unaligned_load<subword_t>(ptr));
return bswap(unaligned_load<subword_t>(ptr));
}

template <size_t N, Traits traits>
Expand Down Expand Up @@ -84,7 +85,7 @@ namespace monad::vm::interpreter
instr_ptr + 1,
leading_part);

return std::byteswap(word);
return bswap(word);
}();

if constexpr (whole_words == 0) {
Expand Down
12 changes: 6 additions & 6 deletions category/vm/runtime/transmute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ namespace monad::vm::runtime

uint64_t t2;
std::memcpy(&t2, bytes, 8);
t2 = std::byteswap(t2);
t2 = bswap(t2);

uint64_t t1;
std::memcpy(&t1, bytes + 8, 8);
t1 = std::byteswap(t1);
t1 = bswap(t1);

uint32_t t0;
std::memcpy(&t0, bytes + 16, 4);
t0 = std::byteswap(t0);
t0 = bswap(t0);

Address ret;
std::memcpy(ret.bytes, &t0, 4);
Expand All @@ -67,15 +67,15 @@ namespace monad::vm::runtime
{
uint32_t t2;
std::memcpy(&t2, addr.bytes, 4);
t2 = std::byteswap(t2);
t2 = bswap(t2);

uint64_t t1;
std::memcpy(&t1, addr.bytes + 4, 8);
t1 = std::byteswap(t1);
t1 = bswap(t1);

uint64_t t0;
std::memcpy(&t0, addr.bytes + 12, 8);
t0 = std::byteswap(t0);
t0 = bswap(t0);

alignas(uint256_t) uint8_t ret[32];
std::memcpy(ret, &t0, 8);
Expand Down
50 changes: 50 additions & 0 deletions zkvm/category/core/runtime/bit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (C) 2025-26 Category Labs, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#pragma once

#include <bit>
#include <concepts>
#include <cstdint>

namespace monad::bit
{
template <std::integral T>
[[nodiscard, gnu::always_inline]] inline constexpr T
Comment thread
goodlyrottenapple marked this conversation as resolved.
byteswap(T const x) noexcept
{
if constexpr (sizeof(T) == 8) {
auto v = static_cast<uint64_t>(x);
v = ((v & 0x00FF00FF00FF00FFull) << 8) |
((v >> 8) & 0x00FF00FF00FF00FFull);
v = ((v & 0x0000FFFF0000FFFFull) << 16) |
((v >> 16) & 0x0000FFFF0000FFFFull);
return static_cast<T>((v << 32) | (v >> 32));
}
else if constexpr (sizeof(T) == 4) {
auto v = static_cast<uint32_t>(x);
v = ((v & 0x00FF00FFu) << 8) | ((v >> 8) & 0x00FF00FFu);
return static_cast<T>((v << 16) | (v >> 16));
}
else {
return std::byteswap(x);
}
}

// Smoke-test the two open-coded cases against the generic one.
static_assert(byteswap(uint64_t{0x0123456789ABCDEF}) == 0xEFCDAB8967452301);
static_assert(byteswap(uint32_t{0x01234567}) == 0x67452301);
static_assert(byteswap(uint16_t{0x0123}) == 0x2301);
}
Loading