1 /*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #ifndef ROGUE_UTIL_H
25 #define ROGUE_UTIL_H
26
27 #include <assert.h>
28 #include <stdbool.h>
29 #include <stddef.h>
30 #include <stdint.h>
31
32 #include "util/bitscan.h"
33 #include "util/log.h"
34 #include "util/macros.h"
35
36 /* Input validation helpers. */
37
38 /**
39 * \brief Returns false if "expr" is not asserted.
40 *
41 * \param[in] expr The expression to check.
42 */
43 #define CHECK(expr) \
44 do { \
45 if (!(expr)) \
46 return false; \
47 } while (0)
48
49 /**
50 * \brief Returns false if "expr" is not asserted,
51 * and logs the provided error message.
52 *
53 * \param[in] expr The expression to check.
54 * \param[in] fmt The error message to print.
55 * \param[in] ... The printf-style varable arguments.
56 */
57 #define CHECKF(expr, fmt, ...) \
58 do { \
59 if (!(expr)) { \
60 mesa_log(MESA_LOG_ERROR, "ROGUE", fmt, ##__VA_ARGS__); \
61 return false; \
62 } \
63 } while (0)
64
65 /**
66 * \brief Asserts if "opcode" is invalid.
67 *
68 * \param[in] opcode The opcode to check.
69 */
70 #define ASSERT_OPCODE_RANGE(opcode) assert((opcode) < ROGUE_OP_COUNT)
71
72 /**
73 * \brief Asserts if "operand" is invalid.
74 *
75 * \param[in] operand The operand to check.
76 */
77 #define ASSERT_OPERAND_RANGE(operand) \
78 assert((operand) < ROGUE_OPERAND_TYPE_COUNT)
79
80 /**
81 * \brief Asserts if "operand" is not a register.
82 *
83 * \param[in] operand The operand to check.
84 */
85 #define ASSERT_OPERAND_REG(operand) \
86 assert((operand) <= ROGUE_OPERAND_TYPE_REG_MAX)
87
88 /**
89 * \brief Asserts if "flag" is invalid.
90 *
91 * \param[in] flag The flag to check.
92 */
93 #define ASSERT_INSTR_FLAG_RANGE(flag) assert((flag) < ROGUE_INSTR_FLAG_COUNT)
94
95 /**
96 * \brief Asserts if operand index "index" is out of range.
97 *
98 * \param[in] instr The target instruction.
99 * \param[in] index The operand index to check.
100 */
101 #define ASSERT_INSTR_OPERAND_INDEX(instr, index) \
102 assert((index) < (instr)->num_operands)
103
104 /**
105 * \brief Asserts if "stage" is invalid.
106 *
107 * \param[in] stage The stage to check.
108 */
109 #define ASSERT_SHADER_STAGE_RANGE(stage) assert((stage) < MESA_SHADER_STAGES)
110
111 /**
112 * \brief Creates a "n"-bit mask starting from bit "b".
113 *
114 * \param[in] b The starting bit.
115 * \param[in] n The number of bits in the mask.
116 */
117 #define BITMASK64_N(b, n) (((~0ULL) << (64 - (n))) >> (63 - (b)))
118
119 /**
120 * \brief Compile-time rogue_onehot.
121 *
122 * \sa #rogue_onehot()
123 */
124 #define ROH(OFFSET) BITFIELD64_BIT(OFFSET)
125
126 /* TODO: Consider integrating the following into src/util/{macros,bitscan}.h */
127
128 /**
129 * \brief Converts a one-hot encoding to an offset encoding.
130 *
131 * E.g. 0b10000 -> 4
132 *
133 * \param[in] onehot The one-hot encoding.
134 * \return The offset encoding.
135 */
rogue_offset(uint64_t onehot)136 static inline uint64_t rogue_offset(uint64_t onehot)
137 {
138 assert(util_bitcount64(onehot) == 1);
139 return ffsll(onehot) - 1;
140 }
141
142 /**
143 * \brief Converts an offset encoding to a one-hot encoding.
144 *
145 * E.g. 0 -> 0b1
146 *
147 * \param[in] offset The offset encoding.
148 * \return The one-hot encoding.
149 */
rogue_onehot(uint64_t offset)150 static inline uint64_t rogue_onehot(uint64_t offset)
151 {
152 assert(offset < 64ULL);
153 return (1ULL << offset);
154 }
155
156 /**
157 * \brief Checks whether an input bitfield contains only a valid bitset.
158 *
159 * E.g. rogue_check_bitset(0b00001100, 0b00001111) -> true
160 * rogue_check_bitset(0b00001100, 0b00000111) -> false
161 *
162 * \param[in] input The input bitfield.
163 * \param[in] valid_bits The valid bitset.
164 * \return true if "input" contains only "valid_bits", false otherwise.
165 */
rogue_check_bitset(uint64_t input,uint64_t valid_bits)166 static inline bool rogue_check_bitset(uint64_t input, uint64_t valid_bits)
167 {
168 input &= ~valid_bits;
169 return !input;
170 }
171
172 /**
173 * \brief Describes a downward range of bits within an arbitrarily-sized
174 * sequence.
175 *
176 * E.g. for start = 7 and num = 3:
177 *
178 * 76543210
179 * abcdefgh
180 *
181 * the bit range would be: abc.
182 */
183 struct rogue_bitrange {
184 size_t start;
185 size_t num;
186 };
187
188 /**
189 * \brief Describes a collection of bit-ranges within an arbitrarily-sized
190 * sequence that are meaningful together.
191 *
192 * E.g. an 8-bit value that is encoded within a larger value:
193 * 8-bit value: abcdefgh
194 * Parent value: 010ab0cdef0010gh
195 *
196 */
197 struct rogue_rangelist {
198 size_t num_ranges;
199 struct rogue_bitrange *ranges;
200 };
201
202 /**
203 * \brief Counts the total number of bits described in a rangelist.
204 *
205 * \param[in] rangelist The input rangelist.
206 * \return The total number of bits.
207 */
208 static inline size_t
rogue_rangelist_bits(const struct rogue_rangelist * rangelist)209 rogue_rangelist_bits(const struct rogue_rangelist *rangelist)
210 {
211 size_t total_bits = 0U;
212
213 for (size_t u = 0U; u < rangelist->num_ranges; ++u)
214 total_bits += rangelist->ranges[u].num;
215
216 return total_bits;
217 }
218
219 /**
220 * \brief Returns the byte offset of the bitrange moving left from the LSB.
221 *
222 * \param[in] bitrange The input bit-range.
223 * \return The byte offset.
224 */
rogue_byte_num(const struct rogue_bitrange * bitrange)225 static inline size_t rogue_byte_num(const struct rogue_bitrange *bitrange)
226 {
227 /* Make sure there are enough bits. */
228 assert(bitrange->num <= (bitrange->start + 1));
229
230 return bitrange->start / 8;
231 }
232
233 /**
234 * \brief Returns the array-indexable byte offset of a bit-range if the sequence
235 * it represents were to be stored in an byte-array containing "num_bytes"
236 * bytes.
237 *
238 * E.g. uint8_t array[2] is a sequence of 16 bits:
239 * bit(0) is located in array[1].
240 * bit(15) is located in array[0].
241 *
242 * For uint8_t array[4]:
243 * bit(0) is located in array[3].
244 * bit(15) is located in array[2].
245 *
246 * \param[in] bitrange The input bit-range.
247 * \param[in] num_bytes The number of bytes that are used to contain the
248 * bit-range. \return The byte offset.
249 */
rogue_byte_index(const struct rogue_bitrange * bitrange,size_t num_bytes)250 static inline size_t rogue_byte_index(const struct rogue_bitrange *bitrange,
251 size_t num_bytes)
252 {
253 /* Make sure there are enough bits. */
254 assert(bitrange->num <= (bitrange->start + 1));
255
256 return num_bytes - rogue_byte_num(bitrange) - 1;
257 }
258
259 /**
260 * \brief Returns the bit offset of a bit-range if the sequence it represents is
261 * being accessed in a byte-wise manner.
262 *
263 * E.g. bit 17 has a bit offset of 1.
264 *
265 * \param[in] bitrange The input bit-range.
266 * \return The bit offset.
267 */
rogue_bit_offset(const struct rogue_bitrange * bitrange)268 static inline size_t rogue_bit_offset(const struct rogue_bitrange *bitrange)
269 {
270 /* Make sure there are enough bits. */
271 assert(bitrange->num <= (bitrange->start + 1));
272
273 return bitrange->start % 8;
274 }
275
276 /**
277 * \brief Returns the number of additional bytes that the bit-range spills into
278 * (excluding its "starting" byte).
279 *
280 * \param[in] bitrange The input bit-range.
281 * \return The number of bytes spilled.
282 */
rogue_bytes_spilled(const struct rogue_bitrange * bitrange)283 static inline size_t rogue_bytes_spilled(const struct rogue_bitrange *bitrange)
284 {
285 /* Make sure there are enough bits. */
286 assert(bitrange->num <= (bitrange->start + 1));
287
288 return ((bitrange->num - 1) / 8) +
289 ((bitrange->num % 8) > (rogue_bit_offset(bitrange) + 1));
290 }
291
292 /**
293 * \brief For a given bit offset, returns the maximum number of bits (including
294 * itself) that are accessible before spilling into the following byte.
295 *
296 * E.g. When trying to insert an 8-bit value offset of 13, a maximum of 6 bits
297 * can be placed; the last 2 bits will need to go into the next byte.
298 *
299 * 8-bit value: abcdefgh
300 *
301 * array[0] array[1]
302 * 15 8 7 0
303 * iiiiiiii jjjjjjjj
304 * ^
305 * abcdef gh
306 *
307 * \param[in] The bit offset.
308 * \return The maximum number of accessible bits.
309 */
rogue_max_bits(size_t offset)310 static inline size_t rogue_max_bits(size_t offset)
311 {
312 return (offset % 8) + 1;
313 }
314
315 bool rogue_distribute_value(uint64_t source,
316 const struct rogue_rangelist *rangelist,
317 size_t dest_size,
318 uint8_t dest_bytes[dest_size]);
319
320 #endif /* ROGUE_UTIL_H */
321