FE 0.14.0
A C++23 toolkit for writing compiler/interpreter frontends.
Loading...
Searching...
No Matches
vla.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4
5#include <array>
6#include <memory>
7#include <ranges>
8#include <tuple>
9#include <type_traits>
10
11#include "fe/algo.h"
12#include "fe/span.h"
13
14namespace fe {
15
16/// CRTP base that gives @p Self one [VLA](https://en.wikipedia.org/wiki/Variable-length_array) per element type in
17/// `Self::VLA_Types` directly behind it in memory - the C flexible array member, generalized to several arrays of
18/// different type.
19/// @p Self must be created through Arena::mk or Arena::ref, which allocate the arrays along with
20/// @p Self and fill them from the *last* `Self::VLA_Types` arguments, in that order.
21/// This base is empty - the element counts live at the front of the VLA block - so that
22/// `Self::VLA_Types` may name types only declared inside @p Self.
23/// ```
24/// class Interval : public Node, public fe::VLA<Interval> {
25/// public:
26/// using VLA_Types = std::tuple<const Expr*, const Expr*>;
27/// Interval(Loc loc, Tag from, Tag to); // no parameter for either array
28/// auto from_args() const { return vla<0>(); } // fe::View<const Expr*>
29/// auto to_args() const { return vla<1>(); }
30/// };
31///
32/// arena.ref<Interval>(loc, from, to, from_args, to_args);
33/// ```
34/// @warning Only the most derived class may carry them.
35/// A class deriving from @p Self would inherit this base and place its arrays at @p Self's offset - on top of its
36/// own members - so Arena::mk and Arena::ref reject it.
37template<class Self>
38class VLA {
39public:
40 /// The type this base was instantiated with - @p Self itself, unless a class deriving from it inherited it.
41 using VLA_Self = Self;
42
43 /// Element type of the @p I th VLA.
44 /// @p S defers the lookup: `Self` is still incomplete while this base is instantiated.
45 template<size_t I, class S = Self>
46 using VLA_Type = std::tuple_element_t<I, typename S::VLA_Types>;
47
48 static constexpr size_t num_vlas() noexcept { return std::tuple_size_v<typename Self::VLA_Types>; }
49
50 static constexpr size_t vla_align() noexcept { return vla_align(seq()); }
51
52 /// Bytes an @p Self with these element counts occupies, `sizeof(Self)` included.
53 template<class C>
54 [[nodiscard]] static constexpr size_t vla_bytes(const C& counts) noexcept {
55 return vla_begin() + offset(num_vlas(), counts);
56 }
57
58 /// The @p I th VLA as a `View<VLA_Type<I>>`.
59 template<size_t I>
60 [[nodiscard]] auto vla() const noexcept {
61 auto block = (const char*)static_cast<const Self*>(this) + vla_begin();
62 auto counts = (const uint32_t*)block;
63 return View<VLA_Type<I>>((const VLA_Type<I>*)(block + offset(I, counts)), counts[I]);
64 }
65
66private:
67 static constexpr auto seq() noexcept { return std::make_index_sequence<num_vlas()>(); }
68 static constexpr size_t vla_begin() noexcept { return pad(sizeof(Self), vla_align()); }
69
70 template<size_t... Is>
71 static constexpr size_t vla_align(std::index_sequence<Is...>) noexcept {
72 return std::max({alignof(uint32_t), alignof(VLA_Type<Is>)...});
73 }
74
75 /// Byte offset of the @p n th array from the block, or the block's size for `n == num_vlas()`.
76 template<class C>
77 static constexpr size_t offset(size_t n, const C& counts) noexcept {
78 return offset(n, counts, seq());
79 }
80
81 template<class C, size_t... Is>
82 static constexpr size_t offset(size_t n, const C& counts, std::index_sequence<Is...>) noexcept {
83 constexpr size_t sizes[] = {sizeof(VLA_Type<Is>)...};
84 constexpr size_t aligns[] = {alignof(VLA_Type<Is>)...};
85
86 size_t off = sizeof...(Is) * sizeof(uint32_t);
87 for (size_t i = 0; i != n; ++i)
88 off = pad(off, aligns[i]) + counts[i] * sizes[i];
89 return n == sizeof...(Is) ? off : pad(off, aligns[n]);
90 }
91
92 /// Arena calls this once @p Self is fully constructed - only then is the downcast valid.
93 template<class... Rs>
94 void fill_vla(const Rs&... rs) {
95 static_assert(sizeof...(Rs) == num_vlas(), "one range per VLA, as the last arguments");
96
97 auto block = (char*)static_cast<Self*>(this) + vla_begin();
98 auto counts = (uint32_t*)block;
99 size_t i = 0;
100 ((counts[i++] = (uint32_t)std::ranges::size(rs)), ...);
101 fill(std::index_sequence_for<Rs...>(), block, rs...);
102 }
103
104 template<size_t... Is, class... Rs>
105 void fill(std::index_sequence<Is...>, char* block, const Rs&... rs) {
106 static_assert((std::same_as<std::ranges::range_value_t<Rs>, VLA_Type<Is>> && ...),
107 "the VLA ranges must come last, in the order VLA_Types declares them");
108 static_assert((std::is_trivially_destructible_v<VLA_Type<Is>> && ...),
109 "a VLA is never destroyed - the Arena reclaims it");
110
111 (std::uninitialized_copy(std::ranges::begin(rs), std::ranges::end(rs),
112 (VLA_Type<Is>*)(block + offset(Is, (const uint32_t*)block))),
113 ...);
114 }
115
116 friend class Arena;
117};
118
119/// Does @p T carry VLAs?
120template<class T>
121concept VLAed = std::derived_from<std::remove_cv_t<T>, VLA<std::remove_cv_t<T>>>;
122
123} // namespace fe
CRTP base that gives Self one VLA per element type in Self::VLA_Types directly behind it in memory - ...
Definition vla.h:38
static constexpr size_t vla_align() noexcept
Definition vla.h:50
static constexpr size_t vla_bytes(const C &counts) noexcept
Bytes an Self with these element counts occupies, sizeof(Self) included.
Definition vla.h:54
friend class Arena
Definition vla.h:116
std::tuple_element_t< I, typename S::VLA_Types > VLA_Type
Element type of the I th VLA.
Definition vla.h:46
auto vla() const noexcept
The I th VLA as a View<VLA_Type<I>>.
Definition vla.h:60
static constexpr size_t num_vlas() noexcept
Definition vla.h:48
Self VLA_Self
The type this base was instantiated with - Self itself, unless a class deriving from it inherited it.
Definition vla.h:41
Does T carry VLAs?
Definition vla.h:121
Definition algo.h:17
Span< const T, N > View
Read-only Span; use Span itself, if you want to write through its elements.
Definition span.h:107
constexpr std::uint64_t pad(std::uint64_t offset, std::uint64_t align) noexcept
Rounds offset up to the next multiple of align.
Definition algo.h:42