Index: llvm/include/llvm/MC/MCInstPrinter.h =================================================================== --- llvm/include/llvm/MC/MCInstPrinter.h +++ llvm/include/llvm/MC/MCInstPrinter.h @@ -129,14 +129,17 @@ struct AliasPatternCond { enum CondKind : uint8_t { - K_Feature, // Match only if a feature is enabled. - K_NegFeature, // Match only if a feature is disabled. - K_Ignore, // Match any operand. - K_Reg, // Match a specific register. - K_TiedReg, // Match another already matched register. - K_Imm, // Match a specific immediate. - K_RegClass, // Match registers in a class. - K_Custom, // Call custom matcher by index. + K_Feature, // Match only if a feature is enabled. + K_NegFeature, // Match only if a feature is disabled. + K_OrFeature, // Match only if one of a set of features is enabled. + K_OrNegFeature, // Match only if one of a set of features is disabled. + K_EndOrFeatures, // Note end of list of K_Or(Neg)?Features. + K_Ignore, // Match any operand. + K_Reg, // Match a specific register. + K_TiedReg, // Match another already matched register. + K_Imm, // Match a specific immediate. + K_RegClass, // Match registers in a class. + K_Custom, // Call custom matcher by index. }; CondKind Kind; Index: llvm/include/llvm/Target/Target.td =================================================================== --- llvm/include/llvm/Target/Target.td +++ llvm/include/llvm/Target/Target.td @@ -657,9 +657,14 @@ /// as alternative condition string used for assembler matcher. /// e.g. "ModeThumb" is translated to "(Bits & ModeThumb) != 0". /// "!ModeThumb" is translated to "(Bits & ModeThumb) == 0". - /// It can also list multiple features separated by ",". + /// Multiple features can be combined in a single string, separated by ',' to + /// indicate that all features must be enabled, or '|' to indicate that at + /// least one in the list must be enabled. + /// Currently ',' and '|' are mutually exclusive. /// e.g. "ModeThumb,FeatureThumb2" is translated to /// "(Bits & ModeThumb) != 0 && (Bits & FeatureThumb2) != 0". + /// e.g. "ModeTumb|FeatureThumb2" is translated to + /// "(Bits & ModeThumb) != 0 || (Bits & FeatureThumb2) != 0". string AssemblerCondString = ""; /// PredicateName - User-level name to use for the predicate. Mainly for use Index: llvm/lib/MC/MCInstPrinter.cpp =================================================================== --- llvm/lib/MC/MCInstPrinter.cpp +++ llvm/lib/MC/MCInstPrinter.cpp @@ -62,12 +62,29 @@ static bool matchAliasCondition(const MCInst &MI, const MCSubtargetInfo *STI, const MCRegisterInfo &MRI, unsigned &OpIdx, const AliasMatchingData &M, - const AliasPatternCond &C) { + const AliasPatternCond &C, + bool &OrPredicateResult) { // Feature tests are special, they don't consume operands. if (C.Kind == AliasPatternCond::K_Feature) return STI->getFeatureBits().test(C.Value); if (C.Kind == AliasPatternCond::K_NegFeature) return !STI->getFeatureBits().test(C.Value); + // For feature tests where just one feature is required in a list, set the + // predicate result bit to whether the expression will return true, and only + // return the real result at the end of list marker. + if (C.Kind == AliasPatternCond::K_OrFeature) { + OrPredicateResult |= STI->getFeatureBits().test(C.Value); + return true; + } + if (C.Kind == AliasPatternCond::K_OrNegFeature) { + OrPredicateResult |= !(STI->getFeatureBits().test(C.Value)); + return true; + } + if (C.Kind == AliasPatternCond::K_EndOrFeatures) { + bool Res = OrPredicateResult; + OrPredicateResult = false; + return Res; + } // Get and consume an operand. const MCOperand &Opnd = MI.getOperand(OpIdx); @@ -95,6 +112,9 @@ return true; case AliasPatternCond::K_Feature: case AliasPatternCond::K_NegFeature: + case AliasPatternCond::K_OrFeature: + case AliasPatternCond::K_OrNegFeature: + case AliasPatternCond::K_EndOrFeatures: llvm_unreachable("handled earlier"); } llvm_unreachable("invalid kind"); @@ -125,8 +145,10 @@ ArrayRef Conds = M.PatternConds.slice(P.AliasCondStart, P.NumConds); unsigned OpIdx = 0; + bool OrPredicateResult = false; if (llvm::all_of(Conds, [&](const AliasPatternCond &C) { - return matchAliasCondition(*MI, STI, MRI, OpIdx, M, C); + return matchAliasCondition(*MI, STI, MRI, OpIdx, M, C, + OrPredicateResult); })) { // If all conditions matched, use this asm string. AsmStrOffset = P.AsmStrOffset; Index: llvm/lib/Target/RISCV/RISCV.td =================================================================== --- llvm/lib/Target/RISCV/RISCV.td +++ llvm/lib/Target/RISCV/RISCV.td @@ -105,7 +105,8 @@ // For instructions that belong to both the basic and the permutation subextensions. // They should be added if any of the two subextension has been specified. def HasStdExtZbbOrZbp - : Predicate<"Subtarget->hasStdExtZbb() || Subtarget->hasStdExtZbp()">; + : Predicate<"Subtarget->hasStdExtZbb() || Subtarget->hasStdExtZbp()">, + AssemblerPredicate<"FeatureExtZbb|FeatureExtZbp">; def FeatureStdExtB : SubtargetFeature<"b", "HasStdExtB", "true", Index: llvm/test/TableGen/AsmPredicateCombining.td =================================================================== --- /dev/null +++ llvm/test/TableGen/AsmPredicateCombining.td @@ -0,0 +1,100 @@ +// RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s | \ +// RUN: FileCheck --check-prefix=DISASS %s +// RUN: llvm-tblgen -gen-asm-matcher -I %p/../../include %s | \ +// RUN: FileCheck --check-prefix=MATCHER %s +// RUN: llvm-tblgen -gen-asm-writer -I %p/../../include %s | \ +// RUN: FileCheck --check-prefix=WRITER %s + +// Check that combining conditions in AssemblerPredicate conditions +// generates the correct patterns when using both the ',' AND operator +// and the '|' OR operator. + +include "llvm/Target/Target.td" + +def archInstrInfo : InstrInfo { } +def archAsmWriter : AsmWriter { + int PassSubtarget = 1; +} + +def arch : Target { + let InstructionSet = archInstrInfo; + let AssemblyWriters = [archAsmWriter]; +} + +let Namespace = "arch" in { + def R0 : Register<"r0">; + def R1 : Register<"r1">; + def R2 : Register<"r2">; + def R3 : Register<"r3">; + def R4 : Register<"r4">; +} +def Regs : RegisterClass<"Regs", [i32], 32, (add R0, R1, R2, R3, R4)>; + +class TestInsn Preds> : Instruction { + let Size = 2; + let OutOperandList = (outs); + let InOperandList = (ins Regs:$r); + field bits<16> Inst; + let Inst = Opc; + let AsmString = NAME # " $r"; + field bits<16> SoftFail = 0; + let Predicates = Preds; +} + + +def AsmCond1 : SubtargetFeature<"cond1", "cond1", "true", "">; + +def AsmPred1 : Predicate<"Pred1">, AssemblerPredicate<"AsmCond1">; +def AsmPred2 : Predicate<"Pred2">, AssemblerPredicate<"AsmCond2a,AsmCond2b">; +def AsmPred3 : Predicate<"Pred3">, AssemblerPredicate<"AsmCond3a|AsmCond3b">; +// MATCHER: if ((FB[arch::AsmCond1])) +// MATCHER-NEXT: Features.set(Feature_AsmPred1Bit); +// MATCHER-NEXT: if ((FB[arch::AsmCond2a]) && (FB[arch::AsmCond2b])) +// MATCHER-NEXT: Features.set(Feature_AsmPred2Bit); +// MATCHER-NEXT: if ((FB[arch::AsmCond3a]) || (FB[arch::AsmCond3b])) +// MATCHER-NEXT: Features.set(Feature_AsmPred3Bit); + +def insn1 : TestInsn<1, [AsmPred1]>; +// DISASS: return (Bits[arch::AsmCond1]); + +def insn2 : TestInsn<2, [AsmPred2]>; +// DISASS: return (Bits[arch::AsmCond2a] && Bits[arch::AsmCond2b]) + +def insn3 : TestInsn<3, [AsmPred3]>; +// DISASS: return ((Bits[arch::AsmCond3a] || Bits[arch::AsmCond3b])) + +def insn4 : TestInsn<4, [AsmPred1, AsmPred2]>; +// DISASS: return (Bits[arch::AsmCond1] && Bits[arch::AsmCond2a] && Bits[arch::AsmCond2b]) + +def insn5 : TestInsn<5, [AsmPred1, AsmPred3]>; +// DISASS: return (Bits[arch::AsmCond1] && (Bits[arch::AsmCond3a] || Bits[arch::AsmCond3b])) + +def insn6 : TestInsn<6, []>; +def : InstAlias<"alias1", (insn6 R0)> { let Predicates = [AsmPred1]; } +// WRITER: // (insn6 R0) +// WRITER-NEXT: {AliasPatternCond::K_Reg, arch::R0}, +// WRITER-NEXT: {AliasPatternCond::K_Feature, arch::AsmCond1}, +def : InstAlias<"alias2", (insn6 R1)> { let Predicates = [AsmPred2]; } +// WRITER: // (insn6 R1) +// WRITER-NEXT: {AliasPatternCond::K_Reg, arch::R1}, +// WRITER-NEXT: {AliasPatternCond::K_Feature, arch::AsmCond2a}, +// WRITER-NEXT: {AliasPatternCond::K_Feature, arch::AsmCond2b}, +def : InstAlias<"alias3", (insn6 R2)> { let Predicates = [AsmPred3]; } +// WRITER: // (insn6 R2) +// WRITER-NEXT: {AliasPatternCond::K_Reg, arch::R2}, +// WRITER-NEXT: {AliasPatternCond::K_OrFeature, arch::AsmCond3a}, +// WRITER-NEXT: {AliasPatternCond::K_OrFeature, arch::AsmCond3b}, +// WRITER-NEXT: {AliasPatternCond::K_EndOrFeatures, 0}, +def : InstAlias<"alias4", (insn6 R3)> { let Predicates = [AsmPred1, AsmPred2]; } +// WRITER: // (insn6 R3) +// WRITER-NEXT: {AliasPatternCond::K_Reg, arch::R3}, +// WRITER-NEXT: {AliasPatternCond::K_Feature, arch::AsmCond1}, +// WRITER-NEXT: {AliasPatternCond::K_Feature, arch::AsmCond2a}, +// WRITER-NEXT: {AliasPatternCond::K_Feature, arch::AsmCond2b}, +def : InstAlias<"alias5", (insn6 R4)> { let Predicates = [AsmPred1, AsmPred3]; } +// WRITER: // (insn6 R4) +// WRITER-NEXT: {AliasPatternCond::K_Reg, arch::R4}, +// WRITER-NEXT: {AliasPatternCond::K_Feature, arch::AsmCond1}, +// WRITER-NEXT: {AliasPatternCond::K_OrFeature, arch::AsmCond3a}, +// WRITER-NEXT: {AliasPatternCond::K_OrFeature, arch::AsmCond3b}, +// WRITER-NEXT: {AliasPatternCond::K_EndOrFeatures, 0}, Index: llvm/test/TableGen/AsmPredicateCombiningRISCV.td =================================================================== --- /dev/null +++ llvm/test/TableGen/AsmPredicateCombiningRISCV.td @@ -0,0 +1,96 @@ +// RUN: llvm-tblgen -gen-compress-inst-emitter -I %p/../../include %s | \ +// RUN: FileCheck --check-prefix=COMPRESS %s + +// Check that combining conditions in AssemblerPredicate conditions +// generates the correct patterns when using both the ',' AND operator +// and the '|' OR operator in the RISC-V specific instruction compressor + +include "llvm/Target/Target.td" + +def archInstrInfo : InstrInfo { } +def archAsmWriter : AsmWriter { + int PassSubtarget = 1; +} + +def arch : Target { + let InstructionSet = archInstrInfo; + let AssemblyWriters = [archAsmWriter]; +} + +let Namespace = "arch" in { + def R0 : Register<"r0">; +} +def Regs : RegisterClass<"Regs", [i32], 32, (add R0)>; + +class RVInst Preds> : Instruction { + let Size = 4; + let OutOperandList = (outs); + let InOperandList = (ins Regs:$r); + field bits<32> Inst; + let Inst = Opc; + let AsmString = NAME # " $r"; + field bits<32> SoftFail = 0; + let Predicates = Preds; +} +class RVInst16 Preds> : Instruction { + let Size = 2; + let OutOperandList = (outs); + let InOperandList = (ins Regs:$r); + field bits<16> Inst; + let Inst = Opc; + let AsmString = NAME # " $r"; + field bits<16> SoftFail = 0; + let Predicates = Preds; +} + +def AsmCond1 : SubtargetFeature<"cond1", "cond1", "true", "">; + +def AsmPred1 : Predicate<"Pred1">, AssemblerPredicate<"AsmCond1">; +def AsmPred2 : Predicate<"Pred2">, AssemblerPredicate<"AsmCond2a,AsmCond2b">; +def AsmPred3 : Predicate<"Pred3">, AssemblerPredicate<"AsmCond3a|AsmCond3b">; + +def BigInst : RVInst<1, [AsmPred1]>; + +class CompressPat predicates> { + dag Input = input; + dag Output = output; + list Predicates = predicates; +} + +// COMPRESS-LABEL: static bool compressInst +// COMPRESS: case arch::BigInst +def SmallInst1 : RVInst16<1, []>; +def : CompressPat<(BigInst Regs:$r), (SmallInst1 Regs:$r), [AsmPred1]>; +// COMPRESS: if (STI.getFeatureBits()[arch::AsmCond1] && +// COMPRESS-NEXT: (MRI.getRegClass(arch::RegsRegClassID).contains(MI.getOperand(0).getReg()))) { +// COMPRESS-NEXT: // SmallInst1 $r + +def SmallInst2 : RVInst16<2, []>; +def : CompressPat<(BigInst Regs:$r), (SmallInst2 Regs:$r), [AsmPred2]>; +// COMPRESS: if (STI.getFeatureBits()[arch::AsmCond2a] && +// COMPRESS-NEXT: STI.getFeatureBits()[arch::AsmCond2b] && +// COMPRESS-NEXT: (MRI.getRegClass(arch::RegsRegClassID).contains(MI.getOperand(0).getReg()))) { +// COMPRESS-NEXT: // SmallInst2 $r + +def SmallInst3 : RVInst16<2, []>; +def : CompressPat<(BigInst Regs:$r), (SmallInst3 Regs:$r), [AsmPred3]>; +// COMPRESS: if ((STI.getFeatureBits()[arch::AsmCond3a] || STI.getFeatureBits()[arch::AsmCond3b]) && +// COMPRESS-NEXT: (MRI.getRegClass(arch::RegsRegClassID).contains(MI.getOperand(0).getReg()))) { +// COMPRESS-NEXT: // SmallInst3 $r + +def SmallInst4 : RVInst16<2, []>; +def : CompressPat<(BigInst Regs:$r), (SmallInst4 Regs:$r), [AsmPred1, AsmPred2]>; +// COMPRESS: if (STI.getFeatureBits()[arch::AsmCond1] && +// COMPRESS-NEXT: STI.getFeatureBits()[arch::AsmCond2a] && +// COMPRESS-NEXT: STI.getFeatureBits()[arch::AsmCond2b] && +// COMPRESS-NEXT: (MRI.getRegClass(arch::RegsRegClassID).contains(MI.getOperand(0).getReg()))) { +// COMPRESS-NEXT: // SmallInst4 $r + +def SmallInst5 : RVInst16<2, []>; +def : CompressPat<(BigInst Regs:$r), (SmallInst5 Regs:$r), [AsmPred1, AsmPred3]>; +// COMPRESS: if (STI.getFeatureBits()[arch::AsmCond1] && +// COMPRESS-NEXT: (STI.getFeatureBits()[arch::AsmCond3a] || STI.getFeatureBits()[arch::AsmCond3b]) && +// COMPRESS-NEXT: (MRI.getRegClass(arch::RegsRegClassID).contains(MI.getOperand(0).getReg()))) { +// COMPRESS-NEXT: // SmallInst5 $r + +// COMPRESS-LABEL: static bool uncompressInst Index: llvm/utils/TableGen/AsmWriterEmitter.cpp =================================================================== --- llvm/utils/TableGen/AsmWriterEmitter.cpp +++ llvm/utils/TableGen/AsmWriterEmitter.cpp @@ -943,9 +943,14 @@ Record *R = *I; StringRef AsmCondString = R->getValueAsString("AssemblerCondString"); - // AsmCondString has syntax [!]F(,[!]F)* + // AsmCondString has syntax [!]F([,|][!]F)* + if (AsmCondString.find(',') != StringRef::npos && + AsmCondString.find('|') != StringRef::npos) + PrintFatalError(R->getLoc(), + "Cannot combine operators in AsmPredicate yet!"); + bool IsOr = AsmCondString.find('|') != StringRef::npos; SmallVector Ops; - SplitString(AsmCondString, Ops, ","); + SplitString(AsmCondString, Ops, ",|"); assert(!Ops.empty() && "AssemblerCondString cannot be empty"); for (StringRef Op : Ops) { @@ -953,9 +958,14 @@ bool IsNeg = Op[0] == '!'; StringRef Feature = Op.drop_front(IsNeg ? 1 : 0); IAP.addCond( - std::string(formatv("AliasPatternCond::K_{0}Feature, {1}::{2}", - IsNeg ? "Neg" : "", Namespace, Feature))); + std::string(formatv("AliasPatternCond::K_{0}{1}Feature, {2}::{3}", + IsOr ? "Or" : "", IsNeg ? "Neg" : "", + Namespace, Feature))); } + // If an AsmPredicate with ors is used, note end of list should these + // be combined. + if (IsOr) + IAP.addCond("AliasPatternCond::K_EndOrFeatures, 0"); } IAPrinterMap[Aliases.first].push_back(std::move(IAP)); Index: llvm/utils/TableGen/FixedLenDecoderEmitter.cpp =================================================================== --- llvm/utils/TableGen/FixedLenDecoderEmitter.cpp +++ llvm/utils/TableGen/FixedLenDecoderEmitter.cpp @@ -1209,13 +1209,26 @@ if (!IsFirstEmission) o << " && "; - std::pair pairs = P.split(','); + if (P.find(',') != StringRef::npos && P.find('|') != StringRef::npos) + PrintFatalError(Pred->getLoc(), + "Cannot combine operators in AsmPredicate yet!"); + bool SplitOr = P.find('|') != StringRef::npos; + + if (SplitOr) + o << "("; + std::pair pairs = + P.split(SplitOr ? '|' : ','); while (!pairs.second.empty()) { emitSinglePredicateMatch(o, pairs.first, Emitter->PredicateNamespace); - o << " && "; - pairs = pairs.second.split(','); + if (SplitOr) + o << " || "; + else + o << " && "; + pairs = pairs.second.split(SplitOr ? '|' : ','); } emitSinglePredicateMatch(o, pairs.first, Emitter->PredicateNamespace); + if (SplitOr) + o << ")"; IsFirstEmission = false; } return !Predicates->empty(); Index: llvm/utils/TableGen/RISCVCompressInstEmitter.cpp =================================================================== --- llvm/utils/TableGen/RISCVCompressInstEmitter.cpp +++ llvm/utils/TableGen/RISCVCompressInstEmitter.cpp @@ -475,14 +475,29 @@ } static void getReqFeatures(std::set &FeaturesSet, + std::set> &AnyOfFeatureSets, const std::vector &ReqFeatures) { for (auto &R : ReqFeatures) { StringRef AsmCondString = R->getValueAsString("AssemblerCondString"); - // AsmCondString has syntax [!]F(,[!]F)* + // AsmCondString has syntax [!]F([,|][!]F)* + if (AsmCondString.find(',') != StringRef::npos && + AsmCondString.find('|') != StringRef::npos) + PrintFatalError(R->getLoc(), + "Cannot combine operators in AsmPredicate yet!"); + bool IsAnyOf = AsmCondString.find('|') != StringRef::npos; SmallVector Ops; - SplitString(AsmCondString, Ops, ","); + SplitString(AsmCondString, Ops, ",|"); assert(!Ops.empty() && "AssemblerCondString cannot be empty"); + if (IsAnyOf) { + std::set AnyOfSet; + for (auto &Op : Ops) { + assert(!Op.empty() && "Empty operator"); + AnyOfSet.insert(Op); + } + AnyOfFeatureSets.insert(AnyOfSet); + return; + } for (auto &Op : Ops) { assert(!Op.empty() && "Empty operator"); FeaturesSet.insert(Op); @@ -652,8 +667,9 @@ } std::set FeaturesSet; + std::set> AnyOfFeatureSets; // Add CompressPat required features. - getReqFeatures(FeaturesSet, CompressPat.PatReqFeatures); + getReqFeatures(FeaturesSet, AnyOfFeatureSets, CompressPat.PatReqFeatures); // Add Dest instruction required features. std::vector ReqFeatures; @@ -661,7 +677,7 @@ copy_if(RF, std::back_inserter(ReqFeatures), [](Record *R) { return R->getValueAsBit("AssemblerMatcherPredicate"); }); - getReqFeatures(FeaturesSet, ReqFeatures); + getReqFeatures(FeaturesSet, AnyOfFeatureSets, ReqFeatures); // Emit checks for all required features. for (auto &Op : FeaturesSet) { @@ -676,6 +692,23 @@ " &&\n"; } + // Emit checks for all required feature groups. + for (auto &Set : AnyOfFeatureSets) { + CondStream.indent(6) << "("; + for (auto &Op : Set) { + bool isLast = &Op == &*Set.rbegin(); + if (Op[0] == '!') + CondStream << ("!STI.getFeatureBits()[" + Namespace + "::" + + Op.substr(1) + "]").str(); + else + CondStream << ("STI.getFeatureBits()[" + Namespace + "::" + Op + + "]").str(); + if (!isLast) + CondStream << " || "; + } + CondStream << ") &&\n"; + } + // Start Source Inst operands validation. unsigned OpNo = 0; for (OpNo = 0; OpNo < Source.Operands.size(); ++OpNo) { Index: llvm/utils/TableGen/SubtargetFeatureInfo.cpp =================================================================== --- llvm/utils/TableGen/SubtargetFeatureInfo.cpp +++ llvm/utils/TableGen/SubtargetFeatureInfo.cpp @@ -122,11 +122,21 @@ std::string CondStorage = std::string(SFI.TheDef->getValueAsString("AssemblerCondString")); StringRef Conds = CondStorage; - std::pair Comma = Conds.split(','); + if (Conds.find(',') != StringRef::npos && + Conds.find('|') != StringRef::npos) + PrintFatalError(SFI.TheDef->getLoc(), + "Cannot combine operators in AsmPredicate yet!"); + bool SplitOr = Conds.find('|') != StringRef::npos; + std::pair Comma = + Conds.split(SplitOr ? '|' : ','); bool First = true; do { - if (!First) - OS << " && "; + if (!First) { + if (SplitOr) + OS << " || "; + else + OS << " && "; + } bool Neg = false; StringRef Cond = Comma.first; @@ -144,7 +154,7 @@ break; First = false; - Comma = Comma.second.split(','); + Comma = Comma.second.split(SplitOr ? '|' : ','); } while (true); OS << ")\n";