| Index: lib/Bitcode/NaCl/Analysis/NaClObjDumpStream.cpp
|
| diff --git a/lib/Bitcode/NaCl/Analysis/NaClObjDumpStream.cpp b/lib/Bitcode/NaCl/Analysis/NaClObjDumpStream.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..783d4ccc1d56f810f60f878a57bea20d057bf1c1
|
| --- /dev/null
|
| +++ b/lib/Bitcode/NaCl/Analysis/NaClObjDumpStream.cpp
|
| @@ -0,0 +1,354 @@
|
| +//===-- NaClObjDumpStream.cpp --------------------------------------------===//
|
| +// Implements 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.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +
|
| +#include "llvm/ADT/STLExtras.h"
|
| +#include "llvm/Bitcode/NaCl/NaClObjDumpStream.h"
|
| +#include "llvm/Support/DataTypes.h"
|
| +#include "llvm/Support/ErrorHandling.h"
|
| +#include "llvm/Support/Format.h"
|
| +
|
| +namespace llvm {
|
| +namespace naclbitc {
|
| +
|
| +TextFormatter::TextFormatter(raw_ostream &BaseStream,
|
| + unsigned LineWidth,
|
| + const char *Tab)
|
| + : TextIndenter(Tab),
|
| + BaseStream(BaseStream),
|
| + TextStream(TextBuffer),
|
| + LineWidth(LineWidth),
|
| + LinePosition(0),
|
| + CurrentIndent(0),
|
| + MinLineWidth(20),
|
| + AtInstructionBeginning(true),
|
| + LineIndent(),
|
| + ContinuationIndent(),
|
| + ClusteringLevel(0),
|
| + ClusteredTextSize(0) {
|
| + if (MinLineWidth > LineWidth) MinLineWidth = LineWidth;
|
| +}
|
| +
|
| +TextFormatter::~TextFormatter() {}
|
| +
|
| +void TextFormatter::WriteEndline() {
|
| + assert(!IsClustering() && "Must close clustering before ending instruction");
|
| + Write('\n');
|
| + CurrentIndent = 0;
|
| + AtInstructionBeginning = true;
|
| + LineIndent.clear();
|
| +}
|
| +
|
| +std::string TextFormatter::GetToken() {
|
| + TextStream.flush();
|
| + std::string Token(TextBuffer);
|
| + TextBuffer.clear();
|
| + if (!Token.empty() && IsClustering())
|
| + AppendForReplay(GetTokenDirective::Allocate(this, Token));
|
| + return Token;
|
| +}
|
| +
|
| +void TextFormatter::Write(char ch) {
|
| + switch (ch) {
|
| + case '\n':
|
| + BaseStream << ch;
|
| + LinePosition = 0;
|
| + break;
|
| + case '\t': {
|
| + size_t TabWidth = GetTabSize();
|
| + size_t NumChars = LinePosition % TabWidth;
|
| + if (NumChars == 0) NumChars = TabWidth;
|
| + for (size_t i = 0; i < NumChars; ++i) Write(' ');
|
| + break;
|
| + }
|
| + default:
|
| + if (LinePosition == 0) {
|
| + WriteLineIndents();
|
| + }
|
| + BaseStream << ch;
|
| + ++LinePosition;
|
| + }
|
| + AtInstructionBeginning = false;
|
| +}
|
| +
|
| +void TextFormatter::Write(const std::string &Text) {
|
| + if (IsClustering()) {
|
| + ClusteredTextSize += Text.size();
|
| + } else {
|
| + for (std::string::const_iterator
|
| + Iter = Text.begin(), IterEnd = Text.end();
|
| + Iter != IterEnd; ++Iter) {
|
| + Write(*Iter);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TextFormatter::StartClustering() {
|
| + ++ClusteringLevel;
|
| +}
|
| +
|
| +void TextFormatter::FinishClustering() {
|
| + assert(IsClustering() && "Can't finish clustering, not in cluster!");
|
| + --ClusteringLevel;
|
| + if (IsClustering()) return;
|
| +
|
| + AddLineWrapIfNeeded(ClusteredTextSize);
|
| +
|
| + // Reapply the directives to generate the token text, and set
|
| + // indentations. Because clustering can be nested, duplicate before
|
| + // replaying, so that nested clusters can replayed and build its own
|
| + // list of clustered directives.
|
| + std::vector<const Directive*> Directives(ClusteredDirectives);
|
| + ClusteredDirectives.clear();
|
| + ClusteredTextSize = 0;
|
| + for (std::vector<const Directive*>::iterator
|
| + Iter = Directives.begin(),
|
| + IterEnd = Directives.end();
|
| + Iter != IterEnd; ++Iter) {
|
| + (*Iter)->Apply();
|
| + }
|
| +}
|
| +
|
| +void TextFormatter::WriteLineIndents() {
|
| + // Add line indent to base stream.
|
| + if (AtInstructionBeginning) LineIndent = GetIndent();
|
| + BaseStream << LineIndent;
|
| + LinePosition += LineIndent.size();
|
| +
|
| + // If not the first line, and local indent not set, add continuation
|
| + // indent to the base stream.
|
| + unsigned UseIndent = CurrentIndent;
|
| + if (!AtInstructionBeginning && CurrentIndent == 0) {
|
| + UseIndent = FixIndentValue(LinePosition + ContinuationIndent.size());
|
| + }
|
| +
|
| + // Add any additional indents local to the current instruction
|
| + // being dumped to the base stream.
|
| + for (; LinePosition < UseIndent; ++LinePosition) {
|
| + BaseStream << ' ';
|
| + }
|
| +}
|
| +
|
| +void TextFormatter::Directive::Reapply() const {
|
| + // Note: We don't want to store top-level start/finish cluster
|
| + // directives on ClusteredDirectives, so that nested replays won't
|
| + // reapply them.
|
| + bool WasClustering = IsClustering();
|
| + MyApply(true);
|
| + if (WasClustering && IsClustering())
|
| + Formatter->ClusteredDirectives.push_back(this);
|
| +}
|
| +
|
| +
|
| +TextFormatter::Directive *TextFormatter::GetTokenDirective::
|
| +Allocate(TextFormatter *Formatter, const std::string &Text) {
|
| + GetTokenDirective *Dir = Formatter->GetTokenFreeList.Allocate(Formatter);
|
| + Dir->Text = Text;
|
| + return Dir;
|
| +}
|
| +
|
| +RecordTextFormatter::RecordTextFormatter(ObjDumpStream *ObjDump)
|
| + : TextFormatter(ObjDump->Records(), 0, " "),
|
| + ObjDump(ObjDump),
|
| + OpenBrace(this, "<"),
|
| + CloseBrace(this, ">"),
|
| + Comma(this, ","),
|
| + Space(this),
|
| + Endline(this),
|
| + StartCluster(this),
|
| + FinishCluster(this) {
|
| + // Handle fact that 64-bit values can take up to 21 characters.
|
| + MinLineWidth = 21;
|
| + Label = RecordAddress(0);
|
| +}
|
| +
|
| +std::string RecordTextFormatter::RecordAddress(uint64_t Bit,
|
| + unsigned MinByteWidth) {
|
| + std::string Buffer;
|
| + raw_string_ostream Stream(Buffer);
|
| + Stream << '%' << MinByteWidth << PRIu64 << ":%u";
|
| + Stream.flush();
|
| + std::string FormatString(Buffer);
|
| + Buffer.clear();
|
| + Stream << format(FormatString.c_str(),
|
| + (Bit / 8),
|
| + static_cast<unsigned>(Bit % 8));
|
| + return Stream.str();
|
| +}
|
| +
|
| +std::string RecordTextFormatter::GetEmptyLabelColumn() {
|
| + std::string Buffer;
|
| + raw_string_ostream StrmBuffer(Buffer);
|
| + for (size_t i = 0; i < Label.size(); ++i) {
|
| + StrmBuffer << ' ';
|
| + }
|
| + StrmBuffer << '|';
|
| + return StrmBuffer.str();
|
| +}
|
| +
|
| +void RecordTextFormatter::WriteLineIndents() {
|
| + if (AtInstructionBeginning) {
|
| + BaseStream << Label << '|';
|
| + } else {
|
| + BaseStream << GetEmptyLabelColumn();
|
| + }
|
| + LinePosition += Label.size() + 1;
|
| + TextFormatter::WriteLineIndents();
|
| +}
|
| +
|
| +void RecordTextFormatter::WriteValues(uint64_t Bit,
|
| + const llvm::NaClBitcodeValues &Values,
|
| + int32_t AbbrevIndex) {
|
| + Label = ObjDump->RecordAddress(Bit);
|
| + if (AbbrevIndex != ABBREV_INDEX_NOT_SPECIFIED) {
|
| + TextStream << AbbrevIndex << ":" << Space;
|
| + }
|
| + TextStream << OpenBrace;
|
| + for (size_t i = 0; i < Values.size(); ++i) {
|
| + if (i > 0) {
|
| + TextStream << Comma << FinishCluster << Space;
|
| + }
|
| + TextStream << StartCluster << Values[i];
|
| + }
|
| + // Note: Because of record codes, Values are never empty. Hence we
|
| + // always need to finish the cluster for the last number printed.
|
| + TextStream << FinishCluster << CloseBrace << Endline;
|
| +}
|
| +
|
| +unsigned ObjDumpStream::DefaultMaxErrors = 20;
|
| +
|
| +unsigned ObjDumpStream::ComboObjDumpSeparatorColumn = 40;
|
| +
|
| +unsigned ObjDumpStream::RecordObjectDumpLength = 80;
|
| +
|
| +ObjDumpStream::ObjDumpStream(raw_ostream &Stream,
|
| + bool DumpRecords, bool DumpAssembly)
|
| + : Stream(Stream),
|
| + DumpRecords(DumpRecords),
|
| + DumpAssembly(DumpAssembly),
|
| + NumErrors(0),
|
| + MaxErrors(DefaultMaxErrors),
|
| + RecordWidth(0),
|
| + StartOffset(0),
|
| + AssemblyBuffer(),
|
| + AssemblyStream(AssemblyBuffer),
|
| + MessageBuffer(),
|
| + MessageStream(MessageBuffer),
|
| + ColumnSeparator('|'),
|
| + LastKnownBit(0),
|
| + RecordBuffer(),
|
| + RecordStream(RecordBuffer),
|
| + RecordFormatter(this) {
|
| + if (DumpRecords) {
|
| + RecordWidth = DumpAssembly
|
| + ? ComboObjDumpSeparatorColumn
|
| + : RecordObjectDumpLength;
|
| + RecordFormatter.SetLineWidth(RecordWidth);
|
| + }
|
| +}
|
| +
|
| +raw_ostream &ObjDumpStream::Error(uint64_t Bit) {
|
| + LastKnownBit = Bit;
|
| + if (NumErrors >= MaxErrors)
|
| + Fatal(Bit, "Too many errors");
|
| + ++NumErrors;
|
| + return PrintMessagePrefix("Error", Bit);
|
| +}
|
| +
|
| +void ObjDumpStream::Fatal(uint64_t Bit, const std::string &Message) {
|
| + LastKnownBit = Bit;
|
| + if (!Message.empty())
|
| + PrintMessagePrefix("Error", Bit) << Message << "\n";
|
| + Flush();
|
| + llvm::report_fatal_error("Unable to continue");
|
| +}
|
| +
|
| +void ObjDumpStream::Fatal(uint64_t Bit,
|
| + const llvm::NaClBitcodeRecordData &Record,
|
| + const std::string &Message) {
|
| + LastKnownBit = Bit;
|
| + PrintMessagePrefix("Error", Bit) << Message;
|
| + Write(Bit, Record);
|
| + llvm::report_fatal_error("Unable to continue");
|
| +}
|
| +
|
| +// Dumps the next line of text in the buffer. Returns the number of characters
|
| +// printed.
|
| +static size_t DumpLine(raw_ostream &Stream,
|
| + const std::string &Buffer,
|
| + size_t &Index,
|
| + size_t Size) {
|
| + size_t Count = 0;
|
| + while (Index < Size) {
|
| + char ch = Buffer[Index];
|
| + if (ch == '\n') {
|
| + // At end of line, stop here.
|
| + ++Index;
|
| + return Count;
|
| + } else {
|
| + Stream << ch;
|
| + ++Index;
|
| + ++Count;
|
| + }
|
| + }
|
| + return Count;
|
| +}
|
| +
|
| +void ObjDumpStream::Flush() {
|
| + // Start by flushing all buffers, so that we know the
|
| + // text that must be written.
|
| + RecordStream.flush();
|
| + AssemblyStream.flush();
|
| + MessageStream.flush();
|
| +
|
| + // See if there is any record/assembly lines to print.
|
| + if ((DumpRecords && !RecordBuffer.empty())
|
| + || (DumpAssembly && !AssemblyBuffer.empty())) {
|
| + size_t RecordIndex = 0;
|
| + size_t RecordSize = DumpRecords ? RecordBuffer.size() : 0;
|
| + size_t AssemblyIndex = 0;
|
| + size_t AssemblySize = DumpAssembly ? AssemblyBuffer.size() : 0;
|
| +
|
| + while (RecordIndex < RecordSize || AssemblyIndex < AssemblySize) {
|
| + // Dump next record line.
|
| + size_t Column = DumpLine(Stream, RecordBuffer, RecordIndex, RecordSize);
|
| + // Now move to separator if assembly is to be printed also.
|
| + if (DumpRecords && DumpAssembly) {
|
| + if (Column == 0) {
|
| + // Add indent filler.
|
| + std::string Label = RecordFormatter.GetEmptyLabelColumn();
|
| + Stream << Label;
|
| + Column += Label.size();
|
| + }
|
| + for (size_t i = Column; i < RecordWidth; ++i) {
|
| + Stream << ' ';
|
| + }
|
| + Stream << ColumnSeparator;
|
| + }
|
| + // Dump next assembly line.
|
| + DumpLine(Stream, AssemblyBuffer, AssemblyIndex, AssemblySize);
|
| + Stream << '\n';
|
| + }
|
| + }
|
| +
|
| + // Print out messages and reset buffers.
|
| + Stream << MessageBuffer;
|
| + ResetBuffers();
|
| + if (NumErrors >= MaxErrors) {
|
| + // Note: we don't call Fatal here because that will call Flush, causing
|
| + // an infinite loop.
|
| + Stream << "Error(" << ObjDumpAddress(LastKnownBit)
|
| + << "): Too many errors\n";
|
| + llvm::report_fatal_error("Unable to continue");
|
| + }
|
| + Stream.flush();
|
| +}
|
| +
|
| +}
|
| +}
|
|
|