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
25namespace llvm {
26namespace mca {
27
28// This virtual dtor serves as the anchor for the CodeRegionGenerator class.
29CodeRegionGenerator::~CodeRegionGenerator() = default;
30
31Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions(
32 const std::unique_ptr<MCInstPrinter> &IP, bool SkipFailures) {
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
46 // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
47 // comments.
48 std::unique_ptr<MCAsmParser> Parser(
49 createMCAsmParser(Regions.getSourceMgr(), Ctx, *Str, MAI));
50 AsmLexer &Lexer = Parser->getLexer();
51 MCACommentConsumer *CCP = getCommentConsumer();
52 Lexer.setCommentConsumer(CCP);
53 // Enable support for MASM literal numbers (example: 05h, 101b).
54 Lexer.setLexMasmIntegers(true);
55
56 std::unique_ptr<MCTargetAsmParser> TAP(
57 TheTarget.createMCAsmParser(STI, Parser&: *Parser, MII: MCII));
58 if (!TAP)
59 return make_error<StringError>(
60 Args: "This target does not support assembly parsing.",
61 Args: inconvertibleErrorCode());
62 Parser->setTargetParser(*TAP);
63 // Parser->Run() confusingly returns true on errors, in which case the errors
64 // were already shown to the user. SkipFailures implies continuing in the
65 // presence of any kind of failure within the parser, in which case failing
66 // input lines are not represented, but the rest of the input remains.
67 bool HadParseError = Parser->Run(NoInitialTextSection: false);
68 if (CCP->hadErr())
69 return make_error<StringError>(Args: "There was an error parsing comments.",
70 Args: inconvertibleErrorCode());
71
72 if (HadParseError && !SkipFailures) {
73 const char *Message = "Assembly input parsing had errors, use "
74 "-skip-unsupported-instructions=parse-failure "
75 "to drop failing lines from the input.";
76 return make_error<StringError>(Args&: Message, Args: inconvertibleErrorCode());
77 }
78
79 // Set the assembler dialect from the input. llvm-mca will use this as the
80 // default dialect when printing reports.
81 AssemblerDialect = Parser->getAssemblerDialect();
82 return Regions;
83}
84
85void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc,
86 StringRef CommentText) {
87 // Skip empty comments.
88 StringRef Comment(CommentText);
89 if (Comment.empty())
90 return;
91
92 // Skip spaces and tabs.
93 unsigned Position = Comment.find_first_not_of(Chars: " \t");
94 if (Position >= Comment.size())
95 // We reached the end of the comment. Bail out.
96 return;
97
98 Comment = Comment.drop_front(N: Position);
99 if (Comment.consume_front(Prefix: "LLVM-MCA-END")) {
100 // Skip spaces and tabs.
101 Position = Comment.find_first_not_of(Chars: " \t");
102 if (Position < Comment.size())
103 Comment = Comment.drop_front(N: Position);
104 Regions.endRegion(Description: Comment, Loc);
105 return;
106 }
107
108 // Try to parse the LLVM-MCA-BEGIN comment.
109 if (!Comment.consume_front(Prefix: "LLVM-MCA-BEGIN"))
110 return;
111
112 // Skip spaces and tabs.
113 Position = Comment.find_first_not_of(Chars: " \t");
114 if (Position < Comment.size())
115 Comment = Comment.drop_front(N: Position);
116 // Use the rest of the string as a descriptor for this code snippet.
117 Regions.beginRegion(Description: Comment, Loc);
118}
119
120void InstrumentRegionCommentConsumer::HandleComment(SMLoc Loc,
121 StringRef CommentText) {
122 // Skip empty comments.
123 StringRef Comment(CommentText);
124 if (Comment.empty())
125 return;
126
127 // Skip spaces and tabs.
128 unsigned Position = Comment.find_first_not_of(Chars: " \t");
129 if (Position >= Comment.size())
130 // We reached the end of the comment. Bail out.
131 return;
132 Comment = Comment.drop_front(N: Position);
133
134 // Bail out if not an MCA style comment
135 if (!Comment.consume_front(Prefix: "LLVM-MCA-"))
136 return;
137
138 // Skip AnalysisRegion comments
139 if (Comment.consume_front(Prefix: "BEGIN") || Comment.consume_front(Prefix: "END"))
140 return;
141
142 if (IM.shouldIgnoreInstruments())
143 return;
144
145 auto [InstrumentKind, Data] = Comment.split(Separator: " ");
146
147 // An error if not of the form LLVM-MCA-TARGET-KIND
148 if (!IM.supportsInstrumentType(Type: InstrumentKind)) {
149 if (InstrumentKind.empty())
150 SM.PrintMessage(
151 Loc, Kind: llvm::SourceMgr::DK_Error,
152 Msg: "No instrumentation kind was provided in LLVM-MCA comment");
153 else
154 SM.PrintMessage(Loc, Kind: llvm::SourceMgr::DK_Error,
155 Msg: "Unknown instrumentation type in LLVM-MCA comment: " +
156 InstrumentKind);
157 FoundError = true;
158 return;
159 }
160
161 UniqueInstrument I = IM.createInstrument(Desc: InstrumentKind, Data);
162 if (!I) {
163 if (Data.empty())
164 SM.PrintMessage(Loc, Kind: llvm::SourceMgr::DK_Error,
165 Msg: "Failed to create " + InstrumentKind +
166 " instrument with no data");
167 else
168 SM.PrintMessage(Loc, Kind: llvm::SourceMgr::DK_Error,
169 Msg: "Failed to create " + InstrumentKind +
170 " instrument with data: " + Data);
171 FoundError = true;
172 return;
173 }
174
175 // End InstrumentType region if one is open
176 if (Regions.isRegionActive(Description: InstrumentKind))
177 Regions.endRegion(Description: InstrumentKind, Loc);
178 // Start new instrumentation region
179 Regions.beginRegion(Description: InstrumentKind, Loc, Instrument: std::move(I));
180}
181
182} // namespace mca
183} // namespace llvm
184