Skip to content

Commit 37d99e9

Browse files
authored
[GOFF] Refactor GOFFOstream (#131143)
GOFFOstream writes the physical 80 byte records. The records are connected by flags indicating if there is a successor or a predecessor. Using the length of the logical record is prone to errors. The new implementation buffers the last physical record, and writes it out when new data is written. In this way, the flags can be easily determined. No obversable change in functionality, therefore no tests.
1 parent e823449 commit 37d99e9

File tree

1 file changed

+134
-124
lines changed

1 file changed

+134
-124
lines changed

llvm/lib/MC/GOFFObjectWriter.cpp

Lines changed: 134 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -63,158 +63,167 @@ constexpr uint8_t RecContinued = Flags(7, 1, 1);
6363
constexpr uint8_t RecContinuation = Flags(6, 1, 1);
6464

6565
// The GOFFOstream is responsible to write the data into the fixed physical
66-
// records of the format. A user of this class announces the start of a new
67-
// logical record and the size of its content. While writing the content, the
68-
// physical records are created for the data. Possible fill bytes at the end of
69-
// a physical record are written automatically. In principle, the GOFFOstream
70-
// is agnostic of the endianness of the content. However, it also supports
71-
// writing data in big endian byte order.
72-
class GOFFOstream : public raw_ostream {
66+
// records of the format. A user of this class announces the begin of a new
67+
// logical record. While writing the payload, the physical records are created
68+
// for the data. Possible fill bytes at the end of a physical record are written
69+
// automatically. In principle, the GOFFOstream is agnostic of the endianness of
70+
// the payload. However, it also supports writing data in big endian byte order.
71+
//
72+
// The physical records use the flag field to indicate if the there is a
73+
// successor and predecessor record. To be able to set these flags while
74+
// writing, the basic implementation idea is to always buffer the last seen
75+
// physical record.
76+
class GOFFOstream {
7377
/// The underlying raw_pwrite_stream.
7478
raw_pwrite_stream &OS;
7579

76-
/// The remaining size of this logical record, including fill bytes.
77-
size_t RemainingSize;
78-
79-
#ifndef NDEBUG
80-
/// The number of bytes needed to fill up the last physical record.
81-
size_t Gap = 0;
82-
#endif
83-
84-
/// The number of logical records emitted to far.
85-
uint32_t LogicalRecords;
86-
87-
/// The type of the current (logical) record.
88-
GOFF::RecordType CurrentType;
89-
90-
/// Signals start of new record.
91-
bool NewLogicalRecord;
80+
/// The number of logical records emitted so far.
81+
uint32_t LogicalRecords = 0;
9282

93-
/// Static allocated buffer for the stream, used by the raw_ostream class. The
94-
/// buffer is sized to hold the content of a physical record.
95-
char Buffer[GOFF::RecordContentLength];
83+
/// The number of physical records emitted so far.
84+
uint32_t PhysicalRecords = 0;
9685

97-
// Return the number of bytes left to write until next physical record.
98-
// Please note that we maintain the total numbers of byte left, not the
99-
// written size.
100-
size_t bytesToNextPhysicalRecord() {
101-
size_t Bytes = RemainingSize % GOFF::RecordContentLength;
102-
return Bytes ? Bytes : GOFF::RecordContentLength;
103-
}
104-
105-
/// Write the record prefix of a physical record, using the given record type.
106-
static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
107-
size_t RemainingSize,
108-
uint8_t Flags = RecContinuation);
86+
/// The size of the buffer. Same as the payload size of a physical record.
87+
static constexpr uint8_t BufferSize = GOFF::PayloadLength;
10988

110-
/// Fill the last physical record of a logical record with zero bytes.
111-
void fillRecord();
89+
/// Current position in buffer.
90+
char *BufferPtr = Buffer;
11291

113-
/// See raw_ostream::write_impl.
114-
void write_impl(const char *Ptr, size_t Size) override;
92+
/// Static allocated buffer for the stream.
93+
char Buffer[BufferSize];
11594

116-
/// Return the current position within the stream, not counting the bytes
117-
/// currently in the buffer.
118-
uint64_t current_pos() const override { return OS.tell(); }
95+
/// The type of the current logical record, and the flags (aka continued and
96+
/// continuation indicators) for the previous (physical) record.
97+
uint8_t TypeAndFlags = 0;
11998

12099
public:
121-
explicit GOFFOstream(raw_pwrite_stream &OS)
122-
: OS(OS), RemainingSize(0), LogicalRecords(0), NewLogicalRecord(false) {
123-
SetBuffer(Buffer, sizeof(Buffer));
124-
}
125-
126-
~GOFFOstream() { finalize(); }
100+
GOFFOstream(raw_pwrite_stream &OS);
101+
~GOFFOstream();
127102

128103
raw_pwrite_stream &getOS() { return OS; }
104+
size_t getWrittenSize() const { return PhysicalRecords * GOFF::RecordLength; }
105+
uint32_t getNumLogicalRecords() { return LogicalRecords; }
129106

130-
void newRecord(GOFF::RecordType Type, size_t Size);
131-
132-
void finalize() { fillRecord(); }
107+
/// Write the specified bytes.
108+
void write(const char *Ptr, size_t Size);
133109

134-
uint32_t logicalRecords() { return LogicalRecords; }
110+
/// Write zeroes, up to a maximum of 16 bytes.
111+
void write_zeros(unsigned NumZeros);
135112

136-
// Support for endian-specific data.
113+
/// Support for endian-specific data.
137114
template <typename value_type> void writebe(value_type Value) {
138115
Value =
139116
support::endian::byte_swap<value_type>(Value, llvm::endianness::big);
140-
write(reinterpret_cast<const char *>(&Value), sizeof(value_type));
117+
write((const char *)&Value, sizeof(value_type));
141118
}
119+
120+
/// Begin a new logical record. Implies finalizing the previous record.
121+
void newRecord(GOFF::RecordType Type);
122+
123+
/// Ends a logical record.
124+
void finalizeRecord();
125+
126+
private:
127+
/// Updates the continued/continuation flags, and writes the record prefix of
128+
/// a physical record.
129+
void updateFlagsAndWritePrefix(bool IsContinued);
130+
131+
/// Returns the remaining size in the buffer.
132+
size_t getRemainingSize();
142133
};
134+
} // namespace
135+
136+
GOFFOstream::GOFFOstream(raw_pwrite_stream &OS) : OS(OS) {}
143137

144-
void GOFFOstream::writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
145-
size_t RemainingSize, uint8_t Flags) {
146-
uint8_t TypeAndFlags = Flags | (Type << 4);
147-
if (RemainingSize > GOFF::RecordLength)
138+
GOFFOstream::~GOFFOstream() { finalizeRecord(); }
139+
140+
void GOFFOstream::updateFlagsAndWritePrefix(bool IsContinued) {
141+
// Update the flags based on the previous state and the flag IsContinued.
142+
if (TypeAndFlags & RecContinued)
143+
TypeAndFlags |= RecContinuation;
144+
if (IsContinued)
148145
TypeAndFlags |= RecContinued;
146+
else
147+
TypeAndFlags &= ~RecContinued;
148+
149149
OS << static_cast<unsigned char>(GOFF::PTVPrefix) // Record Type
150150
<< static_cast<unsigned char>(TypeAndFlags) // Continuation
151151
<< static_cast<unsigned char>(0); // Version
152+
153+
++PhysicalRecords;
152154
}
153155

154-
void GOFFOstream::newRecord(GOFF::RecordType Type, size_t Size) {
155-
fillRecord();
156-
CurrentType = Type;
157-
RemainingSize = Size;
158-
#ifdef NDEBUG
159-
size_t Gap;
160-
#endif
161-
Gap = (RemainingSize % GOFF::RecordContentLength);
162-
if (Gap) {
163-
Gap = GOFF::RecordContentLength - Gap;
164-
RemainingSize += Gap;
165-
}
166-
NewLogicalRecord = true;
167-
++LogicalRecords;
156+
size_t GOFFOstream::getRemainingSize() {
157+
return size_t(&Buffer[BufferSize] - BufferPtr);
168158
}
169159

170-
void GOFFOstream::fillRecord() {
171-
assert((GetNumBytesInBuffer() <= RemainingSize) &&
172-
"More bytes in buffer than expected");
173-
size_t Remains = RemainingSize - GetNumBytesInBuffer();
174-
if (Remains) {
175-
assert(Remains == Gap && "Wrong size of fill gap");
176-
assert((Remains < GOFF::RecordLength) &&
177-
"Attempt to fill more than one physical record");
178-
raw_ostream::write_zeros(Remains);
160+
void GOFFOstream::write(const char *Ptr, size_t Size) {
161+
size_t RemainingSize = getRemainingSize();
162+
163+
// Data fits into the buffer.
164+
if (LLVM_LIKELY(Size <= RemainingSize)) {
165+
memcpy(BufferPtr, Ptr, Size);
166+
BufferPtr += Size;
167+
return;
168+
}
169+
170+
// Otherwise the buffer is partially filled or full, and data does not fit
171+
// into it.
172+
updateFlagsAndWritePrefix(/*IsContinued=*/true);
173+
OS.write(Buffer, size_t(BufferPtr - Buffer));
174+
if (RemainingSize > 0) {
175+
OS.write(Ptr, RemainingSize);
176+
Ptr += RemainingSize;
177+
Size -= RemainingSize;
179178
}
180-
flush();
181-
assert(RemainingSize == 0 && "Not fully flushed");
182-
assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");
183-
}
184179

185-
// This function is called from the raw_ostream implementation if:
186-
// - The internal buffer is full. Size is excactly the size of the buffer.
187-
// - Data larger than the internal buffer is written. Size is a multiple of the
188-
// buffer size.
189-
// - flush() has been called. Size is at most the buffer size.
190-
// The GOFFOstream implementation ensures that flush() is called before a new
191-
// logical record begins. Therefore it is sufficient to check for a new block
192-
// only once.
193-
void GOFFOstream::write_impl(const char *Ptr, size_t Size) {
194-
assert((RemainingSize >= Size) && "Attempt to write too much data");
195-
assert(RemainingSize && "Logical record overflow");
196-
if (!(RemainingSize % GOFF::RecordContentLength)) {
197-
writeRecordPrefix(OS, CurrentType, RemainingSize,
198-
NewLogicalRecord ? 0 : RecContinuation);
199-
NewLogicalRecord = false;
180+
while (Size > BufferSize) {
181+
updateFlagsAndWritePrefix(/*IsContinued=*/true);
182+
OS.write(Ptr, BufferSize);
183+
Ptr += BufferSize;
184+
Size -= BufferSize;
200185
}
201-
assert(!NewLogicalRecord &&
202-
"New logical record not on physical record boundary");
203-
204-
size_t Idx = 0;
205-
while (Size > 0) {
206-
size_t BytesToWrite = bytesToNextPhysicalRecord();
207-
if (BytesToWrite > Size)
208-
BytesToWrite = Size;
209-
OS.write(Ptr + Idx, BytesToWrite);
210-
Idx += BytesToWrite;
211-
Size -= BytesToWrite;
212-
RemainingSize -= BytesToWrite;
213-
if (Size)
214-
writeRecordPrefix(OS, CurrentType, RemainingSize);
186+
187+
// The remaining bytes fit into the buffer.
188+
memcpy(Buffer, Ptr, Size);
189+
BufferPtr = &Buffer[Size];
190+
}
191+
192+
void GOFFOstream::write_zeros(unsigned NumZeros) {
193+
assert(NumZeros <= 16 && "Range for zeros too large");
194+
195+
// Handle the common case first: all fits in the buffer.
196+
size_t RemainingSize = getRemainingSize();
197+
if (LLVM_LIKELY(RemainingSize >= NumZeros)) {
198+
memset(BufferPtr, 0, NumZeros);
199+
BufferPtr += NumZeros;
200+
return;
215201
}
202+
203+
// Otherwise some field value is cleared.
204+
static char Zeros[16] = {
205+
0,
206+
};
207+
write(Zeros, NumZeros);
216208
}
217209

210+
void GOFFOstream::newRecord(GOFF::RecordType Type) {
211+
finalizeRecord();
212+
TypeAndFlags = Type << 4;
213+
++LogicalRecords;
214+
}
215+
216+
void GOFFOstream::finalizeRecord() {
217+
if (Buffer == BufferPtr)
218+
return;
219+
updateFlagsAndWritePrefix(/*IsContinued=*/false);
220+
OS.write(Buffer, size_t(BufferPtr - Buffer));
221+
OS.write_zeros(getRemainingSize());
222+
BufferPtr = Buffer;
223+
}
224+
225+
namespace {
226+
218227
class GOFFObjectWriter : public MCObjectWriter {
219228
// The target specific GOFF writer instance.
220229
std::unique_ptr<MCGOFFObjectTargetWriter> TargetObjectWriter;
@@ -242,7 +251,7 @@ class GOFFObjectWriter : public MCObjectWriter {
242251
} // end anonymous namespace
243252

244253
void GOFFObjectWriter::writeHeader() {
245-
OS.newRecord(GOFF::RT_HDR, /*Size=*/57);
254+
OS.newRecord(GOFF::RT_HDR);
246255
OS.write_zeros(1); // Reserved
247256
OS.writebe<uint32_t>(0); // Target Hardware Environment
248257
OS.writebe<uint32_t>(0); // Target Operating System Environment
@@ -262,7 +271,7 @@ void GOFFObjectWriter::writeEnd() {
262271

263272
// TODO Set Flags/AMODE/ESDID for entry point.
264273

265-
OS.newRecord(GOFF::RT_END, /*Size=*/13);
274+
OS.newRecord(GOFF::RT_END);
266275
OS.writebe<uint8_t>(Flags(6, 2, F)); // Indicator flags
267276
OS.writebe<uint8_t>(AMODE); // AMODE
268277
OS.write_zeros(3); // Reserved
@@ -271,18 +280,19 @@ void GOFFObjectWriter::writeEnd() {
271280
// being zero.
272281
OS.writebe<uint32_t>(0); // Record Count
273282
OS.writebe<uint32_t>(ESDID); // ESDID (of entry point)
274-
OS.finalize();
275283
}
276284

277285
uint64_t GOFFObjectWriter::writeObject(MCAssembler &Asm) {
278-
uint64_t StartOffset = OS.tell();
279-
280286
writeHeader();
281287
writeEnd();
282288

283-
LLVM_DEBUG(dbgs() << "Wrote " << OS.logicalRecords() << " logical records.");
289+
// Make sure all records are written.
290+
OS.finalizeRecord();
291+
292+
LLVM_DEBUG(dbgs() << "Wrote " << OS.getNumLogicalRecords()
293+
<< " logical records.");
284294

285-
return OS.tell() - StartOffset;
295+
return OS.getWrittenSize();
286296
}
287297

288298
std::unique_ptr<MCObjectWriter>

0 commit comments

Comments
 (0)