20
20
#include " llvm/ADT/StringRef.h"
21
21
#include " llvm/Bitstream/BitCodes.h"
22
22
#include " llvm/Support/Endian.h"
23
+ #include " llvm/Support/raw_ostream.h"
23
24
#include < vector>
24
25
25
26
namespace llvm {
26
27
27
28
class BitstreamWriter {
29
+ // / Out - The buffer that keeps unflushed bytes.
28
30
SmallVectorImpl<char > &Out;
29
31
32
+ // / FS - The file stream that Out flushes to. If FS is nullptr, it does not
33
+ // / support read or seek, Out cannot be flushed until all data are written.
34
+ raw_fd_stream *FS;
35
+
36
+ // / FlushThreshold - If FS is valid, this is the threshold (unit B) to flush
37
+ // / FS.
38
+ const uint64_t FlushThreshold;
39
+
30
40
// / CurBit - Always between 0 and 31 inclusive, specifies the next bit to use.
31
41
unsigned CurBit;
32
42
33
- // / CurValue - The current value. Only bits < CurBit are valid.
43
+ // / CurValue - The current value. Only bits < CurBit are valid.
34
44
uint32_t CurValue;
35
45
36
46
// / CurCodeSize - This is the declared size of code values used for the
@@ -64,25 +74,49 @@ class BitstreamWriter {
64
74
65
75
void WriteByte (unsigned char Value) {
66
76
Out.push_back (Value);
77
+ FlushToFile ();
67
78
}
68
79
69
80
void WriteWord (unsigned Value) {
70
81
Value = support::endian::byte_swap<uint32_t , support::little>(Value);
71
82
Out.append (reinterpret_cast <const char *>(&Value),
72
83
reinterpret_cast <const char *>(&Value + 1 ));
84
+ FlushToFile ();
73
85
}
74
86
75
- size_t GetBufferOffset () const { return Out.size (); }
87
+ uint64_t GetNumOfFlushedBytes () const { return FS ? FS->tell () : 0 ; }
88
+
89
+ size_t GetBufferOffset () const { return Out.size () + GetNumOfFlushedBytes (); }
76
90
77
91
size_t GetWordIndex () const {
78
92
size_t Offset = GetBufferOffset ();
79
93
assert ((Offset & 3 ) == 0 && " Not 32-bit aligned" );
80
94
return Offset / 4 ;
81
95
}
82
96
97
+ // / If the related file stream supports reading, seeking and writing, flush
98
+ // / the buffer if its size is above a threshold.
99
+ void FlushToFile () {
100
+ if (!FS)
101
+ return ;
102
+ if (Out.size () < FlushThreshold)
103
+ return ;
104
+ FS->write ((char *)&Out.front (), Out.size ());
105
+ Out.clear ();
106
+ }
107
+
83
108
public:
84
- explicit BitstreamWriter (SmallVectorImpl<char > &O)
85
- : Out(O), CurBit(0 ), CurValue(0 ), CurCodeSize(2 ) {}
109
+ // / Create a BitstreamWriter that writes to Buffer \p O.
110
+ // /
111
+ // / \p FS is the file stream that \p O flushes to incrementally. If \p FS is
112
+ // / null, \p O does not flush incrementially, but writes to disk at the end.
113
+ // /
114
+ // / \p FlushThreshold is the threshold (unit M) to flush \p O if \p FS is
115
+ // / valid.
116
+ BitstreamWriter (SmallVectorImpl<char > &O, raw_fd_stream *FS = nullptr ,
117
+ uint32_t FlushThreshold = 512 )
118
+ : Out(O), FS(FS), FlushThreshold(FlushThreshold << 20 ), CurBit(0 ),
119
+ CurValue (0 ), CurCodeSize(2 ) {}
86
120
87
121
~BitstreamWriter () {
88
122
assert (CurBit == 0 && " Unflushed data remaining" );
@@ -104,11 +138,59 @@ class BitstreamWriter {
104
138
void BackpatchWord (uint64_t BitNo, unsigned NewWord) {
105
139
using namespace llvm ::support;
106
140
uint64_t ByteNo = BitNo / 8 ;
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 );
141
+ uint64_t StartBit = BitNo & 7 ;
142
+ uint64_t NumOfFlushedBytes = GetNumOfFlushedBytes ();
143
+
144
+ if (ByteNo >= NumOfFlushedBytes) {
145
+ assert ((!endian::readAtBitAlignment<uint32_t , little, unaligned>(
146
+ &Out[ByteNo - NumOfFlushedBytes], StartBit)) &&
147
+ " Expected to be patching over 0-value placeholders" );
148
+ endian::writeAtBitAlignment<uint32_t , little, unaligned>(
149
+ &Out[ByteNo - NumOfFlushedBytes], NewWord, StartBit);
150
+ return ;
151
+ }
152
+
153
+ // If the byte offset to backpatch is flushed, use seek to backfill data.
154
+ // First, save the file position to restore later.
155
+ uint64_t CurPos = FS->tell ();
156
+
157
+ // Copy data to update into Bytes from the file FS and the buffer Out.
158
+ char Bytes[8 ];
159
+ size_t BytesNum = StartBit ? 8 : 4 ;
160
+ size_t BytesFromDisk = std::min (BytesNum, NumOfFlushedBytes - ByteNo);
161
+ size_t BytesFromBuffer = BytesNum - BytesFromDisk;
162
+
163
+ // When unaligned, copy existing data into Bytes from the file FS and the
164
+ // buffer Out so that it can be updated before writing. For debug builds
165
+ // read bytes unconditionally in order to check that the existing value is 0
166
+ // as expected.
167
+ #ifdef NDEBUG
168
+ if (StartBit)
169
+ #endif
170
+ {
171
+ FS->seek (ByteNo);
172
+ ssize_t BytesRead = FS->read (Bytes, BytesFromDisk);
173
+ (void )BytesRead; // silence warning
174
+ assert (BytesRead >= 0 && static_cast <size_t >(BytesRead) == BytesFromDisk);
175
+ for (size_t i = 0 ; i < BytesFromBuffer; ++i)
176
+ Bytes[BytesFromDisk + i] = Out[i];
177
+ assert ((!endian::readAtBitAlignment<uint32_t , little, unaligned>(
178
+ Bytes, StartBit)) &&
179
+ " Expected to be patching over 0-value placeholders" );
180
+ }
181
+
182
+ // Update Bytes in terms of bit offset and value.
183
+ endian::writeAtBitAlignment<uint32_t , little, unaligned>(Bytes, NewWord,
184
+ StartBit);
185
+
186
+ // Copy updated data back to the file FS and the buffer Out.
187
+ FS->seek (ByteNo);
188
+ FS->write (Bytes, BytesFromDisk);
189
+ for (size_t i = 0 ; i < BytesFromBuffer; ++i)
190
+ Out[i] = Bytes[BytesFromDisk + i];
191
+
192
+ // Restore the file position.
193
+ FS->seek (CurPos);
112
194
}
113
195
114
196
void BackpatchWord64 (uint64_t BitNo, uint64_t Val) {
0 commit comments