mesa/src/imagination/rogue/rogue_util.h

321 lines
8.7 KiB
C

/*
* 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 <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#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 */