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