blob: fd119ca66510eedf693aea2b018eed3004eed9f9 [file] [log] [blame]
//===- 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&) = delete;
void operator=(const Directive&) = delete;
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 {
RecordTextFormatter(const RecordTextFormatter&) = delete;
RecordTextFormatter &operator=(const RecordTextFormatter&) = delete;
public:
/// The address write width used to print a bit address, when
/// printing records.
static const unsigned AddressWriteWidth = 10;
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();
protected:
void WriteLineIndents() override;
private:
// 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;
// Generates an address label with padding to match AddressWriteWidth;
std::string getBitAddress(uint64_t Bit);
};
/// 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&) = delete;
void operator=(const ObjDumpStream&) = delete;
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 WarningAt(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 &WarningAt(uint64_t Bit) {
LastKnownBit = Bit;
return naclbitc::ErrorAt(Comments(), naclbitc::Warning, Bit);
}
/// Prints "Error(Bit/8:Bit%8): " onto the comments stream using the
/// last know bit position of the input. Then, it records that an
/// error has occurred and 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 ErrorAt(LastKnownBit);
}
/// Prints "Error(Bit/8:Bit%8): " onto the comments stream at the
/// given Bit position. Then, it records that an error has occurred
/// and returns the comments stream. In general errors will be
/// printed after the next record, unless a call to Flush is made.
raw_ostream &ErrorAt(uint64_t Bit) {
return ErrorAt(naclbitc::Error, Bit);
}
/// Prints "Level(Bit/8:Bit%8): " onto the comments stream at the
/// given bit position and severity Level. Then, it 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 &ErrorAt(naclbitc::ErrorLevel Level, uint64_t Bit);
/// 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();
/// Flushes out the last record and/or error, and then stops
/// the executable. Should be called immediately after generating
/// the error message using ErrorAt(naclbitc::Fatal,...).
void FlushThenQuit();
/// 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 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.
void SetRecordBitAddress(uint64_t Bit) {
LastKnownBit = Bit;
}
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 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();
}
};
}
}
#endif