llvm.org GIT mirror llvm / 47c0d49
Output optimization remarks in YAML (Re-committed after moving the template specialization under the yaml namespace. GCC was complaining about this.) This allows various presentation of this data using an external tool. This was first recommended here[1]. As an example, consider this module: 1 int foo(); 2 int bar(); 3 4 int baz() { 5 return foo() + bar(); 6 } The inliner generates these missed-optimization remarks today (the hotness information is pulled from PGO): remark: /tmp/s.c:5:10: foo will not be inlined into baz (hotness: 30) remark: /tmp/s.c:5:18: bar will not be inlined into baz (hotness: 30) Now with -pass-remarks-output=<yaml-file>, we generate this YAML file: --- !Missed Pass: inline Name: NotInlined DebugLoc: { File: /tmp/s.c, Line: 5, Column: 10 } Function: baz Hotness: 30 Args: - Callee: foo - String: will not be inlined into - Caller: baz ... --- !Missed Pass: inline Name: NotInlined DebugLoc: { File: /tmp/s.c, Line: 5, Column: 18 } Function: baz Hotness: 30 Args: - Callee: bar - String: will not be inlined into - Caller: baz ... This is a summary of the high-level decisions: * There is a new streaming interface to emit optimization remarks. E.g. for the inliner remark above: ORE.emit(DiagnosticInfoOptimizationRemarkMissed( DEBUG_TYPE, "NotInlined", &I) << NV("Callee", Callee) << " will not be inlined into " << NV("Caller", CS.getCaller()) << setIsVerbose()); NV stands for named value and allows the YAML client to process a remark using its name (NotInlined) and the named arguments (Callee and Caller) without parsing the text of the message. Subsequent patches will update ORE users to use the new streaming API. * I am using YAML I/O for writing the YAML file. YAML I/O requires you to specify reading and writing at once but reading is highly non-trivial for some of the more complex LLVM types. Since it's not clear that we (ever) want to use LLVM to parse this YAML file, the code supports and asserts that we're writing only. On the other hand, I did experiment that the class hierarchy starting at DiagnosticInfoOptimizationBase can be mapped back from YAML generated here (see D24479). * The YAML stream is stored in the LLVM context. * In the example, we can probably further specify the IR value used, i.e. print "Function" rather than "Value". * As before hotness is computed in the analysis pass instead of DiganosticInfo. This avoids the layering problem since BFI is in Analysis while DiagnosticInfo is in IR. [1] https://reviews.llvm.org/D19678#419445 Differential Revision: https://reviews.llvm.org/D24587 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@282539 91177308-0d34-0410-b5e6-96231b3b80d8 Adam Nemet 3 years ago
10 changed file(s) with 339 addition(s) and 10 deletion(s). Raw diff Collapse all Expand all
1616
1717 #include "llvm/ADT/Optional.h"
1818 #include "llvm/Analysis/BlockFrequencyInfo.h"
19 #include "llvm/IR/DiagnosticInfo.h"
1920 #include "llvm/IR/PassManager.h"
2021 #include "llvm/Pass.h"
2122
6162 return *this;
6263 }
6364
65 /// The new interface to emit remarks.
66 void emit(DiagnosticInfoOptimizationBase &OptDiag);
67
6468 /// Emit an optimization-applied message.
6569 ///
6670 /// \p PassName is the name of the pass emitting the message. If -Rpass= is
197201 /// If we generate BFI on demand, we need to free it when ORE is freed.
198202 std::unique_ptr OwnedBFI;
199203
204 /// Compute hotness from IR value (currently assumed to be a block) if PGO is
205 /// available.
200206 Optional computeHotness(const Value *V);
207
208 /// Similar but use value from \p OptDiag and update hotness there.
209 void computeHotness(DiagnosticInfoOptimizationBase &OptDiag);
201210
202211 /// \brief Only allow verbose messages if we know we're filtering by hotness
203212 /// (BFI is only set in this case).
206215 OptimizationRemarkEmitter(const OptimizationRemarkEmitter &) = delete;
207216 void operator=(const OptimizationRemarkEmitter &) = delete;
208217 };
218
219 /// \brief Add a small namespace to avoid name clashes with the classes used in
220 /// the streaming interface. We want these to be short for better
221 /// write/readability.
222 namespace ore {
223 using NV = DiagnosticInfoOptimizationBase::Argument;
224 using setIsVerbose = DiagnosticInfoOptimizationBase::setIsVerbose;
225 }
209226
210227 /// OptimizationRemarkEmitter legacy analysis pass
211228 ///
1616
1717 #include "llvm-c/Types.h"
1818 #include "llvm/ADT/Optional.h"
19 #include "llvm/ADT/SmallString.h"
1920 #include "llvm/ADT/StringRef.h"
2021 #include "llvm/ADT/Twine.h"
2122 #include "llvm/IR/DebugLoc.h"
2223 #include "llvm/Support/CBindingWrapping.h"
24 #include "llvm/Support/YAMLTraits.h"
2325 #include
2426 #include
2527
374376 /// Common features for diagnostics dealing with optimization remarks.
375377 class DiagnosticInfoOptimizationBase : public DiagnosticInfoWithDebugLocBase {
376378 public:
379 /// \brief Used to set IsVerbose via the stream interface.
380 struct setIsVerbose {};
381
382 /// \brief Used in the streaming interface as the general argument type. It
383 /// internally converts everything into a key-value pair.
384 struct Argument {
385 StringRef Key;
386 std::string Val;
387
388 explicit Argument(StringRef Str = "") : Key("String"), Val(Str) {}
389 explicit Argument(StringRef Key, Value *V) : Key(Key), Val(V->getName()) {}
390 explicit Argument(StringRef Key, int N);
391 };
392
393 /// \p PassName is the name of the pass emitting this diagnostic. \p
394 /// RemarkName is a textual identifier for the remark. \p Fn is the function
395 /// where the diagnostic is being emitted. \p DLoc is the location information
396 /// to use in the diagnostic. If line table information is available, the
397 /// diagnostic will include the source code location. \p CodeRegion is IR
398 /// value (currently basic block) that the optimization operates on. This is
399 /// currently used to provide run-time hotness information with PGO.
400 DiagnosticInfoOptimizationBase(enum DiagnosticKind Kind,
401 enum DiagnosticSeverity Severity,
402 const char *PassName, StringRef RemarkName,
403 const Function &Fn, const DebugLoc &DLoc,
404 Value *CodeRegion = nullptr)
405 : DiagnosticInfoWithDebugLocBase(Kind, Severity, Fn, DLoc),
406 PassName(PassName), RemarkName(RemarkName), CodeRegion(CodeRegion),
407 IsVerbose(false) {}
408
409 /// Legacy interface.
377410 /// \p PassName is the name of the pass emitting this diagnostic.
378411 /// \p Fn is the function where the diagnostic is being emitted. \p DLoc is
379412 /// the location information to use in the diagnostic. If line table
387420 const DebugLoc &DLoc, const Twine &Msg,
388421 Optional Hotness = None)
389422 : DiagnosticInfoWithDebugLocBase(Kind, Severity, Fn, DLoc),
390 PassName(PassName), Msg(Msg), Hotness(Hotness) {}
423 PassName(PassName), Hotness(Hotness), IsVerbose(false) {
424 Args.push_back(Argument(Msg.str()));
425 }
426
427 DiagnosticInfoOptimizationBase &operator<<(StringRef S);
428 DiagnosticInfoOptimizationBase &operator<<(Argument A);
429 DiagnosticInfoOptimizationBase &operator<<(setIsVerbose V);
391430
392431 /// \see DiagnosticInfo::print.
393432 void print(DiagnosticPrinter &DP) const override;
400439 virtual bool isEnabled() const = 0;
401440
402441 const char *getPassName() const { return PassName; }
403 const Twine &getMsg() const { return Msg; }
442 std::string getMsg() const;
404443 Optional getHotness() const { return Hotness; }
444 void setHotness(Optional H) { Hotness = H; }
445
446 Value *getCodeRegion() const { return CodeRegion; }
447
448 bool isVerbose() const { return IsVerbose; }
405449
406450 static bool classof(const DiagnosticInfo *DI) {
407451 return DI->getKind() >= DK_FirstRemark &&
414458 /// be emitted.
415459 const char *PassName;
416460
417 /// Message to report.
418 const Twine &Msg;
461 /// Textual identifier for the remark. Can be used by external tools reading
462 /// the YAML output file for optimization remarks to identify the remark.
463 StringRef RemarkName;
419464
420465 /// If profile information is available, this is the number of times the
421466 /// corresponding code was executed in a profile instrumentation run.
422467 Optional Hotness;
468
469 /// The IR value (currently basic block) that the optimization operates on.
470 /// This is currently used to provide run-time hotness information with PGO.
471 Value *CodeRegion;
472
473 /// Arguments collected via the streaming interface.
474 SmallVector Args;
475
476 /// The remark is expected to be noisy.
477 bool IsVerbose;
478
479 friend struct yaml::MappingTraits;
423480 };
424481
425482 /// Diagnostic information for applied optimization remarks.
465522 Optional Hotness = None)
466523 : DiagnosticInfoOptimizationBase(DK_OptimizationRemarkMissed, DS_Remark,
467524 PassName, Fn, DLoc, Msg, Hotness) {}
525
526 /// \p PassName is the name of the pass emitting this diagnostic. If this name
527 /// matches the regular expression given in -Rpass-missed=, then the
528 /// diagnostic will be emitted. \p RemarkName is a textual identifier for the
529 /// remark. \p Inst is the instruction that the optimization operates on.
530 DiagnosticInfoOptimizationRemarkMissed(const char *PassName,
531 StringRef RemarkName,
532 Instruction *Inst);
468533
469534 static bool classof(const DiagnosticInfo *DI) {
470535 return DI->getKind() == DK_OptimizationRemarkMissed;
3333 class Function;
3434 class DebugLoc;
3535 class OptBisect;
36 namespace yaml {
37 class Output;
38 }
3639
3740 /// This is an important class for using LLVM in a threaded context. It
3841 /// (opaquely) owns and manages the core "global" data of LLVM's core
180183 /// diagnostics.
181184 void setDiagnosticHotnessRequested(bool Requested);
182185
186 /// \brief Return the YAML file used by the backend to save optimization
187 /// diagnostics. If null, diagnostics are not saved in a file but only
188 /// emitted via the diagnostic handler.
189 yaml::Output *getDiagnosticsOutputFile();
190 /// Set the diagnostics output file used for optimization diagnostics.
191 ///
192 /// By default or if invoked with null, diagnostics are not saved in a file
193 /// but only emitted via the diagnostic handler. Even if an output file is
194 /// set, the handler is invoked for each diagnostic message.
195 void setDiagnosticsOutputFile(yaml::Output *F);
196
183197 /// \brief Get the prefix that should be printed in front of a diagnostic of
184198 /// the given \p Severity
185199 static const char *getDiagnosticMessagePrefix(DiagnosticSeverity Severity);
1515 #include "llvm/Analysis/BranchProbabilityInfo.h"
1616 #include "llvm/Analysis/LazyBlockFrequencyInfo.h"
1717 #include "llvm/Analysis/LoopInfo.h"
18 #include "llvm/IR/DebugInfo.h"
1819 #include "llvm/IR/DiagnosticInfo.h"
1920 #include "llvm/IR/Dominators.h"
2021 #include "llvm/IR/LLVMContext.h"
4849 return None;
4950
5051 return BFI->getBlockProfileCount(cast(V));
52 }
53
54 namespace llvm {
55 namespace yaml {
56
57 template <> struct MappingTraits {
58 static void mapping(IO &io, DiagnosticInfoOptimizationBase *&OptDiag) {
59 assert(io.outputting() && "input not yet implemented");
60
61 if (io.mapTag("!Missed", OptDiag->getKind() == DK_OptimizationRemarkMissed))
62 ;
63 else
64 llvm_unreachable("todo");
65
66 // These are read-only for now.
67 DebugLoc DL = OptDiag->getDebugLoc();
68 StringRef FN = OptDiag->getFunction().getName();
69
70 StringRef PassName(OptDiag->PassName);
71 io.mapRequired("Pass", PassName);
72 io.mapRequired("Name", OptDiag->RemarkName);
73 if (!io.outputting() || DL)
74 io.mapOptional("DebugLoc", DL);
75 io.mapRequired("Function", FN);
76 io.mapOptional("Hotness", OptDiag->Hotness);
77 io.mapOptional("Args", OptDiag->Args);
78 }
79 };
80
81 template <> struct MappingTraits {
82 static void mapping(IO &io, DebugLoc &DL) {
83 assert(io.outputting() && "input not yet implemented");
84
85 auto *Scope = cast(DL.getScope());
86 StringRef File = Scope->getFilename();
87 unsigned Line = DL.getLine();
88 unsigned Col = DL.getCol();
89
90 io.mapRequired("File", File);
91 io.mapRequired("Line", Line);
92 io.mapRequired("Column", Col);
93 }
94
95 static const bool flow = true;
96 };
97
98 template <> struct ScalarTraits {
99 static void output(const DiagnosticInfoOptimizationBase::Argument &Arg,
100 void *, llvm::raw_ostream &out) {
101 out << Arg.Key << ": " << Arg.Val;
102 }
103
104 static StringRef input(StringRef scalar, void *,
105 DiagnosticInfoOptimizationBase::Argument &Arg) {
106 llvm_unreachable("input not yet implemented");
107 }
108
109 static bool mustQuote(StringRef) { return false; }
110 };
111
112 } // end namespace yaml
113 } // end namespace llvm
114
115 LLVM_YAML_IS_SEQUENCE_VECTOR(DiagnosticInfoOptimizationBase::Argument)
116
117 void OptimizationRemarkEmitter::computeHotness(
118 DiagnosticInfoOptimizationBase &OptDiag) {
119 Value *V = OptDiag.getCodeRegion();
120 if (V)
121 OptDiag.setHotness(computeHotness(V));
122 }
123
124 void OptimizationRemarkEmitter::emit(DiagnosticInfoOptimizationBase &OptDiag) {
125 computeHotness(OptDiag);
126
127 yaml::Output *Out = F->getContext().getDiagnosticsOutputFile();
128 if (Out && OptDiag.isEnabled()) {
129 auto *P = &const_cast(OptDiag);
130 *Out << P;
131 }
132 // FIXME: now that IsVerbose is part of DI, filtering for this will be moved
133 // from here to clang.
134 if (!OptDiag.isVerbose() || shouldEmitVerbose())
135 F->getContext().diagnose(OptDiag);
51136 }
52137
53138 void OptimizationRemarkEmitter::emitOptimizationRemark(const char *PassName,
1313
1414 #include "llvm/IR/DiagnosticInfo.h"
1515 #include "LLVMContextImpl.h"
16 #include "llvm/ADT/StringExtras.h"
1617 #include "llvm/ADT/Twine.h"
1718 #include "llvm/IR/Constants.h"
1819 #include "llvm/IR/DebugInfo.h"
169170 return (Filename + ":" + Twine(Line) + ":" + Twine(Column)).str();
170171 }
171172
173 DiagnosticInfoOptimizationBase::Argument::Argument(StringRef Key, int N)
174 : Key(Key), Val(itostr(N)) {}
175
172176 void DiagnosticInfoOptimizationBase::print(DiagnosticPrinter &DP) const {
173177 DP << getLocationStr() << ": " << getMsg();
174178 if (Hotness)
179183 return PassRemarksOptLoc.Pattern &&
180184 PassRemarksOptLoc.Pattern->match(getPassName());
181185 }
186
187 DiagnosticInfoOptimizationRemarkMissed::DiagnosticInfoOptimizationRemarkMissed(
188 const char *PassName, StringRef RemarkName, Instruction *Inst)
189 : DiagnosticInfoOptimizationBase(DK_OptimizationRemarkMissed, DS_Remark,
190 PassName, RemarkName,
191 *Inst->getParent()->getParent(),
192 Inst->getDebugLoc(), Inst->getParent()) {}
182193
183194 bool DiagnosticInfoOptimizationRemarkMissed::isEnabled() const {
184195 return PassRemarksMissedOptLoc.Pattern &&
265276 void DiagnosticInfoISelFallback::print(DiagnosticPrinter &DP) const {
266277 DP << "Instruction selection used fallback path for " << getFunction();
267278 }
279
280 DiagnosticInfoOptimizationBase &DiagnosticInfoOptimizationBase::
281 operator<<(StringRef S) {
282 Args.emplace_back(S);
283 return *this;
284 }
285
286 DiagnosticInfoOptimizationBase &DiagnosticInfoOptimizationBase::
287 operator<<(Argument A) {
288 Args.push_back(std::move(A));
289 return *this;
290 }
291
292 DiagnosticInfoOptimizationBase &DiagnosticInfoOptimizationBase::
293 operator<<(setIsVerbose V) {
294 IsVerbose = true;
295 return *this;
296 }
297
298 std::string DiagnosticInfoOptimizationBase::getMsg() const {
299 std::string Str;
300 raw_string_ostream OS(Str);
301 for (const DiagnosticInfoOptimizationBase::Argument &Arg : Args)
302 OS << Arg.Val;
303 return OS.str();
304 }
202202 return pImpl->DiagnosticHotnessRequested;
203203 }
204204
205 yaml::Output *LLVMContext::getDiagnosticsOutputFile() {
206 return pImpl->DiagnosticsOutputFile.get();
207 }
208
209 void LLVMContext::setDiagnosticsOutputFile(yaml::Output *F) {
210 pImpl->DiagnosticsOutputFile.reset(F);
211 }
212
205213 LLVMContext::DiagnosticHandlerTy LLVMContext::getDiagnosticHandler() const {
206214 return pImpl->DiagnosticHandler;
207215 }
3232 #include "llvm/IR/Metadata.h"
3333 #include "llvm/IR/ValueHandle.h"
3434 #include "llvm/Support/Dwarf.h"
35 #include "llvm/Support/YAMLTraits.h"
3536 #include
3637
3738 namespace llvm {
10421043 void *DiagnosticContext;
10431044 bool RespectDiagnosticFilters;
10441045 bool DiagnosticHotnessRequested;
1046 std::unique_ptr DiagnosticsOutputFile;
10451047
10461048 LLVMContext::YieldCallbackTy YieldCallback;
10471049 void *YieldOpaqueHandle;
468468 // direct call, so we keep it.
469469 if (Function *Callee = CS.getCalledFunction())
470470 if (Callee->isDeclaration()) {
471 ORE.emitOptimizationRemarkMissedAndAnalysis(
472 DEBUG_TYPE, &I,
473 Twine(Callee->getName()) + " will not be inlined into " +
474 CS.getCaller()->getName(),
475 Twine("definition of ") + Callee->getName() +
476 " is not available",
471 ORE.emitOptimizationRemarkAnalysis(
472 DEBUG_TYPE, &I, Twine("definition of ") + Callee->getName() +
473 " is not available",
477474 /*Verbose=*/true);
475 using namespace ore;
476 ORE.emit(DiagnosticInfoOptimizationRemarkMissed(DEBUG_TYPE,
477 "NotInlined", &I)
478 << NV("Callee", Callee) << " will not be inlined into "
479 << NV("Caller", CS.getCaller()) << setIsVerbose());
478480 continue;
479481 }
480482
0 ; RUN: opt < %s -inline -pass-remarks-missed=inline -pass-remarks-with-hotness \
1 ; RUN: -pass-remarks-output=%t 2>&1 | FileCheck %s
2 ; RUN: cat %t | FileCheck -check-prefix=YAML %s
3
4 ; Check the YAML file generated for inliner remarks for this program:
5 ;
6 ; 1 int foo();
7 ; 2 int bar();
8 ; 3
9 ; 4 int baz() {
10 ; 5 return foo() + bar();
11 ; 6 }
12
13 ; CHECK: remark: /tmp/s.c:5:10: foo will not be inlined into baz (hotness: 30)
14 ; CHECK-NEXT: remark: /tmp/s.c:5:18: bar will not be inlined into baz (hotness: 30)
15
16 ; YAML: --- !Missed
17 ; YAML-NEXT: Pass: inline
18 ; YAML-NEXT: Name: NotInlined
19 ; YAML-NEXT: DebugLoc: { File: /tmp/s.c, Line: 5, Column: 10 }
20 ; YAML-NEXT: Function: baz
21 ; YAML-NEXT: Hotness: 30
22 ; YAML-NEXT: Args:
23 ; YAML-NEXT: - Callee: foo
24 ; YAML-NEXT: - String: will not be inlined into
25 ; YAML-NEXT: - Caller: baz
26 ; YAML-NEXT: ...
27 ; YAML-NEXT: --- !Missed
28 ; YAML-NEXT: Pass: inline
29 ; YAML-NEXT: Name: NotInlined
30 ; YAML-NEXT: DebugLoc: { File: /tmp/s.c, Line: 5, Column: 18 }
31 ; YAML-NEXT: Function: baz
32 ; YAML-NEXT: Hotness: 30
33 ; YAML-NEXT: Args:
34 ; YAML-NEXT: - Callee: bar
35 ; YAML-NEXT: - String: will not be inlined into
36 ; YAML-NEXT: - Caller: baz
37 ; YAML-NEXT: ...
38
39 ; ModuleID = '/tmp/s.c'
40 source_filename = "/tmp/s.c"
41 target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
42 target triple = "x86_64-apple-macosx10.11.0"
43
44 ; Function Attrs: nounwind ssp uwtable
45 define i32 @baz() !dbg !7 !prof !14 {
46 entry:
47 %call = call i32 (...) @foo(), !dbg !9
48 %call1 = call i32 (...) @bar(), !dbg !10
49 %add = add nsw i32 %call, %call1, !dbg !12
50 ret i32 %add, !dbg !13
51 }
52
53 declare i32 @foo(...)
54
55 declare i32 @bar(...)
56
57 !llvm.dbg.cu = !{!0}
58 !llvm.module.flags = !{!3, !4, !5}
59 !llvm.ident = !{!6}
60
61 !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 4.0.0 (trunk 281293) (llvm/trunk 281290)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2)
62 !1 = !DIFile(filename: "/tmp/s.c", directory: "/tmp")
63 !2 = !{}
64 !3 = !{i32 2, !"Dwarf Version", i32 4}
65 !4 = !{i32 2, !"Debug Info Version", i32 3}
66 !5 = !{i32 1, !"PIC Level", i32 2}
67 !6 = !{!"clang version 4.0.0 (trunk 281293) (llvm/trunk 281290)"}
68 !7 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 4, type: !8, isLocal: false, isDefinition: true, scopeLine: 4, isOptimized: true, unit: !0, variables: !2)
69 !8 = !DISubroutineType(types: !2)
70 !9 = !DILocation(line: 5, column: 10, scope: !7)
71 !10 = !DILocation(line: 5, column: 18, scope: !11)
72 !11 = !DILexicalBlockFile(scope: !7, file: !1, discriminator: 1)
73 !12 = !DILocation(line: 5, column: 16, scope: !7)
74 !13 = !DILocation(line: 5, column: 3, scope: !7)
75 !14 = !{!"function_entry_count", i64 30}
4848 #include "llvm/Support/TargetRegistry.h"
4949 #include "llvm/Support/TargetSelect.h"
5050 #include "llvm/Support/ToolOutputFile.h"
51 #include "llvm/Support/YAMLTraits.h"
5152 #include "llvm/Target/TargetMachine.h"
5253 #include "llvm/Transforms/Coroutines.h"
5354 #include "llvm/Transforms/IPO/AlwaysInliner.h"
229230 "pass-remarks-with-hotness",
230231 cl::desc("With PGO, include profile count in optimization remarks"),
231232 cl::Hidden);
233
234 static cl::opt
235 RemarksFilename("pass-remarks-output",
236 cl::desc("YAML output filename for pass remarks"),
237 cl::value_desc("filename"));
232238
233239 static inline void addPass(legacy::PassManagerBase &PM, Pass *P) {
234240 // Add the pass to the pass manager...
407413 if (PassRemarksWithHotness)
408414 Context.setDiagnosticHotnessRequested(true);
409415
416 std::unique_ptr YamlFile;
417 if (RemarksFilename != "") {
418 std::error_code EC;
419 YamlFile = llvm::make_unique(RemarksFilename, EC,
420 sys::fs::F_None);
421 if (EC) {
422 errs() << EC.message() << '\n';
423 return 1;
424 }
425 Context.setDiagnosticsOutputFile(new yaml::Output(YamlFile->os()));
426 }
427
410428 // Load the input module...
411429 std::unique_ptr M = parseIRFile(InputFilename, Err, Context);
412430
715733 "the compile-twice option\n";
716734 Out->os() << BOS->str();
717735 Out->keep();
736 if (YamlFile)
737 YamlFile->keep();
718738 return 1;
719739 }
720740 Out->os() << BOS->str();
724744 if (!NoOutput || PrintBreakpoints)
725745 Out->keep();
726746
747 if (YamlFile)
748 YamlFile->keep();
749
727750 return 0;
728751 }