From: Teresa Johnson Date: Wed, 30 Sep 2015 13:20:37 +0000 (+0000) Subject: Add support for sub-byte aligned writes to lib/Support/Endian.h X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=9260ee0d8842cbb642c762b38ac83ec6adcc2be0 Add support for sub-byte aligned writes to lib/Support/Endian.h Summary: As per Duncan's review for D12536, I extracted the sub-byte bit aligned reading and writing code into lib/Support, and generalized it. Added calls from BackpatchWord. Also added unittests. Reviewers: dexonsmith Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D13189 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@248897 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/Bitcode/BitstreamWriter.h b/include/llvm/Bitcode/BitstreamWriter.h index df07204bc7c..f4c83669dc1 100644 --- a/include/llvm/Bitcode/BitstreamWriter.h +++ b/include/llvm/Bitcode/BitstreamWriter.h @@ -102,18 +102,13 @@ public: /// 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); - } + assert((!endian::readAtBitAlignment( + &Out[ByteNo], BitNo & 7)) && + "Expected to be patching over 0-value placeholders"); + endian::writeAtBitAlignment( + &Out[ByteNo], NewWord, BitNo & 7); } void Emit(uint32_t Val, unsigned NumBits) { diff --git a/include/llvm/Support/Endian.h b/include/llvm/Support/Endian.h index fd59009e0d3..b72200e7ba4 100644 --- a/include/llvm/Support/Endian.h +++ b/include/llvm/Support/Endian.h @@ -77,6 +77,81 @@ inline void write(void *memory, value_type value) { &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 +inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) { + assert(startBit < 8); + if (startBit == 0) + return read(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)), + sizeof(value_type) * 2); + val[0] = byte_swap(val[0]); + val[1] = byte_swap(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 +inline void writeAtBitAlignment(void *memory, value_type value, + uint64_t startBit) { + assert(startBit < 8); + if (startBit == 0) + write(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)), + sizeof(value_type) * 2); + val[0] = byte_swap(val[0]); + val[1] = byte_swap(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(val[0]); + val[1] = byte_swap(val[1]); + memcpy(LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment::value)), + &val[0], sizeof(value_type) * 2); + } +} } // end namespace endian namespace detail { diff --git a/unittests/Support/EndianTest.cpp b/unittests/Support/EndianTest.cpp index 8f935530638..ee236d0eec9 100644 --- a/unittests/Support/EndianTest.cpp +++ b/unittests/Support/EndianTest.cpp @@ -32,6 +32,54 @@ TEST(Endian, Read) { (endian::read(littleval + 1))); } +TEST(Endian, ReadBitAligned) { + // Simple test to make sure we properly pull out the 0x0 word. + unsigned char littleval[] = {0x3f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff}; + unsigned char bigval[] = {0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0}; + EXPECT_EQ( + (endian::readAtBitAlignment(&littleval[0], 6)), + 0x0); + EXPECT_EQ((endian::readAtBitAlignment(&bigval[0], 6)), + 0x0); + // Test to make sure that signed right shift of 0xf0000000 is masked + // properly. + unsigned char littleval2[] = {0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00}; + unsigned char bigval2[] = {0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + EXPECT_EQ( + (endian::readAtBitAlignment(&littleval2[0], 4)), + 0x0f000000); + EXPECT_EQ((endian::readAtBitAlignment(&bigval2[0], 4)), + 0x0f000000); +} + +TEST(Endian, WriteBitAligned) { + // This test ensures that signed right shift of 0xffffaa is masked + // properly. + unsigned char bigval[8] = {0x00}; + endian::writeAtBitAlignment(bigval, (int)0xffffaaaa, + 4); + EXPECT_EQ(bigval[0], 0xff); + EXPECT_EQ(bigval[1], 0xfa); + EXPECT_EQ(bigval[2], 0xaa); + EXPECT_EQ(bigval[3], 0xa0); + EXPECT_EQ(bigval[4], 0x00); + EXPECT_EQ(bigval[5], 0x00); + EXPECT_EQ(bigval[6], 0x00); + EXPECT_EQ(bigval[7], 0x0f); + + unsigned char littleval[8] = {0x00}; + endian::writeAtBitAlignment(littleval, + (int)0xffffaaaa, 4); + EXPECT_EQ(littleval[0], 0xa0); + EXPECT_EQ(littleval[1], 0xaa); + EXPECT_EQ(littleval[2], 0xfa); + EXPECT_EQ(littleval[3], 0xff); + EXPECT_EQ(littleval[4], 0x0f); + EXPECT_EQ(littleval[5], 0x00); + EXPECT_EQ(littleval[6], 0x00); + EXPECT_EQ(littleval[7], 0x00); +} + TEST(Endian, Write) { unsigned char data[5]; endian::write(data, -1362446643);