Index: llvm/trunk/include/llvm/Bitcode/BitstreamWriter.h =================================================================== --- llvm/trunk/include/llvm/Bitcode/BitstreamWriter.h +++ llvm/trunk/include/llvm/Bitcode/BitstreamWriter.h @@ -102,18 +102,13 @@ /// 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) { Index: llvm/trunk/include/llvm/Support/Endian.h =================================================================== --- llvm/trunk/include/llvm/Support/Endian.h +++ llvm/trunk/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 +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 { Index: llvm/trunk/unittests/Support/EndianTest.cpp =================================================================== --- llvm/trunk/unittests/Support/EndianTest.cpp +++ llvm/trunk/unittests/Support/EndianTest.cpp @@ -32,6 +32,54 @@ (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);