1 | //===-- LVReader.cpp ------------------------------------------------------===// |
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 implements the LVReader class. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" |
14 | #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" |
15 | #include "llvm/Support/FileSystem.h" |
16 | #include "llvm/Support/FormatAdapters.h" |
17 | #include "llvm/Support/FormatVariadic.h" |
18 | #include <tuple> |
19 | |
20 | using namespace llvm; |
21 | using namespace llvm::logicalview; |
22 | |
23 | #define DEBUG_TYPE "Reader" |
24 | |
25 | // Detect elements that are inserted more than once at different scopes, |
26 | // causing a crash on the reader destruction, as the element is already |
27 | // deleted from other scope. Helper for CodeView reader. |
28 | bool checkIntegrityScopesTree(LVScope *Root) { |
29 | using LVDuplicateEntry = std::tuple<LVElement *, LVScope *, LVScope *>; |
30 | using LVDuplicate = std::vector<LVDuplicateEntry>; |
31 | LVDuplicate Duplicate; |
32 | |
33 | using LVIntegrity = std::map<LVElement *, LVScope *>; |
34 | LVIntegrity Integrity; |
35 | |
36 | // Add the given element to the integrity map. |
37 | auto AddElement = [&](LVElement *Element, LVScope *Scope) { |
38 | LVIntegrity::iterator Iter = Integrity.find(x: Element); |
39 | if (Iter == Integrity.end()) |
40 | Integrity.emplace(args&: Element, args&: Scope); |
41 | else |
42 | // We found a duplicate. |
43 | Duplicate.emplace_back(args&: Element, args&: Scope, args&: Iter->second); |
44 | }; |
45 | |
46 | // Recursively add all the elements in the scope. |
47 | std::function<void(LVScope * Parent)> TraverseScope = [&](LVScope *Parent) { |
48 | auto Traverse = [&](const auto *Set) { |
49 | if (Set) |
50 | for (const auto &Entry : *Set) |
51 | AddElement(Entry, Parent); |
52 | }; |
53 | if (const LVScopes *Scopes = Parent->getScopes()) { |
54 | for (LVScope *Scope : *Scopes) { |
55 | AddElement(Scope, Parent); |
56 | TraverseScope(Scope); |
57 | } |
58 | } |
59 | Traverse(Parent->getSymbols()); |
60 | Traverse(Parent->getTypes()); |
61 | Traverse(Parent->getLines()); |
62 | }; |
63 | |
64 | // Start traversing the scopes root and print any duplicates. |
65 | TraverseScope(Root); |
66 | bool PassIntegrity = true; |
67 | if (Duplicate.size()) { |
68 | llvm::stable_sort(Range&: Duplicate, C: [](const auto &l, const auto &r) { |
69 | return std::get<0>(l)->getID() < std::get<0>(r)->getID(); |
70 | }); |
71 | |
72 | auto PrintIndex = [](unsigned Index) { |
73 | if (Index) |
74 | dbgs() << format(Fmt: "%8d: " , Vals: Index); |
75 | else |
76 | dbgs() << format(Fmt: "%8c: " , Vals: ' '); |
77 | }; |
78 | auto PrintElement = [&](LVElement *Element, unsigned Index = 0) { |
79 | PrintIndex(Index); |
80 | std::string ElementName(Element->getName()); |
81 | dbgs() << format(Fmt: "%15s ID=0x%08x '%s'\n" , Vals: Element->kind(), |
82 | Vals: Element->getID(), Vals: ElementName.c_str()); |
83 | }; |
84 | |
85 | std::string RootName(Root->getName()); |
86 | dbgs() << formatv(Fmt: "{0}\n" , Vals: fmt_repeat(Item: '=', Count: 72)); |
87 | dbgs() << format(Fmt: "Root: '%s'\nDuplicated elements: %d\n" , Vals: RootName.c_str(), |
88 | Vals: Duplicate.size()); |
89 | dbgs() << formatv(Fmt: "{0}\n" , Vals: fmt_repeat(Item: '=', Count: 72)); |
90 | |
91 | unsigned Index = 0; |
92 | for (const LVDuplicateEntry &Entry : Duplicate) { |
93 | LVElement *Element; |
94 | LVScope *First; |
95 | LVScope *Second; |
96 | std::tie(args&: Element, args&: First, args&: Second) = Entry; |
97 | dbgs() << formatv(Fmt: "\n{0}\n" , Vals: fmt_repeat(Item: '-', Count: 72)); |
98 | PrintElement(Element, ++Index); |
99 | PrintElement(First); |
100 | PrintElement(Second); |
101 | dbgs() << formatv(Fmt: "{0}\n" , Vals: fmt_repeat(Item: '-', Count: 72)); |
102 | } |
103 | PassIntegrity = false; |
104 | } |
105 | return PassIntegrity; |
106 | } |
107 | |
108 | //===----------------------------------------------------------------------===// |
109 | // Class to represent a split context. |
110 | //===----------------------------------------------------------------------===// |
111 | Error LVSplitContext::createSplitFolder(StringRef Where) { |
112 | // The 'location' will represent the root directory for the output created |
113 | // by the context. It will contain the different CUs files, that will be |
114 | // extracted from a single ELF. |
115 | Location = std::string(Where); |
116 | |
117 | // Add a trailing slash, if there is none. |
118 | size_t Pos = Location.find_last_of(c: '/'); |
119 | if (Location.length() != Pos + 1) |
120 | Location.append(s: "/" ); |
121 | |
122 | // Make sure the new directory exists, creating it if necessary. |
123 | if (std::error_code EC = llvm::sys::fs::create_directories(path: Location)) |
124 | return createStringError(EC, Fmt: "Error: could not create directory %s" , |
125 | Vals: Location.c_str()); |
126 | |
127 | return Error::success(); |
128 | } |
129 | |
130 | std::error_code LVSplitContext::open(std::string ContextName, |
131 | std::string Extension, raw_ostream &OS) { |
132 | assert(OutputFile == nullptr && "OutputFile already set." ); |
133 | |
134 | // Transforms '/', '\', '.', ':' into '_'. |
135 | std::string Name(flattenedFilePath(Path: ContextName)); |
136 | Name.append(str: Extension); |
137 | // Add the split context location folder name. |
138 | if (!Location.empty()) |
139 | Name.insert(pos1: 0, str: Location); |
140 | |
141 | std::error_code EC; |
142 | OutputFile = std::make_unique<ToolOutputFile>(args&: Name, args&: EC, args: sys::fs::OF_None); |
143 | if (EC) |
144 | return EC; |
145 | |
146 | // Don't remove output file. |
147 | OutputFile->keep(); |
148 | return std::error_code(); |
149 | } |
150 | |
151 | LVReader *CurrentReader = nullptr; |
152 | LVReader &LVReader::getInstance() { |
153 | if (CurrentReader) |
154 | return *CurrentReader; |
155 | outs() << "Invalid instance reader.\n" ; |
156 | llvm_unreachable("Invalid instance reader." ); |
157 | } |
158 | void LVReader::setInstance(LVReader *Reader) { CurrentReader = Reader; } |
159 | |
160 | Error LVReader::createSplitFolder() { |
161 | if (OutputSplit) { |
162 | // If the '--output=split' was specified, but no '--split-folder' |
163 | // option, use the input file as base for the split location. |
164 | if (options().getOutputFolder().empty()) |
165 | options().setOutputFolder(getFilename().str() + "_cus" ); |
166 | |
167 | SmallString<128> SplitFolder; |
168 | SplitFolder = options().getOutputFolder(); |
169 | sys::fs::make_absolute(path&: SplitFolder); |
170 | |
171 | // Return error if unable to create a split context location. |
172 | if (Error Err = SplitContext.createSplitFolder(Where: SplitFolder)) |
173 | return Err; |
174 | |
175 | OS << "\nSplit View Location: '" << SplitContext.getLocation() << "'\n" ; |
176 | } |
177 | |
178 | return Error::success(); |
179 | } |
180 | |
181 | // Get the filename for given object. |
182 | StringRef LVReader::getFilename(LVObject *Object, size_t Index) const { |
183 | // TODO: The current CodeView Reader implementation does not have support |
184 | // for multiple compile units. Until we have a proper offset calculation, |
185 | // check only in the current compile unit. |
186 | if (CompileUnits.size()) { |
187 | // Get Compile Unit for the given object. |
188 | LVCompileUnits::const_iterator Iter = |
189 | std::prev(x: CompileUnits.lower_bound(x: Object->getOffset())); |
190 | if (Iter != CompileUnits.end()) |
191 | return Iter->second->getFilename(Index); |
192 | } |
193 | |
194 | return CompileUnit ? CompileUnit->getFilename(Index) : StringRef(); |
195 | } |
196 | |
197 | void LVReader::addSectionRange(LVSectionIndex SectionIndex, LVScope *Scope) { |
198 | LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); |
199 | ScopesWithRanges->addEntry(Scope); |
200 | } |
201 | |
202 | void LVReader::addSectionRange(LVSectionIndex SectionIndex, LVScope *Scope, |
203 | LVAddress LowerAddress, LVAddress UpperAddress) { |
204 | LVRange *ScopesWithRanges = getSectionRanges(SectionIndex); |
205 | ScopesWithRanges->addEntry(Scope, LowerAddress, UpperAddress); |
206 | } |
207 | |
208 | LVRange *LVReader::getSectionRanges(LVSectionIndex SectionIndex) { |
209 | // Check if we already have a mapping for this section index. |
210 | LVSectionRanges::iterator IterSection = SectionRanges.find(x: SectionIndex); |
211 | if (IterSection == SectionRanges.end()) |
212 | IterSection = |
213 | SectionRanges.emplace(args&: SectionIndex, args: std::make_unique<LVRange>()).first; |
214 | LVRange *Range = IterSection->second.get(); |
215 | assert(Range && "Range is null." ); |
216 | return Range; |
217 | } |
218 | |
219 | LVElement *LVReader::createElement(dwarf::Tag Tag) { |
220 | CurrentScope = nullptr; |
221 | CurrentSymbol = nullptr; |
222 | CurrentType = nullptr; |
223 | CurrentRanges.clear(); |
224 | |
225 | LLVM_DEBUG( |
226 | { dbgs() << "\n[createElement] " << dwarf::TagString(Tag) << "\n" ; }); |
227 | |
228 | if (!options().getPrintSymbols()) { |
229 | switch (Tag) { |
230 | // As the command line options did not specify a request to print |
231 | // logical symbols (--print=symbols or --print=all or --print=elements), |
232 | // skip its creation. |
233 | case dwarf::DW_TAG_formal_parameter: |
234 | case dwarf::DW_TAG_unspecified_parameters: |
235 | case dwarf::DW_TAG_member: |
236 | case dwarf::DW_TAG_variable: |
237 | case dwarf::DW_TAG_inheritance: |
238 | case dwarf::DW_TAG_constant: |
239 | case dwarf::DW_TAG_call_site_parameter: |
240 | case dwarf::DW_TAG_GNU_call_site_parameter: |
241 | return nullptr; |
242 | default: |
243 | break; |
244 | } |
245 | } |
246 | |
247 | switch (Tag) { |
248 | // Types. |
249 | case dwarf::DW_TAG_base_type: |
250 | CurrentType = createType(); |
251 | CurrentType->setIsBase(); |
252 | if (options().getAttributeBase()) |
253 | CurrentType->setIncludeInPrint(); |
254 | return CurrentType; |
255 | case dwarf::DW_TAG_const_type: |
256 | CurrentType = createType(); |
257 | CurrentType->setIsConst(); |
258 | CurrentType->setName("const" ); |
259 | return CurrentType; |
260 | case dwarf::DW_TAG_enumerator: |
261 | CurrentType = createTypeEnumerator(); |
262 | return CurrentType; |
263 | case dwarf::DW_TAG_imported_declaration: |
264 | CurrentType = createTypeImport(); |
265 | CurrentType->setIsImportDeclaration(); |
266 | return CurrentType; |
267 | case dwarf::DW_TAG_imported_module: |
268 | CurrentType = createTypeImport(); |
269 | CurrentType->setIsImportModule(); |
270 | return CurrentType; |
271 | case dwarf::DW_TAG_pointer_type: |
272 | CurrentType = createType(); |
273 | CurrentType->setIsPointer(); |
274 | CurrentType->setName("*" ); |
275 | return CurrentType; |
276 | case dwarf::DW_TAG_ptr_to_member_type: |
277 | CurrentType = createType(); |
278 | CurrentType->setIsPointerMember(); |
279 | CurrentType->setName("*" ); |
280 | return CurrentType; |
281 | case dwarf::DW_TAG_reference_type: |
282 | CurrentType = createType(); |
283 | CurrentType->setIsReference(); |
284 | CurrentType->setName("&" ); |
285 | return CurrentType; |
286 | case dwarf::DW_TAG_restrict_type: |
287 | CurrentType = createType(); |
288 | CurrentType->setIsRestrict(); |
289 | CurrentType->setName("restrict" ); |
290 | return CurrentType; |
291 | case dwarf::DW_TAG_rvalue_reference_type: |
292 | CurrentType = createType(); |
293 | CurrentType->setIsRvalueReference(); |
294 | CurrentType->setName("&&" ); |
295 | return CurrentType; |
296 | case dwarf::DW_TAG_subrange_type: |
297 | CurrentType = createTypeSubrange(); |
298 | return CurrentType; |
299 | case dwarf::DW_TAG_template_value_parameter: |
300 | CurrentType = createTypeParam(); |
301 | CurrentType->setIsTemplateValueParam(); |
302 | return CurrentType; |
303 | case dwarf::DW_TAG_template_type_parameter: |
304 | CurrentType = createTypeParam(); |
305 | CurrentType->setIsTemplateTypeParam(); |
306 | return CurrentType; |
307 | case dwarf::DW_TAG_GNU_template_template_param: |
308 | CurrentType = createTypeParam(); |
309 | CurrentType->setIsTemplateTemplateParam(); |
310 | return CurrentType; |
311 | case dwarf::DW_TAG_typedef: |
312 | CurrentType = createTypeDefinition(); |
313 | return CurrentType; |
314 | case dwarf::DW_TAG_unspecified_type: |
315 | CurrentType = createType(); |
316 | CurrentType->setIsUnspecified(); |
317 | return CurrentType; |
318 | case dwarf::DW_TAG_volatile_type: |
319 | CurrentType = createType(); |
320 | CurrentType->setIsVolatile(); |
321 | CurrentType->setName("volatile" ); |
322 | return CurrentType; |
323 | |
324 | // Symbols. |
325 | case dwarf::DW_TAG_formal_parameter: |
326 | CurrentSymbol = createSymbol(); |
327 | CurrentSymbol->setIsParameter(); |
328 | return CurrentSymbol; |
329 | case dwarf::DW_TAG_unspecified_parameters: |
330 | CurrentSymbol = createSymbol(); |
331 | CurrentSymbol->setIsUnspecified(); |
332 | CurrentSymbol->setName("..." ); |
333 | return CurrentSymbol; |
334 | case dwarf::DW_TAG_member: |
335 | CurrentSymbol = createSymbol(); |
336 | CurrentSymbol->setIsMember(); |
337 | return CurrentSymbol; |
338 | case dwarf::DW_TAG_variable: |
339 | CurrentSymbol = createSymbol(); |
340 | CurrentSymbol->setIsVariable(); |
341 | return CurrentSymbol; |
342 | case dwarf::DW_TAG_inheritance: |
343 | CurrentSymbol = createSymbol(); |
344 | CurrentSymbol->setIsInheritance(); |
345 | return CurrentSymbol; |
346 | case dwarf::DW_TAG_call_site_parameter: |
347 | case dwarf::DW_TAG_GNU_call_site_parameter: |
348 | CurrentSymbol = createSymbol(); |
349 | CurrentSymbol->setIsCallSiteParameter(); |
350 | return CurrentSymbol; |
351 | case dwarf::DW_TAG_constant: |
352 | CurrentSymbol = createSymbol(); |
353 | CurrentSymbol->setIsConstant(); |
354 | return CurrentSymbol; |
355 | |
356 | // Scopes. |
357 | case dwarf::DW_TAG_catch_block: |
358 | CurrentScope = createScope(); |
359 | CurrentScope->setIsCatchBlock(); |
360 | return CurrentScope; |
361 | case dwarf::DW_TAG_lexical_block: |
362 | CurrentScope = createScope(); |
363 | CurrentScope->setIsLexicalBlock(); |
364 | return CurrentScope; |
365 | case dwarf::DW_TAG_try_block: |
366 | CurrentScope = createScope(); |
367 | CurrentScope->setIsTryBlock(); |
368 | return CurrentScope; |
369 | case dwarf::DW_TAG_compile_unit: |
370 | case dwarf::DW_TAG_skeleton_unit: |
371 | CurrentScope = createScopeCompileUnit(); |
372 | CompileUnit = static_cast<LVScopeCompileUnit *>(CurrentScope); |
373 | return CurrentScope; |
374 | case dwarf::DW_TAG_inlined_subroutine: |
375 | CurrentScope = createScopeFunctionInlined(); |
376 | return CurrentScope; |
377 | case dwarf::DW_TAG_namespace: |
378 | CurrentScope = createScopeNamespace(); |
379 | return CurrentScope; |
380 | case dwarf::DW_TAG_template_alias: |
381 | CurrentScope = createScopeAlias(); |
382 | return CurrentScope; |
383 | case dwarf::DW_TAG_array_type: |
384 | CurrentScope = createScopeArray(); |
385 | return CurrentScope; |
386 | case dwarf::DW_TAG_call_site: |
387 | case dwarf::DW_TAG_GNU_call_site: |
388 | CurrentScope = createScopeFunction(); |
389 | CurrentScope->setIsCallSite(); |
390 | return CurrentScope; |
391 | case dwarf::DW_TAG_entry_point: |
392 | CurrentScope = createScopeFunction(); |
393 | CurrentScope->setIsEntryPoint(); |
394 | return CurrentScope; |
395 | case dwarf::DW_TAG_subprogram: |
396 | CurrentScope = createScopeFunction(); |
397 | CurrentScope->setIsSubprogram(); |
398 | return CurrentScope; |
399 | case dwarf::DW_TAG_subroutine_type: |
400 | CurrentScope = createScopeFunctionType(); |
401 | return CurrentScope; |
402 | case dwarf::DW_TAG_label: |
403 | CurrentScope = createScopeFunction(); |
404 | CurrentScope->setIsLabel(); |
405 | return CurrentScope; |
406 | case dwarf::DW_TAG_class_type: |
407 | CurrentScope = createScopeAggregate(); |
408 | CurrentScope->setIsClass(); |
409 | return CurrentScope; |
410 | case dwarf::DW_TAG_structure_type: |
411 | CurrentScope = createScopeAggregate(); |
412 | CurrentScope->setIsStructure(); |
413 | return CurrentScope; |
414 | case dwarf::DW_TAG_union_type: |
415 | CurrentScope = createScopeAggregate(); |
416 | CurrentScope->setIsUnion(); |
417 | return CurrentScope; |
418 | case dwarf::DW_TAG_enumeration_type: |
419 | CurrentScope = createScopeEnumeration(); |
420 | return CurrentScope; |
421 | case dwarf::DW_TAG_GNU_formal_parameter_pack: |
422 | CurrentScope = createScopeFormalPack(); |
423 | return CurrentScope; |
424 | case dwarf::DW_TAG_GNU_template_parameter_pack: |
425 | CurrentScope = createScopeTemplatePack(); |
426 | return CurrentScope; |
427 | case dwarf::DW_TAG_module: |
428 | CurrentScope = createScopeModule(); |
429 | return CurrentScope; |
430 | default: |
431 | // Collect TAGs not implemented. |
432 | if (options().getInternalTag() && Tag) |
433 | CompileUnit->addDebugTag(Target: Tag, Offset: CurrentOffset); |
434 | break; |
435 | } |
436 | |
437 | LLVM_DEBUG({ |
438 | dbgs() << "DWARF Tag not implemented: " << dwarf::TagString(Tag) << "\n" ; |
439 | }); |
440 | |
441 | return nullptr; |
442 | } |
443 | |
444 | // The Reader is the module that creates the logical view using the debug |
445 | // information contained in the binary file specified in the command line. |
446 | // This is the main entry point for the Reader and performs the following |
447 | // steps: |
448 | // - Process any patterns collected from the '--select' options. |
449 | // - For each compile unit in the debug information: |
450 | // * Create the logical elements (scopes, symbols, types, lines). |
451 | // * Collect debug ranges and debug locations. |
452 | // * Move the collected logical lines to their associated scopes. |
453 | // - Once all the compile units have been processed, traverse the scopes |
454 | // tree in order to: |
455 | // * Calculate symbol coverage. |
456 | // * Detect invalid ranges and locations. |
457 | // * "resolve" the logical elements. During this pass, the names and |
458 | // file information are updated, to reflect any dependency with other |
459 | // logical elements. |
460 | Error LVReader::doLoad() { |
461 | // Set current Reader instance. |
462 | setInstance(this); |
463 | |
464 | // Before any scopes creation, process any pattern specified by the |
465 | // --select and --select-offsets options. |
466 | patterns().addGenericPatterns(Patterns&: options().Select.Generic); |
467 | patterns().addOffsetPatterns(Patterns: options().Select.Offsets); |
468 | |
469 | // Add any specific element printing requests based on the element kind. |
470 | patterns().addRequest(Selection&: options().Select.Elements); |
471 | patterns().addRequest(Selection&: options().Select.Lines); |
472 | patterns().addRequest(Selection&: options().Select.Scopes); |
473 | patterns().addRequest(Selection&: options().Select.Symbols); |
474 | patterns().addRequest(Selection&: options().Select.Types); |
475 | |
476 | // Once we have processed the requests for any particular kind of elements, |
477 | // we need to update the report options, in order to have a default value. |
478 | patterns().updateReportOptions(); |
479 | |
480 | // Delegate the scope tree creation to the specific reader. |
481 | if (Error Err = createScopes()) |
482 | return Err; |
483 | |
484 | if (options().getInternalIntegrity() && !checkIntegrityScopesTree(Root)) |
485 | return llvm::make_error<StringError>(Args: "Duplicated elements in Scopes Tree" , |
486 | Args: inconvertibleErrorCode()); |
487 | |
488 | // Calculate symbol coverage and detect invalid debug locations and ranges. |
489 | Root->processRangeInformation(); |
490 | |
491 | // As the elements can depend on elements from a different compile unit, |
492 | // information such as name and file/line source information needs to be |
493 | // updated. |
494 | Root->resolveElements(); |
495 | |
496 | sortScopes(); |
497 | return Error::success(); |
498 | } |
499 | |
500 | // Default handler for a generic reader. |
501 | Error LVReader::doPrint() { |
502 | // Set current Reader instance. |
503 | setInstance(this); |
504 | |
505 | // Check for any '--report' request. |
506 | if (options().getReportExecute()) { |
507 | // Requested details. |
508 | if (options().getReportList()) |
509 | if (Error Err = printMatchedElements(/*UseMatchedElements=*/true)) |
510 | return Err; |
511 | // Requested only children. |
512 | if (options().getReportChildren() && !options().getReportParents()) |
513 | if (Error Err = printMatchedElements(/*UseMatchedElements=*/false)) |
514 | return Err; |
515 | // Requested (parents) or (parents and children). |
516 | if (options().getReportParents() || options().getReportView()) |
517 | if (Error Err = printScopes()) |
518 | return Err; |
519 | |
520 | return Error::success(); |
521 | } |
522 | |
523 | return printScopes(); |
524 | } |
525 | |
526 | Error LVReader::printScopes() { |
527 | if (bool DoPrint = |
528 | (options().getPrintExecute() || options().getComparePrint())) { |
529 | if (Error Err = createSplitFolder()) |
530 | return Err; |
531 | |
532 | // Start printing from the root. |
533 | bool DoMatch = options().getSelectGenericPattern() || |
534 | options().getSelectGenericKind() || |
535 | options().getSelectOffsetPattern(); |
536 | return Root->doPrint(Split: OutputSplit, Match: DoMatch, Print: DoPrint, OS); |
537 | } |
538 | |
539 | return Error::success(); |
540 | } |
541 | |
542 | Error LVReader::printMatchedElements(bool UseMatchedElements) { |
543 | if (Error Err = createSplitFolder()) |
544 | return Err; |
545 | |
546 | return Root->doPrintMatches(Split: OutputSplit, OS, UseMatchedElements); |
547 | } |
548 | |
549 | void LVReader::print(raw_ostream &OS) const { |
550 | OS << "LVReader\n" ; |
551 | LLVM_DEBUG(dbgs() << "PrintReader\n" ); |
552 | } |
553 | |