diff options
| author | Julian Blake Kongslie | 2022-06-25 10:16:12 -0700 |
|---|---|---|
| committer | Julian Blake Kongslie | 2022-06-25 10:16:12 -0700 |
| commit | d80e0dbd20bb8597afe92f770e39d20440557d1f (patch) | |
| tree | 3a2a89d3e8b0a7dd8f9a26f166825cef7c62795d /aisa | |
| parent | Move get-git-tag to a tools subdirectory. (diff) | |
| download | issim-d80e0dbd20bb8597afe92f770e39d20440557d1f.tar.xz | |
Demo for a coroutine-based step evaluator.
Diffstat (limited to 'aisa')
| -rw-r--r-- | aisa/aisa.cpp | 5 | ||||
| -rw-r--r-- | aisa/aisa.h | 73 | ||||
| -rw-r--r-- | aisa/coroutine.h | 120 |
3 files changed, 192 insertions, 6 deletions
diff --git a/aisa/aisa.cpp b/aisa/aisa.cpp index 0c806de..97a3b17 100644 --- a/aisa/aisa.cpp +++ b/aisa/aisa.cpp | |||
| @@ -4,9 +4,4 @@ | |||
| 4 | 4 | ||
| 5 | namespace aisa { | 5 | namespace aisa { |
| 6 | 6 | ||
| 7 | void do_something() | ||
| 8 | { | ||
| 9 | std::cout << "Hello, world!\n"; | ||
| 10 | } | ||
| 11 | |||
| 12 | } | 7 | } |
diff --git a/aisa/aisa.h b/aisa/aisa.h index b570d6b..8cb302e 100644 --- a/aisa/aisa.h +++ b/aisa/aisa.h | |||
| @@ -1,7 +1,78 @@ | |||
| 1 | #pragma once | 1 | #pragma once |
| 2 | 2 | ||
| 3 | #include <coroutine> | ||
| 4 | #include <cstdint> | ||
| 5 | #include <iostream> | ||
| 6 | #include <optional> | ||
| 7 | #include <utility> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "aisa/coroutine.h" | ||
| 11 | |||
| 3 | namespace aisa { | 12 | namespace aisa { |
| 4 | 13 | ||
| 5 | void do_something(); | 14 | using regnum_t = std::uint_fast64_t; |
| 15 | using regval_t = std::uint64_t; | ||
| 16 | |||
| 17 | template<typename CRTP> struct EvalState { | ||
| 18 | CRTP & crtp() noexcept { return static_cast<CRTP &>(*this); } | ||
| 19 | task<regval_t> async_load_reg(regnum_t rn) | ||
| 20 | { | ||
| 21 | while (true) { | ||
| 22 | if (auto rv = crtp().load_reg(rn); rv.has_value()) | ||
| 23 | co_return *rv; | ||
| 24 | co_await suspend(); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct Step { | ||
| 30 | const std::optional<std::pair<regnum_t, regval_t>> predicate; | ||
| 31 | const std::vector<regnum_t> source_regs; | ||
| 32 | const std::vector<regnum_t> destination_regs; | ||
| 33 | |||
| 34 | std::optional<regnum_t> predicate_reg() const | ||
| 35 | { | ||
| 36 | if (predicate.has_value()) | ||
| 37 | return predicate->first; | ||
| 38 | return {}; | ||
| 39 | } | ||
| 40 | |||
| 41 | std::optional<regnum_t> expected_predicate_val() const | ||
| 42 | { | ||
| 43 | if (predicate.has_value()) | ||
| 44 | return predicate->second; | ||
| 45 | return {}; | ||
| 46 | } | ||
| 47 | |||
| 48 | template<typename State> task<void> evaluate(State &state) const | ||
| 49 | { | ||
| 50 | if (predicate.has_value()) { | ||
| 51 | std::cout << "checking predicate...\n"; | ||
| 52 | std::cout << "\texpect " << predicate->second << "\n"; | ||
| 53 | regval_t pval = co_await state.async_load_reg(predicate->first); | ||
| 54 | std::cout << "\tgot " << pval << "\n"; | ||
| 55 | if (pval != predicate->second) { | ||
| 56 | std::cout << "\tpredicate skipped\n"; | ||
| 57 | co_return; | ||
| 58 | } else { | ||
| 59 | std::cout << "\tpredicate not skipped\n"; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | std::cout << "reading sources...\n"; | ||
| 63 | std::vector<regval_t> source_vals; | ||
| 64 | source_vals.reserve(source_regs.size()); | ||
| 65 | for (unsigned int i = 0; i < source_regs.size(); ++i) { | ||
| 66 | std::cout << "\tgetting source " << i << "...\n"; | ||
| 67 | source_vals.emplace_back(co_await state.async_load_reg(source_regs[i])); | ||
| 68 | std::cout << "\t\tgot " << source_vals.back() << "\n"; | ||
| 69 | } | ||
| 70 | std::cout << "sources:"; | ||
| 71 | for (unsigned int i = 0; i < source_regs.size(); ++i) | ||
| 72 | std::cout << " " << source_regs[i] << "=" << source_vals[i]; | ||
| 73 | std::cout << "\n"; | ||
| 74 | std::cout << "done with evaluate\n"; | ||
| 75 | } | ||
| 76 | }; | ||
| 6 | 77 | ||
| 7 | } | 78 | } |
diff --git a/aisa/coroutine.h b/aisa/coroutine.h new file mode 100644 index 0000000..40a6982 --- /dev/null +++ b/aisa/coroutine.h | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <coroutine> | ||
| 4 | |||
| 5 | namespace aisa { | ||
| 6 | |||
| 7 | inline auto suspend() noexcept { return std::suspend_always{}; } | ||
| 8 | |||
| 9 | template<typename result_t> struct promise; | ||
| 10 | |||
| 11 | template<typename result_t> struct task : public std::coroutine_handle<promise<result_t>> { | ||
| 12 | using handle = std::coroutine_handle<struct promise<result_t>>; | ||
| 13 | using promise_type = struct promise<result_t>; | ||
| 14 | bool await_ready() const noexcept { return handle::done(); } | ||
| 15 | result_t await_resume() const noexcept; | ||
| 16 | template<typename other_t> void await_suspend(std::coroutine_handle<struct promise<other_t>> h) const noexcept; | ||
| 17 | std::optional<result_t> operator()() noexcept; | ||
| 18 | }; | ||
| 19 | |||
| 20 | template<> struct task<void> : public std::coroutine_handle<promise<void>> { | ||
| 21 | using handle = std::coroutine_handle<struct promise<void>>; | ||
| 22 | using promise_type = struct promise<void>; | ||
| 23 | bool await_ready() const noexcept { return handle::done(); } | ||
| 24 | void await_resume() const noexcept; | ||
| 25 | template<typename other_t> void await_suspend(std::coroutine_handle<struct promise<other_t>> h) const noexcept; | ||
| 26 | bool operator()() noexcept; | ||
| 27 | }; | ||
| 28 | |||
| 29 | template<typename result_t> struct promise { | ||
| 30 | std::coroutine_handle<> precursor; | ||
| 31 | std::optional<result_t> result; | ||
| 32 | promise() = default; | ||
| 33 | promise(const promise &) = delete; | ||
| 34 | task<result_t> get_return_object() noexcept { return task<result_t>{std::coroutine_handle<promise<result_t>>::from_promise(*this)}; } | ||
| 35 | std::suspend_never initial_suspend() const noexcept { return {}; } | ||
| 36 | std::suspend_always final_suspend() const noexcept { return {}; } | ||
| 37 | void unhandled_exception() { } | ||
| 38 | void return_value(result_t x) noexcept { result = std::move(x); } | ||
| 39 | }; | ||
| 40 | |||
| 41 | template<> struct promise<void> { | ||
| 42 | std::coroutine_handle<> precursor; | ||
| 43 | promise() = default; | ||
| 44 | promise(const promise &) = delete; | ||
| 45 | task<void> get_return_object() noexcept { return task<void>{std::coroutine_handle<promise<void>>::from_promise(*this)}; } | ||
| 46 | std::suspend_never initial_suspend() const noexcept { return {}; } | ||
| 47 | std::suspend_always final_suspend() const noexcept { return {}; } | ||
| 48 | void unhandled_exception() { } | ||
| 49 | void return_void() noexcept { } | ||
| 50 | }; | ||
| 51 | |||
| 52 | template<typename result_t> result_t task<result_t>::await_resume() const noexcept | ||
| 53 | { | ||
| 54 | auto x = std::move(handle::promise().result.value()); | ||
| 55 | handle::destroy(); | ||
| 56 | return std::move(x); | ||
| 57 | } | ||
| 58 | |||
| 59 | template<typename result_t> template<typename other_t> void task<result_t>::await_suspend(std::coroutine_handle<struct promise<other_t>> h) const noexcept | ||
| 60 | { | ||
| 61 | h.promise().precursor = *this; | ||
| 62 | } | ||
| 63 | |||
| 64 | template<typename result_t> std::optional<result_t> task<result_t>::operator()() noexcept | ||
| 65 | { | ||
| 66 | if (!handle::operator bool()) | ||
| 67 | return {}; | ||
| 68 | if (!handle::done()) { | ||
| 69 | auto &precursor = handle::promise().precursor; | ||
| 70 | if (precursor) { | ||
| 71 | if (!precursor.done()) | ||
| 72 | precursor.resume(); | ||
| 73 | if (precursor.done()) | ||
| 74 | precursor = nullptr; | ||
| 75 | } | ||
| 76 | if (!precursor) | ||
| 77 | handle::resume(); | ||
| 78 | } | ||
| 79 | if (handle::done()) { | ||
| 80 | auto x = await_resume(); | ||
| 81 | handle::operator=(nullptr); | ||
| 82 | return std::move(x); | ||
| 83 | } | ||
| 84 | return {}; | ||
| 85 | } | ||
| 86 | |||
| 87 | inline void task<void>::await_resume() const noexcept | ||
| 88 | { | ||
| 89 | handle::destroy(); | ||
| 90 | } | ||
| 91 | |||
| 92 | template<typename other_t> void task<void>::await_suspend(std::coroutine_handle<struct promise<other_t>> h) const noexcept | ||
| 93 | { | ||
| 94 | h.promise().precursor = *this; | ||
| 95 | } | ||
| 96 | |||
| 97 | inline bool task<void>::operator()() noexcept | ||
| 98 | { | ||
| 99 | if (!handle::operator bool()) | ||
| 100 | return true; | ||
| 101 | if (!handle::done()) { | ||
| 102 | auto &precursor = handle::promise().precursor; | ||
| 103 | if (precursor) { | ||
| 104 | if (!precursor.done()) | ||
| 105 | precursor.resume(); | ||
| 106 | if (precursor.done()) | ||
| 107 | precursor = nullptr; | ||
| 108 | } | ||
| 109 | if (!precursor) | ||
| 110 | handle::resume(); | ||
| 111 | } | ||
| 112 | if (handle::done()) { | ||
| 113 | await_resume(); | ||
| 114 | handle::operator=(nullptr); | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | return false; | ||
| 118 | } | ||
| 119 | |||
| 120 | } | ||
