Index: include/llvm/Bitcode/BitstreamWriter.h =================================================================== --- include/llvm/Bitcode/BitstreamWriter.h +++ 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: 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 +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 {