Skip to content

Commit eaa3d2a

Browse files
committedSep 30, 2015
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 llvm-svn: 248897
1 parent de0e424 commit eaa3d2a

File tree

3 files changed

+129
-11
lines changed

3 files changed

+129
-11
lines changed
 

‎llvm/include/llvm/Bitcode/BitstreamWriter.h

+6-11
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,13 @@ class BitstreamWriter {
102102
/// Backpatch a 32-bit word in the output at the given bit offset
103103
/// with the specified value.
104104
void BackpatchWord(uint64_t BitNo, unsigned NewWord) {
105+
using namespace llvm::support;
105106
unsigned ByteNo = BitNo / 8;
106-
if ((BitNo & 7) == 0) {
107-
// Already 8-bit aligned
108-
support::endian::write32le(&Out[ByteNo], NewWord);
109-
} else {
110-
uint64_t CurDWord = support::endian::read64le(&Out[ByteNo]);
111-
unsigned StartBit = BitNo & 7;
112-
// Currently expect to backpatch 0-value placeholders.
113-
assert(((CurDWord >> StartBit) & 0xffffffff) == 0);
114-
CurDWord |= NewWord << StartBit;
115-
support::endian::write64le(&Out[ByteNo], CurDWord);
116-
}
107+
assert((!endian::readAtBitAlignment<uint32_t, little, unaligned>(
108+
&Out[ByteNo], BitNo & 7)) &&
109+
"Expected to be patching over 0-value placeholders");
110+
endian::writeAtBitAlignment<uint32_t, little, unaligned>(
111+
&Out[ByteNo], NewWord, BitNo & 7);
117112
}
118113

119114
void Emit(uint32_t Val, unsigned NumBits) {

‎llvm/include/llvm/Support/Endian.h

+75
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,81 @@ inline void write(void *memory, value_type value) {
7777
&value,
7878
sizeof(value_type));
7979
}
80+
81+
/// Read a value of a particular endianness from memory, for a location
82+
/// that starts at the given bit offset within the first byte.
83+
template <typename value_type, endianness endian, std::size_t alignment>
84+
inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) {
85+
assert(startBit < 8);
86+
if (startBit == 0)
87+
return read<value_type, endian, alignment>(memory);
88+
else {
89+
// Read two values and compose the result from them.
90+
value_type val[2];
91+
memcpy(&val[0],
92+
LLVM_ASSUME_ALIGNED(
93+
memory, (detail::PickAlignment<value_type, alignment>::value)),
94+
sizeof(value_type) * 2);
95+
val[0] = byte_swap<value_type, endian>(val[0]);
96+
val[1] = byte_swap<value_type, endian>(val[1]);
97+
98+
// Shift bits from the lower value into place.
99+
unsigned lowerVal = val[0] >> startBit;
100+
// Mask off upper bits after right shift in case of signed type.
101+
unsigned numBitsFirstVal = (sizeof(value_type) * 8) - startBit;
102+
lowerVal &= (1 << numBitsFirstVal) - 1;
103+
104+
// Get the bits from the upper value.
105+
unsigned upperVal = val[1] & ((1 << startBit) - 1);
106+
// Shift them in to place.
107+
upperVal <<= numBitsFirstVal;
108+
109+
return lowerVal | upperVal;
110+
}
111+
}
112+
113+
/// Write a value to memory with a particular endianness, for a location
114+
/// that starts at the given bit offset within the first byte.
115+
template <typename value_type, endianness endian, std::size_t alignment>
116+
inline void writeAtBitAlignment(void *memory, value_type value,
117+
uint64_t startBit) {
118+
assert(startBit < 8);
119+
if (startBit == 0)
120+
write<value_type, endian, alignment>(memory, value);
121+
else {
122+
// Read two values and shift the result into them.
123+
value_type val[2];
124+
memcpy(&val[0],
125+
LLVM_ASSUME_ALIGNED(
126+
memory, (detail::PickAlignment<value_type, alignment>::value)),
127+
sizeof(value_type) * 2);
128+
val[0] = byte_swap<value_type, endian>(val[0]);
129+
val[1] = byte_swap<value_type, endian>(val[1]);
130+
131+
// Mask off any existing bits in the upper part of the lower value that
132+
// we want to replace.
133+
val[0] &= (1 << startBit) - 1;
134+
// Now shift in the new bits
135+
val[0] |= value << startBit;
136+
137+
// Mask off any existing bits in the lower part of the upper value that
138+
// we want to replace.
139+
val[1] &= ~((1 << startBit) - 1);
140+
// Next shift the bits that go into the upper value into position.
141+
unsigned numBitsFirstVal = (sizeof(value_type) * 8) - startBit;
142+
unsigned upperVal = value >> numBitsFirstVal;
143+
// Mask off upper bits after right shift in case of signed type.
144+
upperVal &= (1 << startBit) - 1;
145+
val[1] |= upperVal;
146+
147+
// Finally, rewrite values.
148+
val[0] = byte_swap<value_type, endian>(val[0]);
149+
val[1] = byte_swap<value_type, endian>(val[1]);
150+
memcpy(LLVM_ASSUME_ALIGNED(
151+
memory, (detail::PickAlignment<value_type, alignment>::value)),
152+
&val[0], sizeof(value_type) * 2);
153+
}
154+
}
80155
} // end namespace endian
81156

82157
namespace detail {

‎llvm/unittests/Support/EndianTest.cpp

+48
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,54 @@ TEST(Endian, Read) {
3232
(endian::read<int32_t, little, unaligned>(littleval + 1)));
3333
}
3434

35+
TEST(Endian, ReadBitAligned) {
36+
// Simple test to make sure we properly pull out the 0x0 word.
37+
unsigned char littleval[] = {0x3f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff};
38+
unsigned char bigval[] = {0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0};
39+
EXPECT_EQ(
40+
(endian::readAtBitAlignment<int, little, unaligned>(&littleval[0], 6)),
41+
0x0);
42+
EXPECT_EQ((endian::readAtBitAlignment<int, big, unaligned>(&bigval[0], 6)),
43+
0x0);
44+
// Test to make sure that signed right shift of 0xf0000000 is masked
45+
// properly.
46+
unsigned char littleval2[] = {0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00};
47+
unsigned char bigval2[] = {0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
48+
EXPECT_EQ(
49+
(endian::readAtBitAlignment<int, little, unaligned>(&littleval2[0], 4)),
50+
0x0f000000);
51+
EXPECT_EQ((endian::readAtBitAlignment<int, big, unaligned>(&bigval2[0], 4)),
52+
0x0f000000);
53+
}
54+
55+
TEST(Endian, WriteBitAligned) {
56+
// This test ensures that signed right shift of 0xffffaa is masked
57+
// properly.
58+
unsigned char bigval[8] = {0x00};
59+
endian::writeAtBitAlignment<int32_t, big, unaligned>(bigval, (int)0xffffaaaa,
60+
4);
61+
EXPECT_EQ(bigval[0], 0xff);
62+
EXPECT_EQ(bigval[1], 0xfa);
63+
EXPECT_EQ(bigval[2], 0xaa);
64+
EXPECT_EQ(bigval[3], 0xa0);
65+
EXPECT_EQ(bigval[4], 0x00);
66+
EXPECT_EQ(bigval[5], 0x00);
67+
EXPECT_EQ(bigval[6], 0x00);
68+
EXPECT_EQ(bigval[7], 0x0f);
69+
70+
unsigned char littleval[8] = {0x00};
71+
endian::writeAtBitAlignment<int32_t, little, unaligned>(littleval,
72+
(int)0xffffaaaa, 4);
73+
EXPECT_EQ(littleval[0], 0xa0);
74+
EXPECT_EQ(littleval[1], 0xaa);
75+
EXPECT_EQ(littleval[2], 0xfa);
76+
EXPECT_EQ(littleval[3], 0xff);
77+
EXPECT_EQ(littleval[4], 0x0f);
78+
EXPECT_EQ(littleval[5], 0x00);
79+
EXPECT_EQ(littleval[6], 0x00);
80+
EXPECT_EQ(littleval[7], 0x00);
81+
}
82+
3583
TEST(Endian, Write) {
3684
unsigned char data[5];
3785
endian::write<int32_t, big, unaligned>(data, -1362446643);

0 commit comments

Comments
 (0)
Please sign in to comment.