runtime 1.0.0
Checked CEL programs, owned values, and reusable execution stacks
Loading...
Searching...
No Matches
raw_segment.hpp
1// SPDX-License-Identifier: BSL-1.0
2#ifndef CEL_RUNTIME_RAW_SEGMENT_HPP
3#define CEL_RUNTIME_RAW_SEGMENT_HPP
4#include <array>
5#include <cel/runtime/detail/frame.hpp>
6#include <cel/runtime/detail/retirement.hpp>
7#include <concepts>
8#include <functional>
9#include <memory>
10#include <tuple>
11#include <type_traits>
12#include <utility>
13
14namespace cel::detail {
15struct instruction_storage;
16struct segment_access;
34class raw_segment {
35 detail::retirement_ptr<detail::instruction_storage> _storage;
36 friend struct detail::segment_access;
37
39 void* allocate_capture(std::size_t size, std::size_t alignment);
42 void append(const void* capture,
43 void (*execute)(const void*, frame&),
44 void (*destroy)(const void*) noexcept,
45 void (*retire)(const void*, detail::retirement_queue&) noexcept = nullptr);
46
48 template <class F, class R, class... Args>
49 struct operation {
50 std::array<value_type, sizeof...(Args)> inputs;
51 std::array<value_type, std::is_void_v<R> ? 0 : 1>
52 outputs;
53 F callback;
55 static auto result_types() {
56 if constexpr (std::is_void_v<R>)
57 return std::array<value_type, 0>{};
58 else
59 return std::array{detail::concrete_adapter<R>::type()};
60 }
62 template <class G>
63 requires std::constructible_from<F, G&&> explicit operation(G&& input) :
64 inputs{detail::concrete_adapter<Args>::type()...}, outputs(result_types()),
65 callback(std::forward<G>(input)) {}
68 template <std::size_t... I>
69 void invoke(frame& scope, std::index_sequence<I...>) const {
70 auto arguments =
71 scope.template acquire_operands<Args...>(inputs, std::index_sequence<I...>{});
72 scope.discard(sizeof...(Args));
73 if constexpr (std::is_void_v<R>) {
74 std::apply([&](auto&... args) { std::invoke(callback, scope, std::move(args)...); },
75 arguments);
76 } else {
77 R result = std::apply(
78 [&](auto&... args) -> R {
79 return std::invoke(callback, scope, std::move(args)...);
80 },
81 arguments);
82 scope.push_result(outputs[0], result);
83 }
84 }
87 static void execute(const void* capture, frame& scope) {
88 static_cast<const operation*>(capture)->invoke(scope,
89 std::index_sequence_for<Args...>{});
90 }
92 static void destroy(const void* capture) noexcept {
93 std::destroy_at(static_cast<const operation*>(capture));
94 }
95 };
96
97public:
99 raw_segment() noexcept;
102 ~raw_segment();
104 raw_segment(const raw_segment&) = delete;
106 raw_segment& operator=(const raw_segment&) = delete;
108 raw_segment(raw_segment&& other) noexcept;
111 raw_segment& operator=(raw_segment&& other) noexcept;
121 // [knowledge] Retain release-build depth/layout checks even for trusted instruction signatures.
122 void execute(frame& scope) const;
136 template <class R, class... Args, class F>
137 void push_op(F&& callback) {
138 using capture_type = std::decay_t<F>;
139 static_assert(std::is_nothrow_destructible_v<capture_type>);
140 static_assert(std::is_invocable_r_v<R, const capture_type&, frame&, Args...>);
141 using stored = operation<capture_type, R, Args...>;
142 auto* location = static_cast<stored*>(allocate_capture(sizeof(stored), alignof(stored)));
143 auto* capture = std::construct_at(location, std::forward<F>(callback));
144 try {
145 append(capture, stored::execute, stored::destroy);
146 } catch (...) {
147 std::destroy_at(capture);
148 throw;
149 }
150 }
154 template <class R, class F>
155 void push_op0(F&& callback) {
156 push_op<R>(std::forward<F>(callback));
157 }
161 template <class R, class A, class F>
162 void push_op1(F&& callback) {
163 push_op<R, A>(std::forward<F>(callback));
164 }
170 template <class R, class A, class B, class F>
171 void push_op2(F&& callback) {
172 push_op<R, A, B>(std::forward<F>(callback));
173 }
179 template <class R, class A, class B, class C, class F>
180 void push_op3(F&& callback) {
181 push_op<R, A, B, C>(std::forward<F>(callback));
182 }
183};
184} // namespace cel::detail
185#endif