Index: llvm/trunk/include/llvm/MC/MCParser/MCTargetAsmParser.h =================================================================== --- llvm/trunk/include/llvm/MC/MCParser/MCTargetAsmParser.h +++ llvm/trunk/include/llvm/MC/MCParser/MCTargetAsmParser.h @@ -132,6 +132,139 @@ MatchOperand_ParseFail // operand matched but had errors }; +// When matching of an assembly instruction fails, there may be multiple +// encodings that are close to being a match. It's often ambiguous which one +// the programmer intended to use, so we want to report an error which mentions +// each of these "near-miss" encodings. This struct contains information about +// one such encoding, and why it did not match the parsed instruction. +class NearMissInfo { +public: + enum NearMissKind { + NoNearMiss, + NearMissOperand, + NearMissFeature, + NearMissPredicate, + NearMissTooFewOperands, + }; + + // The encoding is valid for the parsed assembly string. This is only used + // internally to the table-generated assembly matcher. + static NearMissInfo getSuccess() { return NearMissInfo(); } + + // The instruction encoding is not valid because it requires some target + // features that are not currently enabled. MissingFeatures has a bit set for + // each feature that the encoding needs but which is not enabled. + static NearMissInfo getMissedFeature(uint64_t MissingFeatures) { + NearMissInfo Result; + Result.Kind = NearMissFeature; + Result.Features = MissingFeatures; + return Result; + } + + // The instruction encoding is not valid because the target-specific + // predicate function returned an error code. FailureCode is the + // target-specific error code returned by the predicate. + static NearMissInfo getMissedPredicate(unsigned FailureCode) { + NearMissInfo Result; + Result.Kind = NearMissPredicate; + Result.PredicateError = FailureCode; + return Result; + } + + // The instruction encoding is not valid because one (and only one) parsed + // operand is not of the correct type. OperandError is the error code + // relating to the operand class expected by the encoding. OperandClass is + // the type of the expected operand. Opcode is the opcode of the encoding. + // OperandIndex is the index into the parsed operand list. + static NearMissInfo getMissedOperand(unsigned OperandError, + unsigned OperandClass, unsigned Opcode, + unsigned OperandIndex) { + NearMissInfo Result; + Result.Kind = NearMissOperand; + Result.MissedOperand.Error = OperandError; + Result.MissedOperand.Class = OperandClass; + Result.MissedOperand.Opcode = Opcode; + Result.MissedOperand.Index = OperandIndex; + return Result; + } + + // The instruction encoding is not valid because it expects more operands + // than were parsed. OperandClass is the class of the expected operand that + // was not provided. Opcode is the instruction encoding. + static NearMissInfo getTooFewOperands(unsigned OperandClass, + unsigned Opcode) { + NearMissInfo Result; + Result.Kind = NearMissTooFewOperands; + Result.TooFewOperands.Class = OperandClass; + Result.TooFewOperands.Opcode = Opcode; + return Result; + } + + operator bool() const { return Kind != NoNearMiss; } + + NearMissKind getKind() const { return Kind; } + + // Feature flags required by the instruction, that the current target does + // not have. + uint64_t getFeatures() const { + assert(Kind == NearMissFeature); + return Features; + } + // Error code returned by the target predicate when validating this + // instruction encoding. + unsigned getPredicateError() const { + assert(Kind == NearMissPredicate); + return PredicateError; + } + // MatchClassKind of the operand that we expected to see. + unsigned getOperandClass() const { + assert(Kind == NearMissOperand || Kind == NearMissTooFewOperands); + return MissedOperand.Class; + } + // Opcode of the encoding we were trying to match. + unsigned getOpcode() const { + assert(Kind == NearMissOperand || Kind == NearMissTooFewOperands); + return MissedOperand.Opcode; + } + // Error code returned when validating the operand. + unsigned getOperandError() const { + assert(Kind == NearMissOperand); + return MissedOperand.Error; + } + // Index of the actual operand we were trying to match in the list of parsed + // operands. + unsigned getOperandIndex() const { + assert(Kind == NearMissOperand); + return MissedOperand.Index; + } + +private: + NearMissKind Kind; + + // These two structs share a common prefix, so we can safely rely on the fact + // that they overlap in the union. + struct MissedOpInfo { + unsigned Class; + unsigned Opcode; + unsigned Error; + unsigned Index; + }; + + struct TooFewOperandsInfo { + unsigned Class; + unsigned Opcode; + }; + + union { + uint64_t Features; + unsigned PredicateError; + MissedOpInfo MissedOperand; + TooFewOperandsInfo TooFewOperands; + }; + + NearMissInfo() : Kind(NoNearMiss) {} +}; + /// MCTargetAsmParser - Generic interface to target specific assembly parsers. class MCTargetAsmParser : public MCAsmParserExtension { public: @@ -140,6 +273,7 @@ Match_MissingFeature, Match_MnemonicFail, Match_Success, + Match_NearMisses, FIRST_TARGET_MATCH_RESULT_TY }; Index: llvm/trunk/include/llvm/Target/Target.td =================================================================== --- llvm/trunk/include/llvm/Target/Target.td +++ llvm/trunk/include/llvm/Target/Target.td @@ -1126,6 +1126,14 @@ // HasMnemonicFirst - Set to false if target instructions don't always // start with a mnemonic as the first token. bit HasMnemonicFirst = 1; + + // ReportMultipleNearMisses - + // When 0, the assembly matcher reports an error for one encoding or operand + // that did not match the parsed instruction. + // When 1, the assmebly matcher returns a list of encodings that were close + // to matching the parsed instruction, so to allow more detailed error + // messages. + bit ReportMultipleNearMisses = 0; } def DefaultAsmParser : AsmParser; Index: llvm/trunk/utils/TableGen/AsmMatcherEmitter.cpp =================================================================== --- llvm/trunk/utils/TableGen/AsmMatcherEmitter.cpp +++ llvm/trunk/utils/TableGen/AsmMatcherEmitter.cpp @@ -2824,6 +2824,8 @@ bool HasMnemonicFirst = AsmParser->getValueAsBit("HasMnemonicFirst"); bool HasOptionalOperands = Info.hasOptionalOperands(); + bool ReportMultipleNearMisses = + AsmParser->getValueAsBit("ReportMultipleNearMisses"); // Write the output. @@ -2846,9 +2848,12 @@ OS << " void convertToMapAndConstraints(unsigned Kind,\n "; OS << " const OperandVector &Operands) override;\n"; OS << " unsigned MatchInstructionImpl(const OperandVector &Operands,\n" - << " MCInst &Inst,\n" - << " uint64_t &ErrorInfo," - << " bool matchingInlineAsm,\n" + << " MCInst &Inst,\n"; + if (ReportMultipleNearMisses) + OS << " SmallVectorImpl *NearMisses,\n"; + else + OS << " uint64_t &ErrorInfo,\n"; + OS << " bool matchingInlineAsm,\n" << " unsigned VariantID = 0);\n"; if (!Info.OperandMatchInfo.empty()) { @@ -3031,16 +3036,22 @@ // Finally, build the match function. OS << "unsigned " << Target.getName() << ClassName << "::\n" << "MatchInstructionImpl(const OperandVector &Operands,\n"; - OS << " MCInst &Inst, uint64_t &ErrorInfo,\n" - << " bool matchingInlineAsm, unsigned VariantID) {\n"; + OS << " MCInst &Inst,\n"; + if (ReportMultipleNearMisses) + OS << " SmallVectorImpl *NearMisses,\n"; + else + OS << " uint64_t &ErrorInfo,\n"; + OS << " bool matchingInlineAsm, unsigned VariantID) {\n"; - OS << " // Eliminate obvious mismatches.\n"; - OS << " if (Operands.size() > " - << (MaxNumOperands + HasMnemonicFirst) << ") {\n"; - OS << " ErrorInfo = " - << (MaxNumOperands + HasMnemonicFirst) << ";\n"; - OS << " return Match_InvalidOperand;\n"; - OS << " }\n\n"; + if (!ReportMultipleNearMisses) { + OS << " // Eliminate obvious mismatches.\n"; + OS << " if (Operands.size() > " + << (MaxNumOperands + HasMnemonicFirst) << ") {\n"; + OS << " ErrorInfo = " + << (MaxNumOperands + HasMnemonicFirst) << ";\n"; + OS << " return Match_InvalidOperand;\n"; + OS << " }\n\n"; + } // Emit code to get the available features. OS << " // Get the current feature set.\n"; @@ -3063,17 +3074,20 @@ } // Emit code to compute the class list for this operand vector. - OS << " // Some state to try to produce better error messages.\n"; - OS << " bool HadMatchOtherThanFeatures = false;\n"; - OS << " bool HadMatchOtherThanPredicate = false;\n"; - OS << " unsigned RetCode = Match_InvalidOperand;\n"; - OS << " uint64_t MissingFeatures = ~0ULL;\n"; + if (!ReportMultipleNearMisses) { + OS << " // Some state to try to produce better error messages.\n"; + OS << " bool HadMatchOtherThanFeatures = false;\n"; + OS << " bool HadMatchOtherThanPredicate = false;\n"; + OS << " unsigned RetCode = Match_InvalidOperand;\n"; + OS << " uint64_t MissingFeatures = ~0ULL;\n"; + OS << " // Set ErrorInfo to the operand that mismatches if it is\n"; + OS << " // wrong for all instances of the instruction.\n"; + OS << " ErrorInfo = ~0ULL;\n"; + } + if (HasOptionalOperands) { OS << " SmallBitVector OptionalOperandsMask(" << MaxNumOperands << ");\n"; } - OS << " // Set ErrorInfo to the operand that mismatches if it is\n"; - OS << " // wrong for all instances of the instruction.\n"; - OS << " ErrorInfo = ~0ULL;\n"; // Emit code to search the table. OS << " // Find the appropriate table for this asm variant.\n"; @@ -3108,13 +3122,23 @@ << "*ie = MnemonicRange.second;\n"; OS << " it != ie; ++it) {\n"; + if (ReportMultipleNearMisses) { + OS << " // Some state to record ways in which this instruction did not match.\n"; + OS << " NearMissInfo OperandNearMiss = NearMissInfo::getSuccess();\n"; + OS << " NearMissInfo FeaturesNearMiss = NearMissInfo::getSuccess();\n"; + OS << " NearMissInfo EarlyPredicateNearMiss = NearMissInfo::getSuccess();\n"; + OS << " NearMissInfo LatePredicateNearMiss = NearMissInfo::getSuccess();\n"; + OS << " bool MultipleInvalidOperands = false;\n"; + } + if (HasMnemonicFirst) { OS << " // equal_range guarantees that instruction mnemonic matches.\n"; OS << " assert(Mnemonic == it->getMnemonic());\n"; } // Emit check that the subclasses match. - OS << " bool OperandsValid = true;\n"; + if (!ReportMultipleNearMisses) + OS << " bool OperandsValid = true;\n"; if (HasOptionalOperands) { OS << " OptionalOperandsMask.reset(0, " << MaxNumOperands << ");\n"; } @@ -3124,14 +3148,30 @@ OS << " auto Formal = " << "static_cast(it->Classes[FormalIdx]);\n"; OS << " if (ActualIdx >= Operands.size()) {\n"; - OS << " OperandsValid = (Formal == " <<"InvalidMatchClass) || " - "isSubclass(Formal, OptionalMatchClass);\n"; - OS << " if (!OperandsValid) ErrorInfo = ActualIdx;\n"; - if (HasOptionalOperands) { - OS << " OptionalOperandsMask.set(FormalIdx, " << MaxNumOperands - << ");\n"; + if (ReportMultipleNearMisses) { + OS << " bool ThisOperandValid = (Formal == " <<"InvalidMatchClass) || " + "isSubclass(Formal, OptionalMatchClass);\n"; + OS << " if (!ThisOperandValid) {\n"; + OS << " if (!OperandNearMiss) {\n"; + OS << " // Record info about match failure for later use.\n"; + OS << " OperandNearMiss =\n"; + OS << " NearMissInfo::getTooFewOperands(Formal, it->Opcode);\n"; + OS << " } else {\n"; + OS << " // If more than one operand is invalid, give up on this match entry.\n"; + OS << " MultipleInvalidOperands = true;\n"; + OS << " break;\n"; + OS << " }\n"; + OS << " }\n"; + OS << " continue;\n"; + } else { + OS << " OperandsValid = (Formal == InvalidMatchClass) || isSubclass(Formal, OptionalMatchClass);\n"; + OS << " if (!OperandsValid) ErrorInfo = ActualIdx;\n"; + if (HasOptionalOperands) { + OS << " OptionalOperandsMask.set(FormalIdx, " << MaxNumOperands + << ");\n"; + } + OS << " break;\n"; } - OS << " break;\n"; OS << " }\n"; OS << " MCParsedAsmOperand &Actual = *Operands[ActualIdx];\n"; OS << " unsigned Diag = validateOperandClass(Actual, Formal);\n"; @@ -3157,34 +3197,58 @@ } OS << " continue;\n"; OS << " }\n"; - OS << " // If this operand is broken for all of the instances of this\n"; - OS << " // mnemonic, keep track of it so we can report loc info.\n"; - OS << " // If we already had a match that only failed due to a\n"; - OS << " // target predicate, that diagnostic is preferred.\n"; - OS << " if (!HadMatchOtherThanPredicate &&\n"; - OS << " (it == MnemonicRange.first || ErrorInfo <= ActualIdx)) {\n"; - OS << " ErrorInfo = ActualIdx;\n"; - OS << " // InvalidOperand is the default. Prefer specificity.\n"; - OS << " if (Diag != Match_InvalidOperand)\n"; - OS << " RetCode = Diag;\n"; - OS << " }\n"; - OS << " // Otherwise, just reject this instance of the mnemonic.\n"; - OS << " OperandsValid = false;\n"; - OS << " break;\n"; - OS << " }\n\n"; - OS << " if (!OperandsValid) continue;\n"; + if (ReportMultipleNearMisses) { + OS << " if (!OperandNearMiss) {\n"; + OS << " // If this is the first invalid operand we have seen, record some\n"; + OS << " // information about it.\n"; + OS << " OperandNearMiss =\n"; + OS << " NearMissInfo::getMissedOperand(Diag, Formal, it->Opcode, ActualIdx);\n"; + OS << " ++ActualIdx;\n"; + OS << " } else {\n"; + OS << " // If more than one operand is invalid, give up on this match entry.\n"; + OS << " MultipleInvalidOperands = true;\n"; + OS << " break;\n"; + OS << " }\n"; + OS << " }\n\n"; + } else { + OS << " // If this operand is broken for all of the instances of this\n"; + OS << " // mnemonic, keep track of it so we can report loc info.\n"; + OS << " // If we already had a match that only failed due to a\n"; + OS << " // target predicate, that diagnostic is preferred.\n"; + OS << " if (!HadMatchOtherThanPredicate &&\n"; + OS << " (it == MnemonicRange.first || ErrorInfo <= ActualIdx)) {\n"; + OS << " ErrorInfo = ActualIdx;\n"; + OS << " // InvalidOperand is the default. Prefer specificity.\n"; + OS << " if (Diag != Match_InvalidOperand)\n"; + OS << " RetCode = Diag;\n"; + OS << " }\n"; + OS << " // Otherwise, just reject this instance of the mnemonic.\n"; + OS << " OperandsValid = false;\n"; + OS << " break;\n"; + OS << " }\n\n"; + } + + if (ReportMultipleNearMisses) + OS << " if (MultipleInvalidOperands) continue;\n\n"; + else + OS << " if (!OperandsValid) continue;\n\n"; // Emit check that the required features are available. OS << " if ((AvailableFeatures & it->RequiredFeatures) " << "!= it->RequiredFeatures) {\n"; - OS << " HadMatchOtherThanFeatures = true;\n"; + if (!ReportMultipleNearMisses) + OS << " HadMatchOtherThanFeatures = true;\n"; OS << " uint64_t NewMissingFeatures = it->RequiredFeatures & " "~AvailableFeatures;\n"; - OS << " if (countPopulation(NewMissingFeatures) <=\n" - " countPopulation(MissingFeatures))\n"; - OS << " MissingFeatures = NewMissingFeatures;\n"; - OS << " continue;\n"; + if (ReportMultipleNearMisses) { + OS << " FeaturesNearMiss = NearMissInfo::getMissedFeature(NewMissingFeatures);\n"; + } else { + OS << " if (countPopulation(NewMissingFeatures) <=\n" + " countPopulation(MissingFeatures))\n"; + OS << " MissingFeatures = NewMissingFeatures;\n"; + OS << " continue;\n"; + } OS << " }\n"; OS << "\n"; OS << " Inst.clear();\n\n"; @@ -3200,11 +3264,28 @@ << " unsigned MatchResult;\n" << " if ((MatchResult = checkEarlyTargetMatchPredicate(Inst, " "Operands)) != Match_Success) {\n" - << " Inst.clear();\n" - << " RetCode = MatchResult;\n" - << " HadMatchOtherThanPredicate = true;\n" - << " continue;\n" - << " }\n\n"; + << " Inst.clear();\n"; + if (ReportMultipleNearMisses) { + OS << " EarlyPredicateNearMiss = NearMissInfo::getMissedPredicate(MatchResult);\n"; + } else { + OS << " RetCode = MatchResult;\n" + << " HadMatchOtherThanPredicate = true;\n" + << " continue;\n"; + } + OS << " }\n\n"; + + if (ReportMultipleNearMisses) { + OS << " // If we did not successfully match the operands, then we can't convert to\n"; + OS << " // an MCInst, so bail out on this instruction variant now.\n"; + OS << " if (OperandNearMiss) {\n"; + OS << " // If the operand mismatch was the only problem, reprrt it as a near-miss.\n"; + OS << " if (NearMisses && !FeaturesNearMiss && !EarlyPredicateNearMiss) {\n"; + OS << " NearMisses->push_back(OperandNearMiss);\n"; + OS << " }\n"; + OS << " continue;\n"; + OS << " }\n\n"; + } + OS << " if (matchingInlineAsm) {\n"; OS << " convertToMapAndConstraints(it->ConvertFn, Operands);\n"; OS << " return Match_Success;\n"; @@ -3224,11 +3305,37 @@ << " // handle any context sensitive constraints.\n" << " if ((MatchResult = checkTargetMatchPredicate(Inst)) !=" << " Match_Success) {\n" - << " Inst.clear();\n" - << " RetCode = MatchResult;\n" - << " HadMatchOtherThanPredicate = true;\n" - << " continue;\n" - << " }\n\n"; + << " Inst.clear();\n"; + if (ReportMultipleNearMisses) { + OS << " LatePredicateNearMiss = NearMissInfo::getMissedPredicate(MatchResult);\n"; + } else { + OS << " RetCode = MatchResult;\n" + << " HadMatchOtherThanPredicate = true;\n" + << " continue;\n"; + } + OS << " }\n\n"; + + if (ReportMultipleNearMisses) { + OS << " int NumNearMisses = ((int)(bool)OperandNearMiss +\n"; + OS << " (int)(bool)FeaturesNearMiss +\n"; + OS << " (int)(bool)EarlyPredicateNearMiss +\n"; + OS << " (int)(bool)LatePredicateNearMiss);\n"; + OS << " if (NumNearMisses == 1) {\n"; + OS << " // We had exactly one type of near-miss, so add that to the list.\n"; + OS << " assert(!OperandNearMiss && \"OperandNearMiss was handled earlier\");\n"; + OS << " if (NearMisses && FeaturesNearMiss)\n"; + OS << " NearMisses->push_back(FeaturesNearMiss);\n"; + OS << " else if (NearMisses && EarlyPredicateNearMiss)\n"; + OS << " NearMisses->push_back(EarlyPredicateNearMiss);\n"; + OS << " else if (NearMisses && LatePredicateNearMiss)\n"; + OS << " NearMisses->push_back(LatePredicateNearMiss);\n"; + OS << "\n"; + OS << " continue;\n"; + OS << " } else if (NumNearMisses > 1) {\n"; + OS << " // This instruction missed in more than one way, so ignore it.\n"; + OS << " continue;\n"; + OS << " }\n"; + } // Call the post-processing function, if used. StringRef InsnCleanupFn = AsmParser->getValueAsString("AsmParserInstCleanup"); @@ -3249,12 +3356,17 @@ OS << " return Match_Success;\n"; OS << " }\n\n"; - OS << " // Okay, we had no match. Try to return a useful error code.\n"; - OS << " if (HadMatchOtherThanPredicate || !HadMatchOtherThanFeatures)\n"; - OS << " return RetCode;\n\n"; - OS << " // Missing feature matches return which features were missing\n"; - OS << " ErrorInfo = MissingFeatures;\n"; - OS << " return Match_MissingFeature;\n"; + if (ReportMultipleNearMisses) { + OS << " // No instruction variants matched exactly.\n"; + OS << " return Match_NearMisses;\n"; + } else { + OS << " // Okay, we had no match. Try to return a useful error code.\n"; + OS << " if (HadMatchOtherThanPredicate || !HadMatchOtherThanFeatures)\n"; + OS << " return RetCode;\n\n"; + OS << " // Missing feature matches return which features were missing\n"; + OS << " ErrorInfo = MissingFeatures;\n"; + OS << " return Match_MissingFeature;\n"; + } OS << "}\n\n"; if (!Info.OperandMatchInfo.empty())