Coverage Report

Created: 2025-11-16 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/util/bitutils.h
Line
Count
Source
1
// Copyright (c) 2015-2016 The Khronos Group Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#ifndef SOURCE_UTIL_BITUTILS_H_
16
#define SOURCE_UTIL_BITUTILS_H_
17
18
#include <cassert>
19
#include <cstdint>
20
#include <cstring>
21
#include <type_traits>
22
23
namespace spvtools {
24
namespace utils {
25
26
// Performs a bitwise copy of source to the destination type Dest.
27
template <typename Dest, typename Src>
28
3.80M
Dest BitwiseCast(Src source) {
29
3.80M
  Dest dest;
30
3.80M
  static_assert(sizeof(source) == sizeof(dest),
31
3.80M
                "BitwiseCast: Source and destination must have the same size");
32
3.80M
  std::memcpy(&dest, &source, sizeof(dest));
33
3.80M
  return dest;
34
3.80M
}
float spvtools::utils::BitwiseCast<float, unsigned int>(unsigned int)
Line
Count
Source
28
2.45M
Dest BitwiseCast(Src source) {
29
2.45M
  Dest dest;
30
2.45M
  static_assert(sizeof(source) == sizeof(dest),
31
2.45M
                "BitwiseCast: Source and destination must have the same size");
32
2.45M
  std::memcpy(&dest, &source, sizeof(dest));
33
2.45M
  return dest;
34
2.45M
}
unsigned int spvtools::utils::BitwiseCast<unsigned int, float>(float)
Line
Count
Source
28
1.21M
Dest BitwiseCast(Src source) {
29
1.21M
  Dest dest;
30
1.21M
  static_assert(sizeof(source) == sizeof(dest),
31
1.21M
                "BitwiseCast: Source and destination must have the same size");
32
1.21M
  std::memcpy(&dest, &source, sizeof(dest));
33
1.21M
  return dest;
34
1.21M
}
double spvtools::utils::BitwiseCast<double, unsigned long>(unsigned long)
Line
Count
Source
28
32.6k
Dest BitwiseCast(Src source) {
29
32.6k
  Dest dest;
30
32.6k
  static_assert(sizeof(source) == sizeof(dest),
31
32.6k
                "BitwiseCast: Source and destination must have the same size");
32
32.6k
  std::memcpy(&dest, &source, sizeof(dest));
33
32.6k
  return dest;
34
32.6k
}
unsigned long spvtools::utils::BitwiseCast<unsigned long, double>(double)
Line
Count
Source
28
58.0k
Dest BitwiseCast(Src source) {
29
58.0k
  Dest dest;
30
58.0k
  static_assert(sizeof(source) == sizeof(dest),
31
58.0k
                "BitwiseCast: Source and destination must have the same size");
32
58.0k
  std::memcpy(&dest, &source, sizeof(dest));
33
58.0k
  return dest;
34
58.0k
}
unsigned int spvtools::utils::BitwiseCast<unsigned int, spvtools::utils::HexFloat<spvtools::utils::FloatProxy<float>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<float> > > >(spvtools::utils::HexFloat<spvtools::utils::FloatProxy<float>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<float> > >)
Line
Count
Source
28
22.2k
Dest BitwiseCast(Src source) {
29
22.2k
  Dest dest;
30
22.2k
  static_assert(sizeof(source) == sizeof(dest),
31
22.2k
                "BitwiseCast: Source and destination must have the same size");
32
22.2k
  std::memcpy(&dest, &source, sizeof(dest));
33
22.2k
  return dest;
34
22.2k
}
unsigned long spvtools::utils::BitwiseCast<unsigned long, spvtools::utils::HexFloat<spvtools::utils::FloatProxy<double>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<double> > > >(spvtools::utils::HexFloat<spvtools::utils::FloatProxy<double>, spvtools::utils::HexFloatTraits<spvtools::utils::FloatProxy<double> > >)
Line
Count
Source
28
25.4k
Dest BitwiseCast(Src source) {
29
25.4k
  Dest dest;
30
25.4k
  static_assert(sizeof(source) == sizeof(dest),
31
25.4k
                "BitwiseCast: Source and destination must have the same size");
32
25.4k
  std::memcpy(&dest, &source, sizeof(dest));
33
25.4k
  return dest;
34
25.4k
}
35
36
// Calculates the bit width of the integer type |T|.
37
template <typename T>
38
struct IntegerBitWidth {
39
  static_assert(std::is_integral<T>::value, "Integer type required");
40
  static const size_t kBitsPerByte = 8;
41
  static const size_t get = sizeof(T) * kBitsPerByte;
42
};
43
44
// SetBits<T, First, Num> returns an integer of type <T> with bits set
45
// for position <First> through <First + Num - 1>, counting from the least
46
// significant bit. In particular when Num == 0, no positions are set to 1.
47
// A static assert will be triggered if First + Num > sizeof(T) * 8, that is,
48
// a bit that will not fit in the underlying type is set.
49
template <typename T, size_t First = 0, size_t Num = 0>
50
struct SetBits {
51
  static_assert(First < IntegerBitWidth<T>::get,
52
                "Tried to set a bit that is shifted too far.");
53
  const static T get = (T(1) << First) | SetBits<T, First + 1, Num - 1>::get;
54
};
55
56
template <typename T, size_t Last>
57
struct SetBits<T, Last, 0> {
58
  const static T get = T(0);
59
};
60
61
// This is all compile-time so we can put our tests right here.
62
static_assert(IntegerBitWidth<uint32_t>::get == 32, "IntegerBitWidth mismatch");
63
static_assert(IntegerBitWidth<int32_t>::get == 32, "IntegerBitWidth mismatch");
64
static_assert(IntegerBitWidth<uint64_t>::get == 64, "IntegerBitWidth mismatch");
65
static_assert(IntegerBitWidth<uint8_t>::get == 8, "IntegerBitWidth mismatch");
66
67
static_assert(SetBits<uint32_t, 0, 0>::get == uint32_t(0x00000000),
68
              "SetBits failed");
69
static_assert(SetBits<uint32_t, 0, 1>::get == uint32_t(0x00000001),
70
              "SetBits failed");
71
static_assert(SetBits<uint32_t, 31, 1>::get == uint32_t(0x80000000),
72
              "SetBits failed");
73
static_assert(SetBits<uint32_t, 1, 2>::get == uint32_t(0x00000006),
74
              "SetBits failed");
75
static_assert(SetBits<uint32_t, 30, 2>::get == uint32_t(0xc0000000),
76
              "SetBits failed");
77
static_assert(SetBits<uint32_t, 0, 31>::get == uint32_t(0x7FFFFFFF),
78
              "SetBits failed");
79
static_assert(SetBits<uint32_t, 0, 32>::get == uint32_t(0xFFFFFFFF),
80
              "SetBits failed");
81
static_assert(SetBits<uint32_t, 16, 16>::get == uint32_t(0xFFFF0000),
82
              "SetBits failed");
83
84
static_assert(SetBits<uint64_t, 0, 1>::get == uint64_t(0x0000000000000001LL),
85
              "SetBits failed");
86
static_assert(SetBits<uint64_t, 63, 1>::get == uint64_t(0x8000000000000000LL),
87
              "SetBits failed");
88
static_assert(SetBits<uint64_t, 62, 2>::get == uint64_t(0xc000000000000000LL),
89
              "SetBits failed");
90
static_assert(SetBits<uint64_t, 31, 1>::get == uint64_t(0x0000000080000000LL),
91
              "SetBits failed");
92
static_assert(SetBits<uint64_t, 16, 16>::get == uint64_t(0x00000000FFFF0000LL),
93
              "SetBits failed");
94
95
// Returns number of '1' bits in a word.
96
template <typename T>
97
2.07k
size_t CountSetBits(T word) {
98
2.07k
  static_assert(std::is_integral<T>::value,
99
2.07k
                "CountSetBits requires integer type");
100
2.07k
  uint32_t count = 0;
101
3.40k
  while (word) {
102
1.32k
    word &= word - 1;
103
1.32k
    ++count;
104
1.32k
  }
105
2.07k
  return count;
106
2.07k
}
107
108
// Checks if the bit at the |position| is set to '1'.
109
// Bits zero-indexed starting at the least significant bit.
110
// |position| must be within the bit width of |T|.
111
template <typename T>
112
870k
bool IsBitAtPositionSet(T word, size_t position) {
113
870k
  static_assert(std::is_integral<T>::value, "Integer type required");
114
870k
  static_assert(std::is_unsigned<T>::value, "Unsigned type required");
115
870k
  assert(position < IntegerBitWidth<T>::get &&
116
870k
         "position must be less than the bit width");
117
870k
  return word & T(T(1) << position);
118
870k
}
bool spvtools::utils::IsBitAtPositionSet<unsigned long>(unsigned long, unsigned long)
Line
Count
Source
112
870k
bool IsBitAtPositionSet(T word, size_t position) {
113
870k
  static_assert(std::is_integral<T>::value, "Integer type required");
114
870k
  static_assert(std::is_unsigned<T>::value, "Unsigned type required");
115
870k
  assert(position < IntegerBitWidth<T>::get &&
116
870k
         "position must be less than the bit width");
117
870k
  return word & T(T(1) << position);
118
870k
}
Unexecuted instantiation: bool spvtools::utils::IsBitAtPositionSet<unsigned int>(unsigned int, unsigned long)
119
120
// Returns a value obtained by setting a range of adjacent bits of |word| to
121
// |value|. Affected bits are within the range:
122
//   [first_position, first_position + num_bits_to_mutate),
123
// assuming zero-based indexing starting at the least
124
// significant bit. Bits to mutate must be within the bit width of |T|.
125
template <typename T>
126
T MutateBits(T word, size_t first_position, size_t num_bits_to_mutate,
127
878k
             bool value) {
128
878k
  static_assert(std::is_integral<T>::value, "Integer type required");
129
878k
  static_assert(std::is_unsigned<T>::value, "Unsigned type required");
130
878k
  static const size_t word_bit_width = IntegerBitWidth<T>::get;
131
878k
  assert(first_position < word_bit_width &&
132
878k
         "Mutated bits must be within bit width");
133
878k
  assert(first_position + num_bits_to_mutate <= word_bit_width &&
134
878k
         "Mutated bits must be within bit width");
135
878k
  if (num_bits_to_mutate == 0) {
136
0
    return word;
137
0
  }
138
139
878k
  const T all_ones = ~T(0);
140
878k
  const size_t num_unaffected_low_bits = first_position;
141
878k
  const T unaffected_low_mask =
142
878k
      T(T(all_ones >> num_unaffected_low_bits) << num_unaffected_low_bits);
143
144
878k
  const size_t num_unaffected_high_bits =
145
878k
      word_bit_width - (first_position + num_bits_to_mutate);
146
878k
  const T unaffected_high_mask =
147
878k
      T(T(all_ones << num_unaffected_high_bits) >> num_unaffected_high_bits);
148
149
878k
  const T mutation_mask = unaffected_low_mask & unaffected_high_mask;
150
878k
  if (value) {
151
84.9k
    return word | mutation_mask;
152
84.9k
  }
153
793k
  return word & T(~mutation_mask);
154
878k
}
unsigned long spvtools::utils::MutateBits<unsigned long>(unsigned long, unsigned long, unsigned long, bool)
Line
Count
Source
127
878k
             bool value) {
128
878k
  static_assert(std::is_integral<T>::value, "Integer type required");
129
878k
  static_assert(std::is_unsigned<T>::value, "Unsigned type required");
130
878k
  static const size_t word_bit_width = IntegerBitWidth<T>::get;
131
878k
  assert(first_position < word_bit_width &&
132
878k
         "Mutated bits must be within bit width");
133
878k
  assert(first_position + num_bits_to_mutate <= word_bit_width &&
134
878k
         "Mutated bits must be within bit width");
135
878k
  if (num_bits_to_mutate == 0) {
136
0
    return word;
137
0
  }
138
139
878k
  const T all_ones = ~T(0);
140
878k
  const size_t num_unaffected_low_bits = first_position;
141
878k
  const T unaffected_low_mask =
142
878k
      T(T(all_ones >> num_unaffected_low_bits) << num_unaffected_low_bits);
143
144
878k
  const size_t num_unaffected_high_bits =
145
878k
      word_bit_width - (first_position + num_bits_to_mutate);
146
878k
  const T unaffected_high_mask =
147
878k
      T(T(all_ones << num_unaffected_high_bits) >> num_unaffected_high_bits);
148
149
878k
  const T mutation_mask = unaffected_low_mask & unaffected_high_mask;
150
878k
  if (value) {
151
84.9k
    return word | mutation_mask;
152
84.9k
  }
153
793k
  return word & T(~mutation_mask);
154
878k
}
Unexecuted instantiation: unsigned int spvtools::utils::MutateBits<unsigned int>(unsigned int, unsigned long, unsigned long, bool)
155
156
// Returns a value obtained by setting the |num_bits_to_set| highest bits to
157
// '1'. |num_bits_to_set| must be not be greater than the bit width of |T|.
158
template <typename T>
159
84.9k
T SetHighBits(T word, size_t num_bits_to_set) {
160
84.9k
  if (num_bits_to_set == 0) {
161
0
    return word;
162
0
  }
163
84.9k
  const size_t word_bit_width = IntegerBitWidth<T>::get;
164
84.9k
  assert(num_bits_to_set <= word_bit_width &&
165
84.9k
         "Can't set more bits than bit width");
166
84.9k
  return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set,
167
84.9k
                    true);
168
84.9k
}
unsigned long spvtools::utils::SetHighBits<unsigned long>(unsigned long, unsigned long)
Line
Count
Source
159
84.9k
T SetHighBits(T word, size_t num_bits_to_set) {
160
84.9k
  if (num_bits_to_set == 0) {
161
0
    return word;
162
0
  }
163
84.9k
  const size_t word_bit_width = IntegerBitWidth<T>::get;
164
84.9k
  assert(num_bits_to_set <= word_bit_width &&
165
84.9k
         "Can't set more bits than bit width");
166
84.9k
  return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set,
167
84.9k
                    true);
168
84.9k
}
Unexecuted instantiation: unsigned int spvtools::utils::SetHighBits<unsigned int>(unsigned int, unsigned long)
169
170
// Returns a value obtained by setting the |num_bits_to_set| highest bits to
171
// '0'. |num_bits_to_set| must be not be greater than the bit width of |T|.
172
template <typename T>
173
793k
T ClearHighBits(T word, size_t num_bits_to_set) {
174
793k
  if (num_bits_to_set == 0) {
175
0
    return word;
176
0
  }
177
793k
  const size_t word_bit_width = IntegerBitWidth<T>::get;
178
793k
  assert(num_bits_to_set <= word_bit_width &&
179
793k
         "Can't clear more bits than bit width");
180
793k
  return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set,
181
793k
                    false);
182
793k
}
unsigned long spvtools::utils::ClearHighBits<unsigned long>(unsigned long, unsigned long)
Line
Count
Source
173
793k
T ClearHighBits(T word, size_t num_bits_to_set) {
174
793k
  if (num_bits_to_set == 0) {
175
0
    return word;
176
0
  }
177
793k
  const size_t word_bit_width = IntegerBitWidth<T>::get;
178
793k
  assert(num_bits_to_set <= word_bit_width &&
179
793k
         "Can't clear more bits than bit width");
180
793k
  return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set,
181
793k
                    false);
182
793k
}
Unexecuted instantiation: unsigned int spvtools::utils::ClearHighBits<unsigned int>(unsigned int, unsigned long)
183
184
// Returns the value obtained by extracting the |number_of_bits| least
185
// significant bits from |value|, and sign-extending it to 64-bits.
186
template <typename T>
187
870k
T SignExtendValue(T value, uint32_t number_of_bits) {
188
870k
  const uint32_t bit_width = sizeof(value) * 8;
189
870k
  if (number_of_bits == bit_width) return value;
190
191
870k
  bool is_negative = utils::IsBitAtPositionSet(value, number_of_bits - 1);
192
870k
  if (is_negative) {
193
84.9k
    value = utils::SetHighBits(value, bit_width - number_of_bits);
194
785k
  } else {
195
785k
    value = utils::ClearHighBits(value, bit_width - number_of_bits);
196
785k
  }
197
870k
  return value;
198
870k
}
unsigned long spvtools::utils::SignExtendValue<unsigned long>(unsigned long, unsigned int)
Line
Count
Source
187
870k
T SignExtendValue(T value, uint32_t number_of_bits) {
188
870k
  const uint32_t bit_width = sizeof(value) * 8;
189
870k
  if (number_of_bits == bit_width) return value;
190
191
870k
  bool is_negative = utils::IsBitAtPositionSet(value, number_of_bits - 1);
192
870k
  if (is_negative) {
193
84.9k
    value = utils::SetHighBits(value, bit_width - number_of_bits);
194
785k
  } else {
195
785k
    value = utils::ClearHighBits(value, bit_width - number_of_bits);
196
785k
  }
197
870k
  return value;
198
870k
}
Unexecuted instantiation: unsigned int spvtools::utils::SignExtendValue<unsigned int>(unsigned int, unsigned int)
199
200
// Returns the value obtained by extracting the |number_of_bits| least
201
// significant bits from |value|, and zero-extending it to 64-bits.
202
template <typename T>
203
8.24k
T ZeroExtendValue(T value, uint32_t number_of_bits) {
204
8.24k
  const uint32_t bit_width = sizeof(value) * 8;
205
8.24k
  if (number_of_bits == bit_width) return value;
206
8.24k
  return utils::ClearHighBits(value, bit_width - number_of_bits);
207
8.24k
}
208
209
// Returns the the least significant bit from |value|.
210
template <typename T>
211
36
constexpr T LSB(T value) {
212
36
  static_assert(std::is_integral<T>::value, "LSB requires integer type");
213
36
  if constexpr (std::is_unsigned_v<T>) {
214
    // Prevent warnings about doing a -x on unsigned values.
215
36
    return value & (~value + 1);
216
  } else {
217
    return value & -value;
218
  }
219
36
}
Unexecuted instantiation: int spvtools::utils::LSB<int>(int)
unsigned int spvtools::utils::LSB<unsigned int>(unsigned int)
Line
Count
Source
211
36
constexpr T LSB(T value) {
212
36
  static_assert(std::is_integral<T>::value, "LSB requires integer type");
213
36
  if constexpr (std::is_unsigned_v<T>) {
214
    // Prevent warnings about doing a -x on unsigned values.
215
36
    return value & (~value + 1);
216
  } else {
217
    return value & -value;
218
  }
219
36
}
Unexecuted instantiation: unsigned long spvtools::utils::LSB<unsigned long>(unsigned long)
220
221
static_assert(LSB<uint32_t>(UINT32_MAX) == uint32_t(0x00000001), "LSB failed");
222
static_assert(LSB<uint32_t>(0x10001000) == uint32_t(0x00001000), "LSB failed");
223
static_assert(LSB<uint32_t>(0x10000000) == uint32_t(0x10000000), "LSB failed");
224
static_assert(LSB<int32_t>(-1) == int32_t(0x00000001), "LSB failed");
225
226
}  // namespace utils
227
}  // namespace spvtools
228
229
#endif  // SOURCE_UTIL_BITUTILS_H_