diff options
author | 2017-10-07 14:09:43 +0000 | |
---|---|---|
committer | 2017-10-07 14:14:58 +0000 | |
commit | 1e77d2760862ee3a2ce908cdce652a6f6db0c842 (patch) | |
tree | eae305649e4e7f42bccbb24cf580eed1d662d8ab | |
parent | 5e008b3c5a907015116aa95623205ac8fc2c6768 (diff) |
Revert "Revert "base: Add more bit utils and bit struct class""
This reverts commit 5e008b3c5a907015116aa95623205ac8fc2c6768.
Change-Id: I05d26dd4a1f8d35e4377f65e209587b67b2ca04e
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/base/bit_struct.h | 290 | ||||
-rw-r--r-- | runtime/base/bit_struct_detail.h | 90 | ||||
-rw-r--r-- | runtime/base/bit_struct_test.cc | 269 | ||||
-rw-r--r-- | runtime/base/bit_utils.h | 122 | ||||
-rw-r--r-- | runtime/base/bit_utils_test.cc | 91 |
6 files changed, 863 insertions, 0 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index 711bc65892..ea776e7c64 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -530,6 +530,7 @@ art_cc_test { "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 0000000000..1f86ee1917 --- /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 0000000000..9f629c0970 --- /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 0000000000..872ada324c --- /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 87dac0261e..da3c7048b6 100644 --- a/runtime/base/bit_utils.h +++ b/runtime/base/bit_utils.h @@ -371,6 +371,128 @@ inline static uint64_t ReverseBits64(uint64_t opnd) { 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 c96c6dc933..0276d8ded2 100644 --- a/runtime/base/bit_utils_test.cc +++ b/runtime/base/bit_utils_test.cc @@ -345,6 +345,97 @@ static_assert(IsAbsoluteUint<32, int64_t>(std::numeric_limits<uint32_t>::max()), "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(); |