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/MCStreamer.h"
21 #include "llvm/MC/MCTargetOptions.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/SMLoc.h"
24 #include <memory>
25 
26 namespace llvm {
27 namespace mca {
28 
29 // This virtual dtor serves as the anchor for the CodeRegionGenerator class.
~CodeRegionGenerator()30 CodeRegionGenerator::~CodeRegionGenerator() {}
31 
32 // A comment consumer that parses strings.  The only valid tokens are strings.
33 class MCACommentConsumer : public AsmCommentConsumer {
34 public:
35   CodeRegions &Regions;
36 
MCACommentConsumer(CodeRegions & R)37   MCACommentConsumer(CodeRegions &R) : Regions(R) {}
38   void HandleComment(SMLoc Loc, StringRef CommentText) override;
39 };
40 
41 // This class provides the callbacks that occur when parsing input assembly.
42 class MCStreamerWrapper final : public MCStreamer {
43   CodeRegions &Regions;
44 
45 public:
MCStreamerWrapper(MCContext & Context,mca::CodeRegions & R)46   MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
47       : MCStreamer(Context), Regions(R) {}
48 
49   // We only want to intercept the emission of new instructions.
emitInstruction(const MCInst & Inst,const MCSubtargetInfo &)50   virtual void emitInstruction(const MCInst &Inst,
51                                const MCSubtargetInfo & /* unused */) override {
52     Regions.addInstruction(Inst);
53   }
54 
emitSymbolAttribute(MCSymbol * Symbol,MCSymbolAttr Attribute)55   bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
56     return true;
57   }
58 
emitCommonSymbol(MCSymbol * Symbol,uint64_t Size,unsigned ByteAlignment)59   void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
60                         unsigned ByteAlignment) override {}
emitZerofill(MCSection * Section,MCSymbol * Symbol=nullptr,uint64_t Size=0,unsigned ByteAlignment=0,SMLoc Loc=SMLoc ())61   void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
62                     uint64_t Size = 0, unsigned ByteAlignment = 0,
63                     SMLoc Loc = SMLoc()) override {}
emitGPRel32Value(const MCExpr * Value)64   void emitGPRel32Value(const MCExpr *Value) override {}
BeginCOFFSymbolDef(const MCSymbol * Symbol)65   void BeginCOFFSymbolDef(const MCSymbol *Symbol) override {}
EmitCOFFSymbolStorageClass(int StorageClass)66   void EmitCOFFSymbolStorageClass(int StorageClass) override {}
EmitCOFFSymbolType(int Type)67   void EmitCOFFSymbolType(int Type) override {}
EndCOFFSymbolDef()68   void EndCOFFSymbolDef() override {}
69 
GetInstructionSequence(unsigned Index) const70   ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const {
71     return Regions.getInstructionSequence(Index);
72   }
73 };
74 
HandleComment(SMLoc Loc,StringRef CommentText)75 void MCACommentConsumer::HandleComment(SMLoc Loc, StringRef CommentText) {
76   // Skip empty comments.
77   StringRef Comment(CommentText);
78   if (Comment.empty())
79     return;
80 
81   // Skip spaces and tabs.
82   unsigned Position = Comment.find_first_not_of(" \t");
83   if (Position >= Comment.size())
84     // We reached the end of the comment. Bail out.
85     return;
86 
87   Comment = Comment.drop_front(Position);
88   if (Comment.consume_front("LLVM-MCA-END")) {
89     // Skip spaces and tabs.
90     Position = Comment.find_first_not_of(" \t");
91     if (Position < Comment.size())
92       Comment = Comment.drop_front(Position);
93     Regions.endRegion(Comment, Loc);
94     return;
95   }
96 
97   // Try to parse the LLVM-MCA-BEGIN comment.
98   if (!Comment.consume_front("LLVM-MCA-BEGIN"))
99     return;
100 
101   // Skip spaces and tabs.
102   Position = Comment.find_first_not_of(" \t");
103   if (Position < Comment.size())
104     Comment = Comment.drop_front(Position);
105   // Use the rest of the string as a descriptor for this code snippet.
106   Regions.beginRegion(Comment, Loc);
107 }
108 
parseCodeRegions(const std::unique_ptr<MCInstPrinter> & IP)109 Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions(
110     const std::unique_ptr<MCInstPrinter> &IP) {
111   MCTargetOptions Opts;
112   Opts.PreserveAsmComments = false;
113   MCStreamerWrapper Str(Ctx, Regions);
114 
115   // Need to initialize an MCTargetStreamer otherwise
116   // certain asm directives will cause a segfault.
117   // Using nulls() so that anything emitted by the MCTagetStreamer
118   // doesn't show up in the llvm-mca output.
119   raw_ostream &OSRef = nulls();
120   formatted_raw_ostream FOSRef(OSRef);
121   TheTarget.createAsmTargetStreamer(Str, FOSRef, IP.get(),
122                                     /*IsVerboseAsm=*/true);
123 
124   // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
125   // comments.
126   std::unique_ptr<MCAsmParser> Parser(
127       createMCAsmParser(Regions.getSourceMgr(), Ctx, Str, MAI));
128   MCAsmLexer &Lexer = Parser->getLexer();
129   MCACommentConsumer CC(Regions);
130   Lexer.setCommentConsumer(&CC);
131   // Enable support for MASM literal numbers (example: 05h, 101b).
132   Lexer.setLexMasmIntegers(true);
133 
134   std::unique_ptr<MCTargetAsmParser> TAP(
135       TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts));
136   if (!TAP)
137     return make_error<StringError>(
138         "This target does not support assembly parsing.",
139         inconvertibleErrorCode());
140   Parser->setTargetParser(*TAP);
141   Parser->Run(false);
142 
143   // Set the assembler dialect from the input. llvm-mca will use this as the
144   // default dialect when printing reports.
145   AssemblerDialect = Parser->getAssemblerDialect();
146   return Regions;
147 }
148 
149 } // namespace mca
150 } // namespace llvm
151