From 70ab3d2af667a97523d2ab4f03b0d2f978693f47 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 21 Feb 2015 01:02:18 +0000 Subject: [PATCH] AsmParser/Writer: Handle symbolic constants in DI 'flags:' Parse (and write) symbolic constants in debug info `flags:` fields. This prevents a readability (and CHECK-ability) regression with the new debug info hierarchy. Old (well, current) assembly, with pretty-printing: !{!"...\\0016387", ...} ; ... [public] [rvalue reference] Flags field without this change: !MDDerivedType(flags: 16387, ...) Flags field with this change: !MDDerivedType(flags: DIFlagPublic | DIFlagRValueReference, ...) As discussed in the review thread, this isn't a final state. Most of these flags correspond to `DW_AT_` symbolic constants, and we might eventually want to support arbitrary attributes in some form. However, as it stands now, some of the flags correspond to other concepts (like `FlagStaticMember`); until things are refactored this is the simplest way to move forward without regressing assembly. llvm-svn: 230111 --- llvm/lib/AsmParser/LLLexer.cpp | 6 +++ llvm/lib/AsmParser/LLParser.cpp | 52 +++++++++++++++++++--- llvm/lib/AsmParser/LLToken.h | 2 + llvm/lib/IR/AsmWriter.cpp | 49 +++++++++++++++----- llvm/test/Assembler/debug-info.ll | 16 +++---- .../invalid-mdsubroutinetype-missing-types.ll | 4 +- llvm/test/Assembler/mdlocalvariable.ll | 8 ++-- llvm/test/Assembler/mdsubprogram.ll | 4 +- llvm/utils/vim/llvm.vim | 1 + 9 files changed, 109 insertions(+), 33 deletions(-) diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 656b169..3bf090a 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -241,6 +241,7 @@ lltok::Kind LLLexer::LexToken() { case ')': return lltok::rparen; case ',': return lltok::comma; case '*': return lltok::star; + case '|': return lltok::bar; } } @@ -760,6 +761,11 @@ lltok::Kind LLLexer::LexIdentifier() { DWKEYWORD(OP, DwarfOp); #undef DWKEYWORD + if (Keyword.startswith("DIFlag")) { + StrVal.assign(Keyword.begin(), Keyword.end()); + return lltok::DIFlag; + } + // Check for [us]0x[0-9A-Fa-f]+ which are Hexadecimal constant generated by // the CFE to avoid forcing it to deal with 64-bit numbers. if ((TokStart[0] == 'u' || TokStart[0] == 's') && diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 28b8d22..fa4653b 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -16,6 +16,7 @@ #include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/InlineAsm.h" @@ -2957,6 +2958,10 @@ struct DwarfLangField : public MDUnsignedField { DwarfLangField() : MDUnsignedField(0, dwarf::DW_LANG_hi_user) {} }; +struct DIFlagField : public MDUnsignedField { + DIFlagField() : MDUnsignedField(0, UINT32_MAX) {} +}; + struct MDSignedField : public MDFieldImpl { int64_t Min; int64_t Max; @@ -3086,6 +3091,43 @@ bool LLParser::ParseMDField(LocTy Loc, StringRef Name, return false; } +/// DIFlagField +/// ::= uint32 +/// ::= DIFlagVector +/// ::= DIFlagVector '|' DIFlagFwdDecl '|' uint32 '|' DIFlagPublic +template <> +bool LLParser::ParseMDField(LocTy Loc, StringRef Name, DIFlagField &Result) { + assert(Result.Max == UINT32_MAX && "Expected only 32-bits"); + + // Parser for a single flag. + auto parseFlag = [&](unsigned &Val) { + if (Lex.getKind() == lltok::APSInt && !Lex.getAPSIntVal().isSigned()) + return ParseUInt32(Val); + + if (Lex.getKind() != lltok::DIFlag) + return TokError("expected debug info flag"); + + Val = DIDescriptor::getFlag(Lex.getStrVal()); + if (!Val) + return TokError(Twine("invalid debug info flag flag '") + + Lex.getStrVal() + "'"); + Lex.Lex(); + return false; + }; + + // Parse the flags and combine them together. + unsigned Combined = 0; + do { + unsigned Val; + if (parseFlag(Val)) + return true; + Combined |= Val; + } while (EatIfPresent(lltok::bar)); + + Result.assign(Combined); + return false; +} + template <> bool LLParser::ParseMDField(LocTy Loc, StringRef Name, MDSignedField &Result) { @@ -3330,7 +3372,7 @@ bool LLParser::ParseMDDerivedType(MDNode *&Result, bool IsDistinct) { OPTIONAL(size, MDUnsignedField, (0, UINT64_MAX)); \ OPTIONAL(align, MDUnsignedField, (0, UINT64_MAX)); \ OPTIONAL(offset, MDUnsignedField, (0, UINT64_MAX)); \ - OPTIONAL(flags, MDUnsignedField, (0, UINT32_MAX)); \ + OPTIONAL(flags, DIFlagField, ); \ OPTIONAL(extraData, MDField, ); PARSE_MD_FIELDS(); #undef VISIT_MD_FIELDS @@ -3353,7 +3395,7 @@ bool LLParser::ParseMDCompositeType(MDNode *&Result, bool IsDistinct) { OPTIONAL(size, MDUnsignedField, (0, UINT64_MAX)); \ OPTIONAL(align, MDUnsignedField, (0, UINT64_MAX)); \ OPTIONAL(offset, MDUnsignedField, (0, UINT64_MAX)); \ - OPTIONAL(flags, MDUnsignedField, (0, UINT32_MAX)); \ + OPTIONAL(flags, DIFlagField, ); \ OPTIONAL(elements, MDField, ); \ OPTIONAL(runtimeLang, DwarfLangField, ); \ OPTIONAL(vtableHolder, MDField, ); \ @@ -3372,7 +3414,7 @@ bool LLParser::ParseMDCompositeType(MDNode *&Result, bool IsDistinct) { bool LLParser::ParseMDSubroutineType(MDNode *&Result, bool IsDistinct) { #define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \ - OPTIONAL(flags, MDUnsignedField, (0, UINT32_MAX)); \ + OPTIONAL(flags, DIFlagField, ); \ REQUIRED(types, MDField, ); PARSE_MD_FIELDS(); #undef VISIT_MD_FIELDS @@ -3449,7 +3491,7 @@ bool LLParser::ParseMDSubprogram(MDNode *&Result, bool IsDistinct) { OPTIONAL(containingType, MDField, ); \ OPTIONAL(virtuality, DwarfVirtualityField, ); \ OPTIONAL(virtualIndex, MDUnsignedField, (0, UINT32_MAX)); \ - OPTIONAL(flags, MDUnsignedField, (0, UINT32_MAX)); \ + OPTIONAL(flags, DIFlagField, ); \ OPTIONAL(isOptimized, MDBoolField, ); \ OPTIONAL(function, MDConstant, ); \ OPTIONAL(templateParams, MDField, ); \ @@ -3585,7 +3627,7 @@ bool LLParser::ParseMDLocalVariable(MDNode *&Result, bool IsDistinct) { OPTIONAL(line, LineField, ); \ OPTIONAL(type, MDField, ); \ OPTIONAL(arg, MDUnsignedField, (0, UINT8_MAX)); \ - OPTIONAL(flags, MDUnsignedField, (0, UINT32_MAX)); \ + OPTIONAL(flags, DIFlagField, ); \ OPTIONAL(inlinedAt, MDField, ); PARSE_MD_FIELDS(); #undef VISIT_MD_FIELDS diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h index 57218da..a7aa17c 100644 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -29,6 +29,7 @@ namespace lltok { less, greater, // < > lparen, rparen, // ( ) exclaim, // ! + bar, // | kw_x, kw_true, kw_false, @@ -203,6 +204,7 @@ namespace lltok { DwarfVirtuality, // DW_VIRTUALITY_foo DwarfLang, // DW_LANG_foo DwarfOp, // DW_OP_foo + DIFlag, // DIFlagFoo // Type valued tokens (TyVal). Type, diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index 47a1b55..104ad4f 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -1275,14 +1275,15 @@ static void writeMDTuple(raw_ostream &Out, const MDTuple *Node, namespace { struct FieldSeparator { bool Skip; - FieldSeparator() : Skip(true) {} + const char *Sep; + FieldSeparator(const char *Sep = ", ") : Skip(true), Sep(Sep) {} }; raw_ostream &operator<<(raw_ostream &OS, FieldSeparator &FS) { if (FS.Skip) { FS.Skip = false; return OS; } - return OS << ", "; + return OS << FS.Sep; } } // end namespace @@ -1387,6 +1388,20 @@ static void writeMDBasicType(raw_ostream &Out, const MDBasicType *N, Out << ")"; } +static void writeDIFlags(raw_ostream &Out, unsigned Flags) { + SmallVector SplitFlags; + unsigned Extra = DIDescriptor::splitFlags(Flags, SplitFlags); + + FieldSeparator FS(" | "); + for (unsigned F : SplitFlags) { + const char *StringF = DIDescriptor::getFlagString(F); + assert(StringF && "Expected valid flag"); + Out << FS << StringF; + } + if (Extra || SplitFlags.empty()) + Out << FS << Extra; +} + static void writeMDDerivedType(raw_ostream &Out, const MDDerivedType *N, TypePrinting *TypePrinter, SlotTracker *Machine, const Module *Context) { @@ -1414,8 +1429,10 @@ static void writeMDDerivedType(raw_ostream &Out, const MDDerivedType *N, Out << FS << "align: " << N->getAlignInBits(); if (N->getOffsetInBits()) Out << FS << "offset: " << N->getOffsetInBits(); - if (N->getFlags()) - Out << FS << "flags: " << N->getFlags(); + if (auto Flags = N->getFlags()) { + Out << FS << "flags: "; + writeDIFlags(Out, Flags); + } if (N->getExtraData()) { Out << FS << "extraData: "; writeMetadataAsOperand(Out, N->getExtraData(), TypePrinter, Machine, @@ -1454,8 +1471,10 @@ static void writeMDCompositeType(raw_ostream &Out, const MDCompositeType *N, Out << FS << "align: " << N->getAlignInBits(); if (N->getOffsetInBits()) Out << FS << "offset: " << N->getOffsetInBits(); - if (N->getFlags()) - Out << FS << "flags: " << N->getFlags(); + if (auto Flags = N->getFlags()) { + Out << FS << "flags: "; + writeDIFlags(Out, Flags); + } if (N->getElements()) { Out << FS << "elements: "; writeMetadataAsOperand(Out, N->getElements(), TypePrinter, Machine, @@ -1489,8 +1508,10 @@ static void writeMDSubroutineType(raw_ostream &Out, const MDSubroutineType *N, SlotTracker *Machine, const Module *Context) { Out << "!MDSubroutineType("; FieldSeparator FS; - if (N->getFlags()) - Out << FS << "flags: " << N->getFlags(); + if (auto Flags = N->getFlags()) { + Out << FS << "flags: "; + writeDIFlags(Out, Flags); + } Out << FS << "types: "; writeMetadataAsOperand(Out, N->getTypeArray(), TypePrinter, Machine, Context); Out << ")"; @@ -1594,8 +1615,10 @@ static void writeMDSubprogram(raw_ostream &Out, const MDSubprogram *N, } if (N->getVirtualIndex()) Out << FS << "virtualIndex: " << N->getVirtualIndex(); - if (N->getFlags()) - Out << FS << "flags: " << N->getFlags(); + if (auto Flags = N->getFlags()) { + Out << FS << "flags: "; + writeDIFlags(Out, Flags); + } Out << FS << "isOptimized: " << (N->isOptimized() ? "true" : "false"); if (N->getFunction()) { Out << FS << "function: "; @@ -1764,8 +1787,10 @@ static void writeMDLocalVariable(raw_ostream &Out, const MDLocalVariable *N, } if (N->getTag() == dwarf::DW_TAG_arg_variable || N->getArg()) Out << FS << "arg: " << N->getArg(); - if (N->getFlags()) - Out << FS << "flags: " << N->getFlags(); + if (auto Flags = N->getFlags()) { + Out << FS << "flags: "; + writeDIFlags(Out, Flags); + } if (N->getInlinedAt()) { Out << FS << "inlinedAt: "; writeMetadataAsOperand(Out, N->getInlinedAt(), TypePrinter, Machine, diff --git a/llvm/test/Assembler/debug-info.ll b/llvm/test/Assembler/debug-info.ll index 9b808bd..435b892 100644 --- a/llvm/test/Assembler/debug-info.ll +++ b/llvm/test/Assembler/debug-info.ll @@ -41,22 +41,22 @@ !15 = !MDDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 32, align: 32) ; CHECK-NEXT: !14 = !MDCompositeType(tag: DW_TAG_structure_type, name: "MyType", file: !10, line: 2, size: 32, align: 32, identifier: "MangledMyType") -; CHECK-NEXT: !15 = distinct !MDCompositeType(tag: DW_TAG_structure_type, name: "Base", file: !10, line: 3, scope: !14, size: 128, align: 32, offset: 64, flags: 3, elements: !16, runtimeLang: DW_LANG_C_plus_plus_11, vtableHolder: !15, templateParams: !18, identifier: "MangledBase") +; CHECK-NEXT: !15 = distinct !MDCompositeType(tag: DW_TAG_structure_type, name: "Base", file: !10, line: 3, scope: !14, size: 128, align: 32, offset: 64, flags: DIFlagPublic, elements: !16, runtimeLang: DW_LANG_C_plus_plus_11, vtableHolder: !15, templateParams: !18, identifier: "MangledBase") ; CHECK-NEXT: !16 = !{!17} -; CHECK-NEXT: !17 = !MDDerivedType(tag: DW_TAG_member, name: "field", file: !10, line: 4, scope: !15, baseType: !6, size: 32, align: 32, offset: 32, flags: 3) +; CHECK-NEXT: !17 = !MDDerivedType(tag: DW_TAG_member, name: "field", file: !10, line: 4, scope: !15, baseType: !6, size: 32, align: 32, offset: 32, flags: DIFlagPublic) ; CHECK-NEXT: !18 = !{!6} -; CHECK-NEXT: !19 = !MDCompositeType(tag: DW_TAG_structure_type, name: "Derived", file: !10, line: 3, scope: !14, baseType: !15, size: 128, align: 32, offset: 64, flags: 3, elements: !20, runtimeLang: DW_LANG_C_plus_plus_11, vtableHolder: !15, templateParams: !18, identifier: "MangledBase") +; CHECK-NEXT: !19 = !MDCompositeType(tag: DW_TAG_structure_type, name: "Derived", file: !10, line: 3, scope: !14, baseType: !15, size: 128, align: 32, offset: 64, flags: DIFlagPublic, elements: !20, runtimeLang: DW_LANG_C_plus_plus_11, vtableHolder: !15, templateParams: !18, identifier: "MangledBase") ; CHECK-NEXT: !20 = !{!21} ; CHECK-NEXT: !21 = !MDDerivedType(tag: DW_TAG_inheritance, scope: !19, baseType: !15) ; CHECK-NEXT: !22 = !MDDerivedType(tag: DW_TAG_ptr_to_member_type, baseType: !6, size: 32, align: 32, extraData: !15) ; CHECK-NEXT: !23 = !MDCompositeType(tag: DW_TAG_structure_type) ; CHECK-NEXT: !24 = !MDCompositeType(tag: DW_TAG_structure_type, runtimeLang: DW_LANG_Cobol85) !16 = !MDCompositeType(tag: DW_TAG_structure_type, name: "MyType", file: !12, line: 2, size: 32, align: 32, identifier: "MangledMyType") -!17 = !MDCompositeType(tag: DW_TAG_structure_type, name: "Base", file: !12, line: 3, scope: !16, size: 128, align: 32, offset: 64, flags: 3, elements: !18, runtimeLang: DW_LANG_C_plus_plus_11, vtableHolder: !17, templateParams: !20, identifier: "MangledBase") +!17 = !MDCompositeType(tag: DW_TAG_structure_type, name: "Base", file: !12, line: 3, scope: !16, size: 128, align: 32, offset: 64, flags: DIFlagPublic, elements: !18, runtimeLang: DW_LANG_C_plus_plus_11, vtableHolder: !17, templateParams: !20, identifier: "MangledBase") !18 = !{!19} -!19 = !MDDerivedType(tag: DW_TAG_member, name: "field", file: !12, line: 4, scope: !17, baseType: !7, size: 32, align: 32, offset: 32, flags: 3) +!19 = !MDDerivedType(tag: DW_TAG_member, name: "field", file: !12, line: 4, scope: !17, baseType: !7, size: 32, align: 32, offset: 32, flags: DIFlagPublic) !20 = !{!7} -!21 = !MDCompositeType(tag: DW_TAG_structure_type, name: "Derived", file: !12, line: 3, scope: !16, baseType: !17, size: 128, align: 32, offset: 64, flags: 3, elements: !22, runtimeLang: DW_LANG_C_plus_plus_11, vtableHolder: !17, templateParams: !20, identifier: "MangledBase") +!21 = !MDCompositeType(tag: DW_TAG_structure_type, name: "Derived", file: !12, line: 3, scope: !16, baseType: !17, size: 128, align: 32, offset: 64, flags: DIFlagPublic, elements: !22, runtimeLang: DW_LANG_C_plus_plus_11, vtableHolder: !17, templateParams: !20, identifier: "MangledBase") !22 = !{!23} !23 = !MDDerivedType(tag: DW_TAG_inheritance, scope: !21, baseType: !17) !24 = !MDDerivedType(tag: DW_TAG_ptr_to_member_type, baseType: !7, size: 32, align: 32, extraData: !17) @@ -64,9 +64,9 @@ !26 = !MDCompositeType(tag: DW_TAG_structure_type, runtimeLang: 6) ; !25 = !{!7, !7} -; !26 = !MDSubroutineType(flags: 7, types: !25) +; !26 = !MDSubroutineType(flags: DIFlagPublic | DIFlagStaticMember, types: !25) ; !27 = !MDSubroutineType(types: !25) !27 = !{!7, !7} -!28 = !MDSubroutineType(flags: 7, types: !27) +!28 = !MDSubroutineType(flags: DIFlagPublic | DIFlagStaticMember, types: !27) !29 = !MDSubroutineType(flags: 0, types: !27) !30 = !MDSubroutineType(types: !27) diff --git a/llvm/test/Assembler/invalid-mdsubroutinetype-missing-types.ll b/llvm/test/Assembler/invalid-mdsubroutinetype-missing-types.ll index 6c976ed..7342417 100644 --- a/llvm/test/Assembler/invalid-mdsubroutinetype-missing-types.ll +++ b/llvm/test/Assembler/invalid-mdsubroutinetype-missing-types.ll @@ -1,4 +1,4 @@ ; RUN: not llvm-as < %s -disable-output 2>&1 | FileCheck %s -; CHECK: [[@LINE+1]]:33: error: missing required field 'types' -!29 = !MDSubroutineType(flags: 7) +; CHECK: [[@LINE+1]]:65: error: missing required field 'types' +!29 = !MDSubroutineType(flags: DIFlagPublic | DIFlagStaticMember) diff --git a/llvm/test/Assembler/mdlocalvariable.ll b/llvm/test/Assembler/mdlocalvariable.ll index 00e350d..7a4185f 100644 --- a/llvm/test/Assembler/mdlocalvariable.ll +++ b/llvm/test/Assembler/mdlocalvariable.ll @@ -12,13 +12,13 @@ !3 = distinct !{} !4 = distinct !{} -; CHECK: !5 = !MDLocalVariable(tag: DW_TAG_arg_variable, scope: !0, name: "foo", file: !2, line: 7, type: !3, arg: 3, flags: 8, inlinedAt: !4) -; CHECK: !6 = !MDLocalVariable(tag: DW_TAG_auto_variable, scope: !0, name: "foo", file: !2, line: 7, type: !3, flags: 8, inlinedAt: !4) +; CHECK: !5 = !MDLocalVariable(tag: DW_TAG_arg_variable, scope: !0, name: "foo", file: !2, line: 7, type: !3, arg: 3, flags: DIFlagArtificial, inlinedAt: !4) +; CHECK: !6 = !MDLocalVariable(tag: DW_TAG_auto_variable, scope: !0, name: "foo", file: !2, line: 7, type: !3, flags: DIFlagArtificial, inlinedAt: !4) !5 = !MDLocalVariable(tag: DW_TAG_arg_variable, scope: !0, name: "foo", file: !2, line: 7, type: !3, arg: 3, - flags: 8, inlinedAt: !4) + flags: DIFlagArtificial, inlinedAt: !4) !6 = !MDLocalVariable(tag: DW_TAG_auto_variable, scope: !0, name: "foo", - file: !2, line: 7, type: !3, flags: 8, inlinedAt: !4) + file: !2, line: 7, type: !3, flags: DIFlagArtificial, inlinedAt: !4) ; CHECK: !7 = !MDLocalVariable(tag: DW_TAG_arg_variable, scope: null, name: "", arg: 0) ; CHECK: !8 = !MDLocalVariable(tag: DW_TAG_auto_variable, scope: null, name: "") diff --git a/llvm/test/Assembler/mdsubprogram.ll b/llvm/test/Assembler/mdsubprogram.ll index 7915a29..aecfefc 100644 --- a/llvm/test/Assembler/mdsubprogram.ll +++ b/llvm/test/Assembler/mdsubprogram.ll @@ -15,12 +15,12 @@ declare void @_Z3foov() !6 = distinct !{} !7 = distinct !{} -; CHECK: !8 = !MDSubprogram(scope: !0, name: "foo", linkageName: "_Zfoov", file: !2, line: 7, type: !3, isLocal: true, isDefinition: false, scopeLine: 8, containingType: !4, virtuality: DW_VIRTUALITY_pure_virtual, virtualIndex: 10, flags: 11, isOptimized: true, function: void ()* @_Z3foov, templateParams: !5, declaration: !6, variables: !7) +; CHECK: !8 = !MDSubprogram(scope: !0, name: "foo", linkageName: "_Zfoov", file: !2, line: 7, type: !3, isLocal: true, isDefinition: false, scopeLine: 8, containingType: !4, virtuality: DW_VIRTUALITY_pure_virtual, virtualIndex: 10, flags: DIFlagPrototyped, isOptimized: true, function: void ()* @_Z3foov, templateParams: !5, declaration: !6, variables: !7) !8 = !MDSubprogram(scope: !0, name: "foo", linkageName: "_Zfoov", file: !2, line: 7, type: !3, isLocal: true, isDefinition: false, scopeLine: 8, containingType: !4, virtuality: DW_VIRTUALITY_pure_virtual, virtualIndex: 10, - flags: 11, isOptimized: true, function: void ()* @_Z3foov, + flags: DIFlagPrototyped, isOptimized: true, function: void ()* @_Z3foov, templateParams: !5, declaration: !6, variables: !7) ; CHECK: !9 = !MDSubprogram(scope: null, name: "bar", isLocal: false, isDefinition: true, isOptimized: false) diff --git a/llvm/utils/vim/llvm.vim b/llvm/utils/vim/llvm.vim index 6b4b8bb..913d0f5 100644 --- a/llvm/utils/vim/llvm.vim +++ b/llvm/utils/vim/llvm.vim @@ -82,6 +82,7 @@ syn match llvmConstant /\/ syn match llvmConstant /\/ syn match llvmConstant /\/ syn match llvmConstant /\/ +syn match llvmConstant /\/ " Syntax-highlight dejagnu test commands. syn match llvmSpecialComment /;\s*RUN:.*$/ -- 2.7.4