mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
81da9f4819
In order to create the code regions for llvm-mca to analyze, llvm-mca creates an AsmCodeRegionGenerator and calls AsmCodeRegionGenerator::parseCodeRegions(). Within this function, both an MCAsmParser and MCTargetAsmParser are created so that MCAsmParser::Run() can be used to create the code regions for us. These parser classes were created for llvm-mc so they are designed to emit code with an MCStreamer and MCTargetStreamer that are expected to be setup and passed into the MCAsmParser constructor. Because llvm-mca doesn’t want to emit any code, an MCStreamerWrapper class gets created instead and passed into the MCAsmParser constructor. This wrapper inherits from MCStreamer and overrides many of the emit methods to just do nothing. The exception is the emitInstruction() method which calls Regions.addInstruction(Inst). This works well and allows llvm-mca to utilize llvm-mc’s MCAsmParser to build our code regions, however there are a few directives which rely on the MCTargetStreamer. llvm-mc assumes that the MCStreamer that gets passed into the MCAsmParser’s constructor has a valid pointer to an MCTargetStreamer. Because llvm-mca doesn’t setup an MCTargetStreamer, when the parser encounters one of those directives, a segfault will occur. In x86, each one of these 7 directives will cause this segfault if they exist in the input assembly to llvm-mca: .cv_fpo_proc .cv_fpo_setframe .cv_fpo_pushreg .cv_fpo_stackalloc .cv_fpo_stackalign .cv_fpo_endprologue .cv_fpo_endproc I haven’t looked at other targets, but I wouldn’t be surprised if some of the other ones also have certain directives which could result in this same segfault. My proposed solution is to simply initialize an MCTargetStreamer after we initialize the MCStreamerWrapper. The MCTargetStreamer requires an ostream object, but we don’t actually want any of these directives to be emitted anywhere, so I use an ostream created with the nulls() function. Since this needs to happen after the MCStreamerWrapper has been initialized, it needs to happen within the AsmCodeRegionGenerator::parseCodeRegions() function. The MCTargetStreamer also needs an MCInstPrinter which is easiest to initialize within the main() function of llvm-mca. So this MCInstPrinter gets constructed within main() then passed into the parseCodeRegions() function as a parameter. (If you feel like it would be appropriate and possible to create the MCInstPrinter within the parseCodeRegions() function, then feel free to modify my solution. That would stop us from having to pass it into the function and would limit its scope / lifetime.) My solution stops the segfault from happening and still passes all of the current (expected) llvm-mca tests. I also added a new test for x86 that checks for this segfault on an input that includes one of the .cv_fpo directives (this test fails without my solution, but passes with it). As far as I can tell, all of the functions that I modified are only called from within llvm-mca so there shouldn’t be any worries about breaking other tools. Differential Revision: https://reviews.llvm.org/D102709
151 lines
5.2 KiB
C++
151 lines
5.2 KiB
C++
//===----------------------- CodeRegionGenerator.cpp ------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/// \file
|
|
///
|
|
/// This file defines classes responsible for generating llvm-mca
|
|
/// CodeRegions from various types of input. llvm-mca only analyzes CodeRegions,
|
|
/// so the classes here provide the input-to-CodeRegions translation.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CodeRegionGenerator.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCTargetOptions.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/SMLoc.h"
|
|
#include <memory>
|
|
|
|
namespace llvm {
|
|
namespace mca {
|
|
|
|
// This virtual dtor serves as the anchor for the CodeRegionGenerator class.
|
|
CodeRegionGenerator::~CodeRegionGenerator() {}
|
|
|
|
// A comment consumer that parses strings. The only valid tokens are strings.
|
|
class MCACommentConsumer : public AsmCommentConsumer {
|
|
public:
|
|
CodeRegions &Regions;
|
|
|
|
MCACommentConsumer(CodeRegions &R) : Regions(R) {}
|
|
void HandleComment(SMLoc Loc, StringRef CommentText) override;
|
|
};
|
|
|
|
// This class provides the callbacks that occur when parsing input assembly.
|
|
class MCStreamerWrapper final : public MCStreamer {
|
|
CodeRegions &Regions;
|
|
|
|
public:
|
|
MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
|
|
: MCStreamer(Context), Regions(R) {}
|
|
|
|
// We only want to intercept the emission of new instructions.
|
|
virtual void emitInstruction(const MCInst &Inst,
|
|
const MCSubtargetInfo & /* unused */) override {
|
|
Regions.addInstruction(Inst);
|
|
}
|
|
|
|
bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
|
|
return true;
|
|
}
|
|
|
|
void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
|
|
unsigned ByteAlignment) override {}
|
|
void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
|
|
uint64_t Size = 0, unsigned ByteAlignment = 0,
|
|
SMLoc Loc = SMLoc()) override {}
|
|
void emitGPRel32Value(const MCExpr *Value) override {}
|
|
void BeginCOFFSymbolDef(const MCSymbol *Symbol) override {}
|
|
void EmitCOFFSymbolStorageClass(int StorageClass) override {}
|
|
void EmitCOFFSymbolType(int Type) override {}
|
|
void EndCOFFSymbolDef() override {}
|
|
|
|
ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const {
|
|
return Regions.getInstructionSequence(Index);
|
|
}
|
|
};
|
|
|
|
void MCACommentConsumer::HandleComment(SMLoc Loc, StringRef CommentText) {
|
|
// Skip empty comments.
|
|
StringRef Comment(CommentText);
|
|
if (Comment.empty())
|
|
return;
|
|
|
|
// Skip spaces and tabs.
|
|
unsigned Position = Comment.find_first_not_of(" \t");
|
|
if (Position >= Comment.size())
|
|
// We reached the end of the comment. Bail out.
|
|
return;
|
|
|
|
Comment = Comment.drop_front(Position);
|
|
if (Comment.consume_front("LLVM-MCA-END")) {
|
|
// Skip spaces and tabs.
|
|
Position = Comment.find_first_not_of(" \t");
|
|
if (Position < Comment.size())
|
|
Comment = Comment.drop_front(Position);
|
|
Regions.endRegion(Comment, Loc);
|
|
return;
|
|
}
|
|
|
|
// Try to parse the LLVM-MCA-BEGIN comment.
|
|
if (!Comment.consume_front("LLVM-MCA-BEGIN"))
|
|
return;
|
|
|
|
// Skip spaces and tabs.
|
|
Position = Comment.find_first_not_of(" \t");
|
|
if (Position < Comment.size())
|
|
Comment = Comment.drop_front(Position);
|
|
// Use the rest of the string as a descriptor for this code snippet.
|
|
Regions.beginRegion(Comment, Loc);
|
|
}
|
|
|
|
Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions(
|
|
const std::unique_ptr<MCInstPrinter> &IP) {
|
|
MCTargetOptions Opts;
|
|
Opts.PreserveAsmComments = false;
|
|
MCStreamerWrapper Str(Ctx, Regions);
|
|
|
|
// Need to initialize an MCTargetStreamer otherwise
|
|
// certain asm directives will cause a segfault.
|
|
// Using nulls() so that anything emitted by the MCTagetStreamer
|
|
// doesn't show up in the llvm-mca output.
|
|
raw_ostream &OSRef = nulls();
|
|
formatted_raw_ostream FOSRef(OSRef);
|
|
TheTarget.createAsmTargetStreamer(Str, FOSRef, IP.get(),
|
|
/*IsVerboseAsm=*/true);
|
|
|
|
// Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
|
|
// comments.
|
|
std::unique_ptr<MCAsmParser> Parser(
|
|
createMCAsmParser(Regions.getSourceMgr(), Ctx, Str, MAI));
|
|
MCAsmLexer &Lexer = Parser->getLexer();
|
|
MCACommentConsumer CC(Regions);
|
|
Lexer.setCommentConsumer(&CC);
|
|
// Enable support for MASM literal numbers (example: 05h, 101b).
|
|
Lexer.setLexMasmIntegers(true);
|
|
|
|
std::unique_ptr<MCTargetAsmParser> TAP(
|
|
TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts));
|
|
if (!TAP)
|
|
return make_error<StringError>(
|
|
"This target does not support assembly parsing.",
|
|
inconvertibleErrorCode());
|
|
Parser->setTargetParser(*TAP);
|
|
Parser->Run(false);
|
|
|
|
// Set the assembler dialect from the input. llvm-mca will use this as the
|
|
// default dialect when printing reports.
|
|
AssemblerDialect = Parser->getAssemblerDialect();
|
|
return Regions;
|
|
}
|
|
|
|
} // namespace mca
|
|
} // namespace llvm
|