Index: include/llvm/Target/Target.td =================================================================== --- include/llvm/Target/Target.td +++ include/llvm/Target/Target.td @@ -216,6 +216,21 @@ // useful as it is sometimes beneficial to assign registers to highly // constrained classes first. The value has to be in the range [0,63]. int AllocationPriority = 0; + + // The diagnostic type to present when referencing this operand in a match + // failure error message. If this is empty, the default Match_InvalidOperand + // diagnostic type will be used. If this is "", a Match_ enum + // value will be generated and used for this operand type. The target + // assembly parser is responsible for converting this into a user-facing + // diagnostic message. + string DiagnosticType = ""; + + // A diagnostic message to emit when an invalid value is provided for this + // register class when it is being used an an assembly operand. If this is + // non-empty, an anonymous diagnostic type enum value will be generated, and + // the assembly matcher will provide a function to map from diagnostic types + // to message strings. + string DiagnosticString = ""; } // The memberList in a RegisterClass is a dag of set operations. TableGen Index: test/MC/AArch64/arm64-diags.s =================================================================== --- test/MC/AArch64/arm64-diags.s +++ test/MC/AArch64/arm64-diags.s @@ -9,7 +9,7 @@ ldr x3, [foo + 4] ; CHECK: ldr x3, foo+4 ; encoding: [0bAAA00011,A,A,0x58] ; CHECK: ; fixup A - offset: 0, value: foo+4, kind: fixup_aarch64_ldr_pcrel_imm19 -; CHECK-ERRORS: error: invalid operand for instruction +; CHECK-ERRORS: error: expected label or encodable integer pc offset ; The last argument should be flagged as an error. rdar://9576009 ld4.8b {v0, v1, v2, v3}, [x0], #33 @@ -42,13 +42,13 @@ ; CHECK-ERRORS: error: index must be an integer in range [-256, 255]. ; CHECK-ERRORS: ldr x0, [x0, #804]! ; CHECK-ERRORS: ^ -; CHECK-ERRORS: error: invalid operand for instruction +; CHECK-ERRORS: error: expected label or encodable integer pc offset ; CHECK-ERRORS: ldr w0, [w0, #301]! ; CHECK-ERRORS: ^ ; CHECK-ERRORS: error: index must be an integer in range [-256, 255]. ; CHECK-ERRORS: ldr x0, [x0], #804 ; CHECK-ERRORS: ^ -; CHECK-ERRORS: error: invalid operand for instruction +; CHECK-ERRORS: error: expected label or encodable integer pc offset ; CHECK-ERRORS: ldr w0, [w0], #301 ; CHECK-ERRORS: ^ ; CHECK-ERRORS: error: index must be a multiple of 4 in range [-256, 252]. @@ -477,7 +477,7 @@ ; CHECK-ERRORS: error: too few operands for instruction ; CHECK-ERRORS: b.ne ; CHECK-ERRORS: ^ -; CHECK-ERRORS: error: invalid operand for instruction +; CHECK-ERRORS: error: expected label or encodable integer pc offset ; CHECK-ERRORS: b.eq 0, 0 ; CHECK-ERRORS: ^ Index: test/MC/AArch64/basic-a64-diagnostics.s =================================================================== --- test/MC/AArch64/basic-a64-diagnostics.s +++ test/MC/AArch64/basic-a64-diagnostics.s @@ -1814,7 +1814,7 @@ ;; Not possible to fmov ZR to a whole vector fmov v0.4s, #0.0 -// CHECK-ERROR: error: invalid operand for instruction +// CHECK-ERROR: error: expected compatible register or floating-point constant // CHECK-ERROR-NEXT: fmov v0.4s, #0.0 // CHECK-ERROR-NEXT: ^ @@ -1963,10 +1963,10 @@ ldr x3, [x4, #25], #0 ldr x4, [x9, #0], #4 // CHECK-ERROR-AARCH64: error: {{expected symbolic reference or integer|index must be a multiple of 8}} in range [0, 32760] -// CHECK-ERROR-ARM64: error: invalid operand for instruction +// CHECK-ERROR-ARM64: error: expected label or encodable integer pc offset // CHECK-ERROR-NEXT: ldr x3, [x4, #25], #0 // CHECK-ERROR-NEXT: ^ -// CHECK-ERROR-AARCH64-NEXT: error: invalid operand for instruction +// CHECK-ERROR-AARCH64-NEXT: error: expected label or encodable integer pc offset // CHECK-ERROR-AARCH64-NEXT: ldr x4, [x9, #0], #4 // CHECK-ERROR-AARCH64-NEXT: ^ @@ -2196,7 +2196,7 @@ // CHECK-ERROR-NEXT: error: {{expected|index must be an}} integer in range [-256, 255] // CHECK-ERROR-NEXT: ldrh w9, [sp, #-257]! // CHECK-ERROR-NEXT: ^ -// CHECK-ERROR-NEXT: error: invalid operand for instruction +// CHECK-ERROR-NEXT: error: expected label or encodable integer pc offset // CHECK-ERROR-NEXT: ldr w1, [x19, #256]! // CHECK-ERROR-NEXT: ^ // CHECK-ERROR-NEXT: error: {{expected|index must be an}} integer in range [-256, 255] @@ -2221,7 +2221,7 @@ // CHECK-ERROR-NEXT: error: {{expected|index must be an}} integer in range [-256, 255] // CHECK-ERROR-NEXT: ldrsh x22, [x13, #-257]! // CHECK-ERROR-NEXT: ^ -// CHECK-ERROR-NEXT: error: invalid operand for instruction +// CHECK-ERROR-NEXT: error: expected label or encodable integer pc offset // CHECK-ERROR-NEXT: ldrsw x2, [x3, #256]! // CHECK-ERROR-NEXT: ^ // CHECK-ERROR-NEXT: error: {{expected|index must be an}} integer in range [-256, 255] @@ -2298,13 +2298,13 @@ // CHECK-ERROR-NEXT: error: {{expected|index must be an}} integer in range [-256, 255] // CHECK-ERROR-NEXT: ldr h3, [x13, #-257]! // CHECK-ERROR-NEXT: ^ -// CHECK-ERROR-NEXT: error: invalid operand for instruction +// CHECK-ERROR-NEXT: error: expected label or encodable integer pc offset // CHECK-ERROR-NEXT: ldr s3, [x3, #256]! // CHECK-ERROR-NEXT: ^ // CHECK-ERROR-NEXT: error: {{expected|index must be an}} integer in range [-256, 255] // CHECK-ERROR-NEXT: ldr s3, [x13, #-257]! // CHECK-ERROR-NEXT: ^ -// CHECK-ERROR-NEXT: error: invalid operand for instruction +// CHECK-ERROR-NEXT: error: expected label or encodable integer pc offset // CHECK-ERROR-NEXT: ldr d3, [x3, #256]! // CHECK-ERROR-NEXT: ^ // CHECK-ERROR-NEXT: error: {{expected|index must be an}} integer in range [-256, 255] @@ -2397,7 +2397,7 @@ //// 32-bit addresses ldr w0, [w20] ldrsh x3, [wsp] -// CHECK-ERROR: error: invalid operand for instruction +// CHECK-ERROR: error: expected label or encodable integer pc offset // CHECK-ERROR-NEXT: ldr w0, [w20] // CHECK-ERROR-NEXT: ^ // CHECK-ERROR-NEXT: error: invalid operand for instruction @@ -2435,7 +2435,7 @@ // CHECK-ERROR-ARM64-NEXT: error: prefetch operand out of range, [0,31] expected // CHECK-ERROR-NEXT: prfm #32, [sp, #8] // CHECK-ERROR-NEXT: ^ -// CHECK-ERROR-NEXT: error: invalid operand for instruction +// CHECK-ERROR-NEXT: error: expected label or encodable integer pc offset // CHECK-ERROR-NEXT: prfm pldl1strm, [w3, #8] // CHECK-ERROR-NEXT: ^ // CHECK-ERROR-AARCH64-NEXT: error: operand specifier not recognised @@ -2453,7 +2453,7 @@ ldr w10, [x6, x9, sxtw #2] ldr w11, [x7, w2, lsl #2] ldr w12, [x8, w1, sxtx] -// CHECK-ERROR-NEXT: error: invalid operand for instruction +// CHECK-ERROR-NEXT: error: expected label or encodable integer pc offset // CHECK-ERROR-NEXT: ldr w3, [xzr, x3] // CHECK-ERROR-NEXT: ^ // CHECK-ERROR-NEXT: error: expected #imm after shift specifier Index: utils/TableGen/AsmMatcherEmitter.cpp =================================================================== --- utils/TableGen/AsmMatcherEmitter.cpp +++ utils/TableGen/AsmMatcherEmitter.cpp @@ -704,13 +704,13 @@ /// Map of AsmOperandClass records to their class information. std::map AsmOperandClasses; + /// Map of RegisterClass records to their class information. + std::map RegisterClassClasses; + private: /// Map of token to class information which has already been constructed. std::map TokenClasses; - /// Map of RegisterClass records to their class information. - std::map RegisterClassClasses; - private: /// getTokenClass - Lookup or create the class for the given token. ClassInfo *getTokenClass(StringRef Token); @@ -1282,6 +1282,19 @@ } else CI->ValueName = CI->ValueName + "," + RC.getName(); + Init *DiagnosticType = Def->getValueInit("DiagnosticType"); + if (StringInit *SI = dyn_cast(DiagnosticType)) + CI->DiagnosticType = SI->getValue(); + + Init *DiagnosticString = Def->getValueInit("DiagnosticString"); + if (StringInit *SI = dyn_cast(DiagnosticString)) + CI->DiagnosticString = SI->getValue(); + + // If we have a diagnostic string but the diagnostic type is not specified + // explicitly, create an anonymous diagnostic type. + if (!CI->DiagnosticString.empty() && CI->DiagnosticType.empty()) + CI->DiagnosticType = RC.getName(); + RegisterClassClasses.insert(std::make_pair(Def, CI)); } @@ -2178,7 +2191,17 @@ OS << "enum MatchClassKind {\n"; OS << " InvalidMatchClass = 0,\n"; OS << " OptionalMatchClass = 1,\n"; + ClassInfo::ClassInfoKind LastKind = ClassInfo::Token; + StringRef LastName = "OptionalMatchClass"; for (const auto &CI : Infos) { + if (LastKind == ClassInfo::Token && CI.Kind != ClassInfo::Token) { + OS << " MCK_LAST_TOKEN = " << LastName << ",\n"; + } else if (LastKind < ClassInfo::UserClass0 && CI.Kind >= ClassInfo::UserClass0) { + OS << " MCK_LAST_REGISTER = " << LastName << ",\n"; + } + LastKind = (ClassInfo::ClassInfoKind)CI.Kind; + LastName = CI.Name; + OS << " " << CI.Name << ", // "; if (CI.Kind == ClassInfo::Token) { OS << "'" << CI.ValueName << "'\n"; @@ -2229,6 +2252,25 @@ OS << "}\n\n"; } +static void emitRegisterMatchErrorFunc(AsmMatcherInfo &Info, raw_ostream &OS) { + OS << "static unsigned getDiagKindFromRegisterClass(MatchClassKind " + "RegisterClass) {\n"; + OS << " switch (RegisterClass) {\n"; + + for (const auto &CI: Info.Classes) { + if (CI.isRegisterClass() && !CI.DiagnosticType.empty()) { + OS << " case " << CI.Name << ":\n"; + OS << " return " << Info.Target.getName() << "AsmParser::Match_" << CI.DiagnosticType << ";\n"; + } + } + + OS << " default:\n"; + OS << " return MCTargetAsmParser::Match_InvalidOperand;\n"; + + OS << " }\n"; + OS << "}\n\n"; +} + /// emitValidateOperandClass - Emit the function to validate an operand class. static void emitValidateOperandClass(AsmMatcherInfo &Info, raw_ostream &OS) { @@ -2243,7 +2285,7 @@ // Check for Token operands first. // FIXME: Use a more specific diagnostic type. - OS << " if (Operand.isToken())\n"; + OS << " if (Operand.isToken() && Kind <= MCK_LAST_TOKEN)\n"; OS << " return isSubclass(matchTokenString(Operand.getToken()), Kind) ?\n" << " MCTargetAsmParser::Match_Success :\n" << " MCTargetAsmParser::Match_InvalidOperand;\n\n"; @@ -2280,7 +2322,11 @@ OS << " }\n"; OS << " return isSubclass(OpKind, Kind) ? " << "MCTargetAsmParser::Match_Success :\n " - << " MCTargetAsmParser::Match_InvalidOperand;\n }\n\n"; + << " getDiagKindFromRegisterClass(Kind);\n }\n\n"; + + // Expected operand is a register, but actual is not. + OS << " if (Kind > MCK_LAST_TOKEN && Kind <= MCK_LAST_REGISTER)\n"; + OS << " return getDiagKindFromRegisterClass(Kind);\n\n"; // Generic fallthrough match failure case for operands that don't have // specialized diagnostic types. @@ -2429,6 +2475,10 @@ if (!OpClassEntry.second->DiagnosticType.empty()) Types.insert(OpClassEntry.second->DiagnosticType); } + for (const auto &OpClassEntry : Info.RegisterClassClasses) { + if (!OpClassEntry.second->DiagnosticType.empty()) + Types.insert(OpClassEntry.second->DiagnosticType); + } if (Types.empty()) return; @@ -2979,6 +3029,9 @@ // match failure in diagnostics. emitOperandMatchErrorDiagStrings(Info, OS); + // Emit a function to map register classes to operand match failure codes. + emitRegisterMatchErrorFunc(Info, OS); + // Emit the routine to match token strings to their match class. emitMatchTokenString(Target, Info.Classes, OS); @@ -3275,14 +3328,18 @@ OS << " }\n"; OS << " // If the generic handler indicates an invalid operand\n"; OS << " // failure, check for a special case.\n"; - OS << " if (Diag == Match_InvalidOperand) {\n"; - OS << " Diag = validateTargetOperandClass(Actual, Formal);\n"; - OS << " if (Diag == Match_Success) {\n"; + OS << " if (Diag != Match_Success) {\n"; + OS << " unsigned TargetDiag = validateTargetOperandClass(Actual, Formal);\n"; + OS << " if (TargetDiag == Match_Success) {\n"; OS << " DEBUG_WITH_TYPE(\"asm-matcher\",\n"; OS << " dbgs() << \"match success using target matcher\\n\");\n"; OS << " ++ActualIdx;\n"; OS << " continue;\n"; OS << " }\n"; + OS << " // If the target matcher returned a specific error code use that, else use the\n"; + OS << " // one from the generic matcher.\n"; + OS << " if (TargetDiag != Match_InvalidOperand)\n"; + OS << " Diag = TargetDiag;\n"; OS << " }\n"; OS << " // If current formal operand wasn't matched and it is optional\n" << " // then try to match next formal operand\n";