diff --git a/llvm/include/llvm/MC/MCInstPrinter.h b/llvm/include/llvm/MC/MCInstPrinter.h --- a/llvm/include/llvm/MC/MCInstPrinter.h +++ b/llvm/include/llvm/MC/MCInstPrinter.h @@ -11,6 +11,7 @@ #include "llvm/Support/Format.h" #include +#include namespace llvm { @@ -34,6 +35,116 @@ } // end namespace HexStyle +enum class MarkupType { + Reg, + Imm, + Mem, +}; + +/// MarkupSpan represents a marked up range in the disassembly. For example: +/// +/// Pos InnerPos +/// v v +/// ... )> ... +/// ~~~~~~~~~~~~~~~ InnerLenth +/// ~~~~~~~~~~~~~~~~~~~~~ Length +/// +struct MarkupSpan { + MarkupType Type; + /// The offset of the beginning of the marked up range in the stream. + size_t Pos; + /// The length of the marked up range. + size_t Length; + /// The offset of the beginning of the inner text in the stream. + size_t InnerPos; + /// The length of the inner text. + size_t InnerLength; + /// Marked up ranges in the inner text. For example: + /// + /// movq , )> + /// ^^^^^^^^^^ ^^^^^^^^^----------^^ + /// 1st span 2nd span (*) + /// ^^^^^^^^^^ + /// 1st span in the (*)'s InnerSpans + /// + /// In this example, the top-level InnerSpans contains two elements that + /// represent and respectively. InnerSpans in the + /// latter one contains a span which represents . + std::vector InnerSpans; + + MarkupSpan(MarkupType Type, size_t Pos, size_t Length, size_t InnerPos, + size_t InnerLength) + : Type(Type), Pos(Pos), Length(Length), InnerPos(InnerPos), + InnerLength(InnerLength) {} +}; + +/// An object which creates a new markup range. When it is streamed to a +/// raw_ostream, along with printing the beginning part of markup string +/// (e.g. " *> &Spans; + MarkupType Type; + +public: + MarkupStart(bool Enabled, std::stack *> &Spans, + MarkupType Type) + : Enabled(Enabled), Spans(Spans), Type(Type) {} + friend raw_ostream &operator<<(raw_ostream &OS, const MarkupStart &M); +}; + +/// An object which closes the current markup range created by MarkupStart. +/// +/// When it is streamed to a raw_ostream, along with printing the ending part +/// of markup string (">"), the last MarkupSpan in Spans will be updated with +/// correct values. +/// +/// MarkupEnd should be instantiated through MCInstPrinter::endMarkup(): +/// +/// OS << startMarkup(MarkupType::Reg) << "%rax" << endMarkup(); +/// +class MarkupEnd { + bool Enabled; + std::stack *> &Spans; + +public: + MarkupEnd(bool Enabled, std::stack *> &Spans) + : Enabled(Enabled), Spans(Spans) {} + friend raw_ostream &operator<<(raw_ostream &OS, const MarkupEnd &M); +}; + +/// A RAII object which automatically closes the markup range to be used +/// through MCInstPrinter::withMarkup() like: +/// +/// withMarkup(OS, MarkupType::Reg) << "%rax"; +/// +/// This is equivalent to: +/// +/// OS << startMarkup(MarkupType::Reg) << "%rax" << endMarkup(); +/// +class WithMarkup { + raw_ostream &OS; + bool Enabled; + std::stack *> &Spans; + +public: + WithMarkup(raw_ostream &OS, bool Enabled, + std::stack *> &Spans, MarkupType Type); + ~WithMarkup(); + + template WithMarkup &operator<<(const T &O) { + OS << O; + return *this; + } +}; + /// This is an instance of a target assembly language printer that /// converts an MCInst to valid target assembly syntax. class MCInstPrinter { @@ -55,6 +166,12 @@ /// Which style to use for printing hexadecimal values. HexStyle::Style PrintHexStyle = HexStyle::C; + /// A stack of pointers which points to &MS set by setMarkupSpans or + /// InnerSpans of unclosed MarkupSpans. This is mutable because it will be + /// updated in a const virtual method "printRegName" and making it non-const + /// requires relatively large changes to the existing code base. + mutable std::stack *> MarkupSpans; + /// Utility function for printing annotations. void printAnnotation(raw_ostream &OS, StringRef Annot); @@ -85,8 +202,18 @@ bool getUseMarkup() const { return UseMarkup; } void setUseMarkup(bool Value) { UseMarkup = Value; } + /// Set the vector to write marked up ranges to. + void setMarkupSpans(std::vector &MS) { + while (!MarkupSpans.empty()) + MarkupSpans.pop(); + MarkupSpans.push(&MS); + } + /// Utility functions to make adding mark ups simpler. StringRef markup(StringRef s) const; + MarkupStart startMarkup(MarkupType Type) const; + MarkupEnd endMarkup() const; + WithMarkup withMarkup(raw_ostream &OS, MarkupType Type) const; bool getPrintImmHex() const { return PrintImmHex; } void setPrintImmHex(bool Value) { PrintImmHex = Value; } diff --git a/llvm/lib/MC/MCInstPrinter.cpp b/llvm/lib/MC/MCInstPrinter.cpp --- a/llvm/lib/MC/MCInstPrinter.cpp +++ b/llvm/lib/MC/MCInstPrinter.cpp @@ -117,3 +117,66 @@ } llvm_unreachable("unsupported print style"); } + +MarkupStart MCInstPrinter::startMarkup(MarkupType Type) const { + return MarkupStart(getUseMarkup(), MarkupSpans, Type); +} + +MarkupEnd MCInstPrinter::endMarkup() const { + return MarkupEnd(getUseMarkup(), MarkupSpans); +} + +WithMarkup MCInstPrinter::withMarkup(raw_ostream &OS, MarkupType Type) const { + return WithMarkup(OS, getUseMarkup(), MarkupSpans, Type); +} + +raw_ostream &llvm::operator<<(raw_ostream &OS, const MarkupStart &M) { + if (M.Enabled) { + StringRef TypeStr; + switch (M.Type) { + case MarkupType::Imm: + TypeStr = "imm"; + break; + case MarkupType::Reg: + TypeStr = "reg"; + break; + case MarkupType::Mem: + TypeStr = "mem"; + break; + } + + size_t Length = 2 + TypeStr.size(); + if (!M.Spans.empty()) { + // Length and InnerLength will be set later. + M.Spans.top()->emplace_back(M.Type, OS.tell(), 0, OS.tell() + Length, 0); + M.Spans.push(&M.Spans.top()->back().InnerSpans); + } + OS << "<" << TypeStr << ":"; + } + + return OS; +} + +raw_ostream &llvm::operator<<(raw_ostream &OS, const MarkupEnd &M) { + if (M.Enabled) { + if (!M.Spans.empty()) { + assert(M.Spans.size() > 1 && "Missing the corresponding markupStart()."); + M.Spans.pop(); + MarkupSpan &Span = M.Spans.top()->back(); + Span.Length = OS.tell() - Span.Pos + 1; + Span.InnerLength = OS.tell() - Span.InnerPos; + } + OS << ">"; + } + + return OS; +} + +WithMarkup::WithMarkup(raw_ostream &OS, bool Enabled, + std::stack *> &Spans, + MarkupType Type) + : OS(OS), Enabled(Enabled), Spans(Spans) { + OS << MarkupStart(Enabled, Spans, Type); +} + +WithMarkup::~WithMarkup() { OS << MarkupEnd(Enabled, Spans); }