summaryrefslogtreecommitdiffstats
path: root/llvm/utils/TableGen/AsmMatcherEmitter.cpp
diff options
context:
space:
mode:
authorOliver Stannard <oliver.stannard@arm.com>2017-10-10 11:00:40 +0000
committerOliver Stannard <oliver.stannard@arm.com>2017-10-10 11:00:40 +0000
commit29ffd3f1d9dfe63b8f088ae1c7d4eefefec388f7 (patch)
treedfaf0156bc435bbc0a409bc23f9b9877251aba13 /llvm/utils/TableGen/AsmMatcherEmitter.cpp
parent7d2375df305b2a7a2e96daa24e3ee63e772edec0 (diff)
downloadbcm5719-llvm-29ffd3f1d9dfe63b8f088ae1c7d4eefefec388f7.tar.gz
bcm5719-llvm-29ffd3f1d9dfe63b8f088ae1c7d4eefefec388f7.zip
[AsmParser] Add DiagnosticString to register classes in tablegen
This allows a DiagnosticType and/or DiagnosticString to be associated with a RegisterClass in tablegen, so that we can emit diagnostics in the assembler when a register operand is incorrect. DiagnosticType creates a predictable enum value, which gets returned as the error code when an operand does not match, and can be used by the assembly parser to map to a user-facing diagnostic. DiagnosticString creates an anonymous enum value (currently based on the tablegen class name), and a function to map from enum values to strings will be generated. Both of these work the same was as they do for AsmOperand. This isn't used by any targets yet, but has one (positive) side-effect. It improves the diagnostic codes returned by validateOperandClass - we always want to emit the diagnostic that relates to the expected operand class, but this wasn't always being done when the expected and actual classes were completely different (token/register/custom). This causes a few AArch64 diagnostics to be improved, as Match_InvalidOperand was being returned instead of a specific diagnostic type. Differential revision: https://reviews.llvm.org/D36691 llvm-svn: 315295
Diffstat (limited to 'llvm/utils/TableGen/AsmMatcherEmitter.cpp')
-rw-r--r--llvm/utils/TableGen/AsmMatcherEmitter.cpp77
1 files changed, 68 insertions, 9 deletions
diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index 191dbdea098..60ec8c3eef2 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -704,13 +704,13 @@ public:
/// Map of AsmOperandClass records to their class information.
std::map<Record*, ClassInfo*> AsmOperandClasses;
+ /// Map of RegisterClass records to their class information.
+ std::map<Record*, ClassInfo*> RegisterClassClasses;
+
private:
/// Map of token to class information which has already been constructed.
std::map<std::string, ClassInfo*> TokenClasses;
- /// Map of RegisterClass records to their class information.
- std::map<Record*, ClassInfo*> RegisterClassClasses;
-
private:
/// getTokenClass - Lookup or create the class for the given token.
ClassInfo *getTokenClass(StringRef Token);
@@ -1282,6 +1282,19 @@ buildRegisterClasses(SmallPtrSetImpl<Record*> &SingletonRegisters) {
} else
CI->ValueName = CI->ValueName + "," + RC.getName();
+ Init *DiagnosticType = Def->getValueInit("DiagnosticType");
+ if (StringInit *SI = dyn_cast<StringInit>(DiagnosticType))
+ CI->DiagnosticType = SI->getValue();
+
+ Init *DiagnosticString = Def->getValueInit("DiagnosticString");
+ if (StringInit *SI = dyn_cast<StringInit>(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,18 @@ static void emitMatchClassEnumeration(CodeGenTarget &Target,
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 +2253,26 @@ static void emitOperandMatchErrorDiagStrings(AsmMatcherInfo &Info, raw_ostream &
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 +2287,7 @@ static void emitValidateOperandClass(AsmMatcherInfo &Info,
// 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";
@@ -2279,8 +2323,12 @@ static void emitValidateOperandClass(AsmMatcherInfo &Info,
<< "; break;\n";
OS << " }\n";
OS << " return isSubclass(OpKind, Kind) ? "
- << "MCTargetAsmParser::Match_Success :\n "
- << " MCTargetAsmParser::Match_InvalidOperand;\n }\n\n";
+ << "(unsigned)MCTargetAsmParser::Match_Success :\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 +2477,10 @@ static void emitOperandDiagnosticTypes(AsmMatcherInfo &Info, raw_ostream &OS) {
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;
@@ -2959,6 +3011,9 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
// 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);
@@ -3226,12 +3281,16 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
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 << " ++ActualIdx;\n";
OS << " continue;\n";
OS << " }\n";
+ OS << " // If the target matcher returned a specific error code use\n";
+ OS << " // that, else use the 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";
OpenPOWER on IntegriCloud