1 | //===----------------------- CodeRegionGenerator.cpp ------------*- C++ -*-===// |
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 | /// \file |
9 | /// |
10 | /// This file defines classes responsible for generating llvm-mca |
11 | /// CodeRegions from various types of input. llvm-mca only analyzes CodeRegions, |
12 | /// so the classes here provide the input-to-CodeRegions translation. |
13 | // |
14 | //===----------------------------------------------------------------------===// |
15 | |
16 | #include "CodeRegionGenerator.h" |
17 | #include "llvm/ADT/ArrayRef.h" |
18 | #include "llvm/ADT/StringRef.h" |
19 | #include "llvm/MC/MCParser/MCTargetAsmParser.h" |
20 | #include "llvm/MC/MCTargetOptions.h" |
21 | #include "llvm/Support/Error.h" |
22 | #include "llvm/Support/SMLoc.h" |
23 | #include <memory> |
24 | |
25 | namespace llvm { |
26 | namespace mca { |
27 | |
28 | // This virtual dtor serves as the anchor for the CodeRegionGenerator class. |
29 | CodeRegionGenerator::~CodeRegionGenerator() {} |
30 | |
31 | Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions( |
32 | const std::unique_ptr<MCInstPrinter> &IP) { |
33 | MCTargetOptions Opts; |
34 | Opts.PreserveAsmComments = false; |
35 | CodeRegions &Regions = getRegions(); |
36 | MCStreamerWrapper *Str = getMCStreamer(); |
37 | |
38 | // Need to initialize an MCTargetStreamer otherwise |
39 | // certain asm directives will cause a segfault. |
40 | // Using nulls() so that anything emitted by the MCTargetStreamer |
41 | // doesn't show up in the llvm-mca output. |
42 | raw_ostream &OSRef = nulls(); |
43 | formatted_raw_ostream FOSRef(OSRef); |
44 | TheTarget.createAsmTargetStreamer(S&: *Str, OS&: FOSRef, InstPrint: IP.get(), |
45 | /*IsVerboseAsm=*/true); |
46 | |
47 | // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM |
48 | // comments. |
49 | std::unique_ptr<MCAsmParser> Parser( |
50 | createMCAsmParser(Regions.getSourceMgr(), Ctx, *Str, MAI)); |
51 | MCAsmLexer &Lexer = Parser->getLexer(); |
52 | MCACommentConsumer *CCP = getCommentConsumer(); |
53 | Lexer.setCommentConsumer(CCP); |
54 | // Enable support for MASM literal numbers (example: 05h, 101b). |
55 | Lexer.setLexMasmIntegers(true); |
56 | |
57 | std::unique_ptr<MCTargetAsmParser> TAP( |
58 | TheTarget.createMCAsmParser(STI, Parser&: *Parser, MII: MCII, Options: Opts)); |
59 | if (!TAP) |
60 | return make_error<StringError>( |
61 | Args: "This target does not support assembly parsing." , |
62 | Args: inconvertibleErrorCode()); |
63 | Parser->setTargetParser(*TAP); |
64 | Parser->Run(NoInitialTextSection: false); |
65 | |
66 | if (CCP->hadErr()) |
67 | return make_error<StringError>(Args: "There was an error parsing comments." , |
68 | Args: inconvertibleErrorCode()); |
69 | |
70 | // Set the assembler dialect from the input. llvm-mca will use this as the |
71 | // default dialect when printing reports. |
72 | AssemblerDialect = Parser->getAssemblerDialect(); |
73 | return Regions; |
74 | } |
75 | |
76 | void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc, |
77 | StringRef ) { |
78 | // Skip empty comments. |
79 | StringRef (CommentText); |
80 | if (Comment.empty()) |
81 | return; |
82 | |
83 | // Skip spaces and tabs. |
84 | unsigned Position = Comment.find_first_not_of(Chars: " \t" ); |
85 | if (Position >= Comment.size()) |
86 | // We reached the end of the comment. Bail out. |
87 | return; |
88 | |
89 | Comment = Comment.drop_front(N: Position); |
90 | if (Comment.consume_front(Prefix: "LLVM-MCA-END" )) { |
91 | // Skip spaces and tabs. |
92 | Position = Comment.find_first_not_of(Chars: " \t" ); |
93 | if (Position < Comment.size()) |
94 | Comment = Comment.drop_front(N: Position); |
95 | Regions.endRegion(Description: Comment, Loc); |
96 | return; |
97 | } |
98 | |
99 | // Try to parse the LLVM-MCA-BEGIN comment. |
100 | if (!Comment.consume_front(Prefix: "LLVM-MCA-BEGIN" )) |
101 | return; |
102 | |
103 | // Skip spaces and tabs. |
104 | Position = Comment.find_first_not_of(Chars: " \t" ); |
105 | if (Position < Comment.size()) |
106 | Comment = Comment.drop_front(N: Position); |
107 | // Use the rest of the string as a descriptor for this code snippet. |
108 | Regions.beginRegion(Description: Comment, Loc); |
109 | } |
110 | |
111 | void InstrumentRegionCommentConsumer::HandleComment(SMLoc Loc, |
112 | StringRef ) { |
113 | // Skip empty comments. |
114 | StringRef (CommentText); |
115 | if (Comment.empty()) |
116 | return; |
117 | |
118 | // Skip spaces and tabs. |
119 | unsigned Position = Comment.find_first_not_of(Chars: " \t" ); |
120 | if (Position >= Comment.size()) |
121 | // We reached the end of the comment. Bail out. |
122 | return; |
123 | Comment = Comment.drop_front(N: Position); |
124 | |
125 | // Bail out if not an MCA style comment |
126 | if (!Comment.consume_front(Prefix: "LLVM-MCA-" )) |
127 | return; |
128 | |
129 | // Skip AnalysisRegion comments |
130 | if (Comment.consume_front(Prefix: "BEGIN" ) || Comment.consume_front(Prefix: "END" )) |
131 | return; |
132 | |
133 | if (IM.shouldIgnoreInstruments()) |
134 | return; |
135 | |
136 | auto [InstrumentKind, Data] = Comment.split(Separator: " " ); |
137 | |
138 | // An error if not of the form LLVM-MCA-TARGET-KIND |
139 | if (!IM.supportsInstrumentType(Type: InstrumentKind)) { |
140 | if (InstrumentKind.empty()) |
141 | SM.PrintMessage( |
142 | Loc, Kind: llvm::SourceMgr::DK_Error, |
143 | Msg: "No instrumentation kind was provided in LLVM-MCA comment" ); |
144 | else |
145 | SM.PrintMessage(Loc, Kind: llvm::SourceMgr::DK_Error, |
146 | Msg: "Unknown instrumentation type in LLVM-MCA comment: " + |
147 | InstrumentKind); |
148 | FoundError = true; |
149 | return; |
150 | } |
151 | |
152 | UniqueInstrument I = IM.createInstrument(Desc: InstrumentKind, Data); |
153 | if (!I) { |
154 | if (Data.empty()) |
155 | SM.PrintMessage(Loc, Kind: llvm::SourceMgr::DK_Error, |
156 | Msg: "Failed to create " + InstrumentKind + |
157 | " instrument with no data" ); |
158 | else |
159 | SM.PrintMessage(Loc, Kind: llvm::SourceMgr::DK_Error, |
160 | Msg: "Failed to create " + InstrumentKind + |
161 | " instrument with data: " + Data); |
162 | FoundError = true; |
163 | return; |
164 | } |
165 | |
166 | // End InstrumentType region if one is open |
167 | if (Regions.isRegionActive(Description: InstrumentKind)) |
168 | Regions.endRegion(Description: InstrumentKind, Loc); |
169 | // Start new instrumentation region |
170 | Regions.beginRegion(Description: InstrumentKind, Loc, Instrument: std::move(I)); |
171 | } |
172 | |
173 | } // namespace mca |
174 | } // namespace llvm |
175 | |