llvm.org GIT mirror llvm / e6480e2
Bitcode: Introduce BitcodeWriter interface. This interface allows clients to write multiple modules to a single bitcode file. Also introduce the llvm-cat utility which can be used to create a bitcode file containing multiple modules. Differential Revision: https://reviews.llvm.org/D26179 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@288195 91177308-0d34-0410-b5e6-96231b3b80d8 Peter Collingbourne 2 years ago
11 changed file(s) with 256 addition(s) and 96 deletion(s). Raw diff Collapse all Expand all
6565 bool ShouldLazyLoadMetadata);
6666
6767 public:
68 ArrayRef getBuffer() const { return Buffer; }
69
6870 /// Read the bitcode module and prepare for lazy deserialization of function
6971 /// bodies. If ShouldLazyLoadMetadata is true, lazily load metadata as well.
7072 Expected>
1717 #include
1818
1919 namespace llvm {
20 class BitstreamWriter;
2021 class Module;
2122 class raw_ostream;
23
24 class BitcodeWriter {
25 SmallVectorImpl &Buffer;
26 std::unique_ptr Stream;
27
28 public:
29 /// Create a BitcodeWriter that writes to Buffer.
30 BitcodeWriter(SmallVectorImpl &Buffer);
31
32 ~BitcodeWriter();
33
34 /// Write the specified module to the buffer specified at construction time.
35 ///
36 /// If \c ShouldPreserveUseListOrder, encode the use-list order for each \a
37 /// Value in \c M. These will be reconstructed exactly when \a M is
38 /// deserialized.
39 ///
40 /// If \c Index is supplied, the bitcode will contain the summary index
41 /// (currently for use in ThinLTO optimization).
42 ///
43 /// \p GenerateHash enables hashing the Module and including the hash in the
44 /// bitcode (currently for use in ThinLTO incremental build).
45 void writeModule(const Module *M, bool ShouldPreserveUseListOrder = false,
46 const ModuleSummaryIndex *Index = nullptr,
47 bool GenerateHash = false);
48 };
2249
2350 /// \brief Write the specified module to the specified raw output stream.
2451 ///
508508 void EnterBlockInfoBlock() {
509509 EnterSubblock(bitc::BLOCKINFO_BLOCK_ID, 2);
510510 BlockInfoCurBID = ~0U;
511 BlockInfoRecords.clear();
511512 }
512513 private:
513514 /// SwitchToBlockID - If we aren't already talking about the specified block
6464 };
6565
6666 /// Abstract class to manage the bitcode writing, subclassed for each bitcode
67 /// file type. Owns the BitstreamWriter, and includes the main entry point for
68 /// writing.
69 class BitcodeWriter {
67 /// file type.
68 class BitcodeWriterBase {
7069 protected:
71 /// Pointer to the buffer allocated by caller for bitcode writing.
72 const SmallVectorImpl &Buffer;
73
74 /// The stream created and owned by the BitodeWriter.
75 BitstreamWriter Stream;
70 /// The stream created and owned by the client.
71 BitstreamWriter &Stream;
7672
7773 /// Saves the offset of the VSTOffset record that must eventually be
7874 /// backpatched with the offset of the actual VST.
7975 uint64_t VSTOffsetPlaceholder = 0;
8076
8177 public:
82 /// Constructs a BitcodeWriter object, and initializes a BitstreamRecord,
83 /// writing to the provided \p Buffer.
84 BitcodeWriter(SmallVectorImpl &Buffer)
85 : Buffer(Buffer), Stream(Buffer) {}
86
87 virtual ~BitcodeWriter() = default;
88
89 /// Main entry point to write the bitcode file, which writes the bitcode
90 /// header and will then invoke the virtual writeBlocks() method.
91 void write();
92
93 private:
94 /// Derived classes must implement this to write the corresponding blocks for
95 /// that bitcode file type.
96 virtual void writeBlocks() = 0;
78 /// Constructs a BitcodeWriterBase object that writes to the provided
79 /// \p Stream.
80 BitcodeWriterBase(BitstreamWriter &Stream) : Stream(Stream) {}
9781
9882 protected:
9983 bool hasVSTOffsetPlaceholder() { return VSTOffsetPlaceholder != 0; }
10286 };
10387
10488 /// Class to manage the bitcode writing for a module.
105 class ModuleBitcodeWriter : public BitcodeWriter {
89 class ModuleBitcodeWriter : public BitcodeWriterBase {
90 /// Pointer to the buffer allocated by caller for bitcode writing.
91 const SmallVectorImpl &Buffer;
92
10693 /// The Module to write to bitcode.
10794 const Module &M;
10895
115102 /// True if a module hash record should be written.
116103 bool GenerateHash;
117104
118 /// The start bit of the module block, for use in generating a module hash
119 uint64_t BitcodeStartBit = 0;
105 /// The start bit of the identification block.
106 uint64_t BitcodeStartBit;
120107
121108 /// Map that holds the correspondence between GUIDs in the summary index,
122109 /// that came from indirect call profiles, and a value id generated by this
130117 /// Constructs a ModuleBitcodeWriter object for the given Module,
131118 /// writing to the provided \p Buffer.
132119 ModuleBitcodeWriter(const Module *M, SmallVectorImpl &Buffer,
133 bool ShouldPreserveUseListOrder,
120 BitstreamWriter &Stream, bool ShouldPreserveUseListOrder,
134121 const ModuleSummaryIndex *Index, bool GenerateHash)
135 : BitcodeWriter(Buffer), M(*M), VE(*M, ShouldPreserveUseListOrder),
136 Index(Index), GenerateHash(GenerateHash) {
137 // Save the start bit of the actual bitcode, in case there is space
138 // saved at the start for the darwin header above. The reader stream
139 // will start at the bitcode, and we need the offset of the VST
140 // to line up.
141 BitcodeStartBit = Stream.GetCurrentBitNo();
142
122 : BitcodeWriterBase(Stream), Buffer(Buffer), M(*M),
123 VE(*M, ShouldPreserveUseListOrder), Index(Index),
124 GenerateHash(GenerateHash), BitcodeStartBit(Stream.GetCurrentBitNo()) {
143125 // Assign ValueIds to any callee values in the index that came from
144126 // indirect call profiles and were recorded as a GUID not a Value*
145127 // (which would have been assigned an ID by the ValueEnumerator).
161143 assignValueId(CallEdge.first.getGUID());
162144 }
163145
146 /// Emit the current module to the bitstream.
147 void write();
148
164149 private:
165 /// Main entry point for writing a module to bitcode, invoked by
166 /// BitcodeWriter::write() after it writes the header.
167 void writeBlocks() override;
168
169 /// Create the "IDENTIFICATION_BLOCK_ID" containing a single string with the
170 /// current llvm version, and a record for the epoch number.
171 void writeIdentificationBlock();
172
173 /// Emit the current module to the bitstream.
174 void writeModule();
175
176150 uint64_t bitcodeStartBit() { return BitcodeStartBit; }
177151
178 void writeStringRecord(unsigned Code, StringRef Str, unsigned AbbrevToUse);
179152 void writeAttributeGroupTable();
180153 void writeAttributeTable();
181154 void writeTypeTable();
309282 };
310283
311284 /// Class to manage the bitcode writing for a combined index.
312 class IndexBitcodeWriter : public BitcodeWriter {
285 class IndexBitcodeWriter : public BitcodeWriterBase {
313286 /// The combined index to write to bitcode.
314287 const ModuleSummaryIndex &Index;
315288
328301 /// Constructs a IndexBitcodeWriter object for the given combined index,
329302 /// writing to the provided \p Buffer. When writing a subset of the index
330303 /// for a distributed backend, provide a \p ModuleToSummariesForIndex map.
331 IndexBitcodeWriter(SmallVectorImpl &Buffer,
332 const ModuleSummaryIndex &Index,
304 IndexBitcodeWriter(BitstreamWriter &Stream, const ModuleSummaryIndex &Index,
333305 const std::map
334306 *ModuleToSummariesForIndex = nullptr)
335 : BitcodeWriter(Buffer), Index(Index),
307 : BitcodeWriterBase(Stream), Index(Index),
336308 ModuleToSummariesForIndex(ModuleToSummariesForIndex) {
337309 // Assign unique value ids to all summaries to be written, for use
338310 // in writing out the call graph edges. Save the mapping from GUID
479451 /// Obtain the end iterator over the summaries to be written.
480452 iterator end() { return iterator(*this, /*IsAtEnd=*/true); }
481453
454 /// Main entry point for writing a combined index to bitcode.
455 void write();
456
482457 private:
483 /// Main entry point for writing a combined index to bitcode, invoked by
484 /// BitcodeWriter::write() after it writes the header.
485 void writeBlocks() override;
486
487458 void writeIndex();
488459 void writeModStrings();
489460 void writeCombinedValueSymbolTable();
596567 llvm_unreachable("Invalid synch scope");
597568 }
598569
599 void ModuleBitcodeWriter::writeStringRecord(unsigned Code, StringRef Str,
600 unsigned AbbrevToUse) {
570 static void writeStringRecord(BitstreamWriter &Stream, unsigned Code,
571 StringRef Str, unsigned AbbrevToUse) {
601572 SmallVector Vals;
602573
603574 // Code: [strchar x N]
921892
922893 // Emit the name if it is present.
923894 if (!ST->getName().empty())
924 writeStringRecord(bitc::TYPE_CODE_STRUCT_NAME, ST->getName(),
895 writeStringRecord(Stream, bitc::TYPE_CODE_STRUCT_NAME, ST->getName(),
925896 StructNameAbbrev);
926897 }
927898 break;
10721043 /// Write a record that will eventually hold the word offset of the
10731044 /// module-level VST. For now the offset is 0, which will be backpatched
10741045 /// after the real VST is written. Saves the bit offset to backpatch.
1075 void BitcodeWriter::writeValueSymbolTableForwardDecl() {
1046 void BitcodeWriterBase::writeValueSymbolTableForwardDecl() {
10761047 // Write a placeholder value in for the offset of the real VST,
10771048 // which is written after the function blocks so that it can include
10781049 // the offset of each function. The placeholder offset will be
11191090 void ModuleBitcodeWriter::writeModuleInfo() {
11201091 // Emit various pieces of data attached to a module.
11211092 if (!M.getTargetTriple().empty())
1122 writeStringRecord(bitc::MODULE_CODE_TRIPLE, M.getTargetTriple(),
1093 writeStringRecord(Stream, bitc::MODULE_CODE_TRIPLE, M.getTargetTriple(),
11231094 0 /*TODO*/);
11241095 const std::string &DL = M.getDataLayoutStr();
11251096 if (!DL.empty())
1126 writeStringRecord(bitc::MODULE_CODE_DATALAYOUT, DL, 0 /*TODO*/);
1097 writeStringRecord(Stream, bitc::MODULE_CODE_DATALAYOUT, DL, 0 /*TODO*/);
11271098 if (!M.getModuleInlineAsm().empty())
1128 writeStringRecord(bitc::MODULE_CODE_ASM, M.getModuleInlineAsm(),
1099 writeStringRecord(Stream, bitc::MODULE_CODE_ASM, M.getModuleInlineAsm(),
11291100 0 /*TODO*/);
11301101
11311102 // Emit information about sections and GC, computing how many there are. Also
11411112 // Give section names unique ID's.
11421113 unsigned &Entry = SectionMap[GV.getSection()];
11431114 if (!Entry) {
1144 writeStringRecord(bitc::MODULE_CODE_SECTIONNAME, GV.getSection(),
1115 writeStringRecord(Stream, bitc::MODULE_CODE_SECTIONNAME, GV.getSection(),
11451116 0 /*TODO*/);
11461117 Entry = SectionMap.size();
11471118 }
11531124 // Give section names unique ID's.
11541125 unsigned &Entry = SectionMap[F.getSection()];
11551126 if (!Entry) {
1156 writeStringRecord(bitc::MODULE_CODE_SECTIONNAME, F.getSection(),
1127 writeStringRecord(Stream, bitc::MODULE_CODE_SECTIONNAME, F.getSection(),
11571128 0 /*TODO*/);
11581129 Entry = SectionMap.size();
11591130 }
11621133 // Same for GC names.
11631134 unsigned &Entry = GCMap[F.getGC()];
11641135 if (!Entry) {
1165 writeStringRecord(bitc::MODULE_CODE_GCNAME, F.getGC(), 0 /*TODO*/);
1136 writeStringRecord(Stream, bitc::MODULE_CODE_GCNAME, F.getGC(),
1137 0 /*TODO*/);
11661138 Entry = GCMap.size();
11671139 }
11681140 }
27602732 // Get the offset of the VST we are writing, and backpatch it into
27612733 // the VST forward declaration record.
27622734 uint64_t VSTOffset = Stream.GetCurrentBitNo();
2763 // The BitcodeStartBit was the stream offset of the actual bitcode
2764 // (e.g. excluding any initial darwin header).
2735 // The BitcodeStartBit was the stream offset of the identification block.
27652736 VSTOffset -= bitcodeStartBit();
27662737 assert((VSTOffset & 31) == 0 && "VST block not 32-bit aligned");
2767 Stream.BackpatchWord(VSTOffsetPlaceholder, VSTOffset / 32);
2738 // Note that we add 1 here because the offset is relative to one word
2739 // before the start of the identification block, which was historically
2740 // always the start of the regular bitcode header.
2741 Stream.BackpatchWord(VSTOffsetPlaceholder, VSTOffset / 32 + 1);
27682742 }
27692743
27702744 Stream.EnterSubblock(bitc::VALUE_SYMTAB_BLOCK_ID, 4);
28522826 // actual bitcode written to the stream).
28532827 uint64_t BitcodeIndex = (*FunctionToBitcodeIndex)[F] - bitcodeStartBit();
28542828 assert((BitcodeIndex & 31) == 0 && "function block not 32-bit aligned");
2855 NameVals.push_back(BitcodeIndex / 32);
2829 // Note that we add 1 here because the offset is relative to one word
2830 // before the start of the identification block, which was historically
2831 // always the start of the regular bitcode header.
2832 NameVals.push_back(BitcodeIndex / 32 + 1);
28562833
28572834 Code = bitc::VST_CODE_FNENTRY;
28582835 AbbrevToUse = FnEntry8BitAbbrev;
36163593 Stream.ExitBlock();
36173594 }
36183595
3619 void ModuleBitcodeWriter::writeIdentificationBlock() {
3596 /// Create the "IDENTIFICATION_BLOCK_ID" containing a single string with the
3597 /// current llvm version, and a record for the epoch number.
3598 void writeIdentificationBlock(BitstreamWriter &Stream) {
36203599 Stream.EnterSubblock(bitc::IDENTIFICATION_BLOCK_ID, 5);
36213600
36223601 // Write the "user readable" string identifying the bitcode producer
36253604 Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
36263605 Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Char6));
36273606 auto StringAbbrev = Stream.EmitAbbrev(Abbv);
3628 writeStringRecord(bitc::IDENTIFICATION_CODE_STRING,
3607 writeStringRecord(Stream, bitc::IDENTIFICATION_CODE_STRING,
36293608 "LLVM" LLVM_VERSION_STRING, StringAbbrev);
36303609
36313610 // Write the epoch version
36543633 Stream.EmitRecord(bitc::MODULE_CODE_HASH, Vals);
36553634 }
36563635
3657 void BitcodeWriter::write() {
3658 // Emit the file header first.
3659 writeBitcodeHeader();
3660
3661 writeBlocks();
3662 }
3663
3664 void ModuleBitcodeWriter::writeBlocks() {
3665 writeIdentificationBlock();
3666 writeModule();
3667 }
3668
3669 void IndexBitcodeWriter::writeBlocks() {
3670 // Index contains only a single outer (module) block.
3671 writeIndex();
3672 }
3673
3674 void ModuleBitcodeWriter::writeModule() {
3636 void ModuleBitcodeWriter::write() {
3637 writeIdentificationBlock(Stream);
3638
36753639 Stream.EnterSubblock(bitc::MODULE_BLOCK_ID, 3);
36763640 size_t BlockStartPos = Buffer.size();
36773641
38003764 }
38013765
38023766 /// Helper to write the header common to all bitcode files.
3803 void BitcodeWriter::writeBitcodeHeader() {
3767 static void writeBitcodeHeader(BitstreamWriter &Stream) {
38043768 // Emit the file header.
38053769 Stream.Emit((unsigned)'B', 8);
38063770 Stream.Emit((unsigned)'C', 8);
38083772 Stream.Emit(0xC, 4);
38093773 Stream.Emit(0xE, 4);
38103774 Stream.Emit(0xD, 4);
3775 }
3776
3777 BitcodeWriter::BitcodeWriter(SmallVectorImpl &Buffer)
3778 : Buffer(Buffer), Stream(new BitstreamWriter(Buffer)) {
3779 writeBitcodeHeader(*Stream);
3780 }
3781
3782 BitcodeWriter::~BitcodeWriter() = default;
3783
3784 void BitcodeWriter::writeModule(const Module *M,
3785 bool ShouldPreserveUseListOrder,
3786 const ModuleSummaryIndex *Index,
3787 bool GenerateHash) {
3788 ModuleBitcodeWriter ModuleWriter(
3789 M, Buffer, *Stream, ShouldPreserveUseListOrder, Index, GenerateHash);
3790 ModuleWriter.write();
38113791 }
38123792
38133793 /// WriteBitcodeToFile - Write the specified module to the specified output
38253805 if (TT.isOSDarwin() || TT.isOSBinFormatMachO())
38263806 Buffer.insert(Buffer.begin(), BWH_HeaderSize, 0);
38273807
3828 // Emit the module into the buffer.
3829 ModuleBitcodeWriter ModuleWriter(M, Buffer, ShouldPreserveUseListOrder, Index,
3830 GenerateHash);
3831 ModuleWriter.write();
3808 BitcodeWriter Writer(Buffer);
3809 Writer.writeModule(M, ShouldPreserveUseListOrder, Index, GenerateHash);
38323810
38333811 if (TT.isOSDarwin() || TT.isOSBinFormatMachO())
38343812 emitDarwinBCHeaderAndTrailer(Buffer, TT);
38373815 Out.write((char*)&Buffer.front(), Buffer.size());
38383816 }
38393817
3840 void IndexBitcodeWriter::writeIndex() {
3818 void IndexBitcodeWriter::write() {
38413819 Stream.EnterSubblock(bitc::MODULE_BLOCK_ID, 3);
38423820
38433821 SmallVector Vals;
38713849 SmallVector Buffer;
38723850 Buffer.reserve(256 * 1024);
38733851
3874 IndexBitcodeWriter IndexWriter(Buffer, Index, ModuleToSummariesForIndex);
3852 BitstreamWriter Stream(Buffer);
3853 writeBitcodeHeader(Stream);
3854
3855 IndexBitcodeWriter IndexWriter(Stream, Index, ModuleToSummariesForIndex);
38753856 IndexWriter.write();
38763857
38773858 Out.write((char *)&Buffer.front(), Buffer.size());
0 define void @f2() {
1 ret void
2 }
0 ; RUN: llvm-cat -o %t %s %S/Inputs/multi-module.ll
1 ; RUN: not llvm-dis -o - %t 2>&1 | FileCheck --check-prefix=ERROR %s
2 ; ERROR: Expected a single module
3
4 ; FIXME: Introduce a tool for extracting modules from bitcode and use it here.
5 ; For now we can at least check that the bitcode contains multiple modules.
6 ; RUN: llvm-bcanalyzer -dump %t | FileCheck --check-prefix=BCA %s
7
8 ; RUN: llvm-as -o %t1 %s
9 ; RUN: llvm-as -o %t2 %S/Inputs/multi-module.ll
10 ; RUN: llvm-cat -o %t %t1 %t2
11 ; RUN: not llvm-dis -o - %t 2>&1 | FileCheck --check-prefix=ERROR %s
12 ; RUN: llvm-bcanalyzer -dump %t | FileCheck --check-prefix=BCA %s
13
14 ; RUN: llvm-cat -b -o %t %t1 %t2
15 ; RUN: not llvm-dis -o - %t 2>&1 | FileCheck --check-prefix=ERROR %s
16 ; RUN: llvm-bcanalyzer -dump %t | FileCheck --check-prefix=BCA %s
17
18 ; RUN: llvm-cat -b -o %t3 %t %t
19 ; RUN: not llvm-dis -o - %t3 2>&1 | FileCheck --check-prefix=ERROR %s
20 ; RUN: llvm-bcanalyzer -dump %t3 | FileCheck --check-prefix=BCA4 %s
21
22 ; BCA:
23 ; BCA:
24 ; BCA:
25 ; BCA:
26
27 ; BCA4:
28 ; BCA4:
29 ; BCA4:
30 ; BCA4:
31 ; BCA4:
32 ; BCA4:
33 ; BCA4:
34 ; BCA4:
35
36 define void @f1() {
37 ret void
38 }
3131 llvm-as
3232 llvm-bcanalyzer
3333 llvm-c-test
34 llvm-cat
3435 llvm-cxxfilt
3536 llvm-config
3637 llvm-cov
2323 llvm-ar
2424 llvm-as
2525 llvm-bcanalyzer
26 llvm-cat
2627 llvm-cov
2728 llvm-diff
2829 llvm-dis
0 set(LLVM_LINK_COMPONENTS
1 IRReader
2 BitWriter
3 Core
4 Support
5 )
6
7 add_llvm_tool(llvm-cat
8 llvm-cat.cpp
9 )
0 ;===- ./tools/llvm-cat/LLVMBuild.txt ---------------------------*- Conf -*--===;
1 ;
2 ; The LLVM Compiler Infrastructure
3 ;
4 ; This file is distributed under the University of Illinois Open Source
5 ; License. See LICENSE.TXT for details.
6 ;
7 ;===------------------------------------------------------------------------===;
8 ;
9 ; This is an LLVMBuild description file for the components in this subdirectory.
10 ;
11 ; For more information on the LLVMBuild system, please see:
12 ;
13 ; http://llvm.org/docs/LLVMBuild.html
14 ;
15 ;===------------------------------------------------------------------------===;
16
17 [component_0]
18 type = Tool
19 name = llvm-cat
20 parent = Tools
21 required_libraries = AsmParser BitWriter
0 //===-- llvm-cat.cpp - LLVM module concatenation utility ------------------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This program is for testing features that rely on multi-module bitcode files.
10 // It takes a list of input modules and uses them to create a multi-module
11 // bitcode file.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "llvm/Bitcode/BitcodeReader.h"
16 #include "llvm/Bitcode/BitcodeWriter.h"
17 #include "llvm/IRReader/IRReader.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/FileSystem.h"
20
21 using namespace llvm;
22
23 static cl::opt
24 BinaryCat("b", cl::desc("Whether to perform binary concatenation"));
25
26 static cl::opt OutputFilename("o", cl::Required,
27 cl::desc("Output filename"),
28 cl::value_desc("filename"));
29
30 static cl::list InputFilenames(cl::Positional, cl::OneOrMore,
31 cl::desc(""));
32
33 int main(int argc, char **argv) {
34 cl::ParseCommandLineOptions(argc, argv, "Module concatenation");
35
36 ExitOnError ExitOnErr("llvm-cat: ");
37 LLVMContext Context;
38
39 SmallVector Buffer;
40 BitcodeWriter Writer(Buffer);
41 if (BinaryCat) {
42 for (std::string InputFilename : InputFilenames) {
43 std::unique_ptr MB = ExitOnErr(
44 errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename)));
45 std::vector Mods = ExitOnErr(getBitcodeModuleList(*MB));
46 for (auto &BitcodeMod : Mods)
47 Buffer.insert(Buffer.end(), BitcodeMod.getBuffer().begin(),
48 BitcodeMod.getBuffer().end());
49 }
50 } else {
51 for (std::string InputFilename : InputFilenames) {
52 SMDiagnostic Err;
53 std::unique_ptr M = parseIRFile(InputFilename, Err, Context);
54 if (!M) {
55 Err.print(argv[0], errs());
56 return 1;
57 }
58 Writer.writeModule(M.get());
59 }
60 }
61
62 std::error_code EC;
63 raw_fd_ostream OS(OutputFilename, EC, sys::fs::OpenFlags::F_None);
64 if (EC) {
65 llvm::errs() << argv[0] << ": cannot open " << OutputFilename
66 << " for writing: " << EC.message();
67 return 1;
68 }
69
70 OS.write(Buffer.data(), Buffer.size());
71 return 0;
72 }