Revert "Revert "base: Add more bit utils and bit struct class""
This reverts commit 5e008b3c5a907015116aa95623205ac8fc2c6768.
Change-Id: I05d26dd4a1f8d35e4377f65e209587b67b2ca04e
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 711bc65..ea776e7 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -530,6 +530,7 @@
"barrier_test.cc",
"base/arena_allocator_test.cc",
"base/bit_field_test.cc",
+ "base/bit_struct_test.cc",
"base/bit_utils_test.cc",
"base/bit_vector_test.cc",
"base/hash_set_test.cc",
diff --git a/runtime/base/bit_struct.h b/runtime/base/bit_struct.h
new file mode 100644
index 0000000..1f86ee1
--- /dev/null
+++ b/runtime/base/bit_struct.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 ART_RUNTIME_BASE_BIT_STRUCT_H_
+#define ART_RUNTIME_BASE_BIT_STRUCT_H_
+
+#include "bit_struct_detail.h"
+#include "bit_utils.h"
+
+//
+// Zero-cost, type-safe, well-defined "structs" of bit fields.
+//
+// ---------------------------------------------
+// Usage example:
+// ---------------------------------------------
+//
+// // Definition for type 'Example'
+// BITSTRUCT_DEFINE_START(Example, 10)
+// BitStructUint<0, 2> u2; // Every field must be a BitStruct[*].
+// BitStructInt<2, 7> i7;
+// BitStructUint<9, 1> i1;
+// BITSTRUCT_DEFINE_END(Example);
+//
+// Would define a bit struct with this layout:
+// <- 1 -> <-- 7 --> <- 2 ->
+// +--------+---------------+-----+
+// | i1 | i7 | u2 +
+// +--------+---------------+-----+
+// 10 9 2 0
+//
+// // Read-write just like regular values.
+// Example ex;
+// ex.u2 = 3;
+// ex.i7 = -25;
+// ex.i1 = true;
+// size_t u2 = ex.u2;
+// int i7 = ex.i7;
+// bool i1 = ex.i1;
+//
+// // It's packed down to the smallest # of machine words.
+// assert(sizeof(Example) == 2);
+// // The exact bit pattern is well-defined by the template parameters.
+// uint16_t cast = *reinterpret_cast<uint16_t*>(ex);
+// assert(cast == ((3) | (0b100111 << 2) | (true << 9);
+//
+// ---------------------------------------------
+// Why not just use C++ bitfields?
+// ---------------------------------------------
+//
+// The layout is implementation-defined.
+// We do not know whether the fields are packed left-to-right or
+// right-to-left, so it makes it useless when the memory layout needs to be
+// precisely controlled.
+//
+// ---------------------------------------------
+// More info:
+// ---------------------------------------------
+// Currently uintmax_t is the largest supported underlying storage type,
+// all (kBitOffset + kBitWidth) must fit into BitSizeOf<uintmax_t>();
+//
+// Using BitStruct[U]int will automatically select an underlying type
+// that's the smallest to fit your (offset + bitwidth).
+//
+// BitStructNumber can be used to manually select an underlying type.
+//
+// BitStructField can be used with custom standard-layout structs,
+// thus allowing for arbitrary nesting of bit structs.
+//
+namespace art {
+// Zero-cost wrapper around a struct 'T', allowing it to be stored as a bitfield
+// at offset 'kBitOffset' and width 'kBitWidth'.
+// The storage is plain unsigned int, whose size is the smallest required to fit
+// 'kBitOffset + kBitWidth'. All operations to this become BitFieldExtract/BitFieldInsert
+// operations to the underlying uint.
+//
+// Field memory representation:
+//
+// MSB <-- width --> LSB
+// +--------+------------+--------+
+// | ?????? | u bitfield | ?????? +
+// +--------+------------+--------+
+// offset 0
+//
+// Reading/writing the bitfield (un)packs it into a temporary T:
+//
+// MSB <-- width --> LSB
+// +-----------------+------------+
+// | 0.............0 | T bitfield |
+// +-----------------+------------+
+// 0
+//
+// It's the responsibility of the StorageType to ensure the bit representation
+// of T can be represented by kBitWidth.
+template <typename T,
+ size_t kBitOffset,
+ size_t kBitWidth = BitStructSizeOf<T>(),
+ typename StorageType = typename detail::MinimumTypeUnsignedHelper<kBitOffset + kBitWidth>::type>
+struct BitStructField {
+ static_assert(std::is_standard_layout<T>::value, "T must be standard layout");
+
+ operator T() const {
+ return Get();
+ }
+
+ // Exclude overload when T==StorageType.
+ template <typename _ = void,
+ typename = std::enable_if_t<std::is_same<T, StorageType>::value, _>>
+ explicit operator StorageType() const {
+ return GetStorage();
+ }
+
+ BitStructField& operator=(T value) {
+ return Assign(*this, value);
+ }
+
+ static constexpr size_t BitStructSizeOf() {
+ return kBitWidth;
+ }
+
+ protected:
+ template <typename T2>
+ T2& Assign(T2& what, T value) {
+ // Since C++ doesn't allow the type of operator= to change out
+ // in the subclass, reimplement operator= in each subclass
+ // manually and call this helper function.
+ static_assert(std::is_base_of<BitStructField, T2>::value, "T2 must inherit BitStructField");
+ what.Set(value);
+ return what;
+ }
+
+ T Get() const {
+ ValueStorage vs;
+ vs.pod_.val_ = GetStorage();
+ return vs.value_;
+ }
+
+ void Set(T value) {
+ ValueStorage value_as_storage;
+ value_as_storage.value_ = value;
+
+ storage_.pod_.val_ = BitFieldInsert(storage_.pod_.val_,
+ value_as_storage.pod_.val_,
+ kBitOffset,
+ kBitWidth);
+ }
+
+ private:
+ StorageType GetStorage() const {
+ return BitFieldExtract(storage_.pod_.val_, kBitOffset, kBitWidth);
+ }
+
+ // Underlying value must be wrapped in a separate standard-layout struct.
+ // See below for more details.
+ struct PodWrapper {
+ StorageType val_;
+ };
+
+ union ValueStorage {
+ // Safely alias pod_ and value_ together.
+ //
+ // See C++ 9.5.1 [class.union]:
+ // If a standard-layout union contains several standard-layout structs that share a common
+ // initial sequence ... it is permitted to inspect the common initial sequence of any of
+ // standard-layout struct members.
+ PodWrapper pod_;
+ T value_;
+ } storage_;
+
+ // Future work: In theory almost non-standard layout can be supported here,
+ // assuming they don't rely on the address of (this).
+ // We just have to use memcpy since the union-aliasing would not work.
+};
+
+// Base class for number-like BitStruct fields.
+// T is the type to store in as a bit field.
+// kBitOffset, kBitWidth define the position and length of the bitfield.
+//
+// (Common usage should be BitStructInt, BitStructUint -- this
+// intermediate template allows a user-defined integer to be used.)
+template <typename T, size_t kBitOffset, size_t kBitWidth>
+struct BitStructNumber : public BitStructField<T, kBitOffset, kBitWidth, /*StorageType*/T> {
+ using StorageType = T;
+
+ BitStructNumber& operator=(T value) {
+ return BaseType::Assign(*this, value);
+ }
+
+ /*implicit*/ operator T() const {
+ return Get();
+ }
+
+ explicit operator bool() const {
+ return static_cast<bool>(Get());
+ }
+
+ BitStructNumber& operator++() {
+ *this = Get() + 1u;
+ return *this;
+ }
+
+ StorageType operator++(int) {
+ return Get() + 1u;
+ }
+
+ BitStructNumber& operator--() {
+ *this = Get() - 1u;
+ return *this;
+ }
+
+ StorageType operator--(int) {
+ return Get() - 1u;
+ }
+
+ private:
+ using BaseType = BitStructField<T, kBitOffset, kBitWidth, /*StorageType*/T>;
+ using BaseType::Get;
+};
+
+// Create a BitStruct field which uses the smallest underlying int storage type,
+// in order to be large enough to fit (kBitOffset + kBitWidth).
+//
+// Values are sign-extended when they are read out.
+template <size_t kBitOffset, size_t kBitWidth>
+using BitStructInt =
+ BitStructNumber<typename detail::MinimumTypeHelper<int, kBitOffset + kBitWidth>::type,
+ kBitOffset,
+ kBitWidth>;
+
+// Create a BitStruct field which uses the smallest underlying uint storage type,
+// in order to be large enough to fit (kBitOffset + kBitWidth).
+//
+// Values are zero-extended when they are read out.
+template <size_t kBitOffset, size_t kBitWidth>
+using BitStructUint =
+ BitStructNumber<typename detail::MinimumTypeHelper<unsigned int, kBitOffset + kBitWidth>::type,
+ kBitOffset,
+ kBitWidth>;
+
+// Start a definition for a bitstruct.
+// A bitstruct is defined to be a union with a common initial subsequence
+// that we call 'DefineBitStructSize<bitwidth>'.
+//
+// See top of file for usage example.
+//
+// This marker is required by the C++ standard in order to
+// have a "common initial sequence".
+//
+// See C++ 9.5.1 [class.union]:
+// If a standard-layout union contains several standard-layout structs that share a common
+// initial sequence ... it is permitted to inspect the common initial sequence of any of
+// standard-layout struct members.
+#define BITSTRUCT_DEFINE_START(name, bitwidth) \
+ union name { \
+ art::detail::DefineBitStructSize<(bitwidth)> _; \
+ static constexpr size_t BitStructSizeOf() { return (bitwidth); }
+
+// End the definition of a bitstruct, and insert a sanity check
+// to ensure that the bitstruct did not exceed the specified size.
+//
+// See top of file for usage example.
+#define BITSTRUCT_DEFINE_END(name) \
+ }; /* NOLINT [readability/braces] [4] */ \
+ static_assert(art::detail::ValidateBitStructSize<name>(), \
+ #name "bitsize incorrect: " \
+ "did you insert extra fields that weren't BitStructX, " \
+ "and does the size match the sum of the field widths?")
+
+// Determine the minimal bit size for a user-defined type T.
+// Used by BitStructField to determine how small a custom type is.
+template <typename T>
+static constexpr size_t BitStructSizeOf() {
+ return T::BitStructSizeOf();
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_BIT_STRUCT_H_
diff --git a/runtime/base/bit_struct_detail.h b/runtime/base/bit_struct_detail.h
new file mode 100644
index 0000000..9f629c0
--- /dev/null
+++ b/runtime/base/bit_struct_detail.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
+#define ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
+
+#include "bit_utils.h"
+#include "globals.h"
+
+#include <type_traits>
+
+// Implementation details for bit_struct.h
+// Not intended to be used stand-alone.
+
+namespace art {
+
+template <typename T>
+static constexpr size_t BitStructSizeOf();
+
+namespace detail {
+ // Select the smallest uintX_t that will fit kBitSize bits.
+ template <size_t kBitSize>
+ struct MinimumTypeUnsignedHelper {
+ using type =
+ typename std::conditional<kBitSize == 0, void,
+ typename std::conditional<kBitSize <= 8, uint8_t,
+ typename std::conditional<kBitSize <= 16, uint16_t,
+ typename std::conditional<kBitSize <= 32, uint32_t,
+ typename std::conditional<kBitSize <= 64, uint64_t,
+ typename std::conditional<kBitSize <= BitSizeOf<uintmax_t>(), uintmax_t,
+ void>::type>::type>::type>::type>::type>::type;
+ };
+
+ // Select the smallest [u]intX_t that will fit kBitSize bits.
+ // Automatically picks intX_t or uintX_t based on the sign-ness of T.
+ template <typename T, size_t kBitSize>
+ struct MinimumTypeHelper {
+ using type_unsigned = typename MinimumTypeUnsignedHelper<kBitSize>::type;
+
+ using type =
+ typename std::conditional</* if */ std::is_signed<T>::value,
+ /* then */ typename std::make_signed<type_unsigned>::type,
+ /* else */ type_unsigned>::type;
+ };
+
+ // Ensure the minimal type storage for 'T' matches its declared BitStructSizeOf.
+ // Nominally used by the BITSTRUCT_DEFINE_END macro.
+ template <typename T>
+ static constexpr bool ValidateBitStructSize() {
+ const size_t kBitStructSizeOf = BitStructSizeOf<T>();
+ const size_t kExpectedSize = (BitStructSizeOf<T>() < kBitsPerByte)
+ ? kBitsPerByte
+ : RoundUpToPowerOfTwo(kBitStructSizeOf);
+
+ // Ensure no extra fields were added in between START/END.
+ const size_t kActualSize = sizeof(T) * kBitsPerByte;
+ return kExpectedSize == kActualSize;
+ }
+
+ // Denotes the beginning of a bit struct.
+ //
+ // This marker is required by the C++ standard in order to
+ // have a "common initial sequence".
+ //
+ // See C++ 9.5.1 [class.union]:
+ // If a standard-layout union contains several standard-layout structs that share a common
+ // initial sequence ... it is permitted to inspect the common initial sequence of any of
+ // standard-layout struct members.
+ template <size_t kSize>
+ struct DefineBitStructSize {
+ private:
+ typename MinimumTypeUnsignedHelper<kSize>::type _;
+ };
+} // namespace detail
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_BIT_STRUCT_DETAIL_H_
diff --git a/runtime/base/bit_struct_test.cc b/runtime/base/bit_struct_test.cc
new file mode 100644
index 0000000..872ada3
--- /dev/null
+++ b/runtime/base/bit_struct_test.cc
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "bit_struct.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+// A copy of detail::ValidateBitStructSize that uses EXPECT for a more
+// human-readable message.
+template <typename T>
+static constexpr bool ValidateBitStructSize(const char* name) {
+ const size_t kBitStructSizeOf = BitStructSizeOf<T>();
+ const size_t kExpectedSize = (BitStructSizeOf<T>() < kBitsPerByte)
+ ? kBitsPerByte
+ : RoundUpToPowerOfTwo(kBitStructSizeOf);
+
+ // Ensure no extra fields were added in between START/END.
+ const size_t kActualSize = sizeof(T) * kBitsPerByte;
+ EXPECT_EQ(kExpectedSize, kActualSize) << name;
+ return true;
+}
+
+#define VALIDATE_BITSTRUCT_SIZE(type) ValidateBitStructSize<type>(#type)
+
+TEST(BitStructs, MinimumType) {
+ EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<1>::type));
+ EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<2>::type));
+ EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<3>::type));
+ EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<8>::type));
+ EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<9>::type));
+ EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<10>::type));
+ EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<15>::type));
+ EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<16>::type));
+ EXPECT_EQ(4u, sizeof(typename detail::MinimumTypeUnsignedHelper<17>::type));
+ EXPECT_EQ(4u, sizeof(typename detail::MinimumTypeUnsignedHelper<32>::type));
+ EXPECT_EQ(8u, sizeof(typename detail::MinimumTypeUnsignedHelper<33>::type));
+ EXPECT_EQ(8u, sizeof(typename detail::MinimumTypeUnsignedHelper<64>::type));
+}
+
+template <typename T>
+size_t AsUint(const T& value) {
+ size_t uint_value = 0;
+ memcpy(&uint_value, &value, sizeof(value));
+ return uint_value;
+}
+
+struct CustomBitStruct {
+ CustomBitStruct() = default;
+ explicit CustomBitStruct(int8_t data) : data(data) {}
+
+ static constexpr size_t BitStructSizeOf() {
+ return 4;
+ }
+
+ int8_t data;
+};
+
+template <typename T>
+void ZeroInitialize(T& value) {
+ memset(&value, 0, sizeof(T));
+ // TODO: replace with value initialization
+}
+
+TEST(BitStructs, Custom) {
+ CustomBitStruct expected(0b1111);
+
+ BitStructField<CustomBitStruct, /*lsb*/4, /*width*/4> f;
+ ZeroInitialize(f);
+
+ EXPECT_EQ(1u, sizeof(f));
+
+ f = CustomBitStruct(0b1111);
+
+ CustomBitStruct read_out = f;
+ EXPECT_EQ(read_out.data, 0b1111);
+
+ EXPECT_EQ(AsUint(f), 0b11110000u);
+}
+
+BITSTRUCT_DEFINE_START(TestTwoCustom, /* size */ 8)
+ BitStructField<CustomBitStruct, /*lsb*/0, /*width*/4> f4_a;
+ BitStructField<CustomBitStruct, /*lsb*/4, /*width*/4> f4_b;
+BITSTRUCT_DEFINE_END(TestTwoCustom);
+
+TEST(BitStructs, TwoCustom) {
+ EXPECT_EQ(sizeof(TestTwoCustom), 1u);
+
+ VALIDATE_BITSTRUCT_SIZE(TestTwoCustom);
+
+ TestTwoCustom cst;
+ ZeroInitialize(cst);
+
+ // Test the write to most-significant field doesn't clobber least-significant.
+ cst.f4_a = CustomBitStruct(0b0110);
+ cst.f4_b = CustomBitStruct(0b0101);
+
+ int8_t read_out = static_cast<CustomBitStruct>(cst.f4_a).data;
+ int8_t read_out_b = static_cast<CustomBitStruct>(cst.f4_b).data;
+
+ EXPECT_EQ(0b0110, static_cast<int>(read_out));
+ EXPECT_EQ(0b0101, static_cast<int>(read_out_b));
+
+ EXPECT_EQ(AsUint(cst), 0b01010110u);
+
+ // Test write to least-significant field doesn't clobber most-significant.
+ cst.f4_a = CustomBitStruct(0);
+
+ read_out = static_cast<CustomBitStruct>(cst.f4_a).data;
+ read_out_b = static_cast<CustomBitStruct>(cst.f4_b).data;
+
+ EXPECT_EQ(0b0, static_cast<int>(read_out));
+ EXPECT_EQ(0b0101, static_cast<int>(read_out_b));
+
+ EXPECT_EQ(AsUint(cst), 0b01010000u);
+}
+
+TEST(BitStructs, Number) {
+ BitStructNumber<uint16_t, /*lsb*/4, /*width*/4> bsn;
+ ZeroInitialize(bsn);
+ EXPECT_EQ(2u, sizeof(bsn));
+
+ bsn = 0b1111;
+
+ uint32_t read_out = static_cast<uint32_t>(bsn);
+ uint32_t read_out_impl = bsn;
+
+ EXPECT_EQ(read_out, read_out_impl);
+ EXPECT_EQ(read_out, 0b1111u);
+ EXPECT_EQ(AsUint(bsn), 0b11110000u);
+}
+
+BITSTRUCT_DEFINE_START(TestBitStruct, /* size */ 8)
+ BitStructInt</*lsb*/0, /*width*/3> i3;
+ BitStructUint</*lsb*/3, /*width*/4> u4;
+
+ BitStructUint</*lsb*/0, /*width*/7> alias_all;
+BITSTRUCT_DEFINE_END(TestBitStruct);
+
+TEST(BitStructs, Test1) {
+ {
+ // Check minimal size selection is correct.
+ BitStructInt</*lsb*/0, /*width*/3> i3;
+ BitStructUint</*lsb*/3, /*width*/4> u4;
+
+ BitStructUint</*lsb*/0, /*width*/7> alias_all;
+
+ EXPECT_EQ(1u, sizeof(i3));
+ EXPECT_EQ(1u, sizeof(u4));
+ EXPECT_EQ(1u, sizeof(alias_all));
+ }
+ TestBitStruct tst;
+ ZeroInitialize(tst);
+
+ // Check minimal size selection is correct.
+ EXPECT_EQ(1u, sizeof(TestBitStruct));
+ EXPECT_EQ(1u, sizeof(tst._));
+ EXPECT_EQ(1u, sizeof(tst.i3));
+ EXPECT_EQ(1u, sizeof(tst.u4));
+ EXPECT_EQ(1u, sizeof(tst.alias_all));
+
+ // Check operator assignment.
+ tst.i3 = -1;
+ tst.u4 = 0b1010;
+
+ // Check implicit operator conversion.
+ int8_t read_i3 = tst.i3;
+ uint8_t read_u4 = tst.u4;
+
+ // Ensure read-out values were correct.
+ EXPECT_EQ(static_cast<int8_t>(-1), read_i3);
+ EXPECT_EQ(0b1010, read_u4);
+
+ // Ensure aliasing is working.
+ EXPECT_EQ(0b1010111, static_cast<uint8_t>(tst.alias_all));
+
+ // Ensure the bit pattern is correct.
+ EXPECT_EQ(0b1010111u, AsUint(tst));
+
+ // Math operator checks
+ {
+ // In-place
+ ++tst.u4;
+ EXPECT_EQ(static_cast<uint8_t>(0b1011), static_cast<uint8_t>(tst.u4));
+ --tst.u4;
+ EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
+
+ // Copy
+ uint8_t read_and_convert = tst.u4++;
+ EXPECT_EQ(static_cast<uint8_t>(0b1011), read_and_convert);
+ EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
+ read_and_convert = tst.u4--;
+ EXPECT_EQ(static_cast<uint8_t>(0b1001), read_and_convert);
+ EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
+
+ // Check boolean operator conversion.
+ tst.u4 = 0b1010;
+ EXPECT_TRUE(static_cast<bool>(tst.u4));
+ bool succ = tst.u4 ? true : false;
+ EXPECT_TRUE(succ);
+
+ tst.u4 = 0;
+ EXPECT_FALSE(static_cast<bool>(tst.u4));
+
+/*
+ // Disabled: Overflow is caught by the BitFieldInsert DCHECKs.
+ // Check overflow for uint.
+ tst.u4 = 0b1111;
+ ++tst.u4;
+ EXPECT_EQ(static_cast<uint8_t>(0), static_cast<uint8_t>(tst.u4));
+*/
+ }
+}
+
+BITSTRUCT_DEFINE_START(MixedSizeBitStruct, /* size */ 32)
+ BitStructUint</*lsb*/0, /*width*/3> u3;
+ BitStructUint</*lsb*/3, /*width*/10> u10;
+ BitStructUint</*lsb*/13, /*width*/19> u19;
+
+ BitStructUint</*lsb*/0, /*width*/32> alias_all;
+BITSTRUCT_DEFINE_END(MixedSizeBitStruct);
+
+// static_assert(sizeof(MixedSizeBitStruct) == sizeof(uint32_t), "TestBitStructs#MixedSize");
+
+TEST(BitStructs, Mixed) {
+ EXPECT_EQ(4u, sizeof(MixedSizeBitStruct));
+
+ MixedSizeBitStruct tst;
+ ZeroInitialize(tst);
+
+ // Check operator assignment.
+ tst.u3 = 0b111u;
+ tst.u10 = 0b1111010100u;
+ tst.u19 = 0b1010101010101010101u;
+
+ // Check implicit operator conversion.
+ uint8_t read_u3 = tst.u3;
+ uint16_t read_u10 = tst.u10;
+ uint32_t read_u19 = tst.u19;
+
+ // Ensure read-out values were correct.
+ EXPECT_EQ(0b111u, read_u3);
+ EXPECT_EQ(0b1111010100u, read_u10);
+ EXPECT_EQ(0b1010101010101010101u, read_u19);
+
+ uint32_t read_all = tst.alias_all;
+
+ // Ensure aliasing is working.
+ EXPECT_EQ(0b10101010101010101011111010100111u, read_all);
+
+ // Ensure the bit pattern is correct.
+ EXPECT_EQ(0b10101010101010101011111010100111u, AsUint(tst));
+}
+
+} // namespace art
diff --git a/runtime/base/bit_utils.h b/runtime/base/bit_utils.h
index 87dac02..da3c704 100644
--- a/runtime/base/bit_utils.h
+++ b/runtime/base/bit_utils.h
@@ -371,6 +371,128 @@
return opnd;
}
+// Create a mask for the least significant "bits"
+// The returned value is always unsigned to prevent undefined behavior for bitwise ops.
+//
+// Given 'bits',
+// Returns:
+// <--- bits --->
+// +-----------------+------------+
+// | 0 ............0 | 1.....1 |
+// +-----------------+------------+
+// msb lsb
+template <typename T = size_t>
+inline static constexpr std::make_unsigned_t<T> MaskLeastSignificant(size_t bits) {
+ DCHECK_GE(BitSizeOf<T>(), bits) << "Bits out of range for type T";
+ using unsigned_T = std::make_unsigned_t<T>;
+ if (bits >= BitSizeOf<T>()) {
+ return std::numeric_limits<unsigned_T>::max();
+ } else {
+ return static_cast<unsigned_T>((1 << bits) - 1);
+ }
+}
+
+// Clears the bitfield starting at the least significant bit "lsb" with a bitwidth of 'width'.
+// (Equivalent of ARM BFC instruction).
+//
+// Given:
+// <-- width -->
+// +--------+------------+--------+
+// | ABC... | bitfield | XYZ... +
+// +--------+------------+--------+
+// lsb 0
+// Returns:
+// <-- width -->
+// +--------+------------+--------+
+// | ABC... | 0........0 | XYZ... +
+// +--------+------------+--------+
+// lsb 0
+template <typename T>
+inline static constexpr T BitFieldClear(T value, size_t lsb, size_t width) {
+ DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value";
+ const auto val = static_cast<std::make_unsigned_t<T>>(value);
+ const auto mask = MaskLeastSignificant<T>(width);
+
+ return static_cast<T>(val & ~(mask << lsb));
+}
+
+// Inserts the contents of 'data' into bitfield of 'value' starting
+// at the least significant bit "lsb" with a bitwidth of 'width'.
+// Note: data must be within range of [MinInt(width), MaxInt(width)].
+// (Equivalent of ARM BFI instruction).
+//
+// Given (data):
+// <-- width -->
+// +--------+------------+--------+
+// | ABC... | bitfield | XYZ... +
+// +--------+------------+--------+
+// lsb 0
+// Returns:
+// <-- width -->
+// +--------+------------+--------+
+// | ABC... | 0...data | XYZ... +
+// +--------+------------+--------+
+// lsb 0
+
+template <typename T, typename T2>
+inline static constexpr T BitFieldInsert(T value, T2 data, size_t lsb, size_t width) {
+ DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value";
+ if (width != 0u) {
+ DCHECK_GE(MaxInt<T2>(width), data) << "Data out of range [too large] for bitwidth";
+ DCHECK_LE(MinInt<T2>(width), data) << "Data out of range [too small] for bitwidth";
+ } else {
+ DCHECK_EQ(static_cast<T2>(0), data) << "Data out of range [nonzero] for bitwidth 0";
+ }
+ const auto data_mask = MaskLeastSignificant<T2>(width);
+ const auto value_cleared = BitFieldClear(value, lsb, width);
+
+ return static_cast<T>(value_cleared | ((data & data_mask) << lsb));
+}
+
+// Extracts the bitfield starting at the least significant bit "lsb" with a bitwidth of 'width'.
+// Signed types are sign-extended during extraction. (Equivalent of ARM UBFX/SBFX instruction).
+//
+// Given:
+// <-- width -->
+// +--------+-------------+-------+
+// | | bitfield | +
+// +--------+-------------+-------+
+// lsb 0
+// (Unsigned) Returns:
+// <-- width -->
+// +----------------+-------------+
+// | 0... 0 | bitfield |
+// +----------------+-------------+
+// 0
+// (Signed) Returns:
+// <-- width -->
+// +----------------+-------------+
+// | S... S | bitfield |
+// +----------------+-------------+
+// 0
+// where S is the highest bit in 'bitfield'.
+template <typename T>
+inline static constexpr T BitFieldExtract(T value, size_t lsb, size_t width) {
+ DCHECK_GE(BitSizeOf(value), lsb + width) << "Bit field out of range for value";
+ const auto val = static_cast<std::make_unsigned_t<T>>(value);
+
+ const T bitfield_unsigned =
+ static_cast<T>((val >> lsb) & MaskLeastSignificant<T>(width));
+ if (std::is_signed<T>::value) {
+ // Perform sign extension
+ if (width == 0) { // Avoid underflow.
+ return static_cast<T>(0);
+ } else if (bitfield_unsigned & (1 << (width - 1))) { // Detect if sign bit was set.
+ // MSB <width> LSB
+ // 0b11111...100...000000
+ const auto ones_negmask = ~MaskLeastSignificant<T>(width);
+ return static_cast<T>(bitfield_unsigned | ones_negmask);
+ }
+ }
+ // Skip sign extension.
+ return bitfield_unsigned;
+}
+
} // namespace art
#endif // ART_RUNTIME_BASE_BIT_UTILS_H_
diff --git a/runtime/base/bit_utils_test.cc b/runtime/base/bit_utils_test.cc
index c96c6dc..0276d8d 100644
--- a/runtime/base/bit_utils_test.cc
+++ b/runtime/base/bit_utils_test.cc
@@ -345,6 +345,97 @@
"TestIsAbsoluteUint64#27");
static_assert(!IsAbsoluteUint<32, int64_t>(kUint32MaxPlus1), "TestIsAbsoluteUint64#28");
+static_assert(MaskLeastSignificant(0) == 0b0, "TestMaskLeastSignificant#1");
+static_assert(MaskLeastSignificant(1) == 0b1, "TestMaskLeastSignificant#2");
+static_assert(MaskLeastSignificant(2) == 0b11, "TestMaskLeastSignificant#3");
+static_assert(MaskLeastSignificant<uint8_t>(8) == 0xFF, "TestMaskLeastSignificant#4");
+static_assert(MaskLeastSignificant<int8_t>(8) == 0xFF, "TestMaskLeastSignificant#5");
+
+static_assert(BitFieldClear(0xFF, /*lsb*/0, /*width*/0) == 0xFF, "TestBitFieldClear#1");
+static_assert(BitFieldClear(std::numeric_limits<uint32_t>::max(), /*lsb*/0, /*width*/32) == 0x0,
+ "TestBitFieldClear#2");
+static_assert(BitFieldClear(std::numeric_limits<int32_t>::max(), /*lsb*/0, /*width*/32) == 0x0,
+ "TestBitFieldClear#3");
+static_assert(BitFieldClear(0xFF, /*lsb*/0, /*width*/2) == 0b11111100, "TestBitFieldClear#4");
+static_assert(BitFieldClear(0xFF, /*lsb*/0, /*width*/3) == 0b11111000, "TestBitFieldClear#5");
+static_assert(BitFieldClear(0xFF, /*lsb*/1, /*width*/3) == 0b11110001, "TestBitFieldClear#6");
+static_assert(BitFieldClear(0xFF, /*lsb*/2, /*width*/3) == 0b11100011, "TestBitFieldClear#7");
+
+static_assert(BitFieldExtract(0xFF, /*lsb*/0, /*width*/0) == 0x0, "TestBitFieldExtract#1");
+static_assert(BitFieldExtract(std::numeric_limits<uint32_t>::max(), /*lsb*/0, /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldExtract#2");
+static_assert(BitFieldExtract(std::numeric_limits<int32_t>::max(), /*lsb*/0, /*width*/32)
+ == std::numeric_limits<int32_t>::max(),
+ "TestBitFieldExtract#3");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/0, /*width*/2) == 0b00000011,
+ "TestBitFieldExtract#4");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/0, /*width*/3) == 0b00000111,
+ "TestBitFieldExtract#5");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/1, /*width*/3) == 0b00000111,
+ "TestBitFieldExtract#6");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/2, /*width*/3) == 0b00000111,
+ "TestBitFieldExtract#7");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/3, /*width*/3) == 0b00000111,
+ "TestBitFieldExtract#8");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/8, /*width*/3) == 0b00000000,
+ "TestBitFieldExtract#9");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/7, /*width*/3) == 0b00000001,
+ "TestBitFieldExtract#10");
+static_assert(BitFieldExtract(static_cast<uint32_t>(0xFF), /*lsb*/6, /*width*/3) == 0b00000011,
+ "TestBitFieldExtract#11");
+static_assert(BitFieldExtract(0xFF, /*lsb*/0, /*width*/2) == -1, "TestBitFieldExtract#12");
+static_assert(BitFieldExtract(0xFF, /*lsb*/0, /*width*/3) == -1, "TestBitFieldExtract#13");
+static_assert(BitFieldExtract(0xFF, /*lsb*/1, /*width*/3) == -1, "TestBitFieldExtract#14");
+static_assert(BitFieldExtract(0xFF, /*lsb*/2, /*width*/3) == -1, "TestBitFieldExtract#15");
+static_assert(BitFieldExtract(0xFF, /*lsb*/3, /*width*/3) == -1, "TestBitFieldExtract#16");
+static_assert(BitFieldExtract(0xFF, /*lsb*/8, /*width*/3) == 0b00000000, "TestBitFieldExtract#17");
+static_assert(BitFieldExtract(0xFF, /*lsb*/7, /*width*/3) == 0b00000001, "TestBitFieldExtract#18");
+static_assert(BitFieldExtract(0xFF, /*lsb*/6, /*width*/3) == 0b00000011, "TestBitFieldExtract#19");
+static_assert(BitFieldExtract(static_cast<uint8_t>(0b01101010), /*lsb*/2, /*width*/4)
+ == 0b00001010,
+ "TestBitFieldExtract#20");
+static_assert(BitFieldExtract(static_cast<int8_t>(0b01101010), /*lsb*/2, /*width*/4)
+ == static_cast<int8_t>(0b11111010),
+ "TestBitFieldExtract#21");
+
+static_assert(BitFieldInsert(0xFF, /*data*/0x0, /*lsb*/0, /*width*/0) == 0xFF,
+ "TestBitFieldInsert#1");
+static_assert(BitFieldInsert(std::numeric_limits<uint32_t>::max(),
+ /*data*/std::numeric_limits<uint32_t>::max(),
+ /*lsb*/0,
+ /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldInsert#2");
+static_assert(BitFieldInsert(std::numeric_limits<int32_t>::max(),
+ /*data*/std::numeric_limits<uint32_t>::max(),
+ /*lsb*/0,
+ /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldInsert#3");
+static_assert(BitFieldInsert(0u,
+ /*data*/std::numeric_limits<uint32_t>::max(),
+ /*lsb*/0,
+ /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldInsert#4");
+static_assert(BitFieldInsert(-(-0),
+ /*data*/std::numeric_limits<uint32_t>::max(),
+ /*lsb*/0,
+ /*width*/32)
+ == std::numeric_limits<uint32_t>::max(),
+ "TestBitFieldInsert#5");
+static_assert(BitFieldInsert(0x00, /*data*/0b11u, /*lsb*/0, /*width*/2) == 0b00000011,
+ "TestBitFieldInsert#6");
+static_assert(BitFieldInsert(0x00, /*data*/0b111u, /*lsb*/0, /*width*/3) == 0b00000111,
+ "TestBitFieldInsert#7");
+static_assert(BitFieldInsert(0x00, /*data*/0b111u, /*lsb*/1, /*width*/3) == 0b00001110,
+ "TestBitFieldInsert#8");
+static_assert(BitFieldInsert(0x00, /*data*/0b111u, /*lsb*/2, /*width*/3) == 0b00011100,
+ "TestBitFieldInsert#9");
+static_assert(BitFieldInsert(0b01011100, /*data*/0b1101u, /*lsb*/4, /*width*/4) == 0b11011100,
+ "TestBitFieldInsert#10");
+
template <typename Container>
void CheckElements(const std::initializer_list<uint32_t>& expected, const Container& elements) {
auto expected_it = expected.begin();