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 |