microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
unwind_hint.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
10#include "register_context.hpp"
11#include <cstddef>
12#include <cstdint>
13#include <type_traits>
14#include <utility>
15
16namespace microfmt {
17
18template <typename Tag> struct unwind_hint_registry_traits;
19template <size_t MaxHints> class unwind_hint_registry_context;
20template <size_t MaxHints = 32> struct fixed_unwind_hint_registry_tag {};
21
22// ============================================================================
23// Unwind Hint Definition
24// ============================================================================
25
37 uintptr_t pc_start{0};
39 uintptr_t pc_end{0};
40
72 using unwind_routine_t = bool (*)(address_space_ref space, register_context_ref reg_ctx, uintptr_t &next_fp,
73 uintptr_t &next_pc) noexcept;
74
79
85 [[nodiscard]] constexpr bool contains(uintptr_t pc) const noexcept { return pc >= pc_start && pc < pc_end; }
86};
87
88// ============================================================================
89// Type-Erased Unwind Hint Registry Handle
90// ============================================================================
91
96public:
100 struct vtable {
105 bool (*find_hint)(const void *ctx, uintptr_t pc, unwind_hint &out_hint) noexcept;
106 };
107
111 constexpr unwind_hint_registry_ref() noexcept = default;
112
119 template <typename Tag, typename Context, typename Traits = unwind_hint_registry_traits<Tag>,
120 std::enable_if_t<std::is_convertible_v<const Context *, const typename Traits::context_type *>, int> = 0>
122 const Context &ctx RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
123 : ctx_(&ctx), vtbl_(&s_vtbl<Tag>) {}
124
125 template <typename Tag, typename Context, std::enable_if_t<!std::is_lvalue_reference_v<Context>, int> = 0>
126 constexpr unwind_hint_registry_ref(Tag, Context &&) = delete;
127
128 template <typename Tag, typename Context, typename Traits = unwind_hint_registry_traits<Tag>>
129 [[nodiscard]] static constexpr unwind_hint_registry_ref make(const Context &ctx RELOCO_LIFETIMEBOUND) noexcept {
130 return unwind_hint_registry_ref(Tag{}, ctx);
131 }
132
133 template <typename Tag, typename Context, std::enable_if_t<!std::is_lvalue_reference_v<Context>, int> = 0>
134 static unwind_hint_registry_ref make(Context &&) = delete;
135
142 [[nodiscard]] bool find_hint(uintptr_t pc, unwind_hint &out_hint) const noexcept {
143 if (!vtbl_)
144 return false;
145 return vtbl_->find_hint(ctx_.get(), pc, out_hint);
146 }
147
152 [[nodiscard]] constexpr explicit operator bool() const noexcept { return vtbl_ != nullptr; }
153
154private:
155 template <typename Tag>
156 static constexpr vtable s_vtbl{[](const void *context, uintptr_t pc, unwind_hint &hint) noexcept {
157 using context_type = typename unwind_hint_registry_traits<Tag>::context_type;
158 const auto &typed_context = *static_cast<const context_type *>(context);
159 return unwind_hint_registry_traits<Tag>::find_hint(value_ref<const context_type>(typed_context), pc, hint);
160 }};
161
162 value_ptr<const void> ctx_{};
163 const vtable *vtbl_{nullptr};
164};
165
169template <typename Tag> class RELOCO_OWNER unwind_hint_registry {
170public:
172 using context_type = typename traits_type::context_type;
173
174 constexpr explicit unwind_hint_registry(context_type context) noexcept : context_(std::move(context)) {}
175
176 [[nodiscard]] constexpr value_ref<context_type> context() & noexcept RELOCO_LIFETIMEBOUND {
177 return value_ref<context_type>(context_);
178 }
179
180 [[nodiscard]] constexpr value_ref<const context_type> context() const & noexcept RELOCO_LIFETIMEBOUND {
181 return value_ref<const context_type>(context_);
182 }
183
184 [[nodiscard]] constexpr unwind_hint_registry_ref ref() const & noexcept RELOCO_LIFETIMEBOUND {
185 return unwind_hint_registry_ref(Tag{}, context_);
186 }
187
188 [[nodiscard]] constexpr operator unwind_hint_registry_ref() const & noexcept RELOCO_LIFETIMEBOUND { return ref(); }
189
190 value_ref<context_type> context() && = delete;
191 value_ref<const context_type> context() const && = delete;
192 unwind_hint_registry_ref ref() const && = delete;
193 operator unwind_hint_registry_ref() const && = delete;
194
195private:
196 context_type context_;
197};
198
199// ============================================================================
200// Concrete Zero-Allocation Fixed-Capacity Registry Context
201// ============================================================================
202
207template <size_t MaxHints> class RELOCO_OWNER unwind_hint_registry_context {
208public:
212 constexpr unwind_hint_registry_context() noexcept = default;
213
219 constexpr bool add_hint(const unwind_hint &hint) noexcept {
220 if (hint_count_ >= MaxHints)
221 return false;
222 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
223
224 hints_[hint_count_++] = hint;
225
226 RELOCO_END_UNSAFE_BUFFER_USAGE;
227
228 return true;
229 }
230
237 [[nodiscard]] constexpr bool find_hint(uintptr_t pc, unwind_hint &out_hint) const noexcept {
238 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
239
240 for (size_t i = 0; i < hint_count_; ++i) {
241 if (hints_[i].contains(pc)) {
242 out_hint = hints_[i];
243 return true;
244 }
245 }
246
247 RELOCO_END_UNSAFE_BUFFER_USAGE;
248
249 return false;
250 }
251
256 [[nodiscard]] constexpr size_t size() const noexcept { return hint_count_; }
257
258private:
260 unwind_hint hints_[MaxHints]{};
262 size_t hint_count_{0};
263};
264
268template <size_t MaxHints> struct unwind_hint_registry_traits<fixed_unwind_hint_registry_tag<MaxHints>> {
270
271 static bool find_hint(value_ref<const context_type> context, uintptr_t pc, unwind_hint &out_hint) noexcept {
272 return context->find_hint(pc, out_hint);
273 }
274};
275
277
278} // namespace microfmt
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
Zero-allocation, type-erased handle for inspecting and modifying CPU registers from a frame context.
Definition register_context.hpp:118
Zero-allocation hint registry with fixed capacity.
Definition unwind_hint.hpp:207
constexpr unwind_hint_registry_context() noexcept=default
Constructs an empty registry.
constexpr size_t size() const noexcept
Returns the number of stored hints.
Definition unwind_hint.hpp:256
constexpr bool find_hint(uintptr_t pc, unwind_hint &out_hint) const noexcept
Linear scan for the hint covering a PC.
Definition unwind_hint.hpp:237
Type-erased, two-word handle to an unwind hint registry.
Definition unwind_hint.hpp:95
constexpr unwind_hint_registry_ref(Tag, Context &&)=delete
constexpr unwind_hint_registry_ref() noexcept=default
Constructs an empty (invalid) handle.
static unwind_hint_registry_ref make(Context &&)=delete
bool find_hint(uintptr_t pc, unwind_hint &out_hint) const noexcept
Looks up the hint covering a PC.
Definition unwind_hint.hpp:142
static constexpr unwind_hint_registry_ref make(const Context &ctx RELOCO_LIFETIMEBOUND) noexcept
Definition unwind_hint.hpp:129
Typed owner for an unwind-hint registry traits specialization.
Definition unwind_hint.hpp:169
value_ref< context_type > context() &&=delete
constexpr value_ref< context_type > context() &noexcept RELOCO_LIFETIMEBOUND
Definition unwind_hint.hpp:176
constexpr unwind_hint_registry(context_type context) noexcept
Definition unwind_hint.hpp:174
constexpr unwind_hint_registry_ref ref() const &noexcept RELOCO_LIFETIMEBOUND
Definition unwind_hint.hpp:184
value_ref< const context_type > context() const &&=delete
typename traits_type::context_type context_type
Definition unwind_hint.hpp:172
constexpr value_ref< const context_type > context() const &noexcept RELOCO_LIFETIMEBOUND
Definition unwind_hint.hpp:180
#define MICROFMT_API_CLASS
Definition compat.hpp:181
Definition assert.hpp:19
Definition unwind_hint.hpp:20
Virtual table of registry operations.
Definition unwind_hint.hpp:100
static bool find_hint(value_ref< const context_type > context, uintptr_t pc, unwind_hint &out_hint) noexcept
Definition unwind_hint.hpp:271
Definition unwind_hint.hpp:18
PC-range descriptor for direct, layout-based fallback unwinding.
Definition unwind_hint.hpp:35
bool(*)(address_space_ref space, register_context_ref reg_ctx, uintptr_t &next_fp, uintptr_t &next_pc) noexcept unwind_routine_t
Signature of a custom unwind routine provided by the hint.
Definition unwind_hint.hpp:73
uintptr_t pc_end
Exclusive end of the covered PC range.
Definition unwind_hint.hpp:39
uintptr_t pc_start
Inclusive start of the covered PC range.
Definition unwind_hint.hpp:37
unwind_routine_t routine
Custom routine handler, or nullptr to use layout-based fallback.
Definition unwind_hint.hpp:78
constexpr bool contains(uintptr_t pc) const noexcept
Reports whether a PC falls within the hint range.
Definition unwind_hint.hpp:85