xref: /freebsd-src/contrib/llvm-project/llvm/lib/ObjectYAML/GOFFEmitter.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1*5f757f3fSDimitry Andric //===- yaml2goff - Convert YAML to a GOFF object file ---------------------===//
2*5f757f3fSDimitry Andric //
3*5f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*5f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*5f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*5f757f3fSDimitry Andric //
7*5f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
8*5f757f3fSDimitry Andric ///
9*5f757f3fSDimitry Andric /// \file
10*5f757f3fSDimitry Andric /// The GOFF component of yaml2obj.
11*5f757f3fSDimitry Andric ///
12*5f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
13*5f757f3fSDimitry Andric 
14*5f757f3fSDimitry Andric #include "llvm/ADT/IndexedMap.h"
15*5f757f3fSDimitry Andric #include "llvm/ObjectYAML/ObjectYAML.h"
16*5f757f3fSDimitry Andric #include "llvm/ObjectYAML/yaml2obj.h"
17*5f757f3fSDimitry Andric #include "llvm/Support/ConvertEBCDIC.h"
18*5f757f3fSDimitry Andric #include "llvm/Support/Endian.h"
19*5f757f3fSDimitry Andric #include "llvm/Support/raw_ostream.h"
20*5f757f3fSDimitry Andric 
21*5f757f3fSDimitry Andric using namespace llvm;
22*5f757f3fSDimitry Andric 
23*5f757f3fSDimitry Andric namespace {
24*5f757f3fSDimitry Andric 
25*5f757f3fSDimitry Andric // Common flag values on records.
26*5f757f3fSDimitry Andric enum {
27*5f757f3fSDimitry Andric   // Flag: This record is continued.
28*5f757f3fSDimitry Andric   Rec_Continued = 1,
29*5f757f3fSDimitry Andric 
30*5f757f3fSDimitry Andric   // Flag: This record is a continuation.
31*5f757f3fSDimitry Andric   Rec_Continuation = 1 << (8 - 6 - 1),
32*5f757f3fSDimitry Andric };
33*5f757f3fSDimitry Andric 
34*5f757f3fSDimitry Andric template <typename ValueType> struct BinaryBeImpl {
35*5f757f3fSDimitry Andric   ValueType Value;
BinaryBeImpl__anon95e3cb370111::BinaryBeImpl36*5f757f3fSDimitry Andric   BinaryBeImpl(ValueType V) : Value(V) {}
37*5f757f3fSDimitry Andric };
38*5f757f3fSDimitry Andric 
39*5f757f3fSDimitry Andric template <typename ValueType>
operator <<(raw_ostream & OS,const BinaryBeImpl<ValueType> & BBE)40*5f757f3fSDimitry Andric raw_ostream &operator<<(raw_ostream &OS, const BinaryBeImpl<ValueType> &BBE) {
41*5f757f3fSDimitry Andric   char Buffer[sizeof(BBE.Value)];
42*5f757f3fSDimitry Andric   support::endian::write<ValueType, llvm::endianness::big, support::unaligned>(
43*5f757f3fSDimitry Andric       Buffer, BBE.Value);
44*5f757f3fSDimitry Andric   OS.write(Buffer, sizeof(BBE.Value));
45*5f757f3fSDimitry Andric   return OS;
46*5f757f3fSDimitry Andric }
47*5f757f3fSDimitry Andric 
binaryBe(ValueType V)48*5f757f3fSDimitry Andric template <typename ValueType> BinaryBeImpl<ValueType> binaryBe(ValueType V) {
49*5f757f3fSDimitry Andric   return BinaryBeImpl<ValueType>(V);
50*5f757f3fSDimitry Andric }
51*5f757f3fSDimitry Andric 
52*5f757f3fSDimitry Andric struct ZerosImpl {
53*5f757f3fSDimitry Andric   size_t NumBytes;
54*5f757f3fSDimitry Andric };
55*5f757f3fSDimitry Andric 
operator <<(raw_ostream & OS,const ZerosImpl & Z)56*5f757f3fSDimitry Andric raw_ostream &operator<<(raw_ostream &OS, const ZerosImpl &Z) {
57*5f757f3fSDimitry Andric   OS.write_zeros(Z.NumBytes);
58*5f757f3fSDimitry Andric   return OS;
59*5f757f3fSDimitry Andric }
60*5f757f3fSDimitry Andric 
zeros(const size_t NumBytes)61*5f757f3fSDimitry Andric ZerosImpl zeros(const size_t NumBytes) { return ZerosImpl{NumBytes}; }
62*5f757f3fSDimitry Andric 
63*5f757f3fSDimitry Andric // The GOFFOstream is responsible to write the data into the fixed physical
64*5f757f3fSDimitry Andric // records of the format. A user of this class announces the start of a new
65*5f757f3fSDimitry Andric // logical record and the size of its payload. While writing the payload, the
66*5f757f3fSDimitry Andric // physical records are created for the data. Possible fill bytes at the end of
67*5f757f3fSDimitry Andric // a physical record are written automatically.
68*5f757f3fSDimitry Andric class GOFFOstream : public raw_ostream {
69*5f757f3fSDimitry Andric public:
GOFFOstream(raw_ostream & OS)70*5f757f3fSDimitry Andric   explicit GOFFOstream(raw_ostream &OS)
71*5f757f3fSDimitry Andric       : OS(OS), LogicalRecords(0), RemainingSize(0), NewLogicalRecord(false) {
72*5f757f3fSDimitry Andric     SetBufferSize(GOFF::PayloadLength);
73*5f757f3fSDimitry Andric   }
74*5f757f3fSDimitry Andric 
~GOFFOstream()75*5f757f3fSDimitry Andric   ~GOFFOstream() { finalize(); }
76*5f757f3fSDimitry Andric 
makeNewRecord(GOFF::RecordType Type,size_t Size)77*5f757f3fSDimitry Andric   void makeNewRecord(GOFF::RecordType Type, size_t Size) {
78*5f757f3fSDimitry Andric     fillRecord();
79*5f757f3fSDimitry Andric     CurrentType = Type;
80*5f757f3fSDimitry Andric     RemainingSize = Size;
81*5f757f3fSDimitry Andric     if (size_t Gap = (RemainingSize % GOFF::PayloadLength))
82*5f757f3fSDimitry Andric       RemainingSize += GOFF::PayloadLength - Gap;
83*5f757f3fSDimitry Andric     NewLogicalRecord = true;
84*5f757f3fSDimitry Andric     ++LogicalRecords;
85*5f757f3fSDimitry Andric   }
86*5f757f3fSDimitry Andric 
finalize()87*5f757f3fSDimitry Andric   void finalize() { fillRecord(); }
88*5f757f3fSDimitry Andric 
logicalRecords()89*5f757f3fSDimitry Andric   uint32_t logicalRecords() { return LogicalRecords; }
90*5f757f3fSDimitry Andric 
91*5f757f3fSDimitry Andric private:
92*5f757f3fSDimitry Andric   // The underlying raw_ostream.
93*5f757f3fSDimitry Andric   raw_ostream &OS;
94*5f757f3fSDimitry Andric 
95*5f757f3fSDimitry Andric   // The number of logical records emitted so far.
96*5f757f3fSDimitry Andric   uint32_t LogicalRecords;
97*5f757f3fSDimitry Andric 
98*5f757f3fSDimitry Andric   // The remaining size of this logical record, including fill bytes.
99*5f757f3fSDimitry Andric   size_t RemainingSize;
100*5f757f3fSDimitry Andric 
101*5f757f3fSDimitry Andric   // The type of the current (logical) record.
102*5f757f3fSDimitry Andric   GOFF::RecordType CurrentType;
103*5f757f3fSDimitry Andric 
104*5f757f3fSDimitry Andric   // Signals start of new record.
105*5f757f3fSDimitry Andric   bool NewLogicalRecord;
106*5f757f3fSDimitry Andric 
107*5f757f3fSDimitry Andric   // Return the number of bytes left to write until next physical record.
108*5f757f3fSDimitry Andric   // Please note that we maintain the total number of bytes left, not the
109*5f757f3fSDimitry Andric   // written size.
bytesToNextPhysicalRecord()110*5f757f3fSDimitry Andric   size_t bytesToNextPhysicalRecord() {
111*5f757f3fSDimitry Andric     size_t Bytes = RemainingSize % GOFF::PayloadLength;
112*5f757f3fSDimitry Andric     return Bytes ? Bytes : GOFF::PayloadLength;
113*5f757f3fSDimitry Andric   }
114*5f757f3fSDimitry Andric 
115*5f757f3fSDimitry Andric   // Write the record prefix of a physical record, using the current record
116*5f757f3fSDimitry Andric   // type.
writeRecordPrefix(raw_ostream & OS,GOFF::RecordType Type,size_t RemainingSize,uint8_t Flags=Rec_Continuation)117*5f757f3fSDimitry Andric   static void writeRecordPrefix(raw_ostream &OS, GOFF::RecordType Type,
118*5f757f3fSDimitry Andric                                 size_t RemainingSize,
119*5f757f3fSDimitry Andric                                 uint8_t Flags = Rec_Continuation) {
120*5f757f3fSDimitry Andric     uint8_t TypeAndFlags = Flags | (Type << 4);
121*5f757f3fSDimitry Andric     if (RemainingSize > GOFF::RecordLength)
122*5f757f3fSDimitry Andric       TypeAndFlags |= Rec_Continued;
123*5f757f3fSDimitry Andric     OS << binaryBe(static_cast<unsigned char>(GOFF::PTVPrefix))
124*5f757f3fSDimitry Andric        << binaryBe(static_cast<unsigned char>(TypeAndFlags))
125*5f757f3fSDimitry Andric        << binaryBe(static_cast<unsigned char>(0));
126*5f757f3fSDimitry Andric   }
127*5f757f3fSDimitry Andric 
128*5f757f3fSDimitry Andric   // Fill the last physical record of a logical record with zero bytes.
fillRecord()129*5f757f3fSDimitry Andric   void fillRecord() {
130*5f757f3fSDimitry Andric     assert((GetNumBytesInBuffer() <= RemainingSize) &&
131*5f757f3fSDimitry Andric            "More bytes in buffer than expected");
132*5f757f3fSDimitry Andric     size_t Remains = RemainingSize - GetNumBytesInBuffer();
133*5f757f3fSDimitry Andric     if (Remains) {
134*5f757f3fSDimitry Andric       assert((Remains < GOFF::RecordLength) &&
135*5f757f3fSDimitry Andric              "Attempting to fill more than one physical record");
136*5f757f3fSDimitry Andric       raw_ostream::write_zeros(Remains);
137*5f757f3fSDimitry Andric     }
138*5f757f3fSDimitry Andric     flush();
139*5f757f3fSDimitry Andric     assert(RemainingSize == 0 && "Not fully flushed");
140*5f757f3fSDimitry Andric     assert(GetNumBytesInBuffer() == 0 && "Buffer not fully empty");
141*5f757f3fSDimitry Andric   }
142*5f757f3fSDimitry Andric 
143*5f757f3fSDimitry Andric   // See raw_ostream::write_impl.
write_impl(const char * Ptr,size_t Size)144*5f757f3fSDimitry Andric   void write_impl(const char *Ptr, size_t Size) override {
145*5f757f3fSDimitry Andric     assert((RemainingSize >= Size) && "Attempt to write too much data");
146*5f757f3fSDimitry Andric     assert(RemainingSize && "Logical record overflow");
147*5f757f3fSDimitry Andric     if (!(RemainingSize % GOFF::PayloadLength)) {
148*5f757f3fSDimitry Andric       writeRecordPrefix(OS, CurrentType, RemainingSize,
149*5f757f3fSDimitry Andric                         NewLogicalRecord ? 0 : Rec_Continuation);
150*5f757f3fSDimitry Andric       NewLogicalRecord = false;
151*5f757f3fSDimitry Andric     }
152*5f757f3fSDimitry Andric     assert(!NewLogicalRecord &&
153*5f757f3fSDimitry Andric            "New logical record not on physical record boundary");
154*5f757f3fSDimitry Andric 
155*5f757f3fSDimitry Andric     size_t Idx = 0;
156*5f757f3fSDimitry Andric     while (Size > 0) {
157*5f757f3fSDimitry Andric       size_t BytesToWrite = bytesToNextPhysicalRecord();
158*5f757f3fSDimitry Andric       if (BytesToWrite > Size)
159*5f757f3fSDimitry Andric         BytesToWrite = Size;
160*5f757f3fSDimitry Andric       OS.write(Ptr + Idx, BytesToWrite);
161*5f757f3fSDimitry Andric       Idx += BytesToWrite;
162*5f757f3fSDimitry Andric       Size -= BytesToWrite;
163*5f757f3fSDimitry Andric       RemainingSize -= BytesToWrite;
164*5f757f3fSDimitry Andric       if (Size) {
165*5f757f3fSDimitry Andric         writeRecordPrefix(OS, CurrentType, RemainingSize);
166*5f757f3fSDimitry Andric       }
167*5f757f3fSDimitry Andric     }
168*5f757f3fSDimitry Andric   }
169*5f757f3fSDimitry Andric 
170*5f757f3fSDimitry Andric   // Return the current position within the stream, not counting the bytes
171*5f757f3fSDimitry Andric   // currently in the buffer.
current_pos() const172*5f757f3fSDimitry Andric   uint64_t current_pos() const override { return OS.tell(); }
173*5f757f3fSDimitry Andric };
174*5f757f3fSDimitry Andric 
175*5f757f3fSDimitry Andric class GOFFState {
176*5f757f3fSDimitry Andric   void writeHeader(GOFFYAML::FileHeader &FileHdr);
177*5f757f3fSDimitry Andric   void writeEnd();
178*5f757f3fSDimitry Andric 
reportError(const Twine & Msg)179*5f757f3fSDimitry Andric   void reportError(const Twine &Msg) {
180*5f757f3fSDimitry Andric     ErrHandler(Msg);
181*5f757f3fSDimitry Andric     HasError = true;
182*5f757f3fSDimitry Andric   }
183*5f757f3fSDimitry Andric 
GOFFState(raw_ostream & OS,GOFFYAML::Object & Doc,yaml::ErrorHandler ErrHandler)184*5f757f3fSDimitry Andric   GOFFState(raw_ostream &OS, GOFFYAML::Object &Doc,
185*5f757f3fSDimitry Andric             yaml::ErrorHandler ErrHandler)
186*5f757f3fSDimitry Andric       : GW(OS), Doc(Doc), ErrHandler(ErrHandler), HasError(false) {}
187*5f757f3fSDimitry Andric 
~GOFFState()188*5f757f3fSDimitry Andric   ~GOFFState() { GW.finalize(); }
189*5f757f3fSDimitry Andric 
190*5f757f3fSDimitry Andric   bool writeObject();
191*5f757f3fSDimitry Andric 
192*5f757f3fSDimitry Andric public:
193*5f757f3fSDimitry Andric   static bool writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,
194*5f757f3fSDimitry Andric                         yaml::ErrorHandler ErrHandler);
195*5f757f3fSDimitry Andric 
196*5f757f3fSDimitry Andric private:
197*5f757f3fSDimitry Andric   GOFFOstream GW;
198*5f757f3fSDimitry Andric   GOFFYAML::Object &Doc;
199*5f757f3fSDimitry Andric   yaml::ErrorHandler ErrHandler;
200*5f757f3fSDimitry Andric   bool HasError;
201*5f757f3fSDimitry Andric };
202*5f757f3fSDimitry Andric 
writeHeader(GOFFYAML::FileHeader & FileHdr)203*5f757f3fSDimitry Andric void GOFFState::writeHeader(GOFFYAML::FileHeader &FileHdr) {
204*5f757f3fSDimitry Andric   SmallString<16> CCSIDName;
205*5f757f3fSDimitry Andric   if (std::error_code EC =
206*5f757f3fSDimitry Andric           ConverterEBCDIC::convertToEBCDIC(FileHdr.CharacterSetName, CCSIDName))
207*5f757f3fSDimitry Andric     reportError("Conversion error on " + FileHdr.CharacterSetName);
208*5f757f3fSDimitry Andric   if (CCSIDName.size() > 16) {
209*5f757f3fSDimitry Andric     reportError("CharacterSetName too long");
210*5f757f3fSDimitry Andric     CCSIDName.resize(16);
211*5f757f3fSDimitry Andric   }
212*5f757f3fSDimitry Andric   SmallString<16> LangProd;
213*5f757f3fSDimitry Andric   if (std::error_code EC = ConverterEBCDIC::convertToEBCDIC(
214*5f757f3fSDimitry Andric           FileHdr.LanguageProductIdentifier, LangProd))
215*5f757f3fSDimitry Andric     reportError("Conversion error on " + FileHdr.LanguageProductIdentifier);
216*5f757f3fSDimitry Andric   if (LangProd.size() > 16) {
217*5f757f3fSDimitry Andric     reportError("LanguageProductIdentifier too long");
218*5f757f3fSDimitry Andric     LangProd.resize(16);
219*5f757f3fSDimitry Andric   }
220*5f757f3fSDimitry Andric 
221*5f757f3fSDimitry Andric   GW.makeNewRecord(GOFF::RT_HDR, GOFF::PayloadLength);
222*5f757f3fSDimitry Andric   GW << binaryBe(FileHdr.TargetEnvironment)     // TargetEnvironment
223*5f757f3fSDimitry Andric      << binaryBe(FileHdr.TargetOperatingSystem) // TargetOperatingSystem
224*5f757f3fSDimitry Andric      << zeros(2)                                // Reserved
225*5f757f3fSDimitry Andric      << binaryBe(FileHdr.CCSID)                 // CCSID
226*5f757f3fSDimitry Andric      << CCSIDName                               // CharacterSetName
227*5f757f3fSDimitry Andric      << zeros(16 - CCSIDName.size())            // Fill bytes
228*5f757f3fSDimitry Andric      << LangProd                                // LanguageProductIdentifier
229*5f757f3fSDimitry Andric      << zeros(16 - LangProd.size())             // Fill bytes
230*5f757f3fSDimitry Andric      << binaryBe(FileHdr.ArchitectureLevel);    // ArchitectureLevel
231*5f757f3fSDimitry Andric   // The module propties are optional. Figure out if we need to write them.
232*5f757f3fSDimitry Andric   uint16_t ModPropLen = 0;
233*5f757f3fSDimitry Andric   if (FileHdr.TargetSoftwareEnvironment)
234*5f757f3fSDimitry Andric     ModPropLen = 3;
235*5f757f3fSDimitry Andric   else if (FileHdr.InternalCCSID)
236*5f757f3fSDimitry Andric     ModPropLen = 2;
237*5f757f3fSDimitry Andric   if (ModPropLen) {
238*5f757f3fSDimitry Andric     GW << binaryBe(ModPropLen) << zeros(6);
239*5f757f3fSDimitry Andric     if (ModPropLen >= 2)
240*5f757f3fSDimitry Andric       GW << binaryBe(FileHdr.InternalCCSID ? *FileHdr.InternalCCSID : 0);
241*5f757f3fSDimitry Andric     if (ModPropLen >= 3)
242*5f757f3fSDimitry Andric       GW << binaryBe(FileHdr.TargetSoftwareEnvironment
243*5f757f3fSDimitry Andric                          ? *FileHdr.TargetSoftwareEnvironment
244*5f757f3fSDimitry Andric                          : 0);
245*5f757f3fSDimitry Andric   }
246*5f757f3fSDimitry Andric }
247*5f757f3fSDimitry Andric 
writeEnd()248*5f757f3fSDimitry Andric void GOFFState::writeEnd() {
249*5f757f3fSDimitry Andric   GW.makeNewRecord(GOFF::RT_END, GOFF::PayloadLength);
250*5f757f3fSDimitry Andric   GW << binaryBe(uint8_t(0)) // No entry point
251*5f757f3fSDimitry Andric      << binaryBe(uint8_t(0)) // No AMODE
252*5f757f3fSDimitry Andric      << zeros(3)             // Reserved
253*5f757f3fSDimitry Andric      << binaryBe(GW.logicalRecords());
254*5f757f3fSDimitry Andric   // No entry point yet. Automatically fill remaining space with zero bytes.
255*5f757f3fSDimitry Andric   GW.finalize();
256*5f757f3fSDimitry Andric }
257*5f757f3fSDimitry Andric 
writeObject()258*5f757f3fSDimitry Andric bool GOFFState::writeObject() {
259*5f757f3fSDimitry Andric   writeHeader(Doc.Header);
260*5f757f3fSDimitry Andric   if (HasError)
261*5f757f3fSDimitry Andric     return false;
262*5f757f3fSDimitry Andric   writeEnd();
263*5f757f3fSDimitry Andric   return true;
264*5f757f3fSDimitry Andric }
265*5f757f3fSDimitry Andric 
writeGOFF(raw_ostream & OS,GOFFYAML::Object & Doc,yaml::ErrorHandler ErrHandler)266*5f757f3fSDimitry Andric bool GOFFState::writeGOFF(raw_ostream &OS, GOFFYAML::Object &Doc,
267*5f757f3fSDimitry Andric                           yaml::ErrorHandler ErrHandler) {
268*5f757f3fSDimitry Andric   GOFFState State(OS, Doc, ErrHandler);
269*5f757f3fSDimitry Andric   return State.writeObject();
270*5f757f3fSDimitry Andric }
271*5f757f3fSDimitry Andric } // namespace
272*5f757f3fSDimitry Andric 
273*5f757f3fSDimitry Andric namespace llvm {
274*5f757f3fSDimitry Andric namespace yaml {
275*5f757f3fSDimitry Andric 
yaml2goff(llvm::GOFFYAML::Object & Doc,raw_ostream & Out,ErrorHandler ErrHandler)276*5f757f3fSDimitry Andric bool yaml2goff(llvm::GOFFYAML::Object &Doc, raw_ostream &Out,
277*5f757f3fSDimitry Andric                ErrorHandler ErrHandler) {
278*5f757f3fSDimitry Andric   return GOFFState::writeGOFF(Out, Doc, ErrHandler);
279*5f757f3fSDimitry Andric }
280*5f757f3fSDimitry Andric 
281*5f757f3fSDimitry Andric } // namespace yaml
282*5f757f3fSDimitry Andric } // namespace llvm
283