diff --git a/ClockReceiver/RangeDispatcher.hpp b/ClockReceiver/RangeDispatcher.hpp deleted file mode 100644 index c0827a37c..000000000 --- a/ClockReceiver/RangeDispatcher.hpp +++ /dev/null @@ -1,174 +0,0 @@ -// -// RangeDispatcher.hpp -// Clock Signal -// -// Created by Thomas Harte on 29/05/2023. -// Copyright © 2023 Thomas Harte. All rights reserved. -// - -#ifndef RangeDispatcher_hpp -#define RangeDispatcher_hpp - -#include -#include - -namespace Reflection { - -/*! - Calls @c t.dispatch(args...) as efficiently as possible. -*/ -template void dispatch(TargetT &t, uint8_t c, Args &&... args) { -#define Opt(x) case x: t.template dispatch(std::forward(args)...); break -#define Opt4(x) Opt(x + 0x00); Opt(x + 0x01); Opt(x + 0x02); Opt(x + 0x03) -#define Opt16(x) Opt4(x + 0x00); Opt4(x + 0x04); Opt4(x + 0x08); Opt4(x + 0x0c) -#define Opt64(x) Opt16(x + 0x00); Opt16(x + 0x10); Opt16(x + 0x20); Opt16(x + 0x30) -#define Opt256(x) Opt64(x + 0x00); Opt64(x + 0x40); Opt64(x + 0x80); Opt64(x + 0xc0) - - switch(c) { - Opt256(0); - } - -#undef Opt256 -#undef Opt64 -#undef Opt16 -#undef Opt4 -#undef Opt -} - -// Yes, macros, yuck. But I want an actual switch statement for the dispatch to start -// and to allow a simple [[fallthrough]] through all subsequent steps up until end. -// So I don't think I have much in the way of options here. -// -// Sensible choices by the optimiser are assumed. -#define index2(n) index(n); index(n+1); -#define index4(n) index2(n); index2(n+2); -#define index8(n) index4(n); index4(n+4); -#define index16(n) index8(n); index8(n+8); -#define index32(n) index16(n); index16(n+16); -#define index64(n) index32(n); index32(n+32); -#define index128(n) index64(n); index64(n+64); -#define index256(n) index128(n); index128(n+128); -#define index512(n) index256(n); index256(n+256); -#define index1024(n) index512(n); index512(n+512); -#define index2048(n) index1024(n); index1024(n+1024); - -#define switch_indices(x) switch(x) { default: assert(false); index2048(0); } -static constexpr int switch_max = 2048; - -/// Provides glue for a run of calls like: -/// -/// SequencerT.perform<0>(...) -/// SequencerT.perform<1>(...) -/// SequencerT.perform<2>(...) -/// ...etc... -/// -/// Allowing the caller to execute any subrange of the calls. -template -struct RangeDispatcher { - static_assert(SequencerT::max < switch_max); - - /// Perform @c target.perform() for the input range `begin <= n < end`. - template - static void dispatch(SequencerT &target, int begin, int end, Args&&... args) { - - // Minor optimisation: do a comparison with end once outside the loop and if it implies so - // then do no further comparisons within the loop. This is somewhat targetted at expected - // use cases. - if(end < SequencerT::max) { - dispatch(target, begin, end, args...); - } else { - dispatch(target, begin, end, args...); - } - } - - private: - template static void dispatch(SequencerT &target, int begin, int end, Args&&... args) { -#define index(n) \ - case n: \ - if constexpr (n <= SequencerT::max) { \ - if constexpr (n == SequencerT::max) return; \ - if constexpr (n < SequencerT::max) { \ - if(use_end && end == n) return; \ - } \ - target.template perform(args...); \ - } \ - [[fallthrough]]; - - switch_indices(begin); - -#undef index - } - -}; - -/// Uses a classifier to divide a range into typed subranges and issues calls to a target of the form: -/// -/// * begin(location) upon entering a new region; -/// * end(location) upon entering a region; and -/// * advance(distance) in as many chunks as required to populate the inside of the region. -/// -/// @c begin and @c end have iterator-style semantics: begin's location will be the first location in the relevant subrange and -/// end's will be the first location not in the relevant subrange. -template -struct SubrangeDispatcher { - static_assert(ClassifierT::max < switch_max); - - static void dispatch(TargetT &target, int begin, int end) { -#define index(n) \ - case n: \ - if constexpr (n <= ClassifierT::max) { \ - constexpr auto region = ClassifierT::region(n); \ - if constexpr (n == find_begin(n)) { \ - if(n >= end) { \ - return; \ - } \ - target.template begin(n); \ - } \ - if constexpr (n == find_end(n) - 1) { \ - const auto clipped_begin = std::max(begin, find_begin(n)); \ - const auto clipped_end = std::min(end, find_end(n)); \ - target.template advance(clipped_end - clipped_begin); \ - \ - if(clipped_end == n + 1) { \ - target.template end(n + 1); \ - } \ - } \ - } \ - [[fallthrough]]; - - switch_indices(begin); - -#undef index - } - - private: - static constexpr int find_begin(int n) { - const auto type = ClassifierT::region(n); - while(n && ClassifierT::region(n - 1) == type) --n; - return n; - } - - static constexpr int find_end(int n) { - const auto type = ClassifierT::region(n); - while(n < ClassifierT::max && ClassifierT::region(n) == type) ++n; - return n; - } -}; - -#undef switch_indices - -#undef index2 -#undef index4 -#undef index8 -#undef index16 -#undef index32 -#undef index64 -#undef index128 -#undef index256 -#undef index512 -#undef index1024 -#undef index2048 - -} - -#endif /* RangeDispatcher_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index a7cbf8743..2803a2130 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1102,7 +1102,6 @@ /* Begin PBXFileReference section */ 428168372A16C25C008ECD27 /* LineLayout.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineLayout.hpp; sourceTree = ""; }; - 428168382A254FBE008ECD27 /* RangeDispatcher.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RangeDispatcher.hpp; sourceTree = ""; }; 428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = ""; }; 42AD552E2A0C4D5000ACE410 /* 68000.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000.hpp; sourceTree = ""; }; 42AD55302A0C4D5000ACE410 /* 68000Storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000Storage.hpp; sourceTree = ""; }; @@ -2783,10 +2782,10 @@ 4B3AF7CF2413470E00873C0B /* Reflection */ = { isa = PBXGroup; children = ( - 4B3AF7D02413470E00873C0B /* Enum.hpp */, - 4B3AF7D12413472200873C0B /* Struct.hpp */, - 4B47F6C4241C87A100ED06F7 /* Struct.cpp */, 4BDA7F8129C4C223007A10A5 /* Dispatcher.hpp */, + 4B3AF7D02413470E00873C0B /* Enum.hpp */, + 4B47F6C4241C87A100ED06F7 /* Struct.cpp */, + 4B3AF7D12413472200873C0B /* Struct.hpp */, ); name = Reflection; path = ../../Reflection; @@ -4975,7 +4974,6 @@ 4BB06B211F316A3F00600C7A /* ForceInline.hpp */, 4B80214322EE7C3E00068002 /* JustInTime.hpp */, 4B644ED023F0FB55006C0CC5 /* ScanSynchroniser.hpp */, - 428168382A254FBE008ECD27 /* RangeDispatcher.hpp */, 4B449C942063389900A095C8 /* TimeTypes.hpp */, 4B996B2D2496DAC2001660EF /* VSyncPredictor.hpp */, ); diff --git a/OSBindings/Mac/Clock SignalTests/6809OperationMapperTests.mm b/OSBindings/Mac/Clock SignalTests/6809OperationMapperTests.mm index fd273bad7..f8757a8c9 100644 --- a/OSBindings/Mac/Clock SignalTests/6809OperationMapperTests.mm +++ b/OSBindings/Mac/Clock SignalTests/6809OperationMapperTests.mm @@ -9,7 +9,7 @@ #import -#include "RangeDispatcher.hpp" +#include "Dispatcher.hpp" #include "../../../InstructionSets/6809/OperationMapper.hpp" using namespace InstructionSet::M6809; diff --git a/OSBindings/Mac/Clock SignalTests/DispatcherTests.mm b/OSBindings/Mac/Clock SignalTests/DispatcherTests.mm index ddf21b2be..42c369dc3 100644 --- a/OSBindings/Mac/Clock SignalTests/DispatcherTests.mm +++ b/OSBindings/Mac/Clock SignalTests/DispatcherTests.mm @@ -8,7 +8,7 @@ #import -#include "RangeDispatcher.hpp" +#include "Dispatcher.hpp" #include #include @@ -17,14 +17,7 @@ @interface DispatcherTests : XCTestCase @end -@implementation DispatcherTests { -} - -- (void)setUp { -} - -- (void)tearDown { -} +@implementation DispatcherTests struct DoStep { static constexpr int max = 100; diff --git a/Reflection/Dispatcher.hpp b/Reflection/Dispatcher.hpp index 71829bc56..a3ac2bb13 100644 --- a/Reflection/Dispatcher.hpp +++ b/Reflection/Dispatcher.hpp @@ -9,11 +9,165 @@ #ifndef Dispatcher_hpp #define Dispatcher_hpp +#include #include namespace Reflection { +/*! + Calls @c t.dispatch(args...) as efficiently as possible. +*/ +template void dispatch(TargetT &t, uint8_t c, Args &&... args) { +#define Opt(x) case x: t.template dispatch(std::forward(args)...); break +#define Opt4(x) Opt(x + 0x00); Opt(x + 0x01); Opt(x + 0x02); Opt(x + 0x03) +#define Opt16(x) Opt4(x + 0x00); Opt4(x + 0x04); Opt4(x + 0x08); Opt4(x + 0x0c) +#define Opt64(x) Opt16(x + 0x00); Opt16(x + 0x10); Opt16(x + 0x20); Opt16(x + 0x30) +#define Opt256(x) Opt64(x + 0x00); Opt64(x + 0x40); Opt64(x + 0x80); Opt64(x + 0xc0) + switch(c) { + Opt256(0); + } + +#undef Opt256 +#undef Opt64 +#undef Opt16 +#undef Opt4 +#undef Opt +} + +// Yes, macros, yuck. But I want an actual switch statement for the dispatch to start +// and to allow a simple [[fallthrough]] through all subsequent steps up until end. +// So I don't think I have much in the way of options here. +// +// Sensible choices by the optimiser are assumed. +#define index2(n) index(n); index(n+1); +#define index4(n) index2(n); index2(n+2); +#define index8(n) index4(n); index4(n+4); +#define index16(n) index8(n); index8(n+8); +#define index32(n) index16(n); index16(n+16); +#define index64(n) index32(n); index32(n+32); +#define index128(n) index64(n); index64(n+64); +#define index256(n) index128(n); index128(n+128); +#define index512(n) index256(n); index256(n+256); +#define index1024(n) index512(n); index512(n+512); +#define index2048(n) index1024(n); index1024(n+1024); + +#define switch_indices(x) switch(x) { default: assert(false); index2048(0); } +static constexpr int switch_max = 2048; + +/// Provides glue for a run of calls like: +/// +/// SequencerT.perform<0>(...) +/// SequencerT.perform<1>(...) +/// SequencerT.perform<2>(...) +/// ...etc... +/// +/// Allowing the caller to execute any subrange of the calls. +template +struct RangeDispatcher { + static_assert(SequencerT::max < switch_max); + + /// Perform @c target.perform() for the input range `begin <= n < end`. + template + static void dispatch(SequencerT &target, int begin, int end, Args&&... args) { + + // Minor optimisation: do a comparison with end once outside the loop and if it implies so + // then do no further comparisons within the loop. This is somewhat targetted at expected + // use cases. + if(end < SequencerT::max) { + dispatch(target, begin, end, args...); + } else { + dispatch(target, begin, end, args...); + } + } + + private: + template static void dispatch(SequencerT &target, int begin, int end, Args&&... args) { +#define index(n) \ + case n: \ + if constexpr (n <= SequencerT::max) { \ + if constexpr (n == SequencerT::max) return; \ + if constexpr (n < SequencerT::max) { \ + if(use_end && end == n) return; \ + } \ + target.template perform(args...); \ + } \ + [[fallthrough]]; + + switch_indices(begin); + +#undef index + } + +}; + +/// Uses a classifier to divide a range into typed subranges and issues calls to a target of the form: +/// +/// * begin(location) upon entering a new region; +/// * end(location) upon entering a region; and +/// * advance(distance) in as many chunks as required to populate the inside of the region. +/// +/// @c begin and @c end have iterator-style semantics: begin's location will be the first location in the relevant subrange and +/// end's will be the first location not in the relevant subrange. +template +struct SubrangeDispatcher { + static_assert(ClassifierT::max < switch_max); + + static void dispatch(TargetT &target, int begin, int end) { +#define index(n) \ + case n: \ + if constexpr (n <= ClassifierT::max) { \ + constexpr auto region = ClassifierT::region(n); \ + if constexpr (n == find_begin(n)) { \ + if(n >= end) { \ + return; \ + } \ + target.template begin(n); \ + } \ + if constexpr (n == find_end(n) - 1) { \ + const auto clipped_begin = std::max(begin, find_begin(n)); \ + const auto clipped_end = std::min(end, find_end(n)); \ + target.template advance(clipped_end - clipped_begin); \ + \ + if(clipped_end == n + 1) { \ + target.template end(n + 1); \ + } \ + } \ + } \ + [[fallthrough]]; + + switch_indices(begin); + +#undef index + } + + private: + static constexpr int find_begin(int n) { + const auto type = ClassifierT::region(n); + while(n && ClassifierT::region(n - 1) == type) --n; + return n; + } + + static constexpr int find_end(int n) { + const auto type = ClassifierT::region(n); + while(n < ClassifierT::max && ClassifierT::region(n) == type) ++n; + return n; + } +}; + +#undef switch_indices + +#undef index2 +#undef index4 +#undef index8 +#undef index16 +#undef index32 +#undef index64 +#undef index128 +#undef index256 +#undef index512 +#undef index1024 +#undef index2048 }