llvm.org GIT mirror llvm / 9260ee0
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 Teresa Johnson 4 years ago
3 changed file(s) with 129 addition(s) and 11 deletion(s). Raw diff Collapse all Expand all
101101 /// Backpatch a 32-bit word in the output at the given bit offset
102102 /// with the specified value.
103103 void BackpatchWord(uint64_t BitNo, unsigned NewWord) {
104 using namespace llvm::support;
104105 unsigned ByteNo = BitNo / 8;
105 if ((BitNo & 7) == 0) {
106 // Already 8-bit aligned
107 support::endian::write32le(&Out[ByteNo], NewWord);
108 } else {
109 uint64_t CurDWord = support::endian::read64le(&Out[ByteNo]);
110 unsigned StartBit = BitNo & 7;
111 // Currently expect to backpatch 0-value placeholders.
112 assert(((CurDWord >> StartBit) & 0xffffffff) == 0);
113 CurDWord |= NewWord << StartBit;
114 support::endian::write64le(&Out[ByteNo], CurDWord);
115 }
106 assert((!endian::readAtBitAlignment(
107 &Out[ByteNo], BitNo & 7)) &&
108 "Expected to be patching over 0-value placeholders");
109 endian::writeAtBitAlignment(
110 &Out[ByteNo], NewWord, BitNo & 7);
116111 }
117112
118113 void Emit(uint32_t Val, unsigned NumBits) {
7676 &value,
7777 sizeof(value_type));
7878 }
79
80 /// Read a value of a particular endianness from memory, for a location
81 /// that starts at the given bit offset within the first byte.
82 template
83 inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) {
84 assert(startBit < 8);
85 if (startBit == 0)
86 return read(memory);
87 else {
88 // Read two values and compose the result from them.
89 value_type val[2];
90 memcpy(&val[0],
91 LLVM_ASSUME_ALIGNED(
92 memory, (detail::PickAlignment::value)),
93 sizeof(value_type) * 2);
94 val[0] = byte_swap(val[0]);
95 val[1] = byte_swap(val[1]);
96
97 // Shift bits from the lower value into place.
98 unsigned lowerVal = val[0] >> startBit;
99 // Mask off upper bits after right shift in case of signed type.
100 unsigned numBitsFirstVal = (sizeof(value_type) * 8) - startBit;
101 lowerVal &= (1 << numBitsFirstVal) - 1;
102
103 // Get the bits from the upper value.
104 unsigned upperVal = val[1] & ((1 << startBit) - 1);
105 // Shift them in to place.
106 upperVal <<= numBitsFirstVal;
107
108 return lowerVal | upperVal;
109 }
110 }
111
112 /// Write a value to memory with a particular endianness, for a location
113 /// that starts at the given bit offset within the first byte.
114 template
115 inline void writeAtBitAlignment(void *memory, value_type value,
116 uint64_t startBit) {
117 assert(startBit < 8);
118 if (startBit == 0)
119 write(memory, value);
120 else {
121 // Read two values and shift the result into them.
122 value_type val[2];
123 memcpy(&val[0],
124 LLVM_ASSUME_ALIGNED(
125 memory, (detail::PickAlignment::value)),
126 sizeof(value_type) * 2);
127 val[0] = byte_swap(val[0]);
128 val[1] = byte_swap(val[1]);
129
130 // Mask off any existing bits in the upper part of the lower value that
131 // we want to replace.
132 val[0] &= (1 << startBit) - 1;
133 // Now shift in the new bits
134 val[0] |= value << startBit;
135
136 // Mask off any existing bits in the lower part of the upper value that
137 // we want to replace.
138 val[1] &= ~((1 << startBit) - 1);
139 // Next shift the bits that go into the upper value into position.
140 unsigned numBitsFirstVal = (sizeof(value_type) * 8) - startBit;
141 unsigned upperVal = value >> numBitsFirstVal;
142 // Mask off upper bits after right shift in case of signed type.
143 upperVal &= (1 << startBit) - 1;
144 val[1] |= upperVal;
145
146 // Finally, rewrite values.
147 val[0] = byte_swap(val[0]);
148 val[1] = byte_swap(val[1]);
149 memcpy(LLVM_ASSUME_ALIGNED(
150 memory, (detail::PickAlignment::value)),
151 &val[0], sizeof(value_type) * 2);
152 }
153 }
79154 } // end namespace endian
80155
81156 namespace detail {
2929
3030 EXPECT_EQ((endian::read(bigval + 1)),
3131 (endian::read(littleval + 1)));
32 }
33
34 TEST(Endian, ReadBitAligned) {
35 // Simple test to make sure we properly pull out the 0x0 word.
36 unsigned char littleval[] = {0x3f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff};
37 unsigned char bigval[] = {0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0};
38 EXPECT_EQ(
39 (endian::readAtBitAlignment(&littleval[0], 6)),
40 0x0);
41 EXPECT_EQ((endian::readAtBitAlignment(&bigval[0], 6)),
42 0x0);
43 // Test to make sure that signed right shift of 0xf0000000 is masked
44 // properly.
45 unsigned char littleval2[] = {0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00};
46 unsigned char bigval2[] = {0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
47 EXPECT_EQ(
48 (endian::readAtBitAlignment(&littleval2[0], 4)),
49 0x0f000000);
50 EXPECT_EQ((endian::readAtBitAlignment(&bigval2[0], 4)),
51 0x0f000000);
52 }
53
54 TEST(Endian, WriteBitAligned) {
55 // This test ensures that signed right shift of 0xffffaa is masked
56 // properly.
57 unsigned char bigval[8] = {0x00};
58 endian::writeAtBitAlignment(bigval, (int)0xffffaaaa,
59 4);
60 EXPECT_EQ(bigval[0], 0xff);
61 EXPECT_EQ(bigval[1], 0xfa);
62 EXPECT_EQ(bigval[2], 0xaa);
63 EXPECT_EQ(bigval[3], 0xa0);
64 EXPECT_EQ(bigval[4], 0x00);
65 EXPECT_EQ(bigval[5], 0x00);
66 EXPECT_EQ(bigval[6], 0x00);
67 EXPECT_EQ(bigval[7], 0x0f);
68
69 unsigned char littleval[8] = {0x00};
70 endian::writeAtBitAlignment(littleval,
71 (int)0xffffaaaa, 4);
72 EXPECT_EQ(littleval[0], 0xa0);
73 EXPECT_EQ(littleval[1], 0xaa);
74 EXPECT_EQ(littleval[2], 0xfa);
75 EXPECT_EQ(littleval[3], 0xff);
76 EXPECT_EQ(littleval[4], 0x0f);
77 EXPECT_EQ(littleval[5], 0x00);
78 EXPECT_EQ(littleval[6], 0x00);
79 EXPECT_EQ(littleval[7], 0x00);
3280 }
3381
3482 TEST(Endian, Write) {