chore: remove vector bsearch code, seperate OptionalRef

to new `lucore` project.
This commit is contained in:
Lily Tsuru 2023-07-16 02:15:55 -04:00
parent 43d0ac9630
commit a8fb72804c
8 changed files with 129 additions and 102 deletions

View File

@ -3,6 +3,8 @@ project(lcpu-native
DESCRIPTION "Superproject for LCPU GMOD Native Module"
)
add_subdirectory(lucore)
# RISC-V emulation library
add_subdirectory(riscv)

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.15)
project(lucore
DESCRIPTION "lucore - shared core stuff for the lcpu native projects"
LANGUAGES CXX
)
add_library(lucore INTERFACE)
target_compile_features(lucore INTERFACE cxx_std_20)
target_include_directories(lucore INTERFACE ${PROJECT_SOURCE_DIR}/include)
add_library(lucore::lucore ALIAS lucore)

View File

@ -0,0 +1,16 @@
//! Lucore Assert Wrappers
// this just plumbs everything to libc by default
// your project can choose to configure its own assertion handler by
// defining LUCORE_ASSERT()
// TODO: LUCORE_VERIFY/plumbing this into our own assertion code?
#ifndef LUCORE_ASSERT
#include <cassert>
#ifdef NDEBUG
#define LUCORE_ASSERT(...) assert(__VA_ARGS__)
#else
#define LUCORE_ASSERT(...)
#endif
#endif

View File

@ -0,0 +1,70 @@
//! OptionalRef - std::optional<T&> for C++20
#include <lucore/Assert.hpp>
namespace lucore::detail {
struct Nullref_t {};
template <class T>
struct OptionalRef; // sfinae on non-reference types
/// Like std::optional<T>, but optimized specifically for references to objects.
///
/// Additionally, as a bonus, since the repressentation is so simple, this is
/// constexpr-safe by default, so it can be relatively well optimized.
///
/// # Safety
/// Treat this class like [std::reference_wrapper].
/// Do *not* give this class invalid references.
template <class T>
struct OptionalRef<T&> {
constexpr OptionalRef() : ptr(nullptr) {
}
// trigger explicit null construction
constexpr OptionalRef(Nullref_t) : OptionalRef() {
}
constexpr OptionalRef(T& ref) : ptr(&ref) {
}
// polymorphic downconstruction from another OptionalRef<U>
template <class U>
constexpr OptionalRef(const OptionalRef<U&>& other) : ptr(&other.ptr) {
}
constexpr T& Value() const {
LUCORE_ASSERT(HasValue() && "Attempt to access OptionalRef without stored value!");
return *ptr;
}
constexpr bool HasValue() const {
return ptr != nullptr;
}
constexpr T& operator*() const {
return Value();
}
// unchecked access: DO NOT use this without checking beforehand
constexpr T* operator->() const {
return ptr;
}
constexpr operator bool() const {
return HasValue();
}
private:
T* ptr {};
};
/// Sentinel value to explicitly not populate an OptionalRef.
inline static Nullref_t NullRef {};
} // namespace lucore::detail
namespace lucore {
using detail::NullRef;
using detail::OptionalRef;
} // namespace lucore

View File

@ -12,4 +12,9 @@ add_library(riscv
target_compile_features(riscv PUBLIC cxx_std_20)
target_include_directories(riscv PUBLIC ${PROJECT_SOURCE_DIR}/include)
target_link_libraries(riscv PUBLIC
# lucore is a public dependency, since it is (techinically) used in public api surfaces
lucore::lucore
)
add_library(riscv::riscv ALIAS riscv)

View File

@ -1,4 +1,5 @@
#include <riscv/Types.hpp>
#include <lucore/OptionalRef.hpp>
#include <vector>
#include <unordered_map>
@ -26,8 +27,8 @@ namespace riscv {
/// Is this device clocked?
virtual bool Clocked() const { return false; }
/// This function is called during clocks to give clocked devices
/// the ability to update
/// This function is called to give clocked devices
/// the ability to... well, clock!
virtual void Clock() {}
// TODO(feat): Need to implement ability to generate interrupts
@ -47,9 +48,13 @@ namespace riscv {
};
~Bus();
/// Attach a device to the bus.
///
/// Note that once this function is called (and the device is successfully added),
/// the object pointed to by [device] is owned by the Bus object, and should not be deleted.
///
/// # Returns
/// This function returns true if the device was able to be put on the bus.
/// This function returns false in the following error cases:
@ -70,21 +75,12 @@ namespace riscv {
void PokeWord(AddressT address, u32 value);
private:
OptionalRef<Device&> FindDeviceForAddress(AddressT address) const;
lucore::OptionalRef<Device&> FindDeviceForAddress(AddressT address) const;
CPU* attachedCpu{};
// TODO: if this ends up being a hotpath replace with robinhood unordered map
std::unordered_map<AddressT, Device*> mapped_devices;
// this is essentially a flat map
// we don't particularly need hashing :p
//struct AttachedDevice {
// AddressT baseAddress{};
// Device* device{};
//};
//std::vector<AttachedDevice> mapped_devices{};
};

View File

@ -21,69 +21,6 @@ namespace riscv {
/// A type that can repressent address space or address space offsets.
using AddressT = u32;
namespace detail {
struct Nullref_t {};
template <class T>
struct OptionalRef; // sfinae on non-reference types
/// Like std::optional, but for references to objects.
///
/// Additionally, as a bonus, since the repressentation is so simple, this is
/// constexpr-safe by default, so it can be relatively well optimized.
///
/// # Safety
/// Treat this class like [std::reference_wrapper].
/// Do *not* give this class invalid references.
template <class T>
struct OptionalRef<T&> {
constexpr OptionalRef() : ptr(nullptr) {
}
// trigger explicit null construction
constexpr OptionalRef(Nullref_t) : OptionalRef() {
}
constexpr OptionalRef(T& ref) : ptr(&ref) {
}
// polymorphic downconstruction from another OptionalRef<U>
template <class U>
constexpr OptionalRef(const OptionalRef<U&>& other) : ptr(&other.ptr) {
}
constexpr T& Value() const {
assert(HasValue() && "Attempt to access OptionalRef without stored value!");
return *ptr;
}
constexpr bool HasValue() const {
return ptr != nullptr;
}
constexpr T& operator*() const {
return Value();
}
// unchecked access: DO NOT use this without checking beforehand
constexpr T* operator->() const {
return ptr;
}
constexpr operator bool() const {
return HasValue();
}
private:
T* ptr {};
};
/// Sentinel value to explicitly not populate an OptionalRef.
inline static Nullref_t NullRef {};
} // namespace detail
using detail::NullRef;
using detail::OptionalRef;
} // namespace riscv

View File

@ -1,9 +1,14 @@
#include <algorithm>
#include <riscv/Bus.hpp>
#include <algorithm>
namespace riscv {
Bus::~Bus() {
// Free all devices
for(auto& pair : mapped_devices)
delete pair.second;
}
bool Bus::AttachDevice(AddressT baseAddress, Device* device) {
if(!device)
return false;
@ -15,9 +20,7 @@ namespace riscv {
else if(FindDeviceForAddress(baseAddress + device->Size()))
return false;
//mapped_devices.push_back({ .baseAddress = baseAddress, .device = device });
mapped_devices[baseAddress] = device;
return true;
}
@ -61,33 +64,18 @@ namespace riscv {
return opt->PokeWord(address - opt->BaseAddress(), value);
}
OptionalRef<Bus::Device&> Bus::FindDeviceForAddress(AddressT address) const {
/*
for(auto& device : mapped_devices) {
// If the requested address directly matches the base address of a device
// mapped into memory, then we do not even need to consider checking the layout.
if(device.baseAddress == address)
return *device.device;
// Otherwise, we *do* unfortunately have to do so.
if(address >= device.baseAddress &&
address < device.baseAddress + device.device->Size())
return *device.device;
}*/
lucore::OptionalRef<Bus::Device&> Bus::FindDeviceForAddress(AddressT address) const {
auto it = std::find_if(mapped_devices.begin(), mapped_devices.end(), [&](const auto& pair) {
return
// We can shorcut the region checking if the requested addess matches base address.
pair.first == address ||
// If it doesn't we really can't, though.
(address >= pair.first &&
address < pair.first + pair.second->Size());
// We can shorcut the region checking if the requested addess matches base address.
pair.first == address ||
// If it doesn't we really can't, though.
(address >= pair.first && address < pair.first + pair.second->Size());
});
// No device was found at this address
if(it == mapped_devices.end())
return NullRef;
return lucore::NullRef;
else
return *it->second;
}