[PATCH] D13189: Add support for sub-byte aligned writes to lib/Support/Endian.h
Duncan P. N. Exon Smith via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 28 17:12:30 PDT 2015
> On 2015-Sep-25, at 22:02, Teresa Johnson <tejohnson at google.com> wrote:
>
> tejohnson created this revision.
> tejohnson added a reviewer: dexonsmith.
> tejohnson added a subscriber: llvm-commits.
>
> As per Duncan's review for D12536, I extracted the sub-byte bit aligned
> reading and writing code into lib/Support. Added calls from
> BackpatchWord.
>
> I made the support general instead of tailoring it to the specific
> use case and data type in the BackpatchWord case. However, I am not
> sure how to add unit testing for the other data types. I did test it
> for signed types with negative values to ensure the shifting/masking
> handled the sign bit properly, by temporarily adding some write/read
> calls into llvm operating on a temporary array.
>
> http://reviews.llvm.org/D13189
>
> Files:
> include/llvm/Bitcode/BitstreamWriter.h
> include/llvm/Support/Endian.h
>
> <D13189.35794.patch>
I wonder if these would be hard to unit-test? If not, it might be nice
to add one or two. I imagine if someone inserts a bug, it'll be easier
to track down unit test failures than bitcode tests. I'm not too
concerned, since this is tested implicitly by the bitcode tests.
> Index: include/llvm/Support/Endian.h
> ===================================================================
> --- include/llvm/Support/Endian.h
> +++ include/llvm/Support/Endian.h
> @@ -77,6 +77,81 @@
> &value,
> sizeof(value_type));
> }
> +
> +/// Read a value of a particular endianness from memory, for a location
> +/// that starts at the given bit offset within the first byte.
> +template <typename value_type, endianness endian, std::size_t alignment>
> +inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) {
> + assert(startBit < 8);
> + if (startBit == 0)
> + return read<value_type, endian, alignment>(memory);
> + else {
> + // Read two values and compose the result from them.
> + value_type val[2];
> + memcpy(&val[0],
> + LLVM_ASSUME_ALIGNED(
> + memory, (detail::PickAlignment<value_type, alignment>::value)),
> + sizeof(value_type) * 2);
> + val[0] = byte_swap<value_type, endian>(val[0]);
> + val[1] = byte_swap<value_type, endian>(val[1]);
> +
> + // Shift bits from the lower value into place.
> + unsigned lowerVal = val[0] >> startBit;
> + // Mask off upper bits after right shift in case of signed type.
> + unsigned numBitsFirstVal = (sizeof(value_type) * 8) - startBit;
> + lowerVal &= (1 << numBitsFirstVal) - 1;
> +
> + // Get the bits from the upper value.
> + unsigned upperVal = val[1] & ((1 << startBit) - 1);
> + // Shift them in to place.
> + upperVal <<= numBitsFirstVal;
> +
> + return lowerVal | upperVal;
> + }
> +}
> +
> +/// Write a value to memory with a particular endianness, for a location
> +/// that starts at the given bit offset within the first byte.
> +template <typename value_type, endianness endian, std::size_t alignment>
> +inline void writeAtBitAlignment(void *memory, value_type value,
> + uint64_t startBit) {
> + assert(startBit < 8);
> + if (startBit == 0)
> + write<value_type, endian, alignment>(memory, value);
> + else {
> + // Read two values and shift the result into them.
> + value_type val[2];
> + memcpy(&val[0],
> + LLVM_ASSUME_ALIGNED(
> + memory, (detail::PickAlignment<value_type, alignment>::value)),
> + sizeof(value_type) * 2);
> + val[0] = byte_swap<value_type, endian>(val[0]);
> + val[1] = byte_swap<value_type, endian>(val[1]);
> +
> + // Mask off any existing bits in the upper part of the lower value that
> + // we want to replace.
> + val[0] &= (1 << startBit) - 1;
> + // Now shift in the new bits
> + val[0] |= value << startBit;
> +
> + // Mask off any existing bits in the lower part of the upper value that
> + // we want to replace.
> + val[1] &= ~((1 << startBit) - 1);
> + // Next shift the bits that go into the upper value into position.
> + unsigned numBitsFirstVal = (sizeof(value_type) * 8) - startBit;
> + unsigned upperVal = value >> numBitsFirstVal;
> + // Mask off upper bits after right shift in case of signed type.
> + upperVal &= (1 << startBit) - 1;
> + val[1] |= upperVal;
> +
> + // Finally, rewrite values.
> + val[0] = byte_swap<value_type, endian>(val[0]);
> + val[1] = byte_swap<value_type, endian>(val[1]);
> + memcpy(LLVM_ASSUME_ALIGNED(
> + memory, (detail::PickAlignment<value_type, alignment>::value)),
> + &val[0], sizeof(value_type) * 2);
> + }
> +}
> } // end namespace endian
>
> namespace detail {
> Index: include/llvm/Bitcode/BitstreamWriter.h
> ===================================================================
> --- include/llvm/Bitcode/BitstreamWriter.h
> +++ include/llvm/Bitcode/BitstreamWriter.h
> @@ -102,18 +102,17 @@
> /// Backpatch a 32-bit word in the output at the given bit offset
> /// with the specified value.
> void BackpatchWord(uint64_t BitNo, unsigned NewWord) {
> + using namespace llvm::support;
> unsigned ByteNo = BitNo / 8;
> - if ((BitNo & 7) == 0) {
> - // Already 8-bit aligned
> - support::endian::write32le(&Out[ByteNo], NewWord);
> - } else {
> - uint64_t CurDWord = support::endian::read64le(&Out[ByteNo]);
> - unsigned StartBit = BitNo & 7;
> - // Currently expect to backpatch 0-value placeholders.
> - assert(((CurDWord >> StartBit) & 0xffffffff) == 0);
> - CurDWord |= NewWord << StartBit;
> - support::endian::write64le(&Out[ByteNo], CurDWord);
> - }
> +#ifndef NDEBUG
> + unsigned CurDWord =
> +#endif
> + endian::readAtBitAlignment<uint32_t, little, unaligned>(&Out[ByteNo],
> + BitNo & 7);
Does readAtBitAlignment() have side-effects? If not, why run it when
NDEBUG?
> + // Currently expect to backpatch 0-value placeholders.
> + assert(CurDWord == 0);
If you move the previous call inside the `#ifndef`, then you can
probably collapse this into:
--
assert(!endian::readAtBitAlignment<uint32_t, little, unaligned>(&Out[ByteNo],
BitNo & 7) &&
"Expected to be patching over 0-value placeholders");
--
(Note, we generally prefer to be assertion comments inside the assertion
message instead of in a code comment.)
> + endian::writeAtBitAlignment<uint32_t, little, unaligned>(
> + &Out[ByteNo], NewWord, BitNo & 7);
> }
>
> void Emit(uint32_t Val, unsigned NumBits) {
>
More information about the llvm-commits
mailing list