Skip to content
This repository was archived by the owner on Mar 28, 2020. It is now read-only.

Commit 8b755a3

Browse files
author
Sjoerd Meijer
committed
[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
1 parent c61a590 commit 8b755a3

File tree

3 files changed

+119
-2
lines changed

3 files changed

+119
-2
lines changed

lib/Target/ARM/AsmParser/ARMAsmParser.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8992,6 +8992,8 @@ unsigned ARMAsmParser::MatchInstruction(OperandVector &Operands, MCInst &Inst,
89928992
return PlainMatchResult;
89938993
}
89948994

8995+
std::string ARMMnemonicSpellCheck(StringRef S, uint64_t FBS);
8996+
89958997
static const char *getSubtargetFeatureName(uint64_t Val);
89968998
bool ARMAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
89978999
OperandVector &Operands,
@@ -9085,9 +9087,13 @@ bool ARMAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
90859087

90869088
return Error(ErrorLoc, "invalid operand for instruction");
90879089
}
9088-
case Match_MnemonicFail:
9089-
return Error(IDLoc, "invalid instruction",
9090+
case Match_MnemonicFail: {
9091+
uint64_t FBS = ComputeAvailableFeatures(getSTI().getFeatureBits());
9092+
std::string Suggestion = ARMMnemonicSpellCheck(
9093+
((ARMOperand &)*Operands[0]).getToken(), FBS);
9094+
return Error(IDLoc, "invalid instruction" + Suggestion,
90909095
((ARMOperand &)*Operands[0]).getLocRange());
9096+
}
90919097
case Match_RequiresNotITBlock:
90929098
return Error(IDLoc, "flag setting instruction only valid outside IT block");
90939099
case Match_RequiresITBlock:
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
@ RUN: not llvm-mc -triple=arm -show-encoding < %s 2>&1 | FileCheck %s
2+
@ RUN: not llvm-mc -triple=thumb -show-encoding < %s 2>&1 | FileCheck %s --check-prefix=CHECK-THUMB
3+
4+
@ This tests the mnemonic spell checker.
5+
6+
@ First check what happens when an instruction is omitted:
7+
8+
r1, r2, r3
9+
10+
@ CHECK: error: unexpected token in operand
11+
@ CHECK-NEXT: r1, r2, r3
12+
@ CHECK-NEXT: ^
13+
14+
@ We don't want to see a suggestion here; the edit distance is too large to
15+
@ give sensible suggestions:
16+
17+
aaaaaaaaaaaaaaa r1, r2, r3
18+
19+
@ CHECK: error: invalid instruction
20+
@ CHECK-NEXT: aaaaaaaaaaaaaaa r1, r2, r3
21+
@ CHECK-NEXT: ^
22+
23+
@ Check that we get one suggestion: 'pushh' is 1 edit away, i.e. an deletion.
24+
25+
pushh r1, r2, r3
26+
27+
@CHECK: error: invalid instruction, did you mean: push?
28+
@CHECK-NEXT: pushh r1, r2, r3
29+
@CHECK-NEXT: ^
30+
31+
adXd r1, r2, r3
32+
33+
@ Check edit distance 1 and 2: 'add' has edit distance of 1 (a deletion),
34+
@ and 'qadd' a distance of 2 (a deletion and an insertion)
35+
36+
@ CHECK: error: invalid instruction, did you mean: add, qadd?
37+
@ CHECK-NEXT: adXd r1, r2, r3
38+
@ CHECK-NEXT: ^
39+
40+
@ Check edit distance 1 and 2, just insertions:
41+
42+
ad r1, r2, r3
43+
44+
@ CHECK: error: invalid instruction, did you mean: adc, add, adr, and, qadd?
45+
@ CHECK-NEXT: ad r1, r2, r3
46+
@ CHECK-NEXT: ^
47+
48+
@ Check an instruction that is 2 edits away, and also has a lot of candidates:
49+
50+
ldre r1, r2, r3
51+
52+
@ CHECK: error: invalid instruction, did you mean: ldr, ldrb, ldrd, ldrex, ldrexb, ldrexd, ldrexh, ldrh, ldrt?
53+
@ CHECK-NEXT: ldre r1, r2, r3
54+
@ CHECK-NEXT: ^
55+
56+
@ Here it is checked that we don't suggest instructions that are not supported.
57+
@ For example, in Thumb mode we don't want to see suggestions 'faddd' of 'qadd'
58+
@ because they are not supported.
59+
60+
fadd r1, r2, r3
61+
62+
@ CHECK-THUMB: error: invalid instruction, did you mean: add?
63+
@ CHECK-THUMB: fadd r1, r2, r3
64+
@ CHECK-THUMB: ^
65+
66+
@ CHECK: error: invalid instruction, did you mean: add, qadd?
67+
@ CHECK-NEXT: fadd r1, r2, r3
68+
@ CHECK-NEXT: ^

utils/TableGen/AsmMatcherEmitter.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2711,6 +2711,47 @@ static void emitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target,
27112711
OS << "}\n\n";
27122712
}
27132713

2714+
static void emitMnemonicSpellChecker(raw_ostream &OS, CodeGenTarget &Target,
2715+
unsigned VariantCount) {
2716+
OS << "std::string " << Target.getName() << "MnemonicSpellCheck(StringRef S, uint64_t FBS) {\n";
2717+
if (!VariantCount)
2718+
OS << " return \"\";";
2719+
else {
2720+
OS << " const unsigned MaxEditDist = 2;\n";
2721+
OS << " std::vector<StringRef> Candidates;\n";
2722+
OS << " StringRef Prev = \"\";\n";
2723+
OS << " auto End = std::end(MatchTable0);\n";
2724+
OS << "\n";
2725+
OS << " for (auto I = std::begin(MatchTable0); I < End; I++) {\n";
2726+
OS << " // Ignore unsupported instructions.\n";
2727+
OS << " if ((FBS & I->RequiredFeatures) != I->RequiredFeatures)\n";
2728+
OS << " continue;\n";
2729+
OS << "\n";
2730+
OS << " StringRef T = I->getMnemonic();\n";
2731+
OS << " // Avoid recomputing the edit distance for the same string.\n";
2732+
OS << " if (T.equals(Prev))\n";
2733+
OS << " continue;\n";
2734+
OS << "\n";
2735+
OS << " Prev = T;\n";
2736+
OS << " unsigned Dist = S.edit_distance(T, false, MaxEditDist);\n";
2737+
OS << " if (Dist <= MaxEditDist)\n";
2738+
OS << " Candidates.push_back(T);\n";
2739+
OS << " }\n";
2740+
OS << "\n";
2741+
OS << " if (Candidates.empty())\n";
2742+
OS << " return \"\";\n";
2743+
OS << "\n";
2744+
OS << " std::string Res = \", did you mean: \";\n";
2745+
OS << " unsigned i = 0;\n";
2746+
OS << " for( ; i < Candidates.size() - 1; i++)\n";
2747+
OS << " Res += Candidates[i].str() + \", \";\n";
2748+
OS << " return Res + Candidates[i].str() + \"?\";\n";
2749+
}
2750+
OS << "}\n";
2751+
OS << "\n";
2752+
}
2753+
2754+
27142755
void AsmMatcherEmitter::run(raw_ostream &OS) {
27152756
CodeGenTarget Target(Records);
27162757
Record *AsmParser = Target.getAsmParser();
@@ -2974,6 +3015,8 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
29743015
OS << "};\n\n";
29753016
}
29763017

3018+
emitMnemonicSpellChecker(OS, Target, VariantCount);
3019+
29773020
// Finally, build the match function.
29783021
OS << "unsigned " << Target.getName() << ClassName << "::\n"
29793022
<< "MatchInstructionImpl(const OperandVector &Operands,\n";

0 commit comments

Comments
 (0)