From a8fb72804c5cd176ceb62948a50b6098ebd282d2 Mon Sep 17 00:00:00 2001 From: modeco80 Date: Sun, 16 Jul 2023 02:15:55 -0400 Subject: [PATCH] chore: remove vector bsearch code, seperate OptionalRef to new `lucore` project. --- native/CMakeLists.txt | 2 + native/lucore/CMakeLists.txt | 13 ++++ native/lucore/include/lucore/Assert.hpp | 16 +++++ native/lucore/include/lucore/OptionalRef.hpp | 70 ++++++++++++++++++++ native/riscv/CMakeLists.txt | 5 ++ native/riscv/include/riscv/Bus.hpp | 20 +++--- native/riscv/include/riscv/Types.hpp | 65 +----------------- native/riscv/src/Bus.cpp | 40 ++++------- 8 files changed, 129 insertions(+), 102 deletions(-) create mode 100644 native/lucore/CMakeLists.txt create mode 100644 native/lucore/include/lucore/Assert.hpp create mode 100644 native/lucore/include/lucore/OptionalRef.hpp diff --git a/native/CMakeLists.txt b/native/CMakeLists.txt index 34e4ea3..52dff80 100644 --- a/native/CMakeLists.txt +++ b/native/CMakeLists.txt @@ -3,6 +3,8 @@ project(lcpu-native DESCRIPTION "Superproject for LCPU GMOD Native Module" ) +add_subdirectory(lucore) + # RISC-V emulation library add_subdirectory(riscv) diff --git a/native/lucore/CMakeLists.txt b/native/lucore/CMakeLists.txt new file mode 100644 index 0000000..e69b939 --- /dev/null +++ b/native/lucore/CMakeLists.txt @@ -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) diff --git a/native/lucore/include/lucore/Assert.hpp b/native/lucore/include/lucore/Assert.hpp new file mode 100644 index 0000000..a69269f --- /dev/null +++ b/native/lucore/include/lucore/Assert.hpp @@ -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 + +#ifdef NDEBUG + #define LUCORE_ASSERT(...) assert(__VA_ARGS__) +#else + #define LUCORE_ASSERT(...) +#endif + +#endif diff --git a/native/lucore/include/lucore/OptionalRef.hpp b/native/lucore/include/lucore/OptionalRef.hpp new file mode 100644 index 0000000..c825810 --- /dev/null +++ b/native/lucore/include/lucore/OptionalRef.hpp @@ -0,0 +1,70 @@ +//! OptionalRef - std::optional for C++20 + +#include + +namespace lucore::detail { + + struct Nullref_t {}; + + template + struct OptionalRef; // sfinae on non-reference types + + /// Like std::optional, 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 + struct OptionalRef { + constexpr OptionalRef() : ptr(nullptr) { + } + + // trigger explicit null construction + constexpr OptionalRef(Nullref_t) : OptionalRef() { + } + + constexpr OptionalRef(T& ref) : ptr(&ref) { + } + + // polymorphic downconstruction from another OptionalRef + template + constexpr OptionalRef(const OptionalRef& 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 diff --git a/native/riscv/CMakeLists.txt b/native/riscv/CMakeLists.txt index ca0787a..666daaa 100644 --- a/native/riscv/CMakeLists.txt +++ b/native/riscv/CMakeLists.txt @@ -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) diff --git a/native/riscv/include/riscv/Bus.hpp b/native/riscv/include/riscv/Bus.hpp index 6fdf6c4..6320822 100644 --- a/native/riscv/include/riscv/Bus.hpp +++ b/native/riscv/include/riscv/Bus.hpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -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 FindDeviceForAddress(AddressT address) const; + lucore::OptionalRef FindDeviceForAddress(AddressT address) const; CPU* attachedCpu{}; // TODO: if this ends up being a hotpath replace with robinhood unordered map std::unordered_map mapped_devices; - - // this is essentially a flat map - // we don't particularly need hashing :p - //struct AttachedDevice { - // AddressT baseAddress{}; - // Device* device{}; - //}; - - //std::vector mapped_devices{}; }; diff --git a/native/riscv/include/riscv/Types.hpp b/native/riscv/include/riscv/Types.hpp index 8e2a2b4..cf17f59 100644 --- a/native/riscv/include/riscv/Types.hpp +++ b/native/riscv/include/riscv/Types.hpp @@ -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 - 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 - struct OptionalRef { - constexpr OptionalRef() : ptr(nullptr) { - } - - // trigger explicit null construction - constexpr OptionalRef(Nullref_t) : OptionalRef() { - } - - constexpr OptionalRef(T& ref) : ptr(&ref) { - } - - // polymorphic downconstruction from another OptionalRef - template - constexpr OptionalRef(const OptionalRef& 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 diff --git a/native/riscv/src/Bus.cpp b/native/riscv/src/Bus.cpp index ad41afa..5691faa 100644 --- a/native/riscv/src/Bus.cpp +++ b/native/riscv/src/Bus.cpp @@ -1,9 +1,14 @@ +#include #include -#include - 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::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::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()); + 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()); }); - // No device was found at this address if(it == mapped_devices.end()) - return NullRef; + return lucore::NullRef; else return *it->second; }