kurnevichstanislav e64c8603cb [PT][Debugger] Fixed incorrect double values
Description:
 - Fixed incorrect reading of double from PtDebugFrame in DebugInfoCache.
 - Fixed bug in ExtractBits related to overflow of unsigned int type.
 - Added more static asserts in bit_cast function

Issue: #IA7YRI

Signed-off-by: kurnevichstanislav <kurnevich.stanislav@huawei-partners.com>
2024-06-26 15:52:30 +03:00

364 lines
12 KiB
C++

/**
* Copyright (c) 2021-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIBPANDABASE_UTILS_BIT_UTILS_H
#define LIBPANDABASE_UTILS_BIT_UTILS_H
#include <cmath>
#include <cstdint>
#include <cstring>
#include <limits>
#include <type_traits>
#include <bitset>
#include <securec.h>
#include "globals.h"
#include "macros.h"
#define panda_bit_utils_ctz __builtin_ctz // NOLINT(cppcoreguidelines-macro-usage)
#define panda_bit_utils_ctzll __builtin_ctzll // NOLINT(cppcoreguidelines-macro-usage)
#define panda_bit_utils_clz __builtin_clz // NOLINT(cppcoreguidelines-macro-usage)
#define panda_bit_utils_clzll __builtin_clzll // NOLINT(cppcoreguidelines-macro-usage)
#define panda_bit_utils_ffs __builtin_ffs // NOLINT(cppcoreguidelines-macro-usage)
#define panda_bit_utils_ffsll __builtin_ffsll // NOLINT(cppcoreguidelines-macro-usage)
#define panda_bit_utils_popcount __builtin_popcount // NOLINT(cppcoreguidelines-macro-usage)
#define panda_bit_utils_popcountll __builtin_popcountll // NOLINT(cppcoreguidelines-macro-usage)
namespace panda {
template <typename T>
constexpr int Clz(T x)
{
constexpr size_t RADIX = 2;
static_assert(std::is_integral<T>::value, "T must be integral");
static_assert(std::is_unsigned<T>::value, "T must be unsigned");
static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
ASSERT(x != 0U);
if (sizeof(T) == sizeof(uint64_t)) {
return panda_bit_utils_clzll(x);
}
return panda_bit_utils_clz(x) - (std::numeric_limits<uint32_t>::digits - std::numeric_limits<T>::digits);
}
template <typename T>
constexpr int Ctz(T x)
{
constexpr size_t RADIX = 2;
static_assert(std::is_integral<T>::value, "T must be integral");
static_assert(std::is_unsigned<T>::value, "T must be unsigned");
static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
ASSERT(x != 0U);
if (sizeof(T) == sizeof(uint64_t)) {
return panda_bit_utils_ctzll(x);
}
return panda_bit_utils_ctz(x);
}
template <typename T>
constexpr int Popcount(T x)
{
constexpr size_t RADIX = 2;
static_assert(std::is_integral<T>::value, "T must be integral");
static_assert(std::is_unsigned<T>::value, "T must be unsigned");
static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
if (sizeof(T) == sizeof(uint64_t)) {
return panda_bit_utils_popcountll(x);
}
return panda_bit_utils_popcount(x);
}
// How many bits (minimally) does it take to store the constant 'value'? i.e. 1 for 1, 2 for 2 and 3, 3 for 4 and 5 etc.
template <typename T>
constexpr size_t MinimumBitsToStore(T value)
{
static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
if constexpr (std::is_enum_v<T>) { // NOLINT
using UnderlyingType = std::make_unsigned_t<std::underlying_type_t<T>>;
auto uvalue = static_cast<UnderlyingType>(value);
if (uvalue == 0) {
uvalue = 1;
}
return std::numeric_limits<UnderlyingType>::digits - Clz(static_cast<UnderlyingType>(uvalue));
} else { // NOLINT
constexpr size_t RADIX = 2;
static_assert(std::is_integral_v<T>, "T must be integral");
static_assert(std::is_unsigned_v<T>, "T must be unsigned");
static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
if (value == 0) {
return 0;
}
return std::numeric_limits<T>::digits - Clz(value);
}
}
template <typename T>
constexpr int Ffs(T x)
{
constexpr size_t RADIX = 2;
static_assert(std::is_integral<T>::value, "T must be integral");
static_assert(std::is_unsigned<T>::value, "T must be unsigned");
static_assert(std::numeric_limits<T>::radix == RADIX, "Unexpected radix!");
static_assert(sizeof(T) == sizeof(uint64_t) || sizeof(T) <= sizeof(uint32_t), "Unsupported sizeof(T)");
if (sizeof(T) == sizeof(uint64_t)) {
return panda_bit_utils_ffsll(x);
}
return panda_bit_utils_ffs(x);
}
template <size_t n, typename T>
constexpr bool IsAligned(T value)
{
static_assert(std::is_integral<T>::value, "T must be integral");
return value % n == 0;
}
template <typename T>
constexpr bool IsAligned(T value, size_t n)
{
static_assert(std::is_integral<T>::value, "T must be integral");
return value % n == 0;
}
template <typename T>
constexpr T RoundUp(T x, size_t n)
{
static_assert(std::is_integral<T>::value, "T must be integral");
return (static_cast<size_t>(x) + n - 1U) & (-n);
}
constexpr size_t BitsToBytesRoundUp(size_t num_bits)
{
return RoundUp(num_bits, BITS_PER_BYTE) / BITS_PER_BYTE;
}
template <typename T>
constexpr T RoundDown(T x, size_t n)
{
static_assert(std::is_integral<T>::value, "T must be integral");
return x & static_cast<size_t>(-n);
}
template <typename T>
constexpr T SwapBits(T value, T mask, uint32_t offset)
{
return ((value >> offset) & mask) | ((value & mask) << offset);
}
template <typename T>
inline uint8_t GetByteFrom(T value, uint64_t index)
{
static_assert(std::is_unsigned<T>::value, "T must be unsigned");
constexpr uint8_t OFFSET_BYTE = 3;
constexpr uint8_t MASK = 0xffU;
uint64_t shift = index << OFFSET_BYTE;
return static_cast<uint8_t>((value >> shift) & MASK);
}
inline uint16_t ReverseBytes(uint16_t value)
{
constexpr uint32_t OFFSET_0 = 8;
return static_cast<uint16_t>(value << OFFSET_0) | static_cast<uint16_t>(value >> OFFSET_0);
}
inline uint32_t ReverseBytes(uint32_t value)
{
constexpr uint32_t BYTES_MASK = 0xff00ffU;
constexpr uint32_t OFFSET_0 = 8;
constexpr uint32_t OFFSET_1 = 16;
value = SwapBits(value, BYTES_MASK, OFFSET_0);
return (value >> OFFSET_1) | (value << OFFSET_1);
}
inline uint64_t ReverseBytes(uint64_t value)
{
constexpr uint64_t BYTES_MASK = 0xff00ff00ff00ffLU;
constexpr uint64_t WORDS_MASK = 0xffff0000ffffLU;
constexpr uint32_t OFFSET_0 = 8;
constexpr uint32_t OFFSET_1 = 16;
constexpr uint32_t OFFSET_2 = 32;
value = SwapBits(value, BYTES_MASK, OFFSET_0);
value = SwapBits(value, WORDS_MASK, OFFSET_1);
return (value >> OFFSET_2) | (value << OFFSET_2);
}
template <typename T>
constexpr T BSWAP(T x)
{
if (sizeof(T) == sizeof(uint16_t)) {
return ReverseBytes(static_cast<uint16_t>(x));
}
if (sizeof(T) == sizeof(uint32_t)) {
return ReverseBytes(static_cast<uint32_t>(x));
}
return ReverseBytes(static_cast<uint64_t>(x));
}
inline uint32_t ReverseBits(uint32_t value)
{
constexpr uint32_t BITS_MASK = 0x55555555U;
constexpr uint32_t TWO_BITS_MASK = 0x33333333U;
constexpr uint32_t HALF_BYTES_MASK = 0x0f0f0f0fU;
constexpr uint32_t OFFSET_0 = 1;
constexpr uint32_t OFFSET_1 = 2;
constexpr uint32_t OFFSET_2 = 4;
value = SwapBits(value, BITS_MASK, OFFSET_0);
value = SwapBits(value, TWO_BITS_MASK, OFFSET_1);
value = SwapBits(value, HALF_BYTES_MASK, OFFSET_2);
return ReverseBytes(value);
}
inline uint64_t ReverseBits(uint64_t value)
{
constexpr uint64_t BITS_MASK = 0x5555555555555555LU;
constexpr uint64_t TWO_BITS_MASK = 0x3333333333333333LU;
constexpr uint64_t HALF_BYTES_MASK = 0x0f0f0f0f0f0f0f0fLU;
constexpr uint32_t OFFSET_0 = 1;
constexpr uint32_t OFFSET_1 = 2;
constexpr uint32_t OFFSET_2 = 4;
value = SwapBits(value, BITS_MASK, OFFSET_0);
value = SwapBits(value, TWO_BITS_MASK, OFFSET_1);
value = SwapBits(value, HALF_BYTES_MASK, OFFSET_2);
return ReverseBytes(value);
}
inline uint32_t BitCount(int32_t value)
{
constexpr size_t BIT_SIZE = sizeof(int32_t) * 8;
return std::bitset<BIT_SIZE>(value).count();
}
inline uint32_t BitCount(uint32_t value)
{
constexpr size_t BIT_SIZE = sizeof(uint32_t) * 8;
return std::bitset<BIT_SIZE>(value).count();
}
inline uint32_t BitCount(int64_t value)
{
constexpr size_t BIT_SIZE = sizeof(int64_t) * 8;
return std::bitset<BIT_SIZE>(value).count();
}
template <typename T>
inline constexpr uint32_t BitNumbers()
{
constexpr int BIT_NUMBER_OF_CHAR = 8;
return sizeof(T) * BIT_NUMBER_OF_CHAR;
}
template <typename T>
inline constexpr T ExtractBits(T value, size_t offset, size_t count)
{
static_assert(std::is_integral<T>::value, "T must be integral");
static_assert(std::is_unsigned<T>::value, "T must be unsigned");
ASSERT(sizeof(value) * panda::BITS_PER_BYTE >= offset + count);
return (value >> offset) & ((1ULL << count) - 1);
}
template <typename T>
inline constexpr uint32_t Low32Bits(T value)
{
return static_cast<uint32_t>(reinterpret_cast<uint64_t>(value));
}
template <typename T>
inline constexpr uint32_t High32Bits(T value)
{
if constexpr (sizeof(T) < sizeof(uint64_t)) { // NOLINT
return 0;
}
return static_cast<uint32_t>(reinterpret_cast<uint64_t>(value) >> BITS_PER_UINT32);
}
template <class To, class From>
inline To bit_cast(const From &src) noexcept // NOLINT(readability-identifier-naming)
{
static_assert(sizeof(To) == sizeof(From), "size of the types must be equal");
static_assert(std::is_trivially_copyable_v<To> && std::is_trivially_copyable_v<From>,
"source and destination types must be trivially copyable");
static_assert(std::is_trivially_constructible_v<To>, "destination type must be default constructible");
To dst;
errno_t res = memcpy_s(&dst, sizeof(To), &src, sizeof(To));
if (res != EOK) {
UNREACHABLE();
}
return dst;
}
template <class To, class From>
inline To down_cast(const From &src) noexcept // NOLINT(readability-identifier-naming)
{
static_assert(sizeof(To) <= sizeof(From), "size of the types must be lesser");
To dst;
errno_t res = memcpy_s(&dst, sizeof(To), &src, sizeof(To));
if (res != EOK) {
UNREACHABLE();
}
return dst;
}
template <typename T>
inline constexpr uint32_t BitsNumInValue(const T v)
{
return sizeof(v) * panda::BITS_PER_BYTE;
}
template <typename T>
inline constexpr uint32_t BitsNumInType()
{
return sizeof(T) * panda::BITS_PER_BYTE;
}
template <typename From, typename To>
inline constexpr To CastFloatToInt(From value)
{
static_assert(std::is_floating_point_v<From>);
static_assert(std::is_integral_v<To>);
To res;
constexpr To MIN_INT = std::numeric_limits<To>::min();
constexpr To MAX_INT = std::numeric_limits<To>::max();
constexpr auto FLOAT_MIN_INT = static_cast<From>(MIN_INT);
constexpr auto FLOAT_MAX_INT = static_cast<From>(MAX_INT);
if (value > FLOAT_MIN_INT) {
if (value < FLOAT_MAX_INT) {
res = static_cast<To>(value);
} else {
res = MAX_INT;
}
} else if (std::isnan(value)) {
res = 0;
} else {
res = MIN_INT;
}
return res;
}
} // namespace panda
#endif // LIBPANDABASE_UTILS_BIT_UTILS_H