Index: include/llvm/Bitcode/BitcodeConvert.h =================================================================== --- /dev/null +++ include/llvm/Bitcode/BitcodeConvert.h @@ -0,0 +1,74 @@ +//===- BitcodeConvert.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the conversion of Bitcode files from binary and textual forms. The +// textual form is modeled as a list of bitcode records, one record per line. +// Each bitcode record is a list of 64-bit integers, separated by commas, and +// terminated with a semicolin. +// +// The goal of textual bitcode records is to provide a simple API to the +// contents of bitcode files that is human readable, easy to fuzz, and easy to +// write test cases. To do this, the textual form removes all notions of +// abbreviations, and thier corresponding bit-encoded forms. Rather, just the +// raw data within the bitcode records is written. +// +// To model block enter/exits, special symbolic constants have been defined. +// The enter block gets a single parameter, the block ID, and the block exits +// gets no parameters. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BITCODE_BITCODECONVERT_H +#define LLVM_BITCODE_BITCODECONVERT_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/MemoryBuffer.h" + +#include + +namespace llvm { + +namespace bitc { + +enum SpecialBlockCodes { BLK_CODE_ENTER = 65535, BLK_CODE_EXIT = 65534 }; + +} // end of namespace bitc + +typedef std::vector BitcodeRecord; + +/// Reads textual bitcode from Input, and updates Records with its contents. If +/// AllowComments is true, the rules are relaxed to allow empty lines and +/// comment lines beginning with a semicolin. +std::error_code +readTextualBitcode(std::unique_ptr Input, + std::vector> &Records, + bool AllowComments = false); + +/// Reads binary bitcode from Input, and updates Records with its contents. +std::error_code +readBinaryBitcode(std::unique_ptr Input, + std::vector> &Records); + +/// Writes the textual representation fo Records into the buffer. If +/// ErrorRecover is true, it will apply repairs and continue when +/// possible. Returns true when successful. +bool writeTextualBitcode(SmallVectorImpl &Buffer, + std::vector> &Records, + bool ErrorRecover = false); + +/// Writes the binary representation fo Records into the byte Buffer. If +/// ErrorRecover is true, it will apply repairs and continue when +/// possible. Returns true when successful. +bool writeBinaryBitcode(SmallVectorImpl &Buffer, + std::vector> &Records, + bool ErrorRecover = false); + +} // end of namespace llvm + +#endif // LLVM_BITCODE_BITCODECONVERT_H Index: lib/Bitcode/Reader/BitcodeRecordReader.cpp =================================================================== --- /dev/null +++ lib/Bitcode/Reader/BitcodeRecordReader.cpp @@ -0,0 +1,374 @@ +//===- BitcodeRecordReader.cpp --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the binary and textual bitcode record readers. +// +// The textual bitcode reader takes a text buffer and creates the corresponding +// sequence of bitcode records. Each bitcode record is a sequence unsigned +// integers. Each integer is a sequence of digits. Each integer is separated by +// a comma, and terminated with a semicolin. Each record begins on a newline. +// +// If comments are allowed, the comment begins with a semicolin. Any text after +// a semicolin, to the end of the line is considered a comment. Empty lines are +// also allowed. +// +// The binary bitcode reader take a binary buffer, and generates the +// corresponding sequence of bitcode records. While reading, it removes all +// references to abbreviations, including the blockinfo block. +// ===---------------------------------------------------------------------===// + +#include "llvm/Bitcode/BitcodeConvert.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Bitcode/ReaderWriter.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace { + +// Defines reader error codes. +enum ReaderErrorType { + NoCodeForRecord = 1, + NoValueAfterSeparator, + NoSeparatorOrTerminator, + NoNewlineAfterTerminator, + MalformedBlock, + MalformedIRFile, + RecordAtTopLevel, + InvalidBitcodeSignature +}; + +// Defines the corresponding error messages. +class ReaderErrorCategoryType : public std::error_category { + ReaderErrorCategoryType(const ReaderErrorCategoryType &) = delete; + void operator=(const ReaderErrorCategoryType &) = delete; + +public: + static const ReaderErrorCategoryType &get() { return Sentinel; } + +private: + static const ReaderErrorCategoryType Sentinel; + ReaderErrorCategoryType() = default; + const char *name() const LLVM_NOEXCEPT override { + return "pnacl.text_bitcode"; + } + std::string message(int IndexError) const override { + switch (static_cast(IndexError)) { + case NoCodeForRecord: + return "Bitcode record doesn't begin with a record code"; + case NoValueAfterSeparator: + return "Value expected after separator, but not found"; + case NoSeparatorOrTerminator: + return "Separator/terminator expected after value"; + case NoNewlineAfterTerminator: + return "Newline expecded after terminating semicolon"; + case MalformedBlock: + return "Malformed block found in bitcode"; + case MalformedIRFile: + return "Malformed IR file"; + case RecordAtTopLevel: + return "Record not allowed outside block"; + case InvalidBitcodeSignature: + return "Bitcode has invalid bitcode signature"; + } + llvm_unreachable("Unknown error type!"); + } + ~ReaderErrorCategoryType() override = default; +}; + +const ReaderErrorCategoryType ReaderErrorCategoryType::Sentinel; + +std::error_code error(ReaderErrorType Error) { + return std::error_code(Error, ReaderErrorCategoryType::get()); +} + +/// Parses text bitcode records, putting them in Records. +class TextRecordParser { + TextRecordParser(const TextRecordParser &) = delete; + void operator=(const TextRecordParser &) = delete; + +public: + /// Creates a parser to parse records from the InputBuffer, and put them into + /// Records. If AllowComments is true, relax the input rules to allow + /// comments as well. + TextRecordParser(StringRef Buffer, + std::vector> &Records, + bool AllowComments) + : Buffer(Buffer), Records(Records), AllowComments(AllowComments) {} + + /// Reads in the list of bitcode records in the input buffer. + std::error_code read(); + +private: + // The input buffer to parse. + StringRef Buffer; + // The list of bitcode records to generate. + std::vector> &Records; + // Allow comments in bitcode file. + bool AllowComments; + // The current location within the input buffer. + size_t Cursor = 0; + // The separator character. + static const char *Separator; + // The terminator character. + static const char *Terminator; + // The newline character that must follow a terminator. + static const char *Newline; + // Valid digits that can be used to define numbers. + static const char *Digits; + + // Returns true if we have reached the end of the input buffer. + bool atEof() const { return Cursor == Buffer.size(); } + + // Tries to read a character in the given set of Characters. Returns + // the character found, or 0 if not found. + char readChar(const char *Characters); + + // Tries to read a (integral) number. If successful, Value is set to + // the parsed number and returns true. Otherwise false is returned. + // Does not check for number overflow. + bool readNumber(uint64_t &Value); + + // Skip empty lines, and lines beginning with a semicolin. + bool skipWhitespace(); + + // Skip rest of line. + void skipComment() { + while (!atEof()) { + char Ch = Buffer[Cursor++]; + if (Ch == *Newline) + return; + } + } + + // Reads a record from the input buffer. + std::error_code readRecord(); +}; + +const char *TextRecordParser::Newline = "\n"; +const char *TextRecordParser::Separator = ","; +const char *TextRecordParser::Terminator = ";"; +const char *TextRecordParser::Digits = "0123456789"; + +std::error_code TextRecordParser::read() { + while (!atEof()) { + if (AllowComments) + while (skipWhitespace()) + ; + if (std::error_code EC = readRecord()) + return EC; + } + return std::error_code(); +} + +bool TextRecordParser::skipWhitespace() { + if (readChar(Newline)) + return true; + if (readChar(Terminator)) { + skipComment(); + return true; + } + return false; +} + +char TextRecordParser::readChar(const char *Characters) { + if (atEof()) + return 0; + char Ch = Buffer[Cursor]; + if (std::strchr(Characters, Ch) == 0) + return 0; + ++Cursor; + return Ch; +} + +bool TextRecordParser::readNumber(uint64_t &Value) { + Value = 0; + bool NumberFound = false; + while (1) { + char Ch = readChar(Digits); + if (!Ch) + return NumberFound; + Value = (Value * 10) + (Ch - '0'); + NumberFound = true; + } +} + +std::error_code TextRecordParser::readRecord() { + // States of parser used to parse bitcode records. + enum ParseState { + // Begin parsing a record. + StartParse, + // Before a value in the record. + BeforeValue, + // Immediately after a value in the record. + AfterValue + } State = StartParse; + std::vector Values; + uint64_t Number = 0; + while (1) { + switch (State) { + case StartParse: + if (!readNumber(Number)) { + if (atEof()) + return std::error_code(); + return error(NoCodeForRecord); + } + Values.push_back(Number); + State = AfterValue; + continue; + case BeforeValue: + if (!readNumber(Number)) + return error(NoValueAfterSeparator); + Values.push_back(Number); + State = AfterValue; + continue; + case AfterValue: + if (readChar(Separator)) { + State = BeforeValue; + continue; + } + if (readChar(Terminator)) { + if (AllowComments) + skipComment(); + else if (!readChar(Newline)) + return error(NoNewlineAfterTerminator); + Records.push_back(make_unique(Values)); + return std::error_code(); + } + return error(NoSeparatorOrTerminator); + } + } +} + +// Parses binary bitcode records. +class BinaryRecordParser { + BinaryRecordParser(const BinaryRecordParser &) = delete; + void operator=(const BinaryRecordParser &) = delete; + +public: + BinaryRecordParser(std::unique_ptr Buffer, + std::vector> &Records) + : Buffer(std::move(Buffer)), Records(Records) {} + + /// read in a module block. + std::error_code readModule(); + +private: + std::unique_ptr Buffer; + std::vector> &Records; + std::unique_ptr Reader; + std::unique_ptr Cursor; + + // read block with ID. + std::error_code readBlock(unsigned ID); +}; + +std::error_code BinaryRecordParser::readModule() { + const unsigned char *BufBegin = + (const unsigned char *)Buffer->getBufferStart(); + const unsigned char *BufEnd = (const unsigned char *)Buffer->getBufferEnd(); + if (isBitcodeWrapper(BufBegin, BufEnd)) { + constexpr bool VerifyBufferSize = true; + if (SkipBitcodeWrapperHeader(BufBegin, BufEnd, VerifyBufferSize)) + return error(MalformedIRFile); + } + + Reader = make_unique(BufBegin, BufEnd); + Cursor = make_unique(*Reader); + + // Sniff for the signature. + if (Cursor->Read(8) != 'B' || Cursor->Read(8) != 'C' || + Cursor->Read(4) != 0x0 || Cursor->Read(4) != 0xC || + Cursor->Read(4) != 0xE || Cursor->Read(4) != 0xD) + return error(InvalidBitcodeSignature); + + while (1) { + if (Cursor->AtEndOfStream()) + return error(MalformedIRFile); + + BitstreamEntry Entry = + Cursor->advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); + + if (Entry.Kind != BitstreamEntry::SubBlock) + return error(MalformedIRFile); + return readBlock(Entry.ID); + } +} + +std::error_code BinaryRecordParser::readBlock(unsigned BlockID) { + std::unique_ptr> RecordValues = + make_unique>(); + RecordValues->push_back(bitc::BLK_CODE_ENTER); + RecordValues->push_back(BlockID); + Records.push_back(std::move(RecordValues)); + Cursor->EnterSubBlock(BlockID); + + SmallVector Values; + while (1) { + BitstreamEntry Entry = Cursor->advance(); + switch (Entry.Kind) { + case BitstreamEntry::Error: + return error(MalformedBlock); + case BitstreamEntry::EndBlock: { + std::unique_ptr> RecordValues = + make_unique>(); + RecordValues->push_back(bitc::BLK_CODE_EXIT); + Records.push_back(std::move(RecordValues)); + return std::error_code(); + } + case BitstreamEntry::SubBlock: { + if (Entry.ID == bitc::BLOCKINFO_BLOCK_ID) { + if (Cursor->ReadBlockInfoBlock()) + return error(MalformedBlock); + continue; + } + if (std::error_code EC = readBlock(Entry.ID)) + return EC; + continue; + } + case BitstreamEntry::Record: { + unsigned Code = Cursor->readRecord(Entry.ID, Values); + std::unique_ptr> RecordValues = + make_unique>(); + RecordValues->push_back(Code); + for (const auto Val : Values) + RecordValues->push_back(Val); + Records.push_back(std::move(RecordValues)); + Values.clear(); + continue; + } + } + } + llvm_unreachable("Should not reach here!"); +} + +} // end of anonymous namespace + +namespace llvm { + +std::error_code +readTextualBitcode(std::unique_ptr Input, + std::vector> &Records, + bool AllowComments) { + TextRecordParser Parser(Input->getBuffer(), Records, AllowComments); + return Parser.read(); +} + +std::error_code +readBinaryBitcode(std::unique_ptr Input, + std::vector> &Records) { + // TODO(kschimpf) Handle bitcode wrappers. + BinaryRecordParser Parser(std::move(Input), Records); + return Parser.readModule(); +} + +} // end of namespace llvm Index: lib/Bitcode/Reader/CMakeLists.txt =================================================================== --- lib/Bitcode/Reader/CMakeLists.txt +++ lib/Bitcode/Reader/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_library(LLVMBitReader BitReader.cpp BitcodeReader.cpp + BitcodeRecordReader.cpp BitstreamReader.cpp ADDITIONAL_HEADER_DIRS Index: lib/Bitcode/Writer/BitcodeRecordWriter.cpp =================================================================== --- /dev/null +++ lib/Bitcode/Writer/BitcodeRecordWriter.cpp @@ -0,0 +1,314 @@ +//===- BitcodeRecordwriter.cpp --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the binary and textual bitcode record writers. +// +// The Textual bitcode writer takes a sequence of bitcode records, and writes +// out textually to a buffer. Each bitcode record is a sequence of 64-bit, +// unsigned integers. Each integer is separated by a comma, and terminated with +// a semicolin. Each record begins on a newline. +// +// The binary bitcode writer constructs the corresponding binary form in a +// buffer. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Bitcode/BitcodeConvert.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +namespace { + +// Abstract bitcode record writer. +class RecordWriter { + RecordWriter(const RecordWriter &) = delete; + void operator=(const RecordWriter &) = delete; + +public: + explicit RecordWriter(bool ErrorRecover) : ErrorRecover(ErrorRecover) {} + virtual ~RecordWriter() = default; + + // Writes out Records. + bool + writeBitcodeRecords(std::vector> &Records); + +protected: + // The block ID associated with records not in any block. + static const unsigned UnknownBlockID = std::numeric_limits::max(); + // True if error recovery should be applied. + bool ErrorRecover; + // Nest level of blocks within bitcode being written. + size_t BlockDepth = 0; + // The number of errors found. + size_t NumErrors = 0; + // The number of error repairs applied. + size_t NumRepairs = 0; + + // Records that an error has occurred, and returns stream to print the + // corresponding error message to. + raw_ostream &error() { + ++NumErrors; + return errs(); + } + + // Records that a recoverable error has occurred, and returns the stream to + // print error message to. + raw_ostream &recoverableError() { + if (ErrorRecover) + ++NumRepairs; + return error(); + } + + bool isSuccessful() const { + return NumErrors == 0 || (ErrorRecover && NumErrors == NumRepairs); + } + + bool atOutermostScope() { return BlockDepth == 0; } + + // Emit the contents of Record. + bool emitRecord(const BitcodeRecord &Record); + + // Write out the enter record for block ID. + virtual void writeEnterBlock(uint64_t ID) { + (void)ID; + ++BlockDepth; + } + + // Write out the contents of the Record in the current block. + virtual void writeRecord(const BitcodeRecord &Record) = 0; + + // Write out the exit record for the current block. + virtual void writeExitBlock() { --BlockDepth; } + + // Apply any necessary cleanups after writing bitcode. + virtual bool finish() { + if (atOutermostScope()) + return true; + + recoverableError() << "Missing " << BlockDepth << " close blocks.\n"; + if (!ErrorRecover) + return false; + + while (!atOutermostScope()) + writeExitBlock(); + return true; + } +}; + +bool RecordWriter::writeBitcodeRecords( + std::vector> &Records) { + for (const std::unique_ptr &Rec : Records) + if (!emitRecord(*Rec)) + return false; + if (!finish()) + return false; + return isSuccessful(); +} + +bool RecordWriter::emitRecord(const std::vector &Record) { + size_t NumValues = Record.size(); + if (NumValues == 0) { + recoverableError() << "Empty records not allowed in bitcode\n"; + return ErrorRecover; + } + switch (Record[0]) { + case bitc::BLK_CODE_ENTER: { + uint64_t WriteBlockID = UnknownBlockID; + if (NumValues != 2) { + recoverableError() << "Block enter record not size 2\n"; + if (!ErrorRecover) + return false; + } + if (NumValues > 1) + WriteBlockID = Record[1]; + if (WriteBlockID == bitc::BLOCKINFO_BLOCK_ID) { + recoverableError() << "BlockInfo block not allowed in bitcode records\n"; + if (!ErrorRecover) + return false; + WriteBlockID = UnknownBlockID; + } + writeEnterBlock(WriteBlockID); + return true; + } + case bitc::BLK_CODE_EXIT: { + if (atOutermostScope()) { + recoverableError() << "Extraneous exit block\n"; + return ErrorRecover; + } + writeExitBlock(); + return true; + } + default: { + if (atOutermostScope()) { + recoverableError() << "Record not allowed outside block\n"; + if (!ErrorRecover) + return false; + writeEnterBlock(UnknownBlockID); + } + writeRecord(Record); + return true; + } + } +} + +// Writer of textual bitcode records to a byte buffer. +class TextRecordWriter : public RecordWriter { + TextRecordWriter(const TextRecordWriter &) = delete; + void operator=(const TextRecordWriter &) = delete; + +public: + TextRecordWriter(SmallVectorImpl &Buffer, bool ErrorRecover) + : RecordWriter(ErrorRecover), Buffer(Buffer), ValueStream(ValueBuffer) {} + + ~TextRecordWriter() final = default; + +private: + // Buffer to write textual bitcode records into. + SmallVectorImpl &Buffer; + // String stream to convert integers to strings. + std::string ValueBuffer; + raw_string_ostream ValueStream; + + void writeValue(uint64_t Value); + + void writeSeparator() { Buffer.push_back(','); } + + void writeTerminator() { + Buffer.push_back(';'); + Buffer.push_back('\n'); + } + + void writeEnterBlock(uint64_t ID) final { + RecordWriter::writeEnterBlock(ID); + writeValue(bitc::BLK_CODE_ENTER); + writeSeparator(); + writeValue(ID); + writeTerminator(); + } + + void writeExitBlock() final { + RecordWriter::writeExitBlock(); + writeValue(bitc::BLK_CODE_EXIT); + writeTerminator(); + } + + void writeRecord(const BitcodeRecord &Record) final { + bool IsFirst = true; + for (const auto Value : Record) { + if (IsFirst) + IsFirst = false; + else + writeSeparator(); + writeValue(Value); + } + writeTerminator(); + } +}; + +void TextRecordWriter::writeValue(uint64_t Value) { + ValueStream << Value; + ValueStream.flush(); + for (auto ch : ValueBuffer) + Buffer.push_back(ch); + ValueBuffer.clear(); +} + +// Writer of binary bitcode records to a byte buffer. +class BinaryRecordWriter : public RecordWriter { + BinaryRecordWriter(const BinaryRecordWriter &) = delete; + void operator=(const BinaryRecordWriter &) = delete; + +public: + BinaryRecordWriter(SmallVectorImpl &Buffer, bool ErrorRecover) + : RecordWriter(ErrorRecover), Writer(Buffer) { + writeHeader(); + } + + ~BinaryRecordWriter() final = default; + +private: + BitstreamWriter Writer; + + void writeHeader() { + Writer.Emit((unsigned)'B', 8); + Writer.Emit((unsigned)'C', 8); + Writer.Emit(0x0, 4); + Writer.Emit(0xC, 4); + Writer.Emit(0xE, 4); + Writer.Emit(0xD, 4); + } + + void writeEnterBlock(uint64_t ID) final { + RecordWriter::writeEnterBlock(ID); + Writer.EnterSubblock(ID, 2); + } + + void writeExitBlock() final { + RecordWriter::writeExitBlock(); + Writer.ExitBlock(); + } + + void writeRecord(const BitcodeRecord &Record) final { + unsigned Code = Record[0]; + SmallVector Values; + for (size_t i = 1; i < Record.size(); ++i) + Values.push_back(Record[i]); + Writer.EmitRecord(Code, Values); + } + + bool finish() final; +}; + +bool BinaryRecordWriter::finish() { + bool Success = true; + // First try to finish gracefully. + if (!RecordWriter::finish()) + Success = false; + + // Force out exit blocks, if needed, to avoid the bitstream writer from assert + // failing. + while (!atOutermostScope()) + writeExitBlock(); + + // Make sure file length divisible by 4. + uint64_t TrailingBits = Writer.GetCurrentBitNo() % 4 * CHAR_BIT; + if (TrailingBits) { + recoverableError() << "Generated bitstream not word aligned\n"; + Writer.Emit(0, TrailingBits); + if (!ErrorRecover) + return false; + } + + return Success; +} + +} // end of anonymous namespace + +namespace llvm { + +bool writeTextualBitcode(SmallVectorImpl &Buffer, + std::vector> &Records, + bool ErrorRecover) { + TextRecordWriter RecordWriter(Buffer, ErrorRecover); + return RecordWriter.writeBitcodeRecords(Records); +} + +bool writeBinaryBitcode(SmallVectorImpl &Buffer, + std::vector> &Records, + bool ErrorRecover) { + BinaryRecordWriter RecordWriter(Buffer, ErrorRecover); + return RecordWriter.writeBitcodeRecords(Records); +} + +} // end of llvm namespace Index: lib/Bitcode/Writer/CMakeLists.txt =================================================================== --- lib/Bitcode/Writer/CMakeLists.txt +++ lib/Bitcode/Writer/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_library(LLVMBitWriter BitWriter.cpp + BitcodeRecordWriter.cpp BitcodeWriter.cpp BitcodeWriterPass.cpp ValueEnumerator.cpp Index: test/Bitcode/text-bitcode.ll =================================================================== --- /dev/null +++ test/Bitcode/text-bitcode.ll @@ -0,0 +1,37 @@ +; Tests that we can convert LLVM bitcode to textual form, and back to binary. + +; RUN: llvm-as %s -o - | llvm-bcconv -to-text \ +; RUN: | FileCheck --check-prefix=TBC %s + +; RUN: llvm-as %s -o - | llvm-bcconv -to-text | llvm-bcconv -to-binary \ +; RUN: | llvm-dis | FileCheck %s + +define void @f() { + ret void +} + +; CHECK: define void @f() { +; CHECK-NEXT: ret void +; CHECK-NEXT: } + +; TBC: 65535,8; +; TBC-NEXT: 1,1; +; TBC-NEXT: 65535,17; +; TBC-NEXT: 1,4; +; TBC-NEXT: 2; +; TBC-NEXT: 21,0,0; +; TBC-NEXT: 8,1,0; +; TBC-NEXT: 16; +; TBC-NEXT: 65534; +; TBC-NEXT: 8,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0; +; TBC-NEXT: 65535,15; +; Intentionally skip contents of metadata block. +; TBC: 65534; +; TBC-NEXT: 65535,14; +; TBC-NEXT: 1,0,102; +; TBC-NEXT: 65534; +; TBC-NEXT: 65535,12; +; TBC-NEXT: 1,1; +; TBC-NEXT: 10; +; TBC-NEXT: 65534; +; TBC-NEXT: 65534; Index: test/lit.cfg =================================================================== --- test/lit.cfg +++ test/lit.cfg @@ -227,6 +227,7 @@ r"\bllvm-ar\b", r"\bllvm-as\b", r"\bllvm-bcanalyzer\b", + r"\bllvm-bcconv\b", r"\bllvm-config\b", r"\bllvm-cov\b", r"\bllvm-cxxdump\b", Index: tools/LLVMBuild.txt =================================================================== --- tools/LLVMBuild.txt +++ tools/LLVMBuild.txt @@ -24,6 +24,7 @@ llvm-ar llvm-as llvm-bcanalyzer + llvm-bcconv llvm-cov llvm-diff llvm-dis Index: tools/Makefile =================================================================== --- tools/Makefile +++ tools/Makefile @@ -27,9 +27,9 @@ # large and three small executables. This is done to minimize memory load # in parallel builds. Please retain this ordering. DIRS := llvm-config -PARALLEL_DIRS := opt llvm-as llvm-dis llc llvm-ar llvm-nm llvm-link \ - lli llvm-extract llvm-mc bugpoint llvm-bcanalyzer llvm-diff \ - macho-dump llvm-objdump llvm-readobj llvm-rtdyld \ +ARALLEL_DIRS := opt llvm-as llvm-dis llc llvm-ar llvm-nm llvm-link \ + lli llvm-extract llvm-mc bugpoint llvm-bcanalyzer llvm-bcconv \ + llvm-diff macho-dump llvm-objdump llvm-readobj llvm-rtdyld \ llvm-dwarfdump llvm-cov llvm-size llvm-stress llvm-mcmarkup \ llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test \ llvm-cxxdump verify-uselistorder dsymutil llvm-pdbdump Index: tools/llvm-bcconv/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-bcconv/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + BitReader + BitWriter + Support + ) + +add_llvm_tool(llvm-bcconv + llvm-bcconv.cpp + ) Index: tools/llvm-bcconv/LLVMBuild.txt =================================================================== --- tools/llvm-bcconv/LLVMBuild.txt +++ tools/llvm-bcconv/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./tools/LLVMBuild.txt ------------------------------------*- Conf -*--===; +;===- ./tools/llvm-bcconv/LLVMBuild.txt ------------------------*- Conf -*--===; ; ; The LLVM Compiler Infrastructure ; @@ -15,36 +15,8 @@ ; ;===------------------------------------------------------------------------===; -[common] -subdirectories = - bugpoint - dsymutil - llc - lli - llvm-ar - llvm-as - llvm-bcanalyzer - llvm-cov - llvm-diff - llvm-dis - llvm-dwarfdump - llvm-extract - llvm-jitlistener - llvm-link - llvm-lto - llvm-mc - llvm-mcmarkup - llvm-nm - llvm-objdump - llvm-pdbdump - llvm-profdata - llvm-rtdyld - llvm-size - macho-dump - opt - verify-uselistorder - [component_0] -type = Group -name = Tools -parent = $ROOT +type = Tool +name = llvm-bcconv +parent = Tools +required_libraries = BitReader BitWriter Support Index: tools/llvm-bcconv/Makefile =================================================================== --- /dev/null +++ tools/llvm-bcconv/Makefile @@ -0,0 +1,17 @@ +##===- tools/llvm-bcconv/Makefile -------------------------*- Makefile -*-====## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../.. +TOOLNAME := llvm-bcconv +LINK_COMPONENTS := bitreader bitwriter Support + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common Index: tools/llvm-bcconv/llvm-bcconv.cpp =================================================================== --- /dev/null +++ tools/llvm-bcconv/llvm-bcconv.cpp @@ -0,0 +1,140 @@ +//===-- llvm-bcanalyzer.cpp - Bitcode Analyzer --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This tool converts bitcode files from binary form to its textual equivalent, +// and from its textual equivalent back to the binary form. +// +// A textual bitcode file is NOT an assembly file (i.e. .ll file). It is an +// alternate format for bitcode files (i.e. .bc files). Its main purpose is for +// testing and fuzzing. Typically, the extension .tbc is used for textual +// bitcode files. +// +// The textual bitcode file is a simplified form of bitcode files. In +// particular, it removes all user-specified abbreviations from the bitcode +// file. Its form is a list of bitcode records. +// +// Each bitcode record is a sequence of (unsigned) integers, separated by +// commas, and terminated with a semicolin (followed by a newline). +// +// When '-allow-comments' is provided as a command-line argument, the semicolin +// not only acts as a terminator, but all remaining text on that line is +// treated as a comment. Between lines, empty blank lines and comment lines +// can be inserted. A comment line is a line beginning with a semicolin. +// +// Note that the '-allow-comments' feature allows one to use textual bitcode +// files as input to FileCheck. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Bitcode/BitcodeConvert.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +static cl::opt + InputFilename(cl::Positional, cl::desc(""), cl::init("-")); + +static cl::opt OutputFilename( + "o", cl::desc("Write output to specified filename (rather than stdout)"), + cl::init("-")); + +enum ConversionKind { BinaryToText, TextToBinary }; + +cl::opt ConversionFlag( + cl::desc("Choose format conversion:"), + cl::values(clEnumValN(TextToBinary, "to-binary", + "Convert from textual form to binary form"), + clEnumValN(BinaryToText, "to-text", + "Convert from binary form to textual form"), + clEnumValEnd)); + +static cl::opt + AllowTextComments("allow-comments", + cl::desc("Allow comments in textual bitcode"), + cl::init(false)); + +static cl::opt ErrorRecover("error-recover", + cl::desc("Try to recover on errors"), + cl::init(false)); + +static bool writeOutput(SmallVectorImpl &Output, + sys::fs::OpenFlags OpenFlags) { + std::error_code EC; + auto FDOut = make_unique(OutputFilename, EC, OpenFlags); + if (EC) { + errs() << EC.message() << '\n'; + return false; + } + for (auto Val : Output) + FDOut->os() << Val; + FDOut->keep(); + return true; +} + +static bool applyBinaryConversion(std::unique_ptr Input) { + std::vector> Records; + if (std::error_code EC = + readTextualBitcode(std::move(Input), Records, AllowTextComments)) { + errs() << "Error: " << EC.message() << "\n"; + return false; + } + SmallVector Output; + if (!writeBinaryBitcode(Output, Records, ErrorRecover)) { + errs() << "Error: Unable to write binary bitcode\n"; + return false; + } + return writeOutput(Output, sys::fs::F_None); +} + +static bool applyTextualConversion(std::unique_ptr Input) { + std::vector> Records; + if (std::error_code EC = readBinaryBitcode(std::move(Input), Records)) { + errs() << "Error: " << EC.message() << "\n"; + return false; + } + SmallVector Output; + if (!writeTextualBitcode(Output, Records, ErrorRecover)) { + errs() << "Error: Unable to write textual bitcode\n"; + return false; + } + return writeOutput(Output, sys::fs::F_Text); +} + +inline static int convertToExitStatus(bool Value) { return !Value; } + +int main(int argc, char *argv[]) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + cl::ParseCommandLineOptions(argc, argv, + "llvm-bcconv: Bitcode format converter\n"); + + ErrorOr> ErrOrInput = + MemoryBuffer::getFileOrSTDIN(InputFilename); + if (std::error_code EC = ErrOrInput.getError()) { + errs() << "Error reading " << InputFilename << ": " << EC.message() << "\n"; + return 1; + } + + std::unique_ptr Input(ErrOrInput.get().release()); + switch (ConversionFlag) { + case BinaryToText: + return convertToExitStatus(applyTextualConversion(std::move(Input))); + case TextToBinary: + return convertToExitStatus(applyBinaryConversion(std::move(Input))); + } +}