Index: llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h +++ llvm/include/llvm/DebugInfo/CodeView/CVTypeVisitor.h @@ -11,6 +11,7 @@ #include "llvm/DebugInfo/CodeView/CVRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/Support/Error.h" namespace llvm { @@ -30,6 +31,9 @@ Error visitTypeRecord(CVType &Record, TypeIndex Index, TypeVisitorCallbacks &Callbacks, VisitorDataSource Source = VDS_BytesPresent); +Error visitTypeRecord(CVType &Record, TypeIndex Index, + TypeVisitorCallbackPipeline &Callbacks, + VisitorDataSource Source = VDS_BytesPresent); Error visitTypeRecord(CVType &Record, TypeVisitorCallbacks &Callbacks, VisitorDataSource Source = VDS_BytesPresent); Index: llvm/include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h +++ llvm/include/llvm/DebugInfo/CodeView/CodeViewRecordIO.h @@ -18,34 +18,50 @@ #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/BinaryStreamWriter.h" +#include "llvm/MC/MCStreamer.h" #include "llvm/Support/Error.h" #include #include #include namespace llvm { + namespace codeview { class CodeViewRecordIO { uint32_t getCurrentOffset() const { - return (isWriting()) ? Writer->getOffset() : Reader->getOffset(); + if (isWriting()) + return Writer->getOffset(); + else if (isReading()) + return Reader->getOffset(); + else + return 0; } public: explicit CodeViewRecordIO(BinaryStreamReader &Reader) : Reader(&Reader) {} explicit CodeViewRecordIO(BinaryStreamWriter &Writer) : Writer(&Writer) {} + explicit CodeViewRecordIO(MCStreamer &Streamer) : Streamer(&Streamer) {} Error beginRecord(Optional MaxLength); Error endRecord(); Error mapInteger(TypeIndex &TypeInd); - bool isReading() const { return Reader != nullptr; } - bool isWriting() const { return !isReading(); } + bool isStreaming() const { return (Streamer != nullptr) && (Reader == nullptr) && (Writer == nullptr); } + bool isReading() const { return (Reader != nullptr) && (Streamer == nullptr) && (Writer == nullptr); } + bool isWriting() const { return (Writer != nullptr) && (Streamer == nullptr) && (Reader == nullptr); } uint32_t maxFieldLength() const; template Error mapObject(T &Value) { + if (isStreaming()) { + StringRef BytesSR = StringRef((reinterpret_cast(&Value)), sizeof(Value)); + Streamer->EmitBytes(BytesSR); + incrStreamedLen(sizeof(T)); + return Error::success(); + } + if (isWriting()) return Writer->writeObject(Value); @@ -57,6 +73,12 @@ } template Error mapInteger(T &Value) { + if (isStreaming()) { + Streamer->EmitIntValue((int)Value, sizeof(T)); + incrStreamedLen(sizeof(T)); + return Error::success(); + } + if (isWriting()) return Writer->writeInteger(Value); @@ -64,18 +86,21 @@ } template Error mapEnum(T &Value) { - if (sizeof(Value) > maxFieldLength()) + if (!isStreaming() && sizeof(Value) > maxFieldLength()) return make_error(cv_error_code::insufficient_buffer); using U = typename std::underlying_type::type; U X; - if (isWriting()) + + if (isWriting() || isStreaming()) X = static_cast(Value); if (auto EC = mapInteger(X)) return EC; + if (isReading()) Value = static_cast(X); + return Error::success(); } @@ -90,7 +115,17 @@ template Error mapVectorN(T &Items, const ElementMapper &Mapper) { SizeType Size; - if (isWriting()) { + if (isStreaming()) { + Size = static_cast(Items.size()); + Streamer->EmitIntValue(Size, sizeof(Size)); + incrStreamedLen(sizeof(Size)); // add 1 for the delimiter + + for (auto &X : Items) { + if (auto EC = Mapper(*this, X)) + return EC; + } + } + else if (isWriting()) { Size = static_cast(Items.size()); if (auto EC = Writer->writeInteger(Size)) return EC; @@ -115,7 +150,7 @@ template Error mapVectorTail(T &Items, const ElementMapper &Mapper) { - if (isWriting()) { + if (isStreaming() || isWriting()) { for (auto &Item : Items) { if (auto EC = Mapper(*this, Item)) return EC; @@ -138,10 +173,30 @@ Error padToAlignment(uint32_t Align); Error skipPadding(); + uint64_t getStreamedLen() { + if(isStreaming()) + return StreamedLen; + else + return 0; + } + private: + void emitEncodedSignedInteger(const int64_t &Value); + void emitEncodedUnsignedInteger(const uint64_t &Value); Error writeEncodedSignedInteger(const int64_t &Value); Error writeEncodedUnsignedInteger(const uint64_t &Value); + void incrStreamedLen(const uint64_t &Len) { + if(isStreaming()) { + StreamedLen += Len; + } + } + + void resetStreamedLen() { + if(isStreaming()) + StreamedLen = 4; // The record prefix is 4 bytes long + } + struct RecordLimit { uint32_t BeginOffset; Optional MaxLength; @@ -162,6 +217,8 @@ BinaryStreamReader *Reader = nullptr; BinaryStreamWriter *Writer = nullptr; + MCStreamer *Streamer = nullptr; + uint64_t StreamedLen = 0; }; } // end namespace codeview Index: llvm/include/llvm/DebugInfo/CodeView/TypeRecordMapping.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeRecordMapping.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeRecordMapping.h @@ -23,9 +23,11 @@ public: explicit TypeRecordMapping(BinaryStreamReader &Reader) : IO(Reader) {} explicit TypeRecordMapping(BinaryStreamWriter &Writer) : IO(Writer) {} + explicit TypeRecordMapping(MCStreamer &Streamer) : IO(Streamer) {} using TypeVisitorCallbacks::visitTypeBegin; Error visitTypeBegin(CVType &Record) override; + Error visitTypeBegin(CVType &Record, TypeIndex Index) override; Error visitTypeEnd(CVType &Record) override; Error visitMemberBegin(CVMemberRecord &Record) override; Index: llvm/include/llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h =================================================================== --- llvm/include/llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h +++ llvm/include/llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h @@ -82,6 +82,11 @@ Pipeline.push_back(&Callbacks); } + void addCallbackToPipelineFront(TypeVisitorCallbacks &Callbacks) { + auto CallBackItr = Pipeline.begin(); + Pipeline.insert(CallBackItr, &Callbacks); + } + #define TYPE_RECORD(EnumName, EnumVal, Name) \ Error visitKnownRecord(CVType &CVR, Name##Record &Record) override { \ return visitKnownRecordImpl(CVR, Record); \ Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -40,6 +40,7 @@ #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/Config/llvm-config.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" +#include "llvm/DebugInfo/CodeView/TypeVisitorCallbackPipeline.h" #include "llvm/DebugInfo/CodeView/CodeView.h" #include "llvm/DebugInfo/CodeView/CodeViewRecordIO.h" #include "llvm/DebugInfo/CodeView/ContinuationRecordBuilder.h" @@ -604,6 +605,8 @@ emitCodeViewMagicVersion(); SmallString<8> CommentPrefix; + SmallString<512> CommentBlock; + raw_svector_ostream CommentOS(CommentBlock); if (OS.isVerboseAsm()) { CommentPrefix += '\t'; CommentPrefix += Asm->MAI->getCommentString(); @@ -616,26 +619,37 @@ // This will fail if the record data is invalid. CVType Record = Table.getType(*B); + TypeVisitorCallbackPipeline Pipeline; + TypeRecordMapping typeMapping(OS); + if (OS.isVerboseAsm()) { // Emit a block comment describing the type record for readability. - SmallString<512> CommentBlock; - raw_svector_ostream CommentOS(CommentBlock); ScopedPrinter SP(CommentOS); SP.setPrefix(CommentPrefix); TypeDumpVisitor TDV(Table, &SP, false); + Pipeline.addCallbackToPipeline(TDV); + } + Pipeline.addCallbackToPipeline(typeMapping); - Error E = codeview::visitTypeRecord(Record, *B, TDV); - if (E) { - logAllUnhandledErrors(std::move(E), errs(), "error: "); - llvm_unreachable("produced malformed type record"); - } + auto RecordLen = Record.length(); + auto RecordKind = Record.kind(); + OS.EmitIntValue(RecordLen-2, 2); // Size is hardcoded as per format requirement, check the reason for the "-2"? + OS.EmitIntValue(RecordKind, sizeof(RecordKind)); + + Error E = codeview::visitTypeRecord(Record, *B, Pipeline); + + if (E) { + logAllUnhandledErrors(std::move(E), errs(), "error: "); + llvm_unreachable("produced malformed type record"); + } + + if (OS.isVerboseAsm()) { // emitRawComment will insert its own tab and comment string before // the first line, so strip off our first one. It also prints its own // newline. OS.emitRawComment( CommentOS.str().drop_front(CommentPrefix.size() - 1).rtrim()); } - OS.EmitBinaryData(Record.str_data()); B = Table.getNext(*B); } } Index: llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp +++ llvm/lib/DebugInfo/CodeView/CVTypeVisitor.cpp @@ -209,6 +209,14 @@ } } + VisitHelper(TypeVisitorCallbackPipeline &Callbacks, VisitorDataSource Source) + : Visitor((Source == VDS_BytesPresent) ? Pipeline : Callbacks) { + if (Source == VDS_BytesPresent) { + Pipeline = Callbacks; + Pipeline.addCallbackToPipelineFront(Deserializer); + } + } + TypeDeserializer Deserializer; TypeVisitorCallbackPipeline Pipeline; CVTypeVisitor Visitor; @@ -222,6 +230,13 @@ return V.Visitor.visitTypeRecord(Record, Index); } +Error llvm::codeview::visitTypeRecord(CVType &Record, TypeIndex Index, + TypeVisitorCallbackPipeline &Callbacks, + VisitorDataSource Source) { + VisitHelper V(Callbacks, Source); + return V.Visitor.visitTypeRecord(Record, Index); +} + Error llvm::codeview::visitTypeRecord(CVType &Record, TypeVisitorCallbacks &Callbacks, VisitorDataSource Source) { Index: llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp +++ llvm/lib/DebugInfo/CodeView/CodeViewRecordIO.cpp @@ -20,6 +20,7 @@ Limit.MaxLength = MaxLength; Limit.BeginOffset = getCurrentOffset(); Limits.push_back(Limit); + resetStreamedLen(); return Error::success(); } @@ -34,10 +35,28 @@ // we don't know how big the record is until we're finished writing it, so // even though we don't commit the extraneous data, we still can't guarantee // we're at the end of the allocated data. + + /* Padding to align with 4byte boundaries */ + if(isStreaming()) { + uint32_t Align = getStreamedLen() % 4; + if (Align == 0) + return Error::success(); + + int PaddingBytes = 4 - Align; + while (PaddingBytes > 0) { + char Pad = static_cast(LF_PAD0 + PaddingBytes); + StringRef BytesSR = StringRef(&Pad, sizeof(Pad)); + Streamer->EmitBytes(BytesSR); + --PaddingBytes; + } + } return Error::success(); } uint32_t CodeViewRecordIO::maxFieldLength() const { + if(isStreaming()) { + return 0; + } assert(!Limits.empty() && "Not in a record!"); // The max length of the next field is the minimum of all lengths that would @@ -78,7 +97,15 @@ } Error CodeViewRecordIO::mapByteVectorTail(ArrayRef &Bytes) { - if (isWriting()) { + if (isStreaming()) { + char *Buffer = (char *)malloc(Bytes.size()*sizeof(uint8_t)); + for (unsigned I = 0; I < Bytes.size(); ++I) + Buffer[I] = uint8_t(Bytes[I]); + StringRef BytesSR = StringRef(Buffer, Bytes.size()); + Streamer->EmitBytes(BytesSR); + incrStreamedLen(Bytes.size()); + } + else if (isWriting()) { if (auto EC = Writer->writeBytes(Bytes)) return EC; } else { @@ -99,21 +126,31 @@ } Error CodeViewRecordIO::mapInteger(TypeIndex &TypeInd) { - if (isWriting()) { + if (isStreaming()) { + Streamer->EmitIntValue(TypeInd.getIndex(), sizeof(TypeInd.getIndex())); + incrStreamedLen(sizeof(TypeInd.getIndex())); + } + else if (isWriting()) { if (auto EC = Writer->writeInteger(TypeInd.getIndex())) return EC; - return Error::success(); } - - uint32_t I; - if (auto EC = Reader->readInteger(I)) - return EC; - TypeInd.setIndex(I); + else { + uint32_t I; + if (auto EC = Reader->readInteger(I)) + return EC; + TypeInd.setIndex(I); + } return Error::success(); } Error CodeViewRecordIO::mapEncodedInteger(int64_t &Value) { - if (isWriting()) { + if (isStreaming()) { + if (Value >= 0) + emitEncodedUnsignedInteger(static_cast(Value)); + else + emitEncodedSignedInteger(Value); + } + else if (isWriting()) { if (Value >= 0) { if (auto EC = writeEncodedUnsignedInteger(static_cast(Value))) return EC; @@ -132,7 +169,10 @@ } Error CodeViewRecordIO::mapEncodedInteger(uint64_t &Value) { - if (isWriting()) { + if (isStreaming()) { + emitEncodedUnsignedInteger(Value); + } + else if (isWriting()) { if (auto EC = writeEncodedUnsignedInteger(Value)) return EC; } else { @@ -145,17 +185,29 @@ } Error CodeViewRecordIO::mapEncodedInteger(APSInt &Value) { - if (isWriting()) { + if (isStreaming()) { + if (Value.isSigned()) + emitEncodedSignedInteger(Value.getSExtValue()); + else + emitEncodedUnsignedInteger(Value.getZExtValue()); + } + else if (isWriting()) { if (Value.isSigned()) return writeEncodedSignedInteger(Value.getSExtValue()); return writeEncodedUnsignedInteger(Value.getZExtValue()); } - - return consume(*Reader, Value); + else + return consume(*Reader, Value); + return Error::success(); } Error CodeViewRecordIO::mapStringZ(StringRef &Value) { - if (isWriting()) { + if (isStreaming()) { + auto NullTerminatedString = StringRef(Value.data(), Value.size() + 1); + Streamer->EmitBytes(NullTerminatedString); + incrStreamedLen(NullTerminatedString.size()); + } + else if (isWriting()) { // Truncate if we attempt to write too much. StringRef S = Value.take_front(maxFieldLength() - 1); if (auto EC = Writer->writeCString(S)) @@ -169,6 +221,14 @@ Error CodeViewRecordIO::mapGuid(GUID &Guid) { constexpr uint32_t GuidSize = 16; + + if (isStreaming()) { + StringRef GuidSR = StringRef((reinterpret_cast(&Guid)), GuidSize); + Streamer->EmitBytes(GuidSR); + incrStreamedLen(GuidSize); + return Error::success(); + } + if (maxFieldLength() < GuidSize) return make_error(cv_error_code::insufficient_buffer); @@ -185,7 +245,15 @@ } Error CodeViewRecordIO::mapStringZVectorZ(std::vector &Value) { - if (isWriting()) { + + if (isStreaming()) { + for (auto V : Value) { + if(auto EC = mapStringZ(V)) + return EC; + } + Streamer->EmitIntValue(0, 1); + } + else if (isWriting()) { for (auto V : Value) { if (auto EC = mapStringZ(V)) return EC; @@ -205,6 +273,46 @@ return Error::success(); } +void CodeViewRecordIO::emitEncodedSignedInteger(const int64_t &Value) { + assert(Value < 0 && "Encoded integer is not signed!"); + if (Value >= std::numeric_limits::min()) { + Streamer->EmitIntValue(LF_CHAR,2); + Streamer->EmitIntValue(Value,1); + incrStreamedLen(3); + } else if (Value >= std::numeric_limits::min()) { + Streamer->EmitIntValue(LF_SHORT,2); + Streamer->EmitIntValue(Value,2); + incrStreamedLen(4); + } else if (Value >= std::numeric_limits::min()) { + Streamer->EmitIntValue(LF_LONG,2); + Streamer->EmitIntValue(Value,4); + incrStreamedLen(6); + } else { + Streamer->EmitIntValue(LF_QUADWORD,2); + Streamer->EmitIntValue(Value,4); + incrStreamedLen(6); + } +} + +void CodeViewRecordIO::emitEncodedUnsignedInteger(const uint64_t &Value) { + if (Value < LF_NUMERIC) { + Streamer->EmitIntValue(Value,2); + incrStreamedLen(2); + } else if (Value <= std::numeric_limits::max()) { + Streamer->EmitIntValue(LF_USHORT,2); + Streamer->EmitIntValue(Value,2); + incrStreamedLen(4); + } else if (Value <= std::numeric_limits::max()) { + Streamer->EmitIntValue(LF_ULONG,2); + Streamer->EmitIntValue(Value,4); // ? can we assume Value to be an unsigned int? if not, which emit API should be used? + incrStreamedLen(6); + } else { + Streamer->EmitIntValue(LF_UQUADWORD,2); + Streamer->EmitIntValue(Value,4); + incrStreamedLen(6); + } +} + Error CodeViewRecordIO::writeEncodedSignedInteger(const int64_t &Value) { assert(Value < 0 && "Encoded integer is not signed!"); if (Value >= std::numeric_limits::min()) { Index: llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp =================================================================== --- llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp +++ llvm/lib/DebugInfo/CodeView/TypeRecordMapping.cpp @@ -29,7 +29,7 @@ error(IO.mapInteger(Method.Type)); if (Method.isIntroducingVirtual()) { error(IO.mapInteger(Method.VFTableOffset)); - } else if (!IO.isWriting()) + } else if (IO.isReading()) Method.VFTableOffset = -1; if (!IsFromOverloadList) @@ -45,7 +45,12 @@ static Error mapNameAndUniqueName(CodeViewRecordIO &IO, StringRef &Name, StringRef &UniqueName, bool HasUniqueName) { - if (IO.isWriting()) { + if (IO.isStreaming()) { + error(IO.mapStringZ(Name)); + if (HasUniqueName) + error(IO.mapStringZ(UniqueName)); + } + else if (IO.isWriting()) { // Try to be smart about what we write here. We can't write anything too // large, so if we're going to go over the limit, truncate both the name // and unique name by the same amount. @@ -96,6 +101,10 @@ return Error::success(); } +Error TypeRecordMapping::visitTypeBegin(CVType &CVR, TypeIndex Index) { + return visitTypeBegin(CVR); +} + Error TypeRecordMapping::visitTypeEnd(CVType &Record) { assert(TypeKind.hasValue() && "Not in a type mapping!"); assert(!MemberKind.hasValue() && "Still in a member mapping!"); @@ -126,7 +135,7 @@ assert(TypeKind.hasValue() && "Not in a type mapping!"); assert(MemberKind.hasValue() && "Not in a member mapping!"); - if (!IO.isWriting()) { + if (IO.isReading()) { if (auto EC = IO.skipPadding()) return EC; } @@ -139,7 +148,6 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR, ModifierRecord &Record) { error(IO.mapInteger(Record.ModifiedType)); error(IO.mapEnum(Record.Modifiers)); - return Error::success(); } @@ -172,7 +180,6 @@ error(IO.mapVectorN( Record.ArgIndices, [](CodeViewRecordIO &IO, TypeIndex &N) { return IO.mapInteger(N); })); - return Error::success(); } @@ -190,7 +197,7 @@ error(IO.mapInteger(Record.Attrs)); if (Record.isPointerToMember()) { - if (!IO.isWriting()) + if(IO.isReading()) Record.MemberInfo.emplace(); MemberPointerInfo &M = *Record.MemberInfo; @@ -260,7 +267,7 @@ Error TypeRecordMapping::visitKnownRecord(CVType &CVR, VFTableShapeRecord &Record) { uint16_t Size; - if (IO.isWriting()) { + if (!IO.isReading()) { ArrayRef Slots = Record.getSlots(); Size = Slots.size(); error(IO.mapInteger(Size)); @@ -291,7 +298,7 @@ error(IO.mapInteger(Record.OverriddenVFTable)); error(IO.mapInteger(Record.VFPtrOffset)); uint32_t NamesLen = 0; - if (IO.isWriting()) { + if (!IO.isReading()) { for (auto Name : Record.MethodNames) NamesLen += Name.size() + 1; } Index: llvm/test/DebugInfo/COFF/inlining.ll =================================================================== --- llvm/test/DebugInfo/COFF/inlining.ll +++ llvm/test/DebugInfo/COFF/inlining.ll @@ -81,8 +81,6 @@ ; ASM: # Arguments [ ; ASM: # ] ; ASM: # } -; ASM: .byte 0x06, 0x00, 0x01, 0x12 -; ASM: .byte 0x00, 0x00, 0x00, 0x00 ; ASM: # Procedure (0x1001) { ; ASM: # TypeLeafKind: LF_PROCEDURE (0x1008) ; ASM: # ReturnType: void (0x3) @@ -92,30 +90,18 @@ ; ASM: # NumParameters: 0 ; ASM: # ArgListType: () (0x1000) ; ASM: # } -; ASM: .byte 0x0e, 0x00, 0x08, 0x10 -; ASM: .byte 0x03, 0x00, 0x00, 0x00 -; ASM: .byte 0x00, 0x00, 0x00, 0x00 -; ASM: .byte 0x00, 0x10, 0x00, 0x00 ; ASM: # FuncId (0x1002) { ; ASM: # TypeLeafKind: LF_FUNC_ID (0x1601) ; ASM: # ParentScope: 0x0 ; ASM: # FunctionType: void () (0x1001) ; ASM: # Name: bar ; ASM: # } -; ASM: .byte 0x0e, 0x00, 0x01, 0x16 -; ASM: .byte 0x00, 0x00, 0x00, 0x00 -; ASM: .byte 0x01, 0x10, 0x00, 0x00 -; ASM: .byte 0x62, 0x61, 0x72, 0x00 ; ASM: # FuncId (0x1003) { ; ASM: # TypeLeafKind: LF_FUNC_ID (0x1601) ; ASM: # ParentScope: 0x0 ; ASM: # FunctionType: void () (0x1001) ; ASM: # Name: foo ; ASM: # } -; ASM: .byte 0x0e, 0x00, 0x01, 0x16 -; ASM: .byte 0x00, 0x00, 0x00, 0x00 -; ASM: .byte 0x01, 0x10, 0x00, 0x00 -; ASM: .byte 0x66, 0x6f, 0x6f, 0x00 ; We should only the LF_FUNC_ID records that we needed to reference. ; OBJ: CodeViewTypes [