| Index: include/llvm/Bitcode/NaCl/NaClObjDumpStream.h
|
| diff --git a/include/llvm/Bitcode/NaCl/NaClObjDumpStream.h b/include/llvm/Bitcode/NaCl/NaClObjDumpStream.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f75a6c7f9394ee250b84b7844adf211cf492a215
|
| --- /dev/null
|
| +++ b/include/llvm/Bitcode/NaCl/NaClObjDumpStream.h
|
| @@ -0,0 +1,1006 @@
|
| +//===- NaClObjDumpStream.h --------------------------------------*- C++ -*-===//
|
| +// Models an objdump stream (bitcode records/assembly code).
|
| +//
|
| +// The LLVM Compiler Infrastructure
|
| +//
|
| +// This file is distributed under the University of Illinois Open Source
|
| +// License. See LICENSE.TXT for details.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +
|
| +#ifndef LLVM_BITCODE_NACL_NACLOBJDUMPSTREAM_H
|
| +#define LLVM_BITCODE_NACL_NACLOBJDUMPSTREAM_H
|
| +
|
| +#include "llvm/ADT/STLExtras.h"
|
| +#include "llvm/Bitcode/NaCl/NaClBitcodeParser.h"
|
| +#include "llvm/Support/raw_ostream.h"
|
| +#include <vector>
|
| +#include <algorithm>
|
| +
|
| +namespace llvm {
|
| +namespace naclbitc {
|
| +
|
| +// The default string assumed for a tab.
|
| +static const char *DefaultTab = " ";
|
| +
|
| +/// Class that implements text indenting for pretty printing text.
|
| +class TextIndenter {
|
| +public:
|
| + /// Creates a text indenter that indents using the given tab.
|
| + TextIndenter(const char* Tab = DefaultTab)
|
| + : Indent(""),
|
| + Tab(Tab),
|
| + TabSize(strlen(Tab)),
|
| + NumTabs(0) {
|
| + Values.push_back(Indent);
|
| + }
|
| +
|
| + virtual ~TextIndenter() {}
|
| +
|
| + /// Returns the current indentation to use.
|
| + const std::string &GetIndent() const {
|
| + return Indent;
|
| + }
|
| +
|
| + /// Returns the indent with the given number of tabs.
|
| + const std::string &GetIndent(unsigned Count) {
|
| + if (Count >= Values.size()) {
|
| + // Indents not yet generated, fill in cache to size needed.
|
| + std::string Results;
|
| + if (Values.size() > 0) Results = Values.back();
|
| + for (size_t i = Values.size(); i <= Count; ++i) {
|
| + Results += Tab;
|
| + Values.push_back(Results);
|
| + }
|
| + }
|
| + return Values[Count];
|
| + }
|
| +
|
| + /// Increments the current indentation by one tab.
|
| + void Inc() {
|
| + ++NumTabs;
|
| + if (NumTabs < Values.size()) {
|
| + Indent = Values[NumTabs];
|
| + } else {
|
| + Indent += Tab;
|
| + Values.push_back(Indent);
|
| + }
|
| + }
|
| +
|
| + /// Decrements the current indentation by one tab.
|
| + void Dec() {
|
| + // Be sure not to underflow!
|
| + if (NumTabs) {
|
| + --NumTabs;
|
| + Indent = Values[NumTabs];
|
| + }
|
| + }
|
| +
|
| + /// Returns the current number of tabs in the current indentation.
|
| + unsigned GetNumTabs() const {
|
| + return NumTabs;
|
| + }
|
| +
|
| + const char *GetTab() const {
|
| + return Tab;
|
| + }
|
| +
|
| + size_t GetTabSize() const {
|
| + return TabSize;
|
| + }
|
| +
|
| +private:
|
| + // The current indentation to use.
|
| + std::string Indent;
|
| + // The set of (previously computed) identations, based on the number
|
| + // of tabs.
|
| + std::vector<std::string> Values;
|
| + // The text defining a tab.
|
| + const char *Tab;
|
| + // The size of the tab.
|
| + size_t TabSize;
|
| + // The number of tabs currently being used.
|
| + unsigned NumTabs;
|
| +};
|
| +
|
| +class TextFormatter;
|
| +
|
| +/// This template class maintains a simply pool of directives for
|
| +/// a text formatter. Assumes that all elements in pool are associated
|
| +/// with the same formatter.
|
| +template<class Directive>
|
| +class DirectiveMemoryPool {
|
| +public:
|
| + DirectiveMemoryPool() {}
|
| +
|
| + ~DirectiveMemoryPool() {
|
| + DeleteContainerPointers(FreeList);
|
| + }
|
| +
|
| + Directive *Allocate(TextFormatter *Fmtr) {
|
| + if (FreeList.empty()) return new Directive(Fmtr);
|
| + Directive *Element = FreeList.back();
|
| + assert(&Element->GetFormatter() == Fmtr
|
| + && "Directive memory pool formatter mismatch");
|
| + FreeList.pop_back();
|
| + return Element;
|
| + }
|
| +
|
| + void Free(Directive *Dir) {
|
| + FreeList.push_back(Dir);
|
| + }
|
| +
|
| +private:
|
| + std::vector<Directive*> FreeList;
|
| +};
|
| +
|
| +/// This class defines a simple formatter for a stream that consists
|
| +/// of a sequence of instructions. In general, this class assumes that
|
| +/// instructions are a single line. In addition, some instructions
|
| +/// define a block around a sequence of instructions. Each block of
|
| +/// instructions is indented to clarify the block structure over that
|
| +/// set of instructions.
|
| +///
|
| +/// To handle line indentation of blocks, this class inherits class
|
| +/// TextIndenter. The TextIndenter controls how blocks of lines are
|
| +/// indented. At the beginning of a block, the user should call method
|
| +/// Inc. It should then write out the sequence of instructions to be
|
| +/// indented. Then, after the last indented instruction of the block
|
| +/// is printed, the user should call method Dec. Nested blocks are
|
| +/// handled by nesting methods Inc and Dec appropriately.
|
| +///
|
| +/// This class mainly focuses on the tools needed to format an
|
| +/// instruction, given a specified viewing width. The issue is that
|
| +/// while instructions should be on a single line, some instructions
|
| +/// are too wide to fit into the viewing width. Hence, we need a way
|
| +/// to deal with line overflow.
|
| +///
|
| +/// The way this class handles the line overflow problem is to
|
| +/// basically force the user to break up the output into a sequence of
|
| +/// tokens. This is done using two streams. The text stream is used to
|
| +/// buffer tokens. The base stream is the stream to write tokens to
|
| +/// once they have been identified and positioned. The goal of the
|
| +/// formatter is to decide where the instruction text should be cut
|
| +/// (i.e. between tokens), so that the instruction does not overflow
|
| +/// the viewing width. It also handles the automatic insertion of line
|
| +/// indentation into the base stream when needed.
|
| +///
|
| +/// To make it easy to print tokens to the text stream, we use text
|
| +/// directives. Whenever a text directive is written to the text
|
| +/// stream, it is assumed that all text written to the text stream
|
| +/// (since the last directive) is an (indivisible) token. The first
|
| +/// thing directives do is move the token from the text stream (if
|
| +/// present) to the base stream.
|
| +///
|
| +/// In addition to defining tokens, text directives can also be
|
| +/// defined to query the formatter state and apply an appropriate
|
| +/// action. An example of this is the space text directive. It only
|
| +/// adds a space if the formatter isn't at the beginning of a new line
|
| +/// (since a newline can act as whitespace).
|
| +///
|
| +/// The text formatter also has a notion of clustering
|
| +/// tokens. Clustering forces a consecutive sequence of tokens to
|
| +/// be treated as a single token, when deciding where to wrap long
|
| +/// lines. In particular, clustered tokens will not be broken up
|
| +/// unless there is no other way to print them, because the cluster is
|
| +/// larger than what can fit in a line.
|
| +///
|
| +/// Clustering is implemented using two passes. In the first pass, the
|
| +/// sequence of tokens/directives are collected to find the clustered
|
| +/// text. They are also applied to collect any text internal to the
|
| +/// directives. Actions that can change (intraline) indenting are
|
| +/// turned off.
|
| +///
|
| +/// Once the first pass is done, the second pass starts. It begins
|
| +/// with a check to see if the clustered text can fit on the current
|
| +/// line, and adds a newline if necessary. Then it replays the
|
| +/// directives to put the tokens of the cluster into the base stream.
|
| +/// The replay also changes (intraline) indenting as necessary.
|
| +///
|
| +/// Clusters can be nested. In such cases, they are stripped one layer
|
| +/// per pass. Nested clusters are replayed after line wrapping of
|
| +/// outer clusters have been resolved.
|
| +class TextFormatter : public TextIndenter {
|
| +public:
|
| +
|
| + /// Creates a text formatter to print instructions onto the given
|
| + /// Base Stream. The viewing width is defined by LineWidth. The
|
| + /// given tab is the assumed value for tab characters and line
|
| + /// indents.
|
| + explicit TextFormatter(raw_ostream &BaseStream,
|
| + unsigned LineWidth = 80,
|
| + const char *Tab = DefaultTab);
|
| +
|
| + ~TextFormatter() override;
|
| +
|
| + /// Returns the user-level text stream of the formatter that tokens
|
| + /// should be written to.
|
| + raw_ostream &Tokens() {
|
| + return TextStream;
|
| + }
|
| +
|
| + /// Changes the line width to the given value.
|
| + void SetLineWidth(unsigned NewLineWidth) {
|
| + LineWidth = NewLineWidth;
|
| + if (MinLineWidth > LineWidth) MinLineWidth = LineWidth;
|
| + }
|
| +
|
| + // Changes the (default) line-wrap, continuation indent.
|
| + void SetContinuationIndent(const std::string &Indent) {
|
| + ContinuationIndent = Indent;
|
| + }
|
| +
|
| + /// Base class for all directives. The basic functionality is that it
|
| + /// has a reference to the text formatter, and is applied by calling
|
| + /// method apply. This method apply is encorporated into the
|
| + /// stream operator<<, so that they can be used as part of the
|
| + /// streamed output.
|
| + ///
|
| + /// Method apply extracts any tokens in the text stream. Moves them
|
| + /// into the base stream. Finally, it calls virtual method MyApply
|
| + /// to do the actions of the directive.
|
| + class Directive {
|
| + Directive(const Directive&) LLVM_DELETED_FUNCTION;
|
| + void operator=(const Directive&) LLVM_DELETED_FUNCTION;
|
| + public:
|
| + /// Creates a directive for the given stream.
|
| + explicit Directive(TextFormatter *Formatter)
|
| + : Formatter(Formatter) {}
|
| +
|
| + virtual ~Directive() {}
|
| +
|
| + /// Returns the formatter associated with the directive.
|
| + TextFormatter &GetFormatter() const {
|
| + return *Formatter;
|
| + }
|
| +
|
| + /// Does action of directive.
|
| + void Apply() const {
|
| + // Start by writing token if Tokens() buffer (if non-empty).
|
| + Formatter->WriteToken();
|
| + // Apply the directive.
|
| + MyApply(false);
|
| + // Save the directive for replay if we are clustering.
|
| + MaybeSaveForReplay();
|
| + }
|
| +
|
| + protected:
|
| + // The formatter associated with the directive.
|
| + TextFormatter *Formatter;
|
| +
|
| + // Like Apply, but called instead of Apply whey doing a replay.
|
| + void Reapply() const;
|
| +
|
| + // Does directive specific action. Replay is true only if the
|
| + // directive was in a cluster, and it is being called to replay
|
| + // the directive a second time.
|
| + virtual void MyApply(bool Replay) const = 0;
|
| +
|
| + // Adds the directive to the clustered directives if appropriate
|
| + // (i.e. inside a cluster).
|
| + virtual void MaybeSaveForReplay() const {
|
| + if (IsClustering()) AppendForReplay(this);
|
| + }
|
| +
|
| + // ***********************************************************
|
| + // Note: The following have been added so that derived classes
|
| + // have public access to protected text formatter methods.
|
| + // (Otherwise, you get a compiler error that they are protected
|
| + // in derived classes).
|
| + // ***********************************************************
|
| +
|
| + bool IsClustering() const {
|
| + return Formatter->IsClustering();
|
| + }
|
| +
|
| + unsigned GetClusteringLevel() const {
|
| + return Formatter->GetClusteringLevel();
|
| + }
|
| +
|
| + void AppendForReplay(const Directive *D) const {
|
| + Formatter->AppendForReplay(D);
|
| + }
|
| +
|
| + raw_ostream &Tokens() const {
|
| + return Formatter->Tokens();
|
| + }
|
| +
|
| + void WriteToken(const std::string &Token) const {
|
| + Formatter->WriteToken(Token);
|
| + }
|
| +
|
| + void WriteEndline() const {
|
| + Formatter->WriteEndline();
|
| + }
|
| +
|
| + void PopIndent() const {
|
| + Formatter->PopIndent();
|
| + }
|
| +
|
| + void PushIndent() const {
|
| + Formatter->PushIndent();
|
| + }
|
| +
|
| + bool AddLineWrapIfNeeded(unsigned TextSize) const {
|
| + return Formatter->AddLineWrapIfNeeded(TextSize);
|
| + }
|
| +
|
| + void StartClustering() const {
|
| + Formatter->StartClustering();
|
| + }
|
| +
|
| + void FinishClustering() const {
|
| + Formatter->FinishClustering();
|
| + }
|
| + };
|
| +
|
| +protected:
|
| + // The base stream to send formatted text to.
|
| + raw_ostream &BaseStream;
|
| + // Buffer that holds token text.
|
| + std::string TextBuffer;
|
| + // The user-level stream for (token) text and directives.
|
| + raw_string_ostream TextStream;
|
| + // The buffered token waiting to be flushed to the base stream.
|
| + // std::string BufferedToken;
|
| + // The expected line width the formatter should try and match.
|
| + unsigned LineWidth;
|
| + // The current position (i.e. column of previous character on line)
|
| + // associated with the current line in the base stream.
|
| + unsigned LinePosition;
|
| + // The stack of (intraline) indents added by PushIndent.
|
| + std::vector<unsigned> IntralineIndents;
|
| + // The current intraline indent to use.
|
| + unsigned CurrentIndent;
|
| + // The minimum line width. Used to limit indents so that there
|
| + // will always be at least this amount of space on the line.
|
| + unsigned MinLineWidth;
|
| + // True if no characters have moved to the base stream for the
|
| + // instruction being printed. Note: This is different than
|
| + // LinePosition == 0. The latter can be true, when
|
| + // AtInstructionBeginning is false, if the current line is a
|
| + // continuation line caused by line overflow.
|
| + bool AtInstructionBeginning;
|
| + // The indent string to use for indenting each line of the instruction.
|
| + std::string LineIndent;
|
| + // The indent to add on continuation (i.e. overflow) lines for an
|
| + // instruction.
|
| + std::string ContinuationIndent;
|
| + // Counts how nested we are within clustering directives.
|
| + unsigned ClusteringLevel;
|
| + // Contains the number of columns needed to hold the generated text,
|
| + // while clustering. Computed so that we can test if it fits on the
|
| + // current line.
|
| + unsigned ClusteredTextSize;
|
| + // Contains the sequence of directives (including tokens) during
|
| + // clustering, so that it can be replayed after we determine if a
|
| + // new line needs to be added.
|
| + std::vector<const Directive*> ClusteredDirectives;
|
| +
|
| + // Returns if we are currently inside a cluster.
|
| + bool IsClustering() const {
|
| + return ClusteringLevel > 0;
|
| + }
|
| +
|
| + // Returns the clustering level of the formatter.
|
| + unsigned GetClusteringLevel() const {
|
| + return ClusteringLevel;
|
| + }
|
| +
|
| + // Appends the given directive to the list of directives to
|
| + // replay, if clustering.
|
| + void AppendForReplay(const Directive *D) {
|
| + ClusteredDirectives.push_back(D);
|
| + }
|
| +
|
| + /// Writes out the token in the text stream. If necessary, moves to
|
| + // a new line first.
|
| + void WriteToken() {
|
| + WriteToken(GetToken());
|
| + }
|
| +
|
| + /// Writes out the given token. If necessary, moves to a new line first.
|
| + void WriteToken(const std::string &Token) {
|
| + if (Token.empty()) return;
|
| + AddLineWrapIfNeeded(Token.size());
|
| + Write(Token);
|
| + }
|
| +
|
| + // Extracts the text that has been added to the text stream, and
|
| + // returns it.
|
| + std::string GetToken();
|
| +
|
| + /// Writes out a non-newline character to the base stream.
|
| + void Write(char ch);
|
| +
|
| + /// Writes out a string.
|
| + void Write(const std::string &Text);
|
| +
|
| + /// Starts a new cluster of tokens.
|
| + /// Note: We do not allow nested clusterings.
|
| + void StartClustering();
|
| +
|
| + /// Ends the existing cluster of tokens.
|
| + void FinishClustering();
|
| +
|
| + /// Adds a newline that ends the current instruction being printed.
|
| + void WriteEndline();
|
| +
|
| + /// Called just before the first character on a line is printed, to
|
| + /// add indentation text for the line.
|
| + virtual void WriteLineIndents();
|
| +
|
| + /// Analyzes the formatter state to determine if a token of the
|
| + /// given TextSize can fit on the current line. If it can't fit,
|
| + /// it wraps the input line. Returns true only if line wrap was
|
| + /// added by this method.
|
| + bool AddLineWrapIfNeeded(unsigned TextSize) {
|
| + if (IsClustering()) {
|
| + // Don't consider line wrapping until all text is clustered. That
|
| + // way we have full information on what we should do.
|
| + return false;
|
| + } else {
|
| + // If the text fits on the current line, there is nothing to do.
|
| + // Otherwise, we should add a newline, unless we are already
|
| + // at the new line. If we are already at the newline, we can't
|
| + // split up the token, so just allow line overflow.
|
| + if (LinePosition + TextSize <= LineWidth
|
| + || LinePosition == 0)
|
| + return false;
|
| + // If reached, it must not fit, so add a line wrap.
|
| + Write('\n');
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + /// Pops the current (intraline) indentation and restores it to the
|
| + /// previous indentation. Used to restore the indentation of
|
| + /// parenthesized subexpressions, where this should be called on the
|
| + /// close parenthesis.
|
| + void PopIndent() {
|
| + // Be pragmatic. If there is underflow, assume that it was due to
|
| + // the fact that the caller of this text formatter used a close
|
| + // directive for a pair of matching parenthesis that crossed
|
| + // multiple (i.e. instruction) lines.
|
| + if (IsClustering() || IntralineIndents.empty()) return;
|
| + CurrentIndent = IntralineIndents.back();
|
| + IntralineIndents.pop_back();
|
| + }
|
| +
|
| + /// Pushes the current (intraline) indentation to match the current
|
| + /// position within the line. Used to indent parenthesized
|
| + /// subexpressions, where this should be called on the open
|
| + /// parenthesis.
|
| + void PushIndent() {
|
| + if (IsClustering()) return;
|
| + IntralineIndents.push_back(CurrentIndent);
|
| + CurrentIndent = FixIndentValue(LinePosition);
|
| + }
|
| +
|
| + /// Fixes the given position, to the appropriate value for setting
|
| + /// CurrentIndent. That is, it makes sure there is always MinLineWidth
|
| + /// printable characters on a line.
|
| + unsigned FixIndentValue(unsigned Position) {
|
| + return std::min(Position, LineWidth - MinLineWidth);
|
| + }
|
| +
|
| +private:
|
| + /// Directive that generates temporary instances to hold tokens from
|
| + /// the Tokens() stream. Generated when GetToken() is called. Used
|
| + /// during clustering to regenerate the corresponding extracted
|
| + /// tokens during a replay.
|
| + class GetTokenDirective : public Directive {
|
| + friend class DirectiveMemoryPool<GetTokenDirective>;
|
| + GetTokenDirective(TextFormatter *Formatter)
|
| + : Directive(Formatter) {}
|
| +
|
| + public:
|
| + ~GetTokenDirective() override {}
|
| +
|
| + /// Allocates an instance of a GetTokenDirective.
|
| + /// Note: Will be reclaimed when MyApply is called.
|
| + static Directive *Allocate(TextFormatter *Formatter,
|
| + const std::string &Text);
|
| +
|
| + protected:
|
| + void MyApply(bool Replay) const override {
|
| + WriteToken(Text);
|
| + if (!IsClustering())
|
| + Formatter->GetTokenFreeList.Free(const_cast<GetTokenDirective*>(this));
|
| + }
|
| +
|
| + private:
|
| + // The token text.
|
| + std::string Text;
|
| + };
|
| +
|
| + // The set of freed GetTokenDirectives, that can be reused.
|
| + DirectiveMemoryPool<GetTokenDirective> GetTokenFreeList;
|
| +};
|
| +
|
| +inline raw_ostream &operator<<(raw_ostream &Stream,
|
| + const TextFormatter::Directive &Directive) {
|
| + // Be sure that the directive is defined for the given stream,
|
| + // before applying the directive, since directives may have
|
| + // different meanings on different text streams.
|
| + assert(&Stream == &Directive.GetFormatter().Tokens());
|
| + Directive.Apply();
|
| + return Stream;
|
| +}
|
| +
|
| +/// Defines a directive that only tokenizes the text in the Tokens()
|
| +/// stream.
|
| +class TokenizeTextDirective : public TextFormatter::Directive {
|
| +public:
|
| + explicit TokenizeTextDirective(TextFormatter *Formatter)
|
| + : TextFormatter::Directive(Formatter) {}
|
| +
|
| + ~TokenizeTextDirective() override {}
|
| +
|
| +protected:
|
| + void MyApply(bool Replay) const override {}
|
| +};
|
| +
|
| +/// Defines a token which doesn't need whitespace on either side of
|
| +/// the token to be a valid token (such as punctuation).
|
| +class TokenTextDirective : public TextFormatter::Directive {
|
| +public:
|
| + TokenTextDirective(TextFormatter *Formatter, const std::string &Str)
|
| + : TextFormatter::Directive(Formatter), Text(Str) {
|
| + }
|
| +
|
| + ~TokenTextDirective() override {}
|
| +
|
| +protected:
|
| + void MyApply(bool Replay) const override {
|
| + WriteToken(Text);
|
| + }
|
| +
|
| +private:
|
| + std::string Text;
|
| +};
|
| +
|
| +/// Writes out the current token. Then adds a space if the base
|
| +/// stream is not at the beginning of a continuation line.
|
| +class SpaceTextDirective : public TextFormatter::Directive {
|
| +public:
|
| + SpaceTextDirective(TextFormatter *Formatter, const std::string &Space)
|
| + : TextFormatter::Directive(Formatter), Space(Space) {}
|
| +
|
| + explicit SpaceTextDirective(TextFormatter *Formatter)
|
| + : TextFormatter::Directive(Formatter), Space(" ") {}
|
| +
|
| + ~SpaceTextDirective() {}
|
| +
|
| +protected:
|
| + void MyApply(bool Replay) const override {
|
| + if (!AddLineWrapIfNeeded(Space.size()))
|
| + WriteToken(Space);
|
| + }
|
| +private:
|
| + std::string Space;
|
| +};
|
| +
|
| +/// Adds a newline that ends the current instruction being printed.
|
| +class EndlineTextDirective : public TextFormatter::Directive {
|
| +public:
|
| + explicit EndlineTextDirective(TextFormatter *Formatter)
|
| + : TextFormatter::Directive(Formatter) {}
|
| +
|
| + ~EndlineTextDirective() override {}
|
| +
|
| +protected:
|
| + void MyApply(bool Replay) const override {
|
| + WriteEndline();
|
| + }
|
| +};
|
| +
|
| +/// Inserts a token and then pushes the current indent to the position
|
| +/// after the token. Used to model a open parenthesis.
|
| +class OpenTextDirective : public TokenTextDirective {
|
| +public:
|
| +
|
| + // Creates an open using the given indent.
|
| + OpenTextDirective(TextFormatter *Formatter, const std::string &Text)
|
| + : TokenTextDirective(Formatter, Text) {}
|
| +
|
| + ~OpenTextDirective() override {}
|
| +
|
| +protected:
|
| + void MyApply(bool Replay) const override {
|
| + TokenTextDirective::MyApply(Replay);
|
| + PushIndent();
|
| + }
|
| +};
|
| +
|
| +/// Inserts a token and then pops current indent. Used to model a
|
| +/// close parenthesis.
|
| +class CloseTextDirective : public TokenTextDirective {
|
| +public:
|
| + CloseTextDirective(TextFormatter *Formatter, const std::string &Text)
|
| + : TokenTextDirective(Formatter, Text) {}
|
| +
|
| + ~CloseTextDirective() override {}
|
| +
|
| +protected:
|
| + void MyApply(bool Replay) const override {
|
| + TokenTextDirective::MyApply(Replay);
|
| + PopIndent();
|
| + }
|
| +};
|
| +
|
| +/// Defines the beginning of a token cluster, which should be put on the
|
| +/// same line if possible.
|
| +class StartClusteringDirective : public TextFormatter::Directive {
|
| +public:
|
| + explicit StartClusteringDirective(TextFormatter *Formatter)
|
| + : TextFormatter::Directive(Formatter) {}
|
| +
|
| + ~StartClusteringDirective() override {}
|
| +
|
| +protected:
|
| + void MyApply(bool Replay) const override {
|
| + StartClustering();
|
| + }
|
| +
|
| + void MaybeSaveForReplay() const override {
|
| + if (GetClusteringLevel() > 1) AppendForReplay(this);
|
| + }
|
| +};
|
| +
|
| +class FinishClusteringDirective : public TextFormatter::Directive {
|
| +public:
|
| + explicit FinishClusteringDirective(TextFormatter *Formatter)
|
| + : TextFormatter::Directive(Formatter) {}
|
| +
|
| + ~FinishClusteringDirective() override {}
|
| +
|
| +protected:
|
| + void MyApply(bool Replay) const override {
|
| + FinishClustering();
|
| + }
|
| +};
|
| +
|
| +class ObjDumpStream;
|
| +
|
| +/// Models that an abbreviation index is not specified when dumping a
|
| +/// bitcode record.
|
| +static int32_t ABBREV_INDEX_NOT_SPECIFIED = -1;
|
| +
|
| +/// The formatter used for dumping records in ObjDumpStream.
|
| +class RecordTextFormatter : public TextFormatter {
|
| +public:
|
| + /// The address write width used to print the number of
|
| + /// bytes in the record bit address, when printing records.
|
| + static const unsigned AddressWriteWidth = 8;
|
| +
|
| + explicit RecordTextFormatter(ObjDumpStream *ObjDump);
|
| +
|
| + ~RecordTextFormatter() override {}
|
| +
|
| + /// Writes out the given record of values as an instruction.
|
| + void WriteValues(uint64_t Bit,
|
| + const llvm::NaClBitcodeValues &Values,
|
| + int32_t AbbrevIndex = ABBREV_INDEX_NOT_SPECIFIED);
|
| +
|
| + /// Returns text corresponding to an empty label column.
|
| + std::string GetEmptyLabelColumn();
|
| +
|
| + // Converts the given start bit to the corresponding address to
|
| + // print. That is, generate "Bit/8:Bit%8" value.
|
| + static std::string RecordAddress(uint64_t Bit, unsigned MinByteWidth);
|
| +
|
| + // Returns the record address (when printing records) associated with
|
| + // the given bit.
|
| + static std::string RecordAddress(uint64_t Bit) {
|
| + return RecordAddress(Bit, AddressWriteWidth);
|
| + }
|
| +
|
| +protected:
|
| + void WriteLineIndents() override;
|
| +
|
| +private:
|
| + // The object dumper this formatter is associated with.
|
| + ObjDumpStream *ObjDump;
|
| + // The address label associated with the current line.
|
| + std::string Label;
|
| + // The open brace '<' for a record.
|
| + OpenTextDirective OpenBrace;
|
| + // The close brace '>' for a record.
|
| + CloseTextDirective CloseBrace;
|
| + // A comma between elements in a record.
|
| + TokenTextDirective Comma;
|
| + // A space inside a record.
|
| + SpaceTextDirective Space;
|
| + // End the line.
|
| + EndlineTextDirective Endline;
|
| + // Start clustering tokens.
|
| + StartClusteringDirective StartCluster;
|
| + // End clustering tokens.
|
| + FinishClusteringDirective FinishCluster;
|
| +};
|
| +
|
| +/// Implements a stream to print out bitcode records, assembly code,
|
| +/// comments, and errors. The general format is to print records and
|
| +/// assembly side by side. Comments and errors (associated with the
|
| +/// record and/or assembly code) follow each printed record.
|
| +///
|
| +/// Alignment of records, assembly, comments, and errors is done by
|
| +/// buffering assembly, comments, and errors until a write or flush
|
| +/// occurs. Then, the various streams are stitched together to
|
| +/// produce the corresponding output text. There are two buffers: one
|
| +/// holds the assembly, and the other holds comments and errors.
|
| +///
|
| +/// There are exactly two methods that can cause text to be added to
|
| +/// the objdump stream. Method Flush just flushes out the assembly and
|
| +/// comments/errors buffer without printing a record. If there is no
|
| +/// buffered assembly/comments/errors, nothing is done. Method Write
|
| +/// prints the given record, and also flushes out the assembly and
|
| +/// comments/errors buffer. Hence, in general, comments and errors
|
| +/// follow the record/assembly. However, if you want them to appear
|
| +/// before, use method Flush.
|
| +///
|
| +/// The constructor is defined with two flags: DumpRecords and
|
| +/// DumpAssembly. Comments and errors are flushed on every write,
|
| +/// independent of these flags. Records are printed out only if
|
| +/// DumpRecords is true. Assembly is flushed only if DumpAssembly is
|
| +/// true.
|
| +///
|
| +/// To buffer assembly, call method Assembly to get a (string) stream
|
| +/// to buffer the assembly code. To buffer comments, call method
|
| +/// Comments() to get a (string) stream to buffer the comments.
|
| +///
|
| +/// To buffer an error, call method Error. This method will increment
|
| +/// the error count, and return the comments stream after writing
|
| +/// "Error(byte:bit): ".
|
| +///
|
| +/// If a single line of text is buffered into the assembly stream, no
|
| +/// new line is needed. The corresponding call to Write will
|
| +/// automatically insert the newline for you, if you did not add
|
| +/// it. If multiple lines are to be buffered into the assembly stream,
|
| +/// each line must be separated with a newline character. It is
|
| +/// always safe to end all assembly lines with a newline character.
|
| +///
|
| +/// Also note that this class takes care of formatting records to fit
|
| +/// into a calculated record width (based on value set to
|
| +/// RecordWidth). On the other hand, we assume that the assembly is
|
| +/// formatted by the caller (i.e. owner of this object).
|
| +class ObjDumpStream {
|
| + ObjDumpStream(const ObjDumpStream&) LLVM_DELETED_FUNCTION;
|
| + void operator=(const ObjDumpStream&) LLVM_DELETED_FUNCTION;
|
| +public:
|
| + /// The default number of error messages that will be printed before
|
| + /// execution is stopped due to too many errors.
|
| + static unsigned DefaultMaxErrors;
|
| +
|
| + /// The default value for the column that separates records and
|
| + /// assembly, when DumpRecords and DumpAssembly is true.
|
| + static unsigned ComboObjDumpSeparatorColumn;
|
| +
|
| + /// The default value for line width when DumpRecords is true,
|
| + /// and DumpAssembly is false. This value is typically larger
|
| + /// than ComboObjDumpSeparatorColumn, since the entire line
|
| + /// can be used to print records.
|
| + static unsigned RecordObjectDumpLength;
|
| +
|
| + /// Creates an objdump stream which will dump records, assembly,
|
| + /// comments and errors into a single (user proved Stream). When
|
| + /// DumpRecords is true, the contents of records will be
|
| + /// dumped. When DumpAssembly is true, the corresponding assembly
|
| + /// will be printed. When both are true, the records and assembly
|
| + /// will be printed side by side. When both are false, only comments
|
| + /// and errors will be printed.
|
| + ObjDumpStream(raw_ostream &Stream, bool DumpRecords, bool DumpAssembly);
|
| +
|
| + ~ObjDumpStream() { Flush(); }
|
| +
|
| + /// Returns stream to buffer assembly that will be printed during the
|
| + /// next write call.
|
| + raw_ostream &Assembly() {
|
| + return AssemblyStream;
|
| + }
|
| +
|
| + /// Returns stream to buffer records that will be printed during the
|
| + /// next write call.
|
| + raw_ostream &Records() {
|
| + return RecordStream;
|
| + }
|
| +
|
| + /// Returns stream to buffer messages that will be printed during the
|
| + // next write call.
|
| + raw_ostream &Comments() {
|
| + return MessageStream;
|
| + }
|
| +
|
| + /// Prints "Warning(Bit/8:Bit%8): " onto the comments stream, and
|
| + /// then returns the comments stream. In general, warnings will be
|
| + /// printed after the next record, unless a call to Flush is made.
|
| + raw_ostream &Warning() {
|
| + return Warning(LastKnownBit);
|
| + }
|
| +
|
| + /// Prints "Warning(Bit/8:Bit%8): " onto the comments stream, and
|
| + /// then returns the comments stream. In general, warnings will be
|
| + /// printed after the next record, unless a call to Flush is made.
|
| + raw_ostream &Warning(uint64_t Bit) {
|
| + LastKnownBit = Bit;
|
| + return PrintMessagePrefix("Warning", Bit);
|
| + }
|
| +
|
| + /// Prints "Error(Bit/8:Bit%8): " onto the comments stream, records
|
| + /// that an error has occurred, and then returns the comments
|
| + /// stream. In general errors will be printed after the next record,
|
| + /// unless a call to Flush is made.
|
| + raw_ostream &Error() {
|
| + return Error(LastKnownBit);
|
| + }
|
| +
|
| + /// Prints "Error(Bit/8:Bit%8): " onto the comments stream, records
|
| + /// that an error has occurred, and then returns the comments
|
| + /// stream. In general errors will be printed after the next record,
|
| + /// unless a call to Flush is made.
|
| + raw_ostream &Error(uint64_t Bit);
|
| +
|
| + /// Write a fatal error message to the dump stream, and then
|
| + /// stop the executable. If any assembly, comments, or errors have
|
| + /// been buffered, they will be printed first.
|
| + void Fatal(const std::string &Message) {
|
| + Fatal(LastKnownBit, Message);
|
| + }
|
| +
|
| + /// Write a fatal error message to the dump stream, and then
|
| + /// stop the executable. If any assembly, comments, or errors have
|
| + /// been buffered, they will be printed first. Associates fatal error
|
| + /// Message with the given Bit.
|
| + void Fatal(uint64_t Bit, const std::string &Message);
|
| +
|
| + /// Write a fatal error message to the dump stream, and then
|
| + /// stop the executable. If any assembly, comments, or errors have
|
| + /// been buffered, they will be printed first, along with the given record.
|
| + void Fatal(uint64_t Bit,
|
| + const llvm::NaClBitcodeRecordData &Record,
|
| + const std::string &Message);
|
| +
|
| + /// Dumps a record (at the given bit), along with all buffered assembly,
|
| + /// comments, and errors, into the objdump stream.
|
| + void Write(uint64_t Bit,
|
| + const llvm::NaClBitcodeRecordData &Record,
|
| + int32_t AbbrevIndex = ABBREV_INDEX_NOT_SPECIFIED) {
|
| + LastKnownBit = Bit;
|
| + NaClBitcodeValues Values(Record);
|
| + RecordFormatter.WriteValues(Bit, Values, AbbrevIndex);
|
| + Flush();
|
| + }
|
| +
|
| + /// Dumps the buffered assembly, comments, and errors, without any
|
| + /// corresponding record, into the objdump stream.
|
| + void Flush();
|
| +
|
| + /// Increments the record indent by one.
|
| + void IncRecordIndent() {
|
| + RecordFormatter.Inc();
|
| + }
|
| +
|
| + /// Decrements the record indent by one.
|
| + void DecRecordIndent() {
|
| + RecordFormatter.Dec();
|
| + }
|
| +
|
| + /// Returns the record indenter being used by the objdump stream, for
|
| + /// the purposes of querying state.
|
| + const TextIndenter &GetRecordIndenter() {
|
| + return RecordFormatter;
|
| + }
|
| +
|
| + /// Returns the number of errors reported to the dump stream.
|
| + unsigned GetNumErrors() const {
|
| + return NumErrors;
|
| + }
|
| +
|
| + // Returns true if dumping record.
|
| + bool GetDumpRecords() const {
|
| + return DumpRecords;
|
| + }
|
| +
|
| + // Returns true if dumping assembly.
|
| + bool GetDumpAssembly() const {
|
| + return DumpAssembly;
|
| + }
|
| +
|
| + /// Changes the default assumption that bit addresses start
|
| + /// at index 0.
|
| + void SetStartOffset(uint64_t Offset) {
|
| + StartOffset = Offset;
|
| + }
|
| +
|
| + /// Changes the maximum number of errors allowed.
|
| + void SetMaxErrors(unsigned NewMax) {
|
| + MaxErrors = NewMax;
|
| + }
|
| +
|
| + /// Changes the width allowed for records (from the default).
|
| + void SetRecordWidth(unsigned Width) {
|
| + RecordWidth = Width;
|
| + }
|
| +
|
| + unsigned GetRecordWidth() const {
|
| + return RecordWidth;
|
| + }
|
| +
|
| + /// Changes the column separator character to the given value.
|
| + void SetColumnSeparator(char Separator) {
|
| + ColumnSeparator = Separator;
|
| + }
|
| +
|
| + /// Changes the internal state, to assume one is processing a record
|
| + /// at the given bit. Used by Error() and Fatal(Message) to fill in
|
| + /// the corresponding bit to associate with the error message.
|
| + void SetRecordBitAddress(uint64_t Bit) {
|
| + LastKnownBit = Bit;
|
| + }
|
| +
|
| + // Converts the given start bit to the corresponding address to
|
| + // print. That is, generate "Bit/8:Bit%8" value.
|
| + std::string ObjDumpAddress(uint64_t Bit, unsigned MinByteWidth=1) {
|
| + return RecordFormatter.RecordAddress(Bit + StartOffset, MinByteWidth);
|
| + }
|
| +
|
| + // Returns the record address (when printing records) associated with
|
| + // the given bit.
|
| + std::string RecordAddress(uint64_t Bit) {
|
| + return RecordFormatter.RecordAddress(Bit + StartOffset);
|
| + }
|
| +
|
| +private:
|
| + // The stream to dump to.
|
| + raw_ostream &Stream;
|
| + // True if records should be dumped to the dump stream.
|
| + bool DumpRecords;
|
| + // True if assembly text should be dumped to the dump stream.
|
| + bool DumpAssembly;
|
| + // The number of errors reported.
|
| + unsigned NumErrors;
|
| + // The maximum number of errors before quitting.
|
| + unsigned MaxErrors;
|
| + // The number of columns available to print bitcode records.
|
| + unsigned RecordWidth;
|
| + // The number of bits to add to the record bit address, to correct
|
| + // the record bit address passed to the write routines.
|
| + uint64_t StartOffset;
|
| + // The buffer for assembly to be printed during the next write.
|
| + std::string AssemblyBuffer;
|
| + // The stream to buffer assembly into the assembly buffer.
|
| + raw_string_ostream AssemblyStream;
|
| + // The buffer for comments and errors.
|
| + std::string MessageBuffer;
|
| + // The stream to buffer comments and errors into the message.
|
| + raw_string_ostream MessageStream;
|
| + // The character used to separate records from assembly.
|
| + char ColumnSeparator;
|
| + // The last known bit passed to the objdump object. Used as default
|
| + // for automatically generated errors.
|
| + uint64_t LastKnownBit;
|
| + /// The buffer for records to be printed to during the next write.
|
| + std::string RecordBuffer;
|
| + /// The stream to buffer recordds into the record buffer.
|
| + raw_string_ostream RecordStream;
|
| + /// The text formatter for generating records.
|
| + RecordTextFormatter RecordFormatter;
|
| +
|
| + // Resets assembly and buffers.
|
| + void ResetBuffers() {
|
| + RecordBuffer.clear();
|
| + AssemblyBuffer.clear();
|
| + MessageBuffer.clear();
|
| + }
|
| +
|
| + // Returns the message stream with 'Label(Bit/8:Bit%8): '.
|
| + raw_ostream &PrintMessagePrefix(const char *Label, uint64_t Bit) {
|
| + return Comments() << Label << "(" << ObjDumpAddress(Bit) << "): ";
|
| + }
|
| +};
|
| +
|
| +}
|
| +}
|
| +
|
| +#endif
|
|
|