From 3b0fe4ec0a60665b7234d4971ba091cfc25b71c8 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Tue, 13 Jan 2015 21:10:44 +0000 Subject: [PATCH] AsmParser/Bitcode: Add support for MDLocation This adds assembly and bitcode support for `MDLocation`. The assembly side is rather big, since this is the first `MDNode` subclass (that isn't `MDTuple`). Part of PR21433. (If you're wondering where the mountains of testcase updates are, we don't need them until I update `DILocation` and `DebugLoc` to actually use this class.) git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@225830 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/LangRef.rst | 17 +++ include/llvm/Bitcode/LLVMBitCodes.h | 2 +- lib/AsmParser/LLParser.cpp | 120 +++++++++++++++++- lib/AsmParser/LLParser.h | 32 +++++ lib/Bitcode/Reader/BitcodeReader.cpp | 14 ++ lib/Bitcode/Writer/BitcodeWriter.cpp | 39 ++++++ lib/Bitcode/Writer/ValueEnumerator.cpp | 4 +- lib/Bitcode/Writer/ValueEnumerator.h | 2 + .../Assembler/invalid-mdlocation-field-bad.ll | 4 + .../invalid-mdlocation-field-twice.ll | 6 + .../invalid-mdlocation-overflow-column.ll | 9 ++ .../invalid-mdlocation-overflow-line.ll | 9 ++ test/Assembler/invalid-specialized-mdnode.ll | 4 + test/Assembler/mdlocation.ll | 20 +++ utils/vim/llvm.vim | 7 +- 15 files changed, 284 insertions(+), 5 deletions(-) create mode 100644 test/Assembler/invalid-mdlocation-field-bad.ll create mode 100644 test/Assembler/invalid-mdlocation-field-twice.ll create mode 100644 test/Assembler/invalid-mdlocation-overflow-column.ll create mode 100644 test/Assembler/invalid-mdlocation-overflow-line.ll create mode 100644 test/Assembler/invalid-specialized-mdnode.ll create mode 100644 test/Assembler/mdlocation.ll diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 8ab199c552c..1388e7161e8 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -2883,6 +2883,23 @@ attached to the ``add`` instruction using the ``!dbg`` identifier: More information about specific metadata nodes recognized by the optimizers and code generator is found below. +Specialized Metadata Nodes +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Specialized metadata nodes are custom data structures in metadata (as opposed +to generic tuples). Their fields are labelled, and can be specified in any +order. + +MDLocation +"""""""""" + +``MDLocation`` nodes represent source debug locations. The ``scope:`` field is +mandatory. + +.. code-block:: llvm + + !0 = !MDLocation(line: 2900, column: 42, scope: !1, inlinedAt: !2) + '``tbaa``' Metadata ^^^^^^^^^^^^^^^^^^^ diff --git a/include/llvm/Bitcode/LLVMBitCodes.h b/include/llvm/Bitcode/LLVMBitCodes.h index 81544ea32d2..043ecd3d65d 100644 --- a/include/llvm/Bitcode/LLVMBitCodes.h +++ b/include/llvm/Bitcode/LLVMBitCodes.h @@ -142,7 +142,7 @@ namespace bitc { METADATA_NAME = 4, // STRING: [values] METADATA_DISTINCT_NODE = 5, // DISTINCT_NODE: [n x md num] METADATA_KIND = 6, // [n x [id, name]] - // 7 is unused. + METADATA_LOCATION = 7, // [distinct, line, col, scope, inlined-at?] METADATA_OLD_NODE = 8, // OLD_NODE: [n x (type num, value num)] METADATA_OLD_FN_NODE = 9, // OLD_FN_NODE: [n x (type num, value num)] METADATA_NAMED_NODE = 10, // NAMED_NODE: [n x mdnodes] diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index 5d4fb8b7946..d209a2087dd 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -587,8 +587,11 @@ bool LLParser::ParseStandaloneMetadata() { return TokError("unexpected type in metadata definition"); bool IsDistinct = EatIfPresent(lltok::kw_distinct); - if (ParseToken(lltok::exclaim, "Expected '!' here") || - ParseMDTuple(Init, IsDistinct)) + if (Lex.getKind() == lltok::MetadataVar) { + if (ParseSpecializedMDNode(Init, IsDistinct)) + return true; + } else if (ParseToken(lltok::exclaim, "Expected '!' here") || + ParseMDTuple(Init, IsDistinct)) return true; // See if this was forward referenced, if so, handle it. @@ -2902,7 +2905,11 @@ bool LLParser::ParseMDTuple(MDNode *&MD, bool IsDistinct) { /// MDNode: /// ::= !{ ... } /// ::= !7 +/// ::= !MDLocation(...) bool LLParser::ParseMDNode(MDNode *&N) { + if (Lex.getKind() == lltok::MetadataVar) + return ParseSpecializedMDNode(N); + return ParseToken(lltok::exclaim, "expected '!' here") || ParseMDNodeTail(N); } @@ -2916,6 +2923,106 @@ bool LLParser::ParseMDNodeTail(MDNode *&N) { return ParseMDNodeID(N); } +bool LLParser::ParseMDField(LocTy Loc, StringRef Name, + MDUnsignedField &Result) { + if (Result.Seen) + return Error(Loc, + "field '" + Name + "' cannot be specified more than once"); + + if (Lex.getKind() != lltok::APSInt || Lex.getAPSIntVal().isSigned()) + return TokError("expected unsigned integer"); + uint64_t Val64 = Lex.getAPSIntVal().getLimitedValue(Result.Max + 1ull); + + if (Val64 > Result.Max) + return TokError("value for '" + Name + "' too large, limit is " + + Twine(Result.Max)); + Result.assign(Val64); + Lex.Lex(); + return false; +} + +bool LLParser::ParseMDField(LocTy Loc, StringRef Name, MDField &Result) { + if (Result.Seen) + return Error(Loc, + "field '" + Name + "' cannot be specified more than once"); + + Metadata *MD; + if (ParseMetadata(MD, nullptr)) + return true; + + Result.assign(MD); + return false; +} + +template +bool LLParser::ParseMDFieldsImpl(ParserTy parseField) { + assert(Lex.getKind() == lltok::MetadataVar && "Expected metadata type name"); + Lex.Lex(); + + if (ParseToken(lltok::lparen, "expected '(' here")) + return true; + if (EatIfPresent(lltok::rparen)) + return false; + + do { + if (Lex.getKind() != lltok::LabelStr) + return TokError("expected field label here"); + + if (parseField()) + return true; + } while (EatIfPresent(lltok::comma)); + + return ParseToken(lltok::rparen, "expected ')' here"); +} + +bool LLParser::ParseSpecializedMDNode(MDNode *&N, bool IsDistinct) { + assert(Lex.getKind() == lltok::MetadataVar && "Expected metadata type name"); +#define DISPATCH_TO_PARSER(CLASS) \ + if (Lex.getStrVal() == #CLASS) \ + return Parse##CLASS(N, IsDistinct); + + DISPATCH_TO_PARSER(MDLocation); +#undef DISPATCH_TO_PARSER + + return TokError("expected metadata type"); +} + +#define PARSE_MD_FIELD(NAME) \ + do { \ + if (Lex.getStrVal() == #NAME) { \ + LocTy Loc = Lex.getLoc(); \ + Lex.Lex(); \ + if (ParseMDField(Loc, #NAME, NAME)) \ + return true; \ + return false; \ + } \ + } while (0) + +/// ParseMDLocationFields: +/// ::= !MDLocation(line: 43, column: 8, scope: !5, inlinedAt: !6) +bool LLParser::ParseMDLocation(MDNode *&Result, bool IsDistinct) { + MDUnsignedField line(0, ~0u >> 8); + MDUnsignedField column(0, ~0u >> 24); + MDField scope; + MDField inlinedAt; + if (ParseMDFieldsImpl([&]() -> bool { + PARSE_MD_FIELD(line); + PARSE_MD_FIELD(column); + PARSE_MD_FIELD(scope); + PARSE_MD_FIELD(inlinedAt); + return TokError(Twine("invalid field '") + Lex.getStrVal() + "'"); + })) + return true; + + if (!scope.Seen) + return TokError("missing required field 'scope'"); + + auto get = (IsDistinct ? MDLocation::getDistinct : MDLocation::get); + Result = get(Context, line.Val, column.Val, scope.Val, inlinedAt.Val); + return false; +} +#undef PARSE_MD_FIELD + /// ParseMetadataAsValue /// ::= metadata i32 %local /// ::= metadata i32 @global @@ -2960,7 +3067,16 @@ bool LLParser::ParseValueAsMetadata(Metadata *&MD, PerFunctionState *PFS) { /// ::= !42 /// ::= !{...} /// ::= !"string" +/// ::= !MDLocation(...) bool LLParser::ParseMetadata(Metadata *&MD, PerFunctionState *PFS) { + if (Lex.getKind() == lltok::MetadataVar) { + MDNode *N; + if (ParseSpecializedMDNode(N)) + return true; + MD = N; + return false; + } + // ValueAsMetadata: // if (Lex.getKind() != lltok::exclaim) diff --git a/lib/AsmParser/LLParser.h b/lib/AsmParser/LLParser.h index 8607b70f312..220562d66fc 100644 --- a/lib/AsmParser/LLParser.h +++ b/lib/AsmParser/LLParser.h @@ -80,6 +80,31 @@ namespace llvm { } }; + /// Structure to represent an optional metadata field. + template struct MDFieldImpl { + typedef MDFieldImpl ImplTy; + FieldTy Val; + bool Seen; + + void assign(FieldTy Val) { + Seen = true; + this->Val = Val; + } + + explicit MDFieldImpl(FieldTy Default) : Val(Default), Seen(false) {} + }; + template struct MDUnsignedField : public MDFieldImpl { + typedef typename MDUnsignedField::ImplTy ImplTy; + NumTy Max; + + MDUnsignedField(NumTy Default = 0, + NumTy Max = std::numeric_limits::max()) + : ImplTy(Default), Max(Max) {} + }; + struct MDField : public MDFieldImpl { + MDField() : ImplTy(nullptr) {} + }; + class LLParser { public: typedef LLLexer::LocTy LocTy; @@ -393,6 +418,13 @@ namespace llvm { bool ParseMDNodeVector(SmallVectorImpl &MDs); bool ParseInstructionMetadata(Instruction *Inst, PerFunctionState *PFS); + bool ParseMDField(LocTy Loc, StringRef Name, + MDUnsignedField &Result); + bool ParseMDField(LocTy Loc, StringRef Name, MDField &Result); + template bool ParseMDFieldsImpl(ParserTy parseField); + bool ParseSpecializedMDNode(MDNode *&N, bool IsDistinct = false); + bool ParseMDLocation(MDNode *&Result, bool IsDistinct); + // Function Parsing. struct ArgInfo { LocTy Loc; diff --git a/lib/Bitcode/Reader/BitcodeReader.cpp b/lib/Bitcode/Reader/BitcodeReader.cpp index 6f58f2241a6..60a380d5d26 100644 --- a/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1267,6 +1267,20 @@ std::error_code BitcodeReader::ParseMetadata() { NextMDValueNo++); break; } + case bitc::METADATA_LOCATION: { + if (Record.size() != 5) + return Error("Invalid record"); + + auto get = Record[0] ? MDLocation::getDistinct : MDLocation::get; + unsigned Line = Record[1]; + unsigned Column = Record[2]; + MDNode *Scope = cast(MDValueList.getValueFwdRef(Record[3])); + Metadata *InlinedAt = + Record[4] ? MDValueList.getValueFwdRef(Record[4] - 1) : nullptr; + MDValueList.AssignValue(get(Context, Line, Column, Scope, InlinedAt), + NextMDValueNo++); + break; + } case bitc::METADATA_STRING: { std::string String(Record.begin(), Record.end()); llvm::UpgradeMDStringConstant(String); diff --git a/lib/Bitcode/Writer/BitcodeWriter.cpp b/lib/Bitcode/Writer/BitcodeWriter.cpp index 9d14a1afb61..a96e866ed2c 100644 --- a/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -779,6 +779,25 @@ static void WriteMDNode(const MDNode *N, Record.clear(); } +static void WriteMDLocation(const MDLocation *N, const ValueEnumerator &VE, + BitstreamWriter &Stream, + SmallVectorImpl &Record, + unsigned Abbrev) { + Record.push_back(N->isDistinct()); + Record.push_back(N->getLine()); + Record.push_back(N->getColumn()); + Record.push_back(VE.getMetadataID(N->getScope())); + + // Always emit the inlined-at location, even though it's optional. + if (Metadata *InlinedAt = N->getInlinedAt()) + Record.push_back(VE.getMetadataID(InlinedAt) + 1); + else + Record.push_back(0); + + Stream.EmitRecord(bitc::METADATA_LOCATION, Record, Abbrev); + Record.clear(); +} + static void WriteModuleMetadata(const Module *M, const ValueEnumerator &VE, BitstreamWriter &Stream) { @@ -798,6 +817,22 @@ static void WriteModuleMetadata(const Module *M, MDSAbbrev = Stream.EmitAbbrev(Abbv); } + unsigned LocAbbrev = 0; + if (VE.hasMDLocation()) { + // Abbrev for METADATA_LOCATION. + // + // Assume the column is usually under 128, and always output the inlined-at + // location (it's never more expensive than building an array size 1). + BitCodeAbbrev *Abbv = new BitCodeAbbrev(); + Abbv->Add(BitCodeAbbrevOp(bitc::METADATA_LOCATION)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); + LocAbbrev = Stream.EmitAbbrev(Abbv); + } + unsigned NameAbbrev = 0; if (!M->named_metadata_empty()) { // Abbrev for METADATA_NAME. @@ -810,6 +845,10 @@ static void WriteModuleMetadata(const Module *M, SmallVector Record; for (const Metadata *MD : MDs) { + if (const MDLocation *Loc = dyn_cast(MD)) { + WriteMDLocation(Loc, VE, Stream, Record, LocAbbrev); + continue; + } if (const MDNode *N = dyn_cast(MD)) { WriteMDNode(N, VE, Stream, Record); continue; diff --git a/lib/Bitcode/Writer/ValueEnumerator.cpp b/lib/Bitcode/Writer/ValueEnumerator.cpp index b117d7bb49a..27a63d8fefc 100644 --- a/lib/Bitcode/Writer/ValueEnumerator.cpp +++ b/lib/Bitcode/Writer/ValueEnumerator.cpp @@ -282,7 +282,8 @@ static bool isIntOrIntVectorValue(const std::pair &V) { return V.first->getType()->isIntOrIntVectorTy(); } -ValueEnumerator::ValueEnumerator(const Module &M) : HasMDString(false) { +ValueEnumerator::ValueEnumerator(const Module &M) + : HasMDString(false), HasMDLocation(false) { if (shouldPreserveBitcodeUseListOrder()) UseListOrders = predictUseListOrder(M); @@ -547,6 +548,7 @@ void ValueEnumerator::EnumerateMetadata(const Metadata *MD) { EnumerateValue(C->getValue()); HasMDString |= isa(MD); + HasMDLocation |= isa(MD); // Replace the dummy ID inserted above with the correct one. MDValueMap may // have changed by inserting operands, so we need a fresh lookup here. diff --git a/lib/Bitcode/Writer/ValueEnumerator.h b/lib/Bitcode/Writer/ValueEnumerator.h index e63394fefa5..d363c1be0dd 100644 --- a/lib/Bitcode/Writer/ValueEnumerator.h +++ b/lib/Bitcode/Writer/ValueEnumerator.h @@ -65,6 +65,7 @@ private: typedef DenseMap MetadataMapType; MetadataMapType MDValueMap; bool HasMDString; + bool HasMDLocation; typedef DenseMap AttributeGroupMapType; AttributeGroupMapType AttributeGroupMap; @@ -111,6 +112,7 @@ public: unsigned getMetadataID(const Metadata *V) const; bool hasMDString() const { return HasMDString; } + bool hasMDLocation() const { return HasMDLocation; } unsigned getTypeID(Type *T) const { TypeMapType::const_iterator I = TypeMap.find(T); diff --git a/test/Assembler/invalid-mdlocation-field-bad.ll b/test/Assembler/invalid-mdlocation-field-bad.ll new file mode 100644 index 00000000000..6ec7c64bb4d --- /dev/null +++ b/test/Assembler/invalid-mdlocation-field-bad.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:18: error: invalid field 'bad' +!0 = !MDLocation(bad: 0) diff --git a/test/Assembler/invalid-mdlocation-field-twice.ll b/test/Assembler/invalid-mdlocation-field-twice.ll new file mode 100644 index 00000000000..2335c935a5f --- /dev/null +++ b/test/Assembler/invalid-mdlocation-field-twice.ll @@ -0,0 +1,6 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!0 = !{} + +; CHECK: :[[@LINE+1]]:38: error: field 'line' cannot be specified more than once +!1 = !MDLocation(line: 3, scope: !0, line: 3) diff --git a/test/Assembler/invalid-mdlocation-overflow-column.ll b/test/Assembler/invalid-mdlocation-overflow-column.ll new file mode 100644 index 00000000000..d1dbb27173d --- /dev/null +++ b/test/Assembler/invalid-mdlocation-overflow-column.ll @@ -0,0 +1,9 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!0 = !{} + +; CHECK-NOT: error +!1 = !MDLocation(column: 255, scope: !0) + +; CHECK: :[[@LINE+1]]:26: error: value for 'column' too large, limit is 255 +!2 = !MDLocation(column: 256, scope: !0) diff --git a/test/Assembler/invalid-mdlocation-overflow-line.ll b/test/Assembler/invalid-mdlocation-overflow-line.ll new file mode 100644 index 00000000000..8e19f6919bf --- /dev/null +++ b/test/Assembler/invalid-mdlocation-overflow-line.ll @@ -0,0 +1,9 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +!0 = !{} + +; CHECK-NOT: error +!1 = !MDLocation(line: 16777215, scope: !0) + +; CHECK: :[[@LINE+1]]:24: error: value for 'line' too large, limit is 16777215 +!2 = !MDLocation(line: 16777216, scope: !0) diff --git a/test/Assembler/invalid-specialized-mdnode.ll b/test/Assembler/invalid-specialized-mdnode.ll new file mode 100644 index 00000000000..9a84abbb314 --- /dev/null +++ b/test/Assembler/invalid-specialized-mdnode.ll @@ -0,0 +1,4 @@ +; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s + +; CHECK: :[[@LINE+1]]:6: error: expected metadata type +!0 = !Invalid(field: 0) diff --git a/test/Assembler/mdlocation.ll b/test/Assembler/mdlocation.ll new file mode 100644 index 00000000000..e1216fa2750 --- /dev/null +++ b/test/Assembler/mdlocation.ll @@ -0,0 +1,20 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; RUN: verify-uselistorder %s + +; CHECK: !named = !{!0, !1, !1, !2, !2, !3, !3} +!named = !{!0, !1, !2, !3, !4, !5, !6} + +; CHECK: !0 = !{} +!0 = !{} + +; CHECK-NEXT: !1 = !MDLocation(line: 3, column: 7, scope: !0) +!1 = !MDLocation(line: 3, column: 7, scope: !0) +!2 = !MDLocation(scope: !0, column: 7, line: 3) + +; CHECK-NEXT: !2 = !MDLocation(line: 3, column: 7, scope: !0, inlinedAt: !1) +!3 = !MDLocation(scope: !0, inlinedAt: !1, column: 7, line: 3) +!4 = !MDLocation(column: 7, line: 3, scope: !0, inlinedAt: !1) + +; CHECK-NEXT: !3 = !MDLocation(scope: !0) +!5 = !MDLocation(scope: !0) +!6 = !MDLocation(scope: !0, column: 0, line: 0) diff --git a/utils/vim/llvm.vim b/utils/vim/llvm.vim index 23a34b51336..b7411c3cd31 100644 --- a/utils/vim/llvm.vim +++ b/utils/vim/llvm.vim @@ -62,7 +62,7 @@ syn keyword llvmKeyword uselistorder_bb syn keyword llvmError getresult begin end " Misc syntax. -syn match llvmNoName /[%@]\d\+\>/ +syn match llvmNoName /[%@!]\d\+\>/ syn match llvmNumber /-\?\<\d\+\>/ syn match llvmFloat /-\?\<\d\+\.\d*\(e[+-]\d\+\)\?\>/ syn match llvmFloat /\<0x\x\+\>/ @@ -73,6 +73,11 @@ syn region llvmString start=/"/ skip=/\\"/ end=/"/ syn match llvmLabel /[-a-zA-Z$._][-a-zA-Z$._0-9]*:/ syn match llvmIdentifier /[%@][-a-zA-Z$._][-a-zA-Z$._0-9]*/ +" Named metadata and specialized metadata keywords. +syn match llvmIdentifier /![-a-zA-Z$._][-a-zA-Z$._0-9]*\ze\s*$/ +syn match llvmIdentifier /![-a-zA-Z$._][-a-zA-Z$._0-9]*\ze\s*[=!]/ +syn match llvmType /!\zs\a\+\ze\s*(/ + " Syntax-highlight dejagnu test commands. syn match llvmSpecialComment /;\s*RUN:.*$/ syn match llvmSpecialComment /;\s*PR\d*\s*$/