Skip to content

Commit 02a8278

Browse files
[AsmMatcher] Add support for conflicting features
This helps avoiding diagnostics for instructions that could never be selected and is required for RISC-V CHERI support. An example here are the CHERI mode-dependent instructions where we have loads/stores that are identical other than the register class for the base register and have predicates that can never both be set. To avoid nonsensical error messages, we should only use the candidate instructions with the currently available feature bits. For RVY (CHERI), loads and stores are mode-dependent, using either a YLEN register or a XLEN register as the base. Prior to the standardization process CHERI assembly used c-prefixed register names for capabilities, so we had the following syntax for RISC-V compatible mode and CHERI pure-capability mode: lw x4, 0(c3) # capability mode: use new `CLW` tablegen instruction lw x4, 0(x3) # integer mode: use existing `LW` tablegen instruction During the standardization this was changed to keep the same register name in both modes, so now we have `lw x4, 0(x3)` in both modes but we have to select between two instructions: one using the normal GPR register class and one using the YGPR register class. We now have a choice between two instructions `LW` and `LW_Y` that have predicates that can never both be true, so we should avoid reporting missing predicates or wrong operands for the "unreachable" instruction. This change was taken from Morello LLVM with a few minor comment clarifications and changes to naming of variables. Co-authored-by: Silviu Baranga <[email protected]>
1 parent 3c009d2 commit 02a8278

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,14 @@ class LLVM_ABI MCTargetAsmParser : public MCAsmParserExtension {
353353

354354
/// AvailableFeatures - The current set of available features.
355355
FeatureBitset AvailableFeatures;
356+
/// A set of features that conflict with each other. This helps improve
357+
/// diagnostics by ignoring instruction variants that are mutually exclusive.
358+
/// An example here are the CHERI mode-dependent instructions where we have
359+
/// loads/stores that are identical other than the register class for the base
360+
/// register and have predicates that can never both be set. To avoid
361+
/// nonsensical error messages, we should only use the candidate instructions
362+
/// with the currently available feature bits.
363+
FeatureBitset ConflictingFeatures;
356364

357365
/// ParsingMSInlineAsm - Are we parsing ms-style inline assembly?
358366
bool ParsingMSInlineAsm = false;
@@ -384,6 +392,15 @@ class LLVM_ABI MCTargetAsmParser : public MCAsmParserExtension {
384392
AvailableFeatures = Value;
385393
}
386394

395+
const FeatureBitset &getConflictingFeatures() const {
396+
return ConflictingFeatures;
397+
}
398+
void setConflictingFeatures(const FeatureBitset &Value) {
399+
assert((Value.none() || Value.count() == 2) &&
400+
"Only zero or two conflicting features supported");
401+
ConflictingFeatures = Value;
402+
}
403+
387404
bool isParsingMSInlineAsm () { return ParsingMSInlineAsm; }
388405
void setParsingMSInlineAsm (bool Value) { ParsingMSInlineAsm = Value; }
389406

llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,15 @@ class RISCVAsmParser : public MCTargetAsmParser {
307307
Parser.addAliasForDirective(".word", ".4byte");
308308
Parser.addAliasForDirective(".dword", ".8byte");
309309
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
310+
// Don't record errors based on the CapMode feature. XOR the two
311+
// bit patterns to get something that has just CapMode and NotCapMode.
312+
// Skipping mnemonics that are not available in the current assembler mode
313+
// significantly improves diagnostics and allows reusing the same register
314+
// names for capmode vs non-capmode.
315+
FeatureBitset CapModeSet =
316+
ComputeAvailableFeatures({RISCV::FeatureCapMode});
317+
FeatureBitset NoCapModeSet = ComputeAvailableFeatures({});
318+
// setConflictingFeatures(CapModeSet ^ NoCapModeSet);
310319

311320
auto ABIName = StringRef(Options.ABIName);
312321
if (ABIName.ends_with("f") && !getSTI().hasFeature(RISCV::FeatureStdExtF)) {

llvm/utils/TableGen/AsmMatcherEmitter.cpp

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3702,6 +3702,8 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
37023702
OS << " // Get the current feature set.\n";
37033703
OS << " const FeatureBitset &AvailableFeatures = "
37043704
"getAvailableFeatures();\n\n";
3705+
OS << " // Get the set of features to not report as missing features\n";
3706+
OS << " const FeatureBitset &ConflictingFeatures = getConflictingFeatures();\n\n";
37053707

37063708
OS << " // Get the instruction mnemonic, which is the first token.\n";
37073709
if (HasMnemonicFirst) {
@@ -3767,7 +3769,10 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
37673769
OS << " // Return a more specific error code if no mnemonics match.\n";
37683770
OS << " if (MnemonicRange.first == MnemonicRange.second)\n";
37693771
OS << " return Match_MnemonicFail;\n\n";
3770-
3772+
if (!ReportMultipleNearMisses)
3773+
OS << " auto FirstValidMnemonic = MnemonicRange.first;\n";
3774+
OS << " bool FoundValidMnemonic = false;\n";
3775+
OS << " bool HasNonConflictingMnemonic = false;\n";
37713776
OS << " for (const MatchEntry *it = MnemonicRange.first, "
37723777
<< "*ie = MnemonicRange.second;\n";
37733778
OS << " it != ie; ++it) {\n";
@@ -3779,7 +3784,20 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
37793784
"opcode \"\n";
37803785
OS << " << MII.getName(it->Opcode) "
37813786
"<< \"\\n\");\n";
3782-
3787+
// If we have a mismatch because we're missing one of the features that are
3788+
// conflicting for diagnostics purposes, skip this mnemonic.
3789+
OS << " if (!HasRequiredFeatures &&\n";
3790+
OS << " (RequiredFeatures & ConflictingFeatures & ~AvailableFeatures).any()) {\n";
3791+
OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \" Skipping mnemonic \"\n";
3792+
OS << " << MII.getName(it->Opcode) << \" due to conflicting features\\n\");\n";
3793+
OS << " continue;\n";
3794+
OS << " }\n";
3795+
OS << " HasNonConflictingMnemonic = true;\n";
3796+
OS << " if (!FoundValidMnemonic) {\n";
3797+
if (!ReportMultipleNearMisses)
3798+
OS << " FirstValidMnemonic = it;\n";
3799+
OS << " FoundValidMnemonic = true;\n";
3800+
OS << " }\n";
37833801
if (ReportMultipleNearMisses) {
37843802
OS << " // Some state to record ways in which this instruction did not "
37853803
"match.\n";
@@ -3942,7 +3960,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
39423960
OS << " // If we already had a match that only failed due to a\n";
39433961
OS << " // target predicate, that diagnostic is preferred.\n";
39443962
OS << " if (!HadMatchOtherThanPredicate &&\n";
3945-
OS << " (it == MnemonicRange.first || ErrorInfo <= ActualIdx)) "
3963+
OS << " (it == FirstValidMnemonic || ErrorInfo <= ActualIdx)) "
39463964
"{\n";
39473965
OS << " if (HasRequiredFeatures && (ErrorInfo != ActualIdx || Diag "
39483966
"!= Match_InvalidOperand))\n";
@@ -3980,6 +3998,11 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
39803998
OS << " if (NewMissingFeatures[I])\n";
39813999
OS << " dbgs() << ' ' << I;\n";
39824000
OS << " dbgs() << \"\\n\");\n";
4001+
OS << " if ((NewMissingFeatures & ConflictingFeatures).any()) {\n";
4002+
if (!ReportMultipleNearMisses)
4003+
OS << " HadMatchOtherThanFeatures = false;\n";
4004+
OS << " continue;\n";
4005+
OS << " }\n";
39834006
if (ReportMultipleNearMisses) {
39844007
OS << " FeaturesNearMiss = "
39854008
"NearMissInfo::getMissedFeature(NewMissingFeatures);\n";
@@ -4190,6 +4213,11 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
41904213
OS << " return Match_Success;\n";
41914214
OS << " }\n\n";
41924215

4216+
OS << " if (!HasNonConflictingMnemonic) {\n";
4217+
OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"Could not find non-conflicting mnemonic\\n\");\n";
4218+
OS << " return Match_MnemonicFail;\n";
4219+
OS << " }\n";
4220+
41934221
if (ReportMultipleNearMisses) {
41944222
OS << " // No instruction variants matched exactly.\n";
41954223
OS << " return Match_NearMisses;\n";

0 commit comments

Comments
 (0)