llvm.org GIT mirror llvm / 8b755a3
[AsmParser] Mnemonic Spell Corrector This implements suggesting other mnemonics when an invalid one is specified, for example: $ echo "adXd r1,r2,#3" | llvm-mc -triple arm <stdin>:1:1: error: invalid instruction, did you mean: add, qadd? adXd r1,r2,#3 ^ The implementation is target agnostic, but as a first step I have added it only to the ARM backend; so the ARM backend is a good example if someone wants to enable this too for another target. Differential Revision: https://reviews.llvm.org/D33128 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@307148 91177308-0d34-0410-b5e6-96231b3b80d8 Sjoerd Meijer 3 years ago
3 changed file(s) with 119 addition(s) and 2 deletion(s). Raw diff Collapse all Expand all
89918991 return PlainMatchResult;
89928992 }
89938993
8994 std::string ARMMnemonicSpellCheck(StringRef S, uint64_t FBS);
8995
89948996 static const char *getSubtargetFeatureName(uint64_t Val);
89958997 bool ARMAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
89968998 OperandVector &Operands,
90849086
90859087 return Error(ErrorLoc, "invalid operand for instruction");
90869088 }
9087 case Match_MnemonicFail:
9088 return Error(IDLoc, "invalid instruction",
9089 case Match_MnemonicFail: {
9090 uint64_t FBS = ComputeAvailableFeatures(getSTI().getFeatureBits());
9091 std::string Suggestion = ARMMnemonicSpellCheck(
9092 ((ARMOperand &)*Operands[0]).getToken(), FBS);
9093 return Error(IDLoc, "invalid instruction" + Suggestion,
90899094 ((ARMOperand &)*Operands[0]).getLocRange());
9095 }
90909096 case Match_RequiresNotITBlock:
90919097 return Error(IDLoc, "flag setting instruction only valid outside IT block");
90929098 case Match_RequiresITBlock:
0 @ RUN: not llvm-mc -triple=arm -show-encoding < %s 2>&1 | FileCheck %s
1 @ RUN: not llvm-mc -triple=thumb -show-encoding < %s 2>&1 | FileCheck %s --check-prefix=CHECK-THUMB
2
3 @ This tests the mnemonic spell checker.
4
5 @ First check what happens when an instruction is omitted:
6
7 r1, r2, r3
8
9 @ CHECK: error: unexpected token in operand
10 @ CHECK-NEXT: r1, r2, r3
11 @ CHECK-NEXT: ^
12
13 @ We don't want to see a suggestion here; the edit distance is too large to
14 @ give sensible suggestions:
15
16 aaaaaaaaaaaaaaa r1, r2, r3
17
18 @ CHECK: error: invalid instruction
19 @ CHECK-NEXT: aaaaaaaaaaaaaaa r1, r2, r3
20 @ CHECK-NEXT: ^
21
22 @ Check that we get one suggestion: 'pushh' is 1 edit away, i.e. an deletion.
23
24 pushh r1, r2, r3
25
26 @CHECK: error: invalid instruction, did you mean: push?
27 @CHECK-NEXT: pushh r1, r2, r3
28 @CHECK-NEXT: ^
29
30 adXd r1, r2, r3
31
32 @ Check edit distance 1 and 2: 'add' has edit distance of 1 (a deletion),
33 @ and 'qadd' a distance of 2 (a deletion and an insertion)
34
35 @ CHECK: error: invalid instruction, did you mean: add, qadd?
36 @ CHECK-NEXT: adXd r1, r2, r3
37 @ CHECK-NEXT: ^
38
39 @ Check edit distance 1 and 2, just insertions:
40
41 ad r1, r2, r3
42
43 @ CHECK: error: invalid instruction, did you mean: adc, add, adr, and, qadd?
44 @ CHECK-NEXT: ad r1, r2, r3
45 @ CHECK-NEXT: ^
46
47 @ Check an instruction that is 2 edits away, and also has a lot of candidates:
48
49 ldre r1, r2, r3
50
51 @ CHECK: error: invalid instruction, did you mean: ldr, ldrb, ldrd, ldrex, ldrexb, ldrexd, ldrexh, ldrh, ldrt?
52 @ CHECK-NEXT: ldre r1, r2, r3
53 @ CHECK-NEXT: ^
54
55 @ Here it is checked that we don't suggest instructions that are not supported.
56 @ For example, in Thumb mode we don't want to see suggestions 'faddd' of 'qadd'
57 @ because they are not supported.
58
59 fadd r1, r2, r3
60
61 @ CHECK-THUMB: error: invalid instruction, did you mean: add?
62 @ CHECK-THUMB: fadd r1, r2, r3
63 @ CHECK-THUMB: ^
64
65 @ CHECK: error: invalid instruction, did you mean: add, qadd?
66 @ CHECK-NEXT: fadd r1, r2, r3
67 @ CHECK-NEXT: ^
27102710 OS << "}\n\n";
27112711 }
27122712
2713 static void emitMnemonicSpellChecker(raw_ostream &OS, CodeGenTarget &Target,
2714 unsigned VariantCount) {
2715 OS << "std::string " << Target.getName() << "MnemonicSpellCheck(StringRef S, uint64_t FBS) {\n";
2716 if (!VariantCount)
2717 OS << " return \"\";";
2718 else {
2719 OS << " const unsigned MaxEditDist = 2;\n";
2720 OS << " std::vector Candidates;\n";
2721 OS << " StringRef Prev = \"\";\n";
2722 OS << " auto End = std::end(MatchTable0);\n";
2723 OS << "\n";
2724 OS << " for (auto I = std::begin(MatchTable0); I < End; I++) {\n";
2725 OS << " // Ignore unsupported instructions.\n";
2726 OS << " if ((FBS & I->RequiredFeatures) != I->RequiredFeatures)\n";
2727 OS << " continue;\n";
2728 OS << "\n";
2729 OS << " StringRef T = I->getMnemonic();\n";
2730 OS << " // Avoid recomputing the edit distance for the same string.\n";
2731 OS << " if (T.equals(Prev))\n";
2732 OS << " continue;\n";
2733 OS << "\n";
2734 OS << " Prev = T;\n";
2735 OS << " unsigned Dist = S.edit_distance(T, false, MaxEditDist);\n";
2736 OS << " if (Dist <= MaxEditDist)\n";
2737 OS << " Candidates.push_back(T);\n";
2738 OS << " }\n";
2739 OS << "\n";
2740 OS << " if (Candidates.empty())\n";
2741 OS << " return \"\";\n";
2742 OS << "\n";
2743 OS << " std::string Res = \", did you mean: \";\n";
2744 OS << " unsigned i = 0;\n";
2745 OS << " for( ; i < Candidates.size() - 1; i++)\n";
2746 OS << " Res += Candidates[i].str() + \", \";\n";
2747 OS << " return Res + Candidates[i].str() + \"?\";\n";
2748 }
2749 OS << "}\n";
2750 OS << "\n";
2751 }
2752
2753
27132754 void AsmMatcherEmitter::run(raw_ostream &OS) {
27142755 CodeGenTarget Target(Records);
27152756 Record *AsmParser = Target.getAsmParser();
29723013
29733014 OS << "};\n\n";
29743015 }
3016
3017 emitMnemonicSpellChecker(OS, Target, VariantCount);
29753018
29763019 // Finally, build the match function.
29773020 OS << "unsigned " << Target.getName() << ClassName << "::\n"