/* * Copyright © 2022 Imagination Technologies Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef ROGUE_UTIL_H #define ROGUE_UTIL_H #include #include #include #include #include "util/bitscan.h" #include "util/log.h" #include "util/macros.h" /* Input validation helpers. */ /** * \brief Returns false if "expr" is not asserted. * * \param[in] expr The expression to check. */ #define CHECK(expr) \ do { \ if (!(expr)) \ return false; \ } while (0) /** * \brief Returns false if "expr" is not asserted, * and logs the provided error message. * * \param[in] expr The expression to check. * \param[in] fmt The error message to print. * \param[in] ... The printf-style varable arguments. */ #define CHECKF(expr, fmt, ...) \ do { \ if (!(expr)) { \ mesa_log(MESA_LOG_ERROR, "ROGUE", fmt, ##__VA_ARGS__); \ return false; \ } \ } while (0) /** * \brief Asserts if "opcode" is invalid. * * \param[in] opcode The opcode to check. */ #define ASSERT_OPCODE_RANGE(opcode) assert((opcode) < ROGUE_OP_COUNT) /** * \brief Asserts if "operand" is invalid. * * \param[in] operand The operand to check. */ #define ASSERT_OPERAND_RANGE(operand) \ assert((operand) < ROGUE_OPERAND_TYPE_COUNT) /** * \brief Asserts if "operand" is not a register. * * \param[in] operand The operand to check. */ #define ASSERT_OPERAND_REG(operand) \ assert((operand) <= ROGUE_OPERAND_TYPE_REG_MAX) /** * \brief Asserts if "flag" is invalid. * * \param[in] flag The flag to check. */ #define ASSERT_INSTR_FLAG_RANGE(flag) assert((flag) < ROGUE_INSTR_FLAG_COUNT) /** * \brief Asserts if operand index "index" is out of range. * * \param[in] instr The target instruction. * \param[in] index The operand index to check. */ #define ASSERT_INSTR_OPERAND_INDEX(instr, index) \ assert((index) < (instr)->num_operands) /** * \brief Asserts if "stage" is invalid. * * \param[in] stage The stage to check. */ #define ASSERT_SHADER_STAGE_RANGE(stage) assert((stage) < MESA_SHADER_STAGES) /** * \brief Creates a "n"-bit mask starting from bit "b". * * \param[in] b The starting bit. * \param[in] n The number of bits in the mask. */ #define BITMASK64_N(b, n) (((~0ULL) << (64 - (n))) >> (63 - (b))) /** * \brief Compile-time rogue_onehot. * * \sa #rogue_onehot() */ #define ROH(OFFSET) BITFIELD64_BIT(OFFSET) /* TODO: Consider integrating the following into src/util/{macros,bitscan}.h */ /** * \brief Converts a one-hot encoding to an offset encoding. * * E.g. 0b10000 -> 4 * * \param[in] onehot The one-hot encoding. * \return The offset encoding. */ static inline uint64_t rogue_offset(uint64_t onehot) { assert(util_bitcount64(onehot) == 1); return ffsll(onehot) - 1; } /** * \brief Converts an offset encoding to a one-hot encoding. * * E.g. 0 -> 0b1 * * \param[in] offset The offset encoding. * \return The one-hot encoding. */ static inline uint64_t rogue_onehot(uint64_t offset) { assert(offset < 64ULL); return (1ULL << offset); } /** * \brief Checks whether an input bitfield contains only a valid bitset. * * E.g. rogue_check_bitset(0b00001100, 0b00001111) -> true * rogue_check_bitset(0b00001100, 0b00000111) -> false * * \param[in] input The input bitfield. * \param[in] valid_bits The valid bitset. * \return true if "input" contains only "valid_bits", false otherwise. */ static inline bool rogue_check_bitset(uint64_t input, uint64_t valid_bits) { input &= ~valid_bits; return !input; } /** * \brief Describes a downward range of bits within an arbitrarily-sized * sequence. * * E.g. for start = 7 and num = 3: * * 76543210 * abcdefgh * * the bit range would be: abc. */ struct rogue_bitrange { size_t start; size_t num; }; /** * \brief Describes a collection of bit-ranges within an arbitrarily-sized * sequence that are meaningful together. * * E.g. an 8-bit value that is encoded within a larger value: * 8-bit value: abcdefgh * Parent value: 010ab0cdef0010gh * */ struct rogue_rangelist { size_t num_ranges; struct rogue_bitrange *ranges; }; /** * \brief Counts the total number of bits described in a rangelist. * * \param[in] rangelist The input rangelist. * \return The total number of bits. */ static inline size_t rogue_rangelist_bits(const struct rogue_rangelist *rangelist) { size_t total_bits = 0U; for (size_t u = 0U; u < rangelist->num_ranges; ++u) total_bits += rangelist->ranges[u].num; return total_bits; } /** * \brief Returns the byte offset of the bitrange moving left from the LSB. * * \param[in] bitrange The input bit-range. * \return The byte offset. */ static inline size_t rogue_byte_num(const struct rogue_bitrange *bitrange) { /* Make sure there are enough bits. */ assert(bitrange->num <= (bitrange->start + 1)); return bitrange->start / 8; } /** * \brief Returns the array-indexable byte offset of a bit-range if the sequence * it represents were to be stored in an byte-array containing "num_bytes" * bytes. * * E.g. uint8_t array[2] is a sequence of 16 bits: * bit(0) is located in array[1]. * bit(15) is located in array[0]. * * For uint8_t array[4]: * bit(0) is located in array[3]. * bit(15) is located in array[2]. * * \param[in] bitrange The input bit-range. * \param[in] num_bytes The number of bytes that are used to contain the * bit-range. \return The byte offset. */ static inline size_t rogue_byte_index(const struct rogue_bitrange *bitrange, size_t num_bytes) { /* Make sure there are enough bits. */ assert(bitrange->num <= (bitrange->start + 1)); return num_bytes - rogue_byte_num(bitrange) - 1; } /** * \brief Returns the bit offset of a bit-range if the sequence it represents is * being accessed in a byte-wise manner. * * E.g. bit 17 has a bit offset of 1. * * \param[in] bitrange The input bit-range. * \return The bit offset. */ static inline size_t rogue_bit_offset(const struct rogue_bitrange *bitrange) { /* Make sure there are enough bits. */ assert(bitrange->num <= (bitrange->start + 1)); return bitrange->start % 8; } /** * \brief Returns the number of additional bytes that the bit-range spills into * (excluding its "starting" byte). * * \param[in] bitrange The input bit-range. * \return The number of bytes spilled. */ static inline size_t rogue_bytes_spilled(const struct rogue_bitrange *bitrange) { /* Make sure there are enough bits. */ assert(bitrange->num <= (bitrange->start + 1)); return ((bitrange->num - 1) / 8) + ((bitrange->num % 8) > (rogue_bit_offset(bitrange) + 1)); } /** * \brief For a given bit offset, returns the maximum number of bits (including * itself) that are accessible before spilling into the following byte. * * E.g. When trying to insert an 8-bit value offset of 13, a maximum of 6 bits * can be placed; the last 2 bits will need to go into the next byte. * * 8-bit value: abcdefgh * * array[0] array[1] * 15 8 7 0 * iiiiiiii jjjjjjjj * ^ * abcdef gh * * \param[in] The bit offset. * \return The maximum number of accessible bits. */ static inline size_t rogue_max_bits(size_t offset) { return (offset % 8) + 1; } bool rogue_distribute_value(uint64_t source, const struct rogue_rangelist *rangelist, size_t dest_size, uint8_t dest_bytes[dest_size]); #endif /* ROGUE_UTIL_H */