[util] Remove enum_bitfields

The enum_bitfields system never worked quite right, and always had edge cases where name
resolution for the SFINAE would fail. Move everything over to use util::bitset, which can
be constexpr and boils down to inline integer bitops in release mode.

Improved util::bitset itself, moving the array-backed base implementation into a new
util::sized_bitset, and making the single-inttype backed implementation the base case.
Also added a distinction between | or |= (which work with real bit values) and + or +=
(which work with bit indexes).
This commit is contained in:
Justin C. Miller
2024-02-25 23:40:14 -08:00
parent f7ea46e49e
commit 9f54927a82
36 changed files with 352 additions and 622 deletions

View File

@@ -3,15 +3,9 @@
/// Data structures for reading jsix_boot.dat
#include <stdint.h>
#include <util/enum_bitfields.h>
namespace bootproto {
enum class desc_flags : uint16_t {
graphical = 0x0001,
panic = 0x0002,
symbols = 0x0004,
};
is_bitfield(desc_flags);
enum class desc_flags { graphical, panic, symbols };
} // namespace bootproto

View File

@@ -6,8 +6,8 @@
#include <stddef.h>
#include <stdint.h>
#include <util/bitset.h>
#include <util/counted.h>
#include <util/enum_bitfields.h>
namespace bootproto {
@@ -18,19 +18,13 @@ constexpr uint64_t header_magic = 0x4c454e52454b366aull; // 'j6KERNEL'
constexpr uint16_t header_version = 2;
constexpr uint16_t min_header_version = 2;
enum class section_flags : uint32_t {
none = 0,
execute = 1,
write = 2,
read = 4,
};
is_bitfield(section_flags);
enum class section_flags { none, execute, write, read };
struct program_section {
uintptr_t phys_addr;
uintptr_t virt_addr;
uint32_t size;
section_flags type;
util::bitset32 type;
};
struct program {
@@ -112,18 +106,13 @@ struct frame_block
uint64_t *bitmap;
};
enum class boot_flags : uint16_t {
none = 0x0000,
debug = 0x0001,
test = 0x0002,
};
is_bitfield(boot_flags);
enum class boot_flags { none, debug, test };
struct args
{
uint32_t magic;
uint16_t version;
boot_flags flags;
util::bitset16 flags;
void *pml4;
util::counted<void> page_tables;

View File

@@ -18,7 +18,7 @@ enum class feature {
max
};
using features = util::bitset<(unsigned)feature::max>;
using features = util::sized_bitset<(unsigned)feature::max>;
class cpu_id
{

View File

@@ -1,6 +1,6 @@
#pragma once
#include <stdint.h>
#include <util/enum_bitfields.h>
#include <util/bitset.h>
namespace elf {
@@ -54,19 +54,12 @@ struct file_header
enum class segment_type : uint32_t { null, load, dynamic, interpreter, note };
enum class segment_flags : uint32_t
{
none = 0x00,
exec = 0x01,
write = 0x02,
read = 0x04,
};
is_bitfield(segment_flags);
enum class segment_flags { exec, write, read };
struct segment_header
{
segment_type type;
segment_flags flags;
util::bitset32 flags;
uint64_t offset;
uint64_t vaddr;
@@ -80,18 +73,13 @@ struct segment_header
enum class section_type : uint32_t { null, progbits };
enum class section_flags : uint64_t
{
write = 0x01,
alloc = 0x02,
exec = 0x04,
};
enum class section_flags { write, alloc, exec };
struct section_header
{
uint32_t name_offset;
section_type type;
section_flags flags;
util::bitset64 flags;
uint64_t addr;
uint64_t offset;
uint64_t size;
@@ -102,5 +90,3 @@ struct section_header
} __attribute__ ((packed));
} // namespace elf
is_bitfield(elf::section_flags);

View File

@@ -3,7 +3,8 @@
/// Enums used as flags for syscalls
enum j6_vm_flags {
#define VM_FLAG(name, v) j6_vm_flag_ ## name = v,
j6_vm_flag_none = 0,
#define VM_FLAG(name, v) j6_vm_flag_ ## name = (1ul << v),
#include <j6/tables/vm_flags.inc>
#undef VM_FLAG
j6_vm_flag_MAX

View File

@@ -1,15 +1,13 @@
VM_FLAG( none, 0x00000000 )
VM_FLAG( write, 0 )
VM_FLAG( exec, 1 )
VM_FLAG( write, 0x00000001 )
VM_FLAG( exec, 0x00000002 )
VM_FLAG( contiguous, 4 )
VM_FLAG( large_pages, 5 )
VM_FLAG( huge_pages, 6 )
VM_FLAG( contiguous, 0x00000010 )
VM_FLAG( large_pages, 0x00000020 )
VM_FLAG( huge_pages, 0x00000040 )
VM_FLAG( write_combine, 8 )
VM_FLAG( write_combine, 0x00000100 )
VM_FLAG( mmio, 12 )
VM_FLAG( mmio, 0x00001000 )
VM_FLAG( exact, 0x00010000 )
VM_FLAG( ring, 0x00020000 )
VM_FLAG( exact, 16 )
VM_FLAG( ring, 17 )

View File

@@ -7,34 +7,148 @@
namespace util {
/// A statically-sized templated bitset
template <unsigned N>
template <typename I>
class bitset
{
public:
using storage_type = I;
private:
template <typename T>
static constexpr storage_type bit_or(T b) { return 1ull << uint64_t(b); }
template <typename T, typename ...Args>
static constexpr storage_type bit_or(T b, Args... bs) { return (1ull << storage_type(b)) | bit_or(bs...); }
public:
constexpr bitset(storage_type v = 0) : m_bits {v} {}
constexpr bitset(const bitset<I> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
__attribute__ ((always_inline))
static constexpr bitset of(Args... args) { return {bit_or(args...)}; }
template <typename T>
__attribute__ ((always_inline))
static constexpr bitset from(T i) { return {storage_type(i)}; }
__attribute__ ((always_inline))
inline constexpr bitset & operator=(bitset b) { m_bits = b.m_bits; return *this; }
inline constexpr operator storage_type () const { return m_bits; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((always_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((always_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
__attribute__ ((always_inline))
inline storage_type range(unsigned start, unsigned count) {
return (m_bits >> start) & ~((1 << count) - 1);
}
__attribute__ ((always_inline))
inline bitset & set_range(unsigned start, unsigned count, storage_type val) {
const storage_type mask = ~((1 << count) - 1) << start;
m_bits = (m_bits & ~mask) | ((val << start) & mask);
return *this;
}
template <typename T>
__attribute__ ((always_inline))
inline constexpr bool operator[](T i) const { return get(i); }
__attribute__ ((always_inline))
inline constexpr bitset operator|(bitset b) const { return {storage_type(m_bits | b.m_bits)}; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bitset operator+(T i) const { return {storage_type(m_bits | bit(i))}; }
__attribute__ ((always_inline))
inline constexpr bitset operator|=(bitset b) { *this = *this|b; return *this; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bitset operator+=(T i) { set(i); return *this; }
__attribute__ ((always_inline))
inline constexpr bitset operator&(const bitset &b) const { return {storage_type(m_bits & b.m_bits)}; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bitset operator&(T i) const { return {m_bits & bit(i)}; }
__attribute__ ((always_inline))
inline constexpr bitset & operator&=(const bitset &b) { m_bits &= b.m_bits; return *this; }
__attribute__ ((always_inline))
inline constexpr bool operator==(const bitset &b) const { return m_bits == b.m_bits; }
template <typename T>
__attribute__ ((always_inline))
inline constexpr bool operator==(T i) const { return m_bits == storage_type(i); }
inline constexpr bool empty() const { return m_bits == 0; }
inline constexpr uint64_t value() const { return m_bits; }
private:
template <typename T>
inline constexpr storage_type bit(T i) const { return (storage_type(1) << int(i)); }
storage_type m_bits;
};
using bitset64 = bitset<uint64_t>;
using bitset32 = bitset<uint32_t>;
using bitset16 = bitset<uint16_t>;
using bitset8 = bitset<uint8_t>;
template <unsigned N>
class sized_bitset
{
static constexpr unsigned num_elems = (N + 63) / 64;
public:
template <typename T>
__attribute__ ((force_inline))
__attribute__ ((always_inline))
inline bool get(T i) const {
return bits(i) & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
__attribute__ ((always_inline))
inline sized_bitset & set(T i) {
bits(i) |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
__attribute__ ((always_inline))
inline sized_bitset & clear(T i) {
bits(i) &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
__attribute__ ((always_inline))
inline bool operator[](T i) const { return get(i); }
inline bool empty() const {
@@ -45,260 +159,18 @@ public:
private:
template <typename T>
__attribute__ ((force_inline))
__attribute__ ((always_inline))
inline uint64_t bit(T i) const { return (1ull << (static_cast<uint64_t>(i) & 63)); }
template <typename T>
__attribute__ ((force_inline))
__attribute__ ((always_inline))
inline uint64_t &bits(T i) { return m_bits[static_cast<uint64_t>(i) >> 6]; }
template <typename T>
__attribute__ ((force_inline))
__attribute__ ((always_inline))
inline uint64_t bits(T i) const { return m_bits[static_cast<uint64_t>(i) >> 6]; }
uint64_t m_bits[num_elems] = {0};
};
namespace {
}
/// A statically-sized templated bitset
template <>
class bitset<64>
{
template <typename T>
static constexpr uint64_t bit_or(T b) { return 1ull << uint64_t(b); }
template <typename T, typename ...Args>
static constexpr uint64_t bit_or(T b, Args... bs) { return (1ull << uint64_t(b)) | bit_or(bs...); }
public:
bitset(uint64_t v = 0) : m_bits {v} {}
bitset(const bitset<64> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
constexpr explicit bitset(Args... args) : m_bits(bit_or(args...)) {}
template <typename T>
__attribute__ ((force_inline))
inline bitset & operator=(T v) { m_bits = static_cast<uint64_t>(v); return *this; }
inline constexpr operator const uint64_t () const { return m_bits; }
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool operator[](T i) const { return get(i); }
inline constexpr bool empty() const { return m_bits == 0; }
inline constexpr uint64_t value() const { return m_bits; }
private:
template <typename T>
inline constexpr uint64_t bit(T i) const { return (1ull << static_cast<uint64_t>(i)); }
uint64_t m_bits;
};
/// A statically-sized templated bitset
template <>
class bitset<32>
{
template <typename T>
static constexpr uint32_t bit_or(T b) { return 1u << uint32_t(b); }
template <typename T, typename ...Args>
static constexpr uint32_t bit_or(T b, Args... bs) { return (1u << uint32_t(b)) | bit_or(bs...); }
public:
bitset(uint32_t v = 0) : m_bits {v} {}
bitset(const bitset<32> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
constexpr bitset(Args... args) : m_bits(bit_or(args...)) {}
template <typename T>
inline bitset & operator=(T v) { m_bits = static_cast<uint32_t>(v); return *this; }
inline constexpr operator uint32_t () const { return m_bits; }
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bool operator[](T i) const { return get(i); }
inline bool empty() const { return m_bits == 0; }
inline constexpr uint32_t value() const { return m_bits; }
private:
template <typename T>
inline uint32_t bit(T i) const { return (1u << static_cast<uint32_t>(i)); }
uint32_t m_bits;
};
/// A statically-sized templated bitset
template <>
class bitset<16>
{
template <typename T>
static constexpr uint16_t bit_or(T b) { return 1u << uint16_t(b); }
template <typename T, typename ...Args>
static constexpr uint16_t bit_or(T b, Args... bs) { return (1u << uint16_t(b)) | bit_or(bs...); }
public:
bitset(uint16_t v = 0) : m_bits {v} {}
bitset(const bitset<16> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
constexpr bitset(Args... args) : m_bits(bit_or(args...)) {}
template <typename T>
inline bitset & operator=(T v) { m_bits = static_cast<uint16_t>(v); return *this; }
inline constexpr operator uint16_t () const { return m_bits; }
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bool operator[](T i) const { return get(i); }
inline bool empty() const { return m_bits == 0; }
inline constexpr uint16_t value() const { return m_bits; }
private:
template <typename T>
inline uint16_t bit(T i) const { return (1u << static_cast<uint16_t>(i)); }
uint16_t m_bits;
};
/// A statically-sized templated bitset
template <>
class bitset<8>
{
template <typename T>
static constexpr uint8_t bit_or(T b) { return 1u << uint8_t(b); }
template <typename T, typename ...Args>
static constexpr uint8_t bit_or(T b, Args... bs) { return (1u << uint8_t(b)) | bit_or(bs...); }
public:
bitset(uint8_t v = 0) : m_bits {v} {}
bitset(const bitset<8> &o) : m_bits {o.m_bits} {}
template <typename ...Args>
constexpr bitset(Args... args) : m_bits(bit_or(args...)) {}
template <typename T>
inline bitset & operator=(T v) { m_bits = static_cast<uint8_t>(v); return *this; }
inline constexpr operator uint8_t () const { return m_bits; }
template <typename T>
__attribute__ ((force_inline))
inline constexpr bool get(T i) const {
return m_bits & bit(i);
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & set(T i) {
m_bits |= bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bitset & clear(T i) {
m_bits &= ~bit(i);
return *this;
}
template <typename T>
__attribute__ ((force_inline))
inline bool operator[](T i) const { return get(i); }
inline bool empty() const { return m_bits == 0; }
inline constexpr uint8_t value() const { return m_bits; }
private:
template <typename T>
inline uint8_t bit(T i) const { return (1u << static_cast<uint8_t>(i)); }
uint8_t m_bits;
};
using bitset64 = bitset<64>;
using bitset32 = bitset<32>;
using bitset16 = bitset<16>;
using bitset8 = bitset<8>;
} // namespace util

View File

@@ -1,99 +0,0 @@
#pragma once
#include <util/basic_types.h>
namespace util {
namespace bits {
template <typename E>
constexpr bool is_enum_bitfield(E) { return false; }
template <typename E>
struct enum_or_int {
static constexpr bool value =
is_enum_bitfield(typename types::non_const<E>::type{})
|| types::is_integral<E>::value;
};
template <typename E, typename F>
struct both_enum_or_int {
static constexpr bool value =
types::conjunction< enum_or_int<E>, enum_or_int<F> >::value;
};
template <typename E, typename R>
struct enable_if_bitfield {
using enum_t = typename types::non_const<E>::type;
using type = typename
types::enable_if< is_enum_bitfield(enum_t{}), R >::type;
};
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator |= (E &lhs, F rhs)
{
return lhs = static_cast<E>(
static_cast<typename types::integral<E>::type>(lhs) |
static_cast<typename types::integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator &= (E &lhs, F rhs)
{
return lhs = static_cast<E>(
static_cast<typename types::integral<E>::type>(lhs) &
static_cast<typename types::integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type&
operator ^= (E &lhs, F rhs)
{
return lhs = static_cast<E>(
static_cast<typename types::integral<E>::type>(lhs) ^
static_cast<typename types::integral<F>::type>(rhs));
}
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type
operator & (E lhs, F rhs) { return lhs &= rhs; }
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type
operator | (E lhs, F rhs) { return lhs |= rhs; }
template <typename E, typename F>
constexpr typename types::enable_if<both_enum_or_int<E, F>::value,E>::type
operator ^ (E lhs, F rhs) { return lhs ^= rhs; }
template <typename E>
constexpr typename enable_if_bitfield<E,E>::type
operator ~ (E rhs) { return static_cast<E>(~static_cast<typename types::integral<E>::type>(rhs)); }
template <typename E>
constexpr typename enable_if_bitfield<E,bool>::type
operator ! (E rhs) { return static_cast<typename types::integral<E>::type>(rhs) == 0; }
/// Override logical-and to mean 'rhs contains all bits in lhs'
template <typename E>
constexpr typename enable_if_bitfield<E,bool>::type
operator && (E rhs, E lhs) { return (rhs & lhs) == lhs; }
/// Generic 'has' for non-marked bitfields
template <typename E, typename F>
constexpr bool has(E set, F flags)
{
return
(static_cast<typename types::integral<E>::type>(set) &
static_cast<typename types::integral<F>::type>(flags)) ==
static_cast<typename types::integral<F>::type>(flags);
}
} // namespace bits
} // namespace util
#define is_bitfield(name) \
constexpr bool is_enum_bitfield(name) { return true; } \
using namespace ::util::bits;

View File

@@ -20,7 +20,6 @@ module("util",
"util/cdb.h",
"util/counted.h",
"util/deque.h",
"util/enum_bitfields.h",
"util/format.h",
"util/hash.h",
"util/linked_list.h",