FE
0.14.0
A C++23 toolkit for writing compiler/interpreter 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 <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
14
namespace
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.
37
template
<
class
Self>
38
class
VLA
{
39
public
:
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
66
private
:
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?
120
template
<
class
T>
121
concept
VLAed
= std::derived_from<std::remove_cv_t<T>,
VLA<std::remove_cv_t<T>
>>;
122
123
}
// namespace fe
algo.h
fe::VLA
CRTP base that gives Self one VLA per element type in Self::VLA_Types directly behind it in memory - ...
Definition
vla.h:38
fe::VLA::vla_align
static constexpr size_t vla_align() noexcept
Definition
vla.h:50
fe::VLA::vla_bytes
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
fe::VLA::Arena
friend class Arena
Definition
vla.h:116
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:46
fe::VLA::vla
auto vla() const noexcept
The I th VLA as a View<VLA_Type<I>>.
Definition
vla.h:60
fe::VLA::num_vlas
static constexpr size_t num_vlas() noexcept
Definition
vla.h:48
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:41
fe::VLAed
Does T carry VLAs?
Definition
vla.h:121
fe
Definition
algo.h:17
fe::View
Span< const T, N > View
Read-only Span; use Span itself, if you want to write through its elements.
Definition
span.h:107
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