FE
0.16.1
Fast, Effecient FrontEnds
Toggle main menu visibility
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
13
namespace
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.
36
template
<
class
Self>
37
class
VLA
{
38
public
:
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
68
private
:
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?
123
template
<
class
T>
124
concept
VLAed
= std::derived_from<std::remove_cv_t<T>,
VLA<std::remove_cv_t<T>
>>;
125
126
}
// namespace fe
algo.h
fe::Span
This is a thin wrapper for std::span<T, N> with the following additional features:
Definition
span.h:33
fe::Span::Span
constexpr Span(std::span< T, N > span) noexcept
Definition
span.h:47
fe::VLA
CRTP base that gives Self one VLA per element type in Self::VLA_Types directly behind it in memory - ...
Definition
vla.h:37
fe::VLA::vla_align
static constexpr size_t vla_align() noexcept
Definition
vla.h:49
fe::VLA::Arena
friend class Arena
Definition
vla.h:119
fe::VLA::VLA_Type
std::tuple_element_t< I, typename S::VLA_Types > VLA_Type
Element type of the I th VLA.
Definition
vla.h:45
fe::VLA::vla
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
fe::VLA::num_vlas
static constexpr size_t num_vlas() noexcept
Definition
vla.h:47
fe::VLA::vla_bytes
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
fe::VLA::VLA_Self
Self VLA_Self
The type this base was instantiated with - Self itself, unless a class deriving from it inherited it.
Definition
vla.h:40
fe::VLAed
Does T carry VLAs?
Definition
vla.h:124
fe
Definition
algo.h:17
fe::pad
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
span.h
fe
vla.h
Generated by
1.18.0