1 //===-- llc.cpp - Implement the LLVM Native Code Generator ----------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This is the llc code generator driver. It provides a convenient 10 // command-line interface for generating native assembly-language code 11 // or C code, given LLVM bitcode. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "llvm/ADT/STLExtras.h" 16 #include "llvm/ADT/Triple.h" 17 #include "llvm/Analysis/TargetLibraryInfo.h" 18 #include "llvm/CodeGen/CommandFlags.inc" 19 #include "llvm/CodeGen/LinkAllAsmWriterComponents.h" 20 #include "llvm/CodeGen/LinkAllCodegenComponents.h" 21 #include "llvm/CodeGen/MIRParser/MIRParser.h" 22 #include "llvm/CodeGen/MachineFunctionPass.h" 23 #include "llvm/CodeGen/MachineModuleInfo.h" 24 #include "llvm/CodeGen/TargetPassConfig.h" 25 #include "llvm/CodeGen/TargetSubtargetInfo.h" 26 #include "llvm/IR/AutoUpgrade.h" 27 #include "llvm/IR/DataLayout.h" 28 #include "llvm/IR/DiagnosticInfo.h" 29 #include "llvm/IR/DiagnosticPrinter.h" 30 #include "llvm/IR/IRPrintingPasses.h" 31 #include "llvm/IR/LLVMContext.h" 32 #include "llvm/IR/LegacyPassManager.h" 33 #include "llvm/IR/Module.h" 34 #include "llvm/IR/RemarkStreamer.h" 35 #include "llvm/IR/Verifier.h" 36 #include "llvm/IRReader/IRReader.h" 37 #include "llvm/MC/SubtargetFeature.h" 38 #include "llvm/Pass.h" 39 #include "llvm/Support/CommandLine.h" 40 #include "llvm/Support/Debug.h" 41 #include "llvm/Support/FileSystem.h" 42 #include "llvm/Support/FormattedStream.h" 43 #include "llvm/Support/Host.h" 44 #include "llvm/Support/InitLLVM.h" 45 #include "llvm/Support/ManagedStatic.h" 46 #include "llvm/Support/PluginLoader.h" 47 #include "llvm/Support/SourceMgr.h" 48 #include "llvm/Support/TargetRegistry.h" 49 #include "llvm/Support/TargetSelect.h" 50 #include "llvm/Support/ToolOutputFile.h" 51 #include "llvm/Support/WithColor.h" 52 #include "llvm/Target/TargetMachine.h" 53 #include "llvm/Transforms/Utils/Cloning.h" 54 #include <memory> 55 using namespace llvm; 56 57 // General options for llc. Other pass-specific options are specified 58 // within the corresponding llc passes, and target-specific options 59 // and back-end code generation options are specified with the target machine. 60 // 61 static cl::opt<std::string> 62 InputFilename(cl::Positional, cl::desc("<input bitcode>"), cl::init("-")); 63 64 static cl::opt<std::string> 65 InputLanguage("x", cl::desc("Input language ('ir' or 'mir')")); 66 67 static cl::opt<std::string> 68 OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename")); 69 70 static cl::opt<std::string> 71 SplitDwarfOutputFile("split-dwarf-output", 72 cl::desc(".dwo output filename"), 73 cl::value_desc("filename")); 74 75 static cl::opt<unsigned> 76 TimeCompilations("time-compilations", cl::Hidden, cl::init(1u), 77 cl::value_desc("N"), 78 cl::desc("Repeat compilation N times for timing")); 79 80 static cl::opt<bool> 81 NoIntegratedAssembler("no-integrated-as", cl::Hidden, 82 cl::desc("Disable integrated assembler")); 83 84 static cl::opt<bool> 85 PreserveComments("preserve-as-comments", cl::Hidden, 86 cl::desc("Preserve Comments in outputted assembly"), 87 cl::init(true)); 88 89 // Determine optimization level. 90 static cl::opt<char> 91 OptLevel("O", 92 cl::desc("Optimization level. [-O0, -O1, -O2, or -O3] " 93 "(default = '-O2')"), 94 cl::Prefix, 95 cl::ZeroOrMore, 96 cl::init(' ')); 97 98 static cl::opt<std::string> 99 TargetTriple("mtriple", cl::desc("Override target triple for module")); 100 101 static cl::opt<std::string> SplitDwarfFile( 102 "split-dwarf-file", 103 cl::desc( 104 "Specify the name of the .dwo file to encode in the DWARF output")); 105 106 static cl::opt<bool> NoVerify("disable-verify", cl::Hidden, 107 cl::desc("Do not verify input module")); 108 109 static cl::opt<bool> DisableSimplifyLibCalls("disable-simplify-libcalls", 110 cl::desc("Disable simplify-libcalls")); 111 112 static cl::opt<bool> ShowMCEncoding("show-mc-encoding", cl::Hidden, 113 cl::desc("Show encoding in .s output")); 114 115 static cl::opt<bool> EnableDwarfDirectory( 116 "enable-dwarf-directory", cl::Hidden, 117 cl::desc("Use .file directives with an explicit directory.")); 118 119 static cl::opt<bool> AsmVerbose("asm-verbose", 120 cl::desc("Add comments to directives."), 121 cl::init(true)); 122 123 static cl::opt<bool> 124 CompileTwice("compile-twice", cl::Hidden, 125 cl::desc("Run everything twice, re-using the same pass " 126 "manager and verify the result is the same."), 127 cl::init(false)); 128 129 static cl::opt<bool> DiscardValueNames( 130 "discard-value-names", 131 cl::desc("Discard names from Value (other than GlobalValue)."), 132 cl::init(false), cl::Hidden); 133 134 static cl::list<std::string> IncludeDirs("I", cl::desc("include search path")); 135 136 static cl::opt<bool> RemarksWithHotness( 137 "pass-remarks-with-hotness", 138 cl::desc("With PGO, include profile count in optimization remarks"), 139 cl::Hidden); 140 141 static cl::opt<unsigned> 142 RemarksHotnessThreshold("pass-remarks-hotness-threshold", 143 cl::desc("Minimum profile count required for " 144 "an optimization remark to be output"), 145 cl::Hidden); 146 147 static cl::opt<std::string> 148 RemarksFilename("pass-remarks-output", 149 cl::desc("Output filename for pass remarks"), 150 cl::value_desc("filename")); 151 152 static cl::opt<std::string> 153 RemarksPasses("pass-remarks-filter", 154 cl::desc("Only record optimization remarks from passes whose " 155 "names match the given regular expression"), 156 cl::value_desc("regex")); 157 158 static cl::opt<std::string> RemarksFormat( 159 "pass-remarks-format", 160 cl::desc("The format used for serializing remarks (default: YAML)"), 161 cl::value_desc("format"), cl::init("yaml")); 162 163 namespace { 164 static ManagedStatic<std::vector<std::string>> RunPassNames; 165 166 struct RunPassOption { 167 void operator=(const std::string &Val) const { 168 if (Val.empty()) 169 return; 170 SmallVector<StringRef, 8> PassNames; 171 StringRef(Val).split(PassNames, ',', -1, false); 172 for (auto PassName : PassNames) 173 RunPassNames->push_back(PassName); 174 } 175 }; 176 } 177 178 static RunPassOption RunPassOpt; 179 180 static cl::opt<RunPassOption, true, cl::parser<std::string>> RunPass( 181 "run-pass", 182 cl::desc("Run compiler only for specified passes (comma separated list)"), 183 cl::value_desc("pass-name"), cl::ZeroOrMore, cl::location(RunPassOpt)); 184 185 static int compileModule(char **, LLVMContext &); 186 187 static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName, 188 Triple::OSType OS, 189 const char *ProgName) { 190 // If we don't yet have an output filename, make one. 191 if (OutputFilename.empty()) { 192 if (InputFilename == "-") 193 OutputFilename = "-"; 194 else { 195 // If InputFilename ends in .bc or .ll, remove it. 196 StringRef IFN = InputFilename; 197 if (IFN.endswith(".bc") || IFN.endswith(".ll")) 198 OutputFilename = IFN.drop_back(3); 199 else if (IFN.endswith(".mir")) 200 OutputFilename = IFN.drop_back(4); 201 else 202 OutputFilename = IFN; 203 204 switch (FileType) { 205 case TargetMachine::CGFT_AssemblyFile: 206 if (TargetName[0] == 'c') { 207 if (TargetName[1] == 0) 208 OutputFilename += ".cbe.c"; 209 else if (TargetName[1] == 'p' && TargetName[2] == 'p') 210 OutputFilename += ".cpp"; 211 else 212 OutputFilename += ".s"; 213 } else 214 OutputFilename += ".s"; 215 break; 216 case TargetMachine::CGFT_ObjectFile: 217 if (OS == Triple::Win32) 218 OutputFilename += ".obj"; 219 else 220 OutputFilename += ".o"; 221 break; 222 case TargetMachine::CGFT_Null: 223 OutputFilename += ".null"; 224 break; 225 } 226 } 227 } 228 229 // Decide if we need "binary" output. 230 bool Binary = false; 231 switch (FileType) { 232 case TargetMachine::CGFT_AssemblyFile: 233 break; 234 case TargetMachine::CGFT_ObjectFile: 235 case TargetMachine::CGFT_Null: 236 Binary = true; 237 break; 238 } 239 240 // Open the file. 241 std::error_code EC; 242 sys::fs::OpenFlags OpenFlags = sys::fs::F_None; 243 if (!Binary) 244 OpenFlags |= sys::fs::F_Text; 245 auto FDOut = llvm::make_unique<ToolOutputFile>(OutputFilename, EC, OpenFlags); 246 if (EC) { 247 WithColor::error() << EC.message() << '\n'; 248 return nullptr; 249 } 250 251 return FDOut; 252 } 253 254 struct LLCDiagnosticHandler : public DiagnosticHandler { 255 bool *HasError; 256 LLCDiagnosticHandler(bool *HasErrorPtr) : HasError(HasErrorPtr) {} 257 bool handleDiagnostics(const DiagnosticInfo &DI) override { 258 if (DI.getSeverity() == DS_Error) 259 *HasError = true; 260 261 if (auto *Remark = dyn_cast<DiagnosticInfoOptimizationBase>(&DI)) 262 if (!Remark->isEnabled()) 263 return true; 264 265 DiagnosticPrinterRawOStream DP(errs()); 266 errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": "; 267 DI.print(DP); 268 errs() << "\n"; 269 return true; 270 } 271 }; 272 273 static void InlineAsmDiagHandler(const SMDiagnostic &SMD, void *Context, 274 unsigned LocCookie) { 275 bool *HasError = static_cast<bool *>(Context); 276 if (SMD.getKind() == SourceMgr::DK_Error) 277 *HasError = true; 278 279 SMD.print(nullptr, errs()); 280 281 // For testing purposes, we print the LocCookie here. 282 if (LocCookie) 283 WithColor::note() << "!srcloc = " << LocCookie << "\n"; 284 } 285 286 // main - Entry point for the llc compiler. 287 // 288 int main(int argc, char **argv) { 289 InitLLVM X(argc, argv); 290 291 // Enable debug stream buffering. 292 EnableDebugBuffering = true; 293 294 LLVMContext Context; 295 296 // Initialize targets first, so that --version shows registered targets. 297 InitializeAllTargets(); 298 InitializeAllTargetMCs(); 299 InitializeAllAsmPrinters(); 300 InitializeAllAsmParsers(); 301 302 // Initialize codegen and IR passes used by llc so that the -print-after, 303 // -print-before, and -stop-after options work. 304 PassRegistry *Registry = PassRegistry::getPassRegistry(); 305 initializeCore(*Registry); 306 initializeCodeGen(*Registry); 307 initializeLoopStrengthReducePass(*Registry); 308 initializeLowerIntrinsicsPass(*Registry); 309 initializeEntryExitInstrumenterPass(*Registry); 310 initializePostInlineEntryExitInstrumenterPass(*Registry); 311 initializeUnreachableBlockElimLegacyPassPass(*Registry); 312 initializeConstantHoistingLegacyPassPass(*Registry); 313 initializeScalarOpts(*Registry); 314 initializeVectorization(*Registry); 315 initializeScalarizeMaskedMemIntrinPass(*Registry); 316 initializeExpandReductionsPass(*Registry); 317 initializeHardwareLoopsPass(*Registry); 318 319 // Initialize debugging passes. 320 initializeScavengerTestPass(*Registry); 321 322 // Register the target printer for --version. 323 cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); 324 325 cl::ParseCommandLineOptions(argc, argv, "llvm system compiler\n"); 326 327 Context.setDiscardValueNames(DiscardValueNames); 328 329 // Set a diagnostic handler that doesn't exit on the first error 330 bool HasError = false; 331 Context.setDiagnosticHandler( 332 llvm::make_unique<LLCDiagnosticHandler>(&HasError)); 333 Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError); 334 335 Expected<std::unique_ptr<ToolOutputFile>> RemarksFileOrErr = 336 setupOptimizationRemarks(Context, RemarksFilename, RemarksPasses, 337 RemarksFormat, RemarksWithHotness, 338 RemarksHotnessThreshold); 339 if (Error E = RemarksFileOrErr.takeError()) { 340 WithColor::error(errs(), argv[0]) << toString(std::move(E)) << '\n'; 341 return 1; 342 } 343 std::unique_ptr<ToolOutputFile> RemarksFile = std::move(*RemarksFileOrErr); 344 345 if (InputLanguage != "" && InputLanguage != "ir" && 346 InputLanguage != "mir") { 347 WithColor::error(errs(), argv[0]) 348 << "input language must be '', 'IR' or 'MIR'\n"; 349 return 1; 350 } 351 352 // Compile the module TimeCompilations times to give better compile time 353 // metrics. 354 for (unsigned I = TimeCompilations; I; --I) 355 if (int RetVal = compileModule(argv, Context)) 356 return RetVal; 357 358 if (RemarksFile) 359 RemarksFile->keep(); 360 return 0; 361 } 362 363 static bool addPass(PassManagerBase &PM, const char *argv0, 364 StringRef PassName, TargetPassConfig &TPC) { 365 if (PassName == "none") 366 return false; 367 368 const PassRegistry *PR = PassRegistry::getPassRegistry(); 369 const PassInfo *PI = PR->getPassInfo(PassName); 370 if (!PI) { 371 WithColor::error(errs(), argv0) 372 << "run-pass " << PassName << " is not registered.\n"; 373 return true; 374 } 375 376 Pass *P; 377 if (PI->getNormalCtor()) 378 P = PI->getNormalCtor()(); 379 else { 380 WithColor::error(errs(), argv0) 381 << "cannot create pass: " << PI->getPassName() << "\n"; 382 return true; 383 } 384 std::string Banner = std::string("After ") + std::string(P->getPassName()); 385 PM.add(P); 386 TPC.printAndVerify(Banner); 387 388 return false; 389 } 390 391 static int compileModule(char **argv, LLVMContext &Context) { 392 // Load the module to be compiled... 393 SMDiagnostic Err; 394 std::unique_ptr<Module> M; 395 std::unique_ptr<MIRParser> MIR; 396 Triple TheTriple; 397 398 bool SkipModule = MCPU == "help" || 399 (!MAttrs.empty() && MAttrs.front() == "help"); 400 401 // If user just wants to list available options, skip module loading 402 if (!SkipModule) { 403 if (InputLanguage == "mir" || 404 (InputLanguage == "" && StringRef(InputFilename).endswith(".mir"))) { 405 MIR = createMIRParserFromFile(InputFilename, Err, Context); 406 if (MIR) 407 M = MIR->parseIRModule(); 408 } else 409 M = parseIRFile(InputFilename, Err, Context, false); 410 if (!M) { 411 Err.print(argv[0], WithColor::error(errs(), argv[0])); 412 return 1; 413 } 414 415 // If we are supposed to override the target triple, do so now. 416 if (!TargetTriple.empty()) 417 M->setTargetTriple(Triple::normalize(TargetTriple)); 418 TheTriple = Triple(M->getTargetTriple()); 419 } else { 420 TheTriple = Triple(Triple::normalize(TargetTriple)); 421 } 422 423 if (TheTriple.getTriple().empty()) 424 TheTriple.setTriple(sys::getDefaultTargetTriple()); 425 426 // Get the target specific parser. 427 std::string Error; 428 const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TheTriple, 429 Error); 430 if (!TheTarget) { 431 WithColor::error(errs(), argv[0]) << Error; 432 return 1; 433 } 434 435 std::string CPUStr = getCPUStr(), FeaturesStr = getFeaturesStr(); 436 437 CodeGenOpt::Level OLvl = CodeGenOpt::Default; 438 switch (OptLevel) { 439 default: 440 WithColor::error(errs(), argv[0]) << "invalid optimization level.\n"; 441 return 1; 442 case ' ': break; 443 case '0': OLvl = CodeGenOpt::None; break; 444 case '1': OLvl = CodeGenOpt::Less; break; 445 case '2': OLvl = CodeGenOpt::Default; break; 446 case '3': OLvl = CodeGenOpt::Aggressive; break; 447 } 448 449 TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); 450 Options.DisableIntegratedAS = NoIntegratedAssembler; 451 Options.MCOptions.ShowMCEncoding = ShowMCEncoding; 452 Options.MCOptions.MCUseDwarfDirectory = EnableDwarfDirectory; 453 Options.MCOptions.AsmVerbose = AsmVerbose; 454 Options.MCOptions.PreserveAsmComments = PreserveComments; 455 Options.MCOptions.IASSearchPaths = IncludeDirs; 456 Options.MCOptions.SplitDwarfFile = SplitDwarfFile; 457 458 std::unique_ptr<TargetMachine> Target(TheTarget->createTargetMachine( 459 TheTriple.getTriple(), CPUStr, FeaturesStr, Options, getRelocModel(), 460 getCodeModel(), OLvl)); 461 462 assert(Target && "Could not allocate target machine!"); 463 464 // If we don't have a module then just exit now. We do this down 465 // here since the CPU/Feature help is underneath the target machine 466 // creation. 467 if (SkipModule) 468 return 0; 469 470 assert(M && "Should have exited if we didn't have a module!"); 471 if (FloatABIForCalls != FloatABI::Default) 472 Options.FloatABIType = FloatABIForCalls; 473 474 // Figure out where we are going to send the output. 475 std::unique_ptr<ToolOutputFile> Out = 476 GetOutputStream(TheTarget->getName(), TheTriple.getOS(), argv[0]); 477 if (!Out) return 1; 478 479 std::unique_ptr<ToolOutputFile> DwoOut; 480 if (!SplitDwarfOutputFile.empty()) { 481 std::error_code EC; 482 DwoOut = llvm::make_unique<ToolOutputFile>(SplitDwarfOutputFile, EC, 483 sys::fs::F_None); 484 if (EC) { 485 WithColor::error(errs(), argv[0]) << EC.message() << '\n'; 486 return 1; 487 } 488 } 489 490 // Build up all of the passes that we want to do to the module. 491 legacy::PassManager PM; 492 493 // Add an appropriate TargetLibraryInfo pass for the module's triple. 494 TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple())); 495 496 // The -disable-simplify-libcalls flag actually disables all builtin optzns. 497 if (DisableSimplifyLibCalls) 498 TLII.disableAllFunctions(); 499 PM.add(new TargetLibraryInfoWrapperPass(TLII)); 500 501 // Add the target data from the target machine, if it exists, or the module. 502 M->setDataLayout(Target->createDataLayout()); 503 504 // This needs to be done after setting datalayout since it calls verifier 505 // to check debug info whereas verifier relies on correct datalayout. 506 UpgradeDebugInfo(*M); 507 508 // Verify module immediately to catch problems before doInitialization() is 509 // called on any passes. 510 if (!NoVerify && verifyModule(*M, &errs())) { 511 std::string Prefix = 512 (Twine(argv[0]) + Twine(": ") + Twine(InputFilename)).str(); 513 WithColor::error(errs(), Prefix) << "input module is broken!\n"; 514 return 1; 515 } 516 517 // Override function attributes based on CPUStr, FeaturesStr, and command line 518 // flags. 519 setFunctionAttributes(CPUStr, FeaturesStr, *M); 520 521 if (RelaxAll.getNumOccurrences() > 0 && 522 FileType != TargetMachine::CGFT_ObjectFile) 523 WithColor::warning(errs(), argv[0]) 524 << ": warning: ignoring -mc-relax-all because filetype != obj"; 525 526 { 527 raw_pwrite_stream *OS = &Out->os(); 528 529 // Manually do the buffering rather than using buffer_ostream, 530 // so we can memcmp the contents in CompileTwice mode 531 SmallVector<char, 0> Buffer; 532 std::unique_ptr<raw_svector_ostream> BOS; 533 if ((FileType != TargetMachine::CGFT_AssemblyFile && 534 !Out->os().supportsSeeking()) || 535 CompileTwice) { 536 BOS = make_unique<raw_svector_ostream>(Buffer); 537 OS = BOS.get(); 538 } 539 540 const char *argv0 = argv[0]; 541 LLVMTargetMachine &LLVMTM = static_cast<LLVMTargetMachine&>(*Target); 542 MachineModuleInfo *MMI = new MachineModuleInfo(&LLVMTM); 543 544 // Construct a custom pass pipeline that starts after instruction 545 // selection. 546 if (!RunPassNames->empty()) { 547 if (!MIR) { 548 WithColor::warning(errs(), argv[0]) 549 << "run-pass is for .mir file only.\n"; 550 return 1; 551 } 552 TargetPassConfig &TPC = *LLVMTM.createPassConfig(PM); 553 if (TPC.hasLimitedCodeGenPipeline()) { 554 WithColor::warning(errs(), argv[0]) 555 << "run-pass cannot be used with " 556 << TPC.getLimitedCodeGenPipelineReason(" and ") << ".\n"; 557 return 1; 558 } 559 560 TPC.setDisableVerify(NoVerify); 561 PM.add(&TPC); 562 PM.add(MMI); 563 TPC.printAndVerify(""); 564 for (const std::string &RunPassName : *RunPassNames) { 565 if (addPass(PM, argv0, RunPassName, TPC)) 566 return 1; 567 } 568 TPC.setInitialized(); 569 PM.add(createPrintMIRPass(*OS)); 570 PM.add(createFreeMachineFunctionPass()); 571 } else if (Target->addPassesToEmitFile(PM, *OS, 572 DwoOut ? &DwoOut->os() : nullptr, 573 FileType, NoVerify, MMI)) { 574 WithColor::warning(errs(), argv[0]) 575 << "target does not support generation of this" 576 << " file type!\n"; 577 return 1; 578 } 579 580 if (MIR) { 581 assert(MMI && "Forgot to create MMI?"); 582 if (MIR->parseMachineFunctions(*M, *MMI)) 583 return 1; 584 } 585 586 // Before executing passes, print the final values of the LLVM options. 587 cl::PrintOptionValues(); 588 589 // If requested, run the pass manager over the same module again, 590 // to catch any bugs due to persistent state in the passes. Note that 591 // opt has the same functionality, so it may be worth abstracting this out 592 // in the future. 593 SmallVector<char, 0> CompileTwiceBuffer; 594 if (CompileTwice) { 595 std::unique_ptr<Module> M2(llvm::CloneModule(*M)); 596 PM.run(*M2); 597 CompileTwiceBuffer = Buffer; 598 Buffer.clear(); 599 } 600 601 PM.run(*M); 602 603 auto HasError = 604 ((const LLCDiagnosticHandler *)(Context.getDiagHandlerPtr()))->HasError; 605 if (*HasError) 606 return 1; 607 608 // Compare the two outputs and make sure they're the same 609 if (CompileTwice) { 610 if (Buffer.size() != CompileTwiceBuffer.size() || 611 (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) != 612 0)) { 613 errs() 614 << "Running the pass manager twice changed the output.\n" 615 "Writing the result of the second run to the specified output\n" 616 "To generate the one-run comparison binary, just run without\n" 617 "the compile-twice option\n"; 618 Out->os() << Buffer; 619 Out->keep(); 620 return 1; 621 } 622 } 623 624 if (BOS) { 625 Out->os() << Buffer; 626 } 627 } 628 629 // Declare success. 630 Out->keep(); 631 if (DwoOut) 632 DwoOut->keep(); 633 634 return 0; 635 } 636