From 7ac2ac85a81ccf8c282ae31e3180e2edf34a65c1 Mon Sep 17 00:00:00 2001 From: Jim Grosbach Date: Tue, 24 Apr 2012 22:40:08 +0000 Subject: [PATCH] ARM: improved assembler diagnostics for missing CPU features. When an instruction match is found, but the subtarget features it requires are not available (missing floating point unit, or thumb vs arm mode, for example), issue a diagnostic that identifies what the feature mismatch is. rdar://11257547 llvm-svn: 155499 --- include/llvm/Target/Target.td | 7 +++- lib/Target/ARM/ARMInstrInfo.td | 46 +++++++++++++---------- lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 19 ++++++++-- test/MC/ARM/thumb-diagnostics.s | 18 ++++----- utils/TableGen/AsmMatcherEmitter.cpp | 28 +++++++++++++- 5 files changed, 83 insertions(+), 35 deletions(-) diff --git a/include/llvm/Target/Target.td b/include/llvm/Target/Target.td index e3f4c6465d3..cf87fb157cf 100644 --- a/include/llvm/Target/Target.td +++ b/include/llvm/Target/Target.td @@ -438,6 +438,10 @@ class Predicate { /// e.g. "ModeThumb,FeatureThumb2" is translated to /// "(Bits & ModeThumb) != 0 && (Bits & FeatureThumb2) != 0". string AssemblerCondString = ""; + + /// PredicateName - User-level name to use for the predicate. Mainly for use + /// in diagnostics such as missing feature errors in the asm matcher. + string PredicateName = ""; } /// NoHonorSignDependentRounding - This predicate is true if support for @@ -761,9 +765,10 @@ def DefaultAsmParserVariant : AsmParserVariant; /// AssemblerPredicate - This is a Predicate that can be used when the assembler /// matches instructions and aliases. -class AssemblerPredicate { +class AssemblerPredicate { bit AssemblerMatcherPredicate = 1; string AssemblerCondString = cond; + string PredicateName = name; } /// TokenAlias - This class allows targets to define assembler token diff --git a/lib/Target/ARM/ARMInstrInfo.td b/lib/Target/ARM/ARMInstrInfo.td index 6b874b2cfcc..633b53fefc9 100644 --- a/lib/Target/ARM/ARMInstrInfo.td +++ b/lib/Target/ARM/ARMInstrInfo.td @@ -161,53 +161,59 @@ def ARMbfi : SDNode<"ARMISD::BFI", SDT_ARMBFI>; // ARM Instruction Predicate Definitions. // def HasV4T : Predicate<"Subtarget->hasV4TOps()">, - AssemblerPredicate<"HasV4TOps">; + AssemblerPredicate<"HasV4TOps", "armv4t">; def NoV4T : Predicate<"!Subtarget->hasV4TOps()">; def HasV5T : Predicate<"Subtarget->hasV5TOps()">; def HasV5TE : Predicate<"Subtarget->hasV5TEOps()">, - AssemblerPredicate<"HasV5TEOps">; + AssemblerPredicate<"HasV5TEOps", "armv5te">; def HasV6 : Predicate<"Subtarget->hasV6Ops()">, - AssemblerPredicate<"HasV6Ops">; + AssemblerPredicate<"HasV6Ops", "armv6">; def NoV6 : Predicate<"!Subtarget->hasV6Ops()">; def HasV6T2 : Predicate<"Subtarget->hasV6T2Ops()">, - AssemblerPredicate<"HasV6T2Ops">; + AssemblerPredicate<"HasV6T2Ops", "armv6t2">; def NoV6T2 : Predicate<"!Subtarget->hasV6T2Ops()">; def HasV7 : Predicate<"Subtarget->hasV7Ops()">, - AssemblerPredicate<"HasV7Ops">; + AssemblerPredicate<"HasV7Ops", "armv7">; def NoVFP : Predicate<"!Subtarget->hasVFP2()">; def HasVFP2 : Predicate<"Subtarget->hasVFP2()">, - AssemblerPredicate<"FeatureVFP2">; + AssemblerPredicate<"FeatureVFP2", "VFP2">; def HasVFP3 : Predicate<"Subtarget->hasVFP3()">, - AssemblerPredicate<"FeatureVFP3">; + AssemblerPredicate<"FeatureVFP3", "VFP3">; def HasVFP4 : Predicate<"Subtarget->hasVFP4()">, - AssemblerPredicate<"FeatureVFP4">; + AssemblerPredicate<"FeatureVFP4", "VFP4">; def HasNEON : Predicate<"Subtarget->hasNEON()">, - AssemblerPredicate<"FeatureNEON">; + AssemblerPredicate<"FeatureNEON", "NEON">; def HasFP16 : Predicate<"Subtarget->hasFP16()">, - AssemblerPredicate<"FeatureFP16">; + AssemblerPredicate<"FeatureFP16","half-float">; def HasDivide : Predicate<"Subtarget->hasDivide()">, - AssemblerPredicate<"FeatureHWDiv">; + AssemblerPredicate<"FeatureHWDiv", "divide">; def HasT2ExtractPack : Predicate<"Subtarget->hasT2ExtractPack()">, - AssemblerPredicate<"FeatureT2XtPk">; + AssemblerPredicate<"FeatureT2XtPk", + "pack/extract">; def HasThumb2DSP : Predicate<"Subtarget->hasThumb2DSP()">, - AssemblerPredicate<"FeatureDSPThumb2">; + AssemblerPredicate<"FeatureDSPThumb2", + "thumb2-dsp">; def HasDB : Predicate<"Subtarget->hasDataBarrier()">, - AssemblerPredicate<"FeatureDB">; + AssemblerPredicate<"FeatureDB", + "data-barriers">; def HasMP : Predicate<"Subtarget->hasMPExtension()">, - AssemblerPredicate<"FeatureMP">; + AssemblerPredicate<"FeatureMP", + "mp-extensions">; def UseNEONForFP : Predicate<"Subtarget->useNEONForSinglePrecisionFP()">; def DontUseNEONForFP : Predicate<"!Subtarget->useNEONForSinglePrecisionFP()">; def IsThumb : Predicate<"Subtarget->isThumb()">, - AssemblerPredicate<"ModeThumb">; + AssemblerPredicate<"ModeThumb", "thumb">; def IsThumb1Only : Predicate<"Subtarget->isThumb1Only()">; def IsThumb2 : Predicate<"Subtarget->isThumb2()">, - AssemblerPredicate<"ModeThumb,FeatureThumb2">; + AssemblerPredicate<"ModeThumb,FeatureThumb2", + "thumb2">; def IsMClass : Predicate<"Subtarget->isMClass()">, - AssemblerPredicate<"FeatureMClass">; + AssemblerPredicate<"FeatureMClass", "armv7m">; def IsARClass : Predicate<"!Subtarget->isMClass()">, - AssemblerPredicate<"!FeatureMClass">; + AssemblerPredicate<"!FeatureMClass", + "armv7a/r">; def IsARM : Predicate<"!Subtarget->isThumb()">, - AssemblerPredicate<"!ModeThumb">; + AssemblerPredicate<"!ModeThumb", "arm-mode">; def IsIOS : Predicate<"Subtarget->isTargetIOS()">; def IsNotIOS : Predicate<"!Subtarget->isTargetIOS()">; def IsNaCl : Predicate<"Subtarget->isTargetNaCl()">; diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 2c53e3f8f8c..cbd9bdb77ca 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -7277,6 +7277,7 @@ unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) { return Match_Success; } +static const char *getSubtargetFeatureName(unsigned Val); bool ARMAsmParser:: MatchAndEmitInstruction(SMLoc IDLoc, SmallVectorImpl &Operands, @@ -7317,9 +7318,21 @@ MatchAndEmitInstruction(SMLoc IDLoc, Inst.setLoc(IDLoc); Out.EmitInstruction(Inst); return false; - case Match_MissingFeature: - Error(IDLoc, "instruction requires a CPU feature not currently enabled"); - return true; + case Match_MissingFeature: { + assert(ErrorInfo && "Unknown missing feature!"); + // Special case the error message for the very common case where only + // a single subtarget feature is missing (Thumb vs. ARM, e.g.). + std::string Msg = "instruction requires:"; + unsigned Mask = 1; + for (unsigned i = 0; i < (sizeof(ErrorInfo)*8-1); ++i) { + if (ErrorInfo & Mask) { + Msg += " "; + Msg += getSubtargetFeatureName(ErrorInfo & Mask); + } + Mask <<= 1; + } + return Error(IDLoc, Msg); + } case Match_InvalidOperand: { SMLoc ErrorLoc = IDLoc; if (ErrorInfo != ~0U) { diff --git a/test/MC/ARM/thumb-diagnostics.s b/test/MC/ARM/thumb-diagnostics.s index 99d7e38c7ed..4d09af32ef3 100644 --- a/test/MC/ARM/thumb-diagnostics.s +++ b/test/MC/ARM/thumb-diagnostics.s @@ -67,7 +67,7 @@ error: invalid operand for instruction @ Invalid writeback and register lists for STM stm r1, {r2, r6} stm r1!, {r2, r9} -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: thumb2 @ CHECK-ERRORS: stm r1, {r2, r6} @ CHECK-ERRORS: ^ @ CHECK-ERRORS: error: registers must be in range r0-r7 @@ -95,13 +95,13 @@ error: invalid operand for instruction str r2, [r7, #-1] str r5, [r1, #3] str r3, [r7, #128] -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: thumb2 @ CHECK-ERRORS: str r2, [r7, #-1] @ CHECK-ERRORS: ^ -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: thumb2 @ CHECK-ERRORS: str r5, [r1, #3] @ CHECK-ERRORS: ^ -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: thumb2 @ CHECK-ERRORS: str r3, [r7, #128] @ CHECK-ERRORS: ^ @@ -111,7 +111,7 @@ error: invalid operand for instruction @ CHECK-ERRORS: error: invalid operand for instruction @ CHECK-ERRORS: svc #-1 @ CHECK-ERRORS: ^ -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: arm-mode @ CHECK-ERRORS: svc #256 @ CHECK-ERRORS: ^ @@ -121,15 +121,15 @@ error: invalid operand for instruction add sp, #3 add sp, sp, #512 add r2, sp, #1024 -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: thumb2 @ CHECK-ERRORS: add sp, #-1 @ CHECK-ERRORS: ^ -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: thumb2 @ CHECK-ERRORS: add sp, #3 @ CHECK-ERRORS: ^ -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: thumb2 @ CHECK-ERRORS: add sp, sp, #512 @ CHECK-ERRORS: ^ -@ CHECK-ERRORS: error: instruction requires a CPU feature not currently enabled +@ CHECK-ERRORS: error: instruction requires: arm-mode @ CHECK-ERRORS: add r2, sp, #1024 @ CHECK-ERRORS: ^ diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index a44fbe6256f..03807b10926 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -1951,6 +1951,25 @@ static void emitSubtargetFeatureFlagEnumeration(AsmMatcherInfo &Info, OS << "};\n\n"; } +/// emitGetSubtargetFeatureName - Emit the helper function to get the +/// user-level name for a subtarget feature. +static void emitGetSubtargetFeatureName(AsmMatcherInfo &Info, raw_ostream &OS) { + OS << "// User-level names for subtarget features that participate in\n" + << "// instruction matching.\n" + << "static const char *getSubtargetFeatureName(unsigned Val) {\n" + << " switch(Val) {\n"; + for (std::map::const_iterator + it = Info.SubtargetFeatures.begin(), + ie = Info.SubtargetFeatures.end(); it != ie; ++it) { + SubtargetFeatureInfo &SFI = *it->second; + // FIXME: Totally just a placeholder name to get the algorithm working. + OS << " case " << SFI.getEnumName() << ": return \"" + << SFI.TheDef->getValueAsString("PredicateName") << "\";\n"; + } + OS << " default: return \"(unknown)\";\n"; + OS << " }\n}\n\n"; +} + /// emitComputeAvailableFeatures - Emit the function to compute the list of /// available features given a subtarget. static void emitComputeAvailableFeatures(AsmMatcherInfo &Info, @@ -2380,6 +2399,9 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << "\n#ifdef GET_MATCHER_IMPLEMENTATION\n"; OS << "#undef GET_MATCHER_IMPLEMENTATION\n\n"; + // Generate the helper function to get the names for subtarget features. + emitGetSubtargetFeatureName(Info, OS); + // Generate the function that remaps for mnemonic aliases. bool HasMnemonicAliases = emitMnemonicAliases(OS, Info); @@ -2510,8 +2532,8 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { << Target.getName() << ClassName << "::\n" << "MatchInstructionImpl(const SmallVectorImpl" << " &Operands,\n"; - OS << " MCInst &Inst, unsigned &ErrorInfo,\n"; - OS << " unsigned VariantID) {\n"; + OS << " MCInst &Inst, unsigned &ErrorInfo, "; + OS << "unsigned VariantID) {\n"; // Emit code to get the available features. OS << " // Get the current feature set.\n"; @@ -2586,6 +2608,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " if ((AvailableFeatures & it->RequiredFeatures) " << "!= it->RequiredFeatures) {\n"; OS << " HadMatchOtherThanFeatures = true;\n"; + OS << " ErrorInfo = it->RequiredFeatures & ~AvailableFeatures;\n"; OS << " continue;\n"; OS << " }\n"; OS << "\n"; @@ -2620,6 +2643,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " // Okay, we had no match. Try to return a useful error code.\n"; OS << " if (HadMatchOtherThanPredicate || !HadMatchOtherThanFeatures)"; OS << " return RetCode;\n"; + OS << " assert(ErrorInfo && \"missing feature(s) but what?!\");"; OS << " return Match_MissingFeature;\n"; OS << "}\n\n";