CIRCT 23.0.0git
Loading...
Searching...
No Matches
BitAccess.h
Go to the documentation of this file.
1//===- BitAccess.h - Bit-level helpers for generated types ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// DO NOT EDIT!
10// This file is distributed as part of an ESI package. The source for this file
11// should always be modified within CIRCT.
12//
13// Helpers used by `esiaccel.codegen`-generated headers to read and write
14// arbitrary-width integer fields out of a contiguous wire-byte buffer. The
15// runtime emits each struct/union as a `std::array<uint8_t, N>` matching the
16// on-wire bit layout exactly, and these helpers handle the shift/mask work
17// (including signed sign-extension) without depending on C++ bit-fields,
18// which have implementation-defined layout that differs between the Itanium
19// ABI (GCC/Clang) and MSVC.
20//
21// `BitOffset` and `Width` are non-type template parameters so the per-field
22// constants from the generated code (a) participate in `static_assert`
23// checks against `Storage`, and (b) let the optimiser fully unroll the
24// per-bit loop.
25//
26//===----------------------------------------------------------------------===//
27
28// NOLINTNEXTLINE(llvm-header-guard)
29#ifndef ESI_BITACCESS_H
30#define ESI_BITACCESS_H
31
32#include <cstddef>
33#include <cstdint>
34#include <type_traits>
35
36namespace esi {
37namespace detail {
38
39/// Read `Width` bits starting at `BitOffset` (LSB-first within `bytes`) and
40/// return them zero-extended in `Storage`. `Storage` must be an unsigned
41/// integer type wide enough to hold `Width` bits.
42template <typename Storage, std::size_t BitOffset, std::size_t Width>
43inline constexpr Storage readUnsignedBits(const uint8_t *bytes) {
44 static_assert(std::is_unsigned<Storage>::value,
45 "readUnsignedBits Storage must be unsigned");
46 static_assert(Width != 0, "readUnsignedBits Width must be non-zero");
47 static_assert(sizeof(Storage) * 8 >= Width,
48 "readUnsignedBits Storage is narrower than Width");
49 Storage result = 0;
50 for (std::size_t i = 0; i < Width; ++i) {
51 std::size_t src = BitOffset + i;
52 Storage bit = static_cast<Storage>((bytes[src >> 3] >> (src & 7)) & 1u);
53 result |= static_cast<Storage>(bit << i);
54 }
55 return result;
56}
57
58/// Read `Width` bits starting at `BitOffset` (LSB-first) and sign-extend
59/// into the signed integer type `Signed`. `Signed` must be at least `Width`
60/// bits wide.
61template <typename Signed, std::size_t BitOffset, std::size_t Width>
62inline constexpr Signed readSignedBits(const uint8_t *bytes) {
63 static_assert(std::is_signed<Signed>::value,
64 "readSignedBits Signed must be signed");
65 static_assert(Width != 0, "readSignedBits Width must be non-zero");
66 static_assert(sizeof(Signed) * 8 >= Width,
67 "readSignedBits Signed is narrower than Width");
68 using Unsigned = typename std::make_unsigned<Signed>::type;
69 Unsigned u = readUnsignedBits<Unsigned, BitOffset, Width>(bytes);
70 if constexpr (Width < sizeof(Unsigned) * 8) {
71 // Branch-free sign extend: `(u ^ signBit) - signBit` flips the sign
72 // bit and subtracts it back, leaving the high bits all-ones when the
73 // sign bit was set and unchanged otherwise. Avoids relying on the
74 // arithmetic-shift behaviour of signed right-shift.
75 constexpr Unsigned signBit = static_cast<Unsigned>(1) << (Width - 1);
76 u = static_cast<Unsigned>((u ^ signBit) - signBit);
77 }
78 return static_cast<Signed>(u);
79}
80
81/// Write the low `Width` bits of `value` into `bytes` starting at
82/// `BitOffset` (LSB-first). Other bits in the affected bytes are preserved.
83template <typename Storage, std::size_t BitOffset, std::size_t Width>
84inline constexpr void writeUnsignedBits(uint8_t *bytes, Storage value) {
85 static_assert(std::is_unsigned<Storage>::value,
86 "writeUnsignedBits Storage must be unsigned");
87 static_assert(Width != 0, "writeUnsignedBits Width must be non-zero");
88 static_assert(sizeof(Storage) * 8 >= Width,
89 "writeUnsignedBits Storage is narrower than Width");
90 for (std::size_t i = 0; i < Width; ++i) {
91 std::size_t dst = BitOffset + i;
92 std::size_t shift = dst & 7;
93 uint8_t mask = static_cast<uint8_t>(1u << shift);
94 uint8_t bit = static_cast<uint8_t>((value >> i) & static_cast<Storage>(1));
95 bytes[dst >> 3] =
96 static_cast<uint8_t>((bytes[dst >> 3] & static_cast<uint8_t>(~mask)) |
97 static_cast<uint8_t>(bit << shift));
98 }
99}
100
101/// Convenience overload for signed values. Reinterprets the value as
102/// unsigned (two's complement) and writes the low `Width` bits.
103template <typename Signed, std::size_t BitOffset, std::size_t Width>
104inline constexpr void writeSignedBits(uint8_t *bytes, Signed value) {
105 static_assert(std::is_signed<Signed>::value,
106 "writeSignedBits Signed must be signed");
107 static_assert(Width != 0, "writeSignedBits Width must be non-zero");
108 static_assert(sizeof(Signed) * 8 >= Width,
109 "writeSignedBits Signed is narrower than Width");
110 using Unsigned = typename std::make_unsigned<Signed>::type;
111 writeUnsignedBits<Unsigned, BitOffset, Width>(bytes,
112 static_cast<Unsigned>(value));
113}
114
115/// Copy `Width` bits out of `src` starting at `BitOffset` into `dst`
116/// packed LSB-first from bit 0. Used to embed an aggregate (struct, union,
117/// or array) at an arbitrary bit position inside a parent wire buffer:
118/// the inner type already stores its bits LSB-first in `dst`, so the
119/// destination bits land at the same positions they'd occupy if the inner
120/// was read out at byte alignment. `dst` must be zero-initialised on entry
121/// (the helper only sets `1` bits) and must hold at least
122/// `ceil(Width / 8)` bytes.
123template <std::size_t BitOffset, std::size_t Width>
124inline constexpr void copyBitsIn(const uint8_t *src, uint8_t *dst) {
125 static_assert(Width != 0, "copyBitsIn Width must be non-zero");
126 for (std::size_t i = 0; i < Width; ++i) {
127 std::size_t s = BitOffset + i;
128 uint8_t bit = static_cast<uint8_t>((src[s >> 3] >> (s & 7)) & 1u);
129 dst[i >> 3] = static_cast<uint8_t>(dst[i >> 3] | (bit << (i & 7)));
130 }
131}
132
133/// Inverse of `copyBitsIn`: take `Width` bits packed LSB-first from bit 0
134/// of `src` and write them into `dst` starting at `BitOffset`. Other bits
135/// in the affected bytes of `dst` are preserved.
136template <std::size_t BitOffset, std::size_t Width>
137inline constexpr void copyBitsOut(uint8_t *dst, const uint8_t *src) {
138 static_assert(Width != 0, "copyBitsOut Width must be non-zero");
139 for (std::size_t i = 0; i < Width; ++i) {
140 std::size_t d = BitOffset + i;
141 std::size_t shift = d & 7;
142 uint8_t mask = static_cast<uint8_t>(1u << shift);
143 uint8_t bit = static_cast<uint8_t>((src[i >> 3] >> (i & 7)) & 1u);
144 dst[d >> 3] =
145 static_cast<uint8_t>((dst[d >> 3] & static_cast<uint8_t>(~mask)) |
146 static_cast<uint8_t>(bit << shift));
147 }
148}
149
150} // namespace detail
151} // namespace esi
152
153#endif // ESI_BITACCESS_H
constexpr void copyBitsOut(uint8_t *dst, const uint8_t *src)
Inverse of copyBitsIn: take Width bits packed LSB-first from bit 0 of src and write them into dst sta...
Definition BitAccess.h:137
constexpr Signed readSignedBits(const uint8_t *bytes)
Read Width bits starting at BitOffset (LSB-first) and sign-extend into the signed integer type Signed...
Definition BitAccess.h:62
constexpr void copyBitsIn(const uint8_t *src, uint8_t *dst)
Copy Width bits out of src starting at BitOffset into dst packed LSB-first from bit 0.
Definition BitAccess.h:124
constexpr void writeSignedBits(uint8_t *bytes, Signed value)
Convenience overload for signed values.
Definition BitAccess.h:104
constexpr void writeUnsignedBits(uint8_t *bytes, Storage value)
Write the low Width bits of value into bytes starting at BitOffset (LSB-first).
Definition BitAccess.h:84
constexpr Storage readUnsignedBits(const uint8_t *bytes)
Read Width bits starting at BitOffset (LSB-first within bytes) and return them zero-extended in Stora...
Definition BitAccess.h:43
Definition esi.py:1