[llvm] 779a0f4 - [TableGen] Support HwMode registers in CompressInstEmitter (#203599)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 17:08:49 PDT 2026
Author: Alexander Richardson
Date: 2026-09-30T00:08:43Z
New Revision: 779a0f4938d487a97c82ea583c30501327ba6dff
URL: https://github.com/llvm/llvm-project/commit/779a0f4938d487a97c82ea583c30501327ba6dff
DIFF: https://github.com/llvm/llvm-project/commit/779a0f4938d487a97c82ea583c30501327ba6dff.diff
LOG: [TableGen] Support HwMode registers in CompressInstEmitter (#203599)
Previously, CompressInstEmitter only handled plain Register and
RegisterClass records when matching and validating operands in
CompressPat definitions. Attempting to use a RegisterByHwMode (such as a
mode-dependent stack pointer) or a RegClassByHwMode with mode-dependent
registers failed because CompressInstEmitter could not resolve the
underlying register or register class for a given subtarget
configuration.
Introduce HwModePredicates in CodeGenHwModes to resolve HwModeSelect
records using the subtarget features required by a CompressPat (with
predicates implying the absence of all non-default modes resolving to
DefaultMode). This allows using a single instruction definition for
compressed instructions like RISC-V C_ADDI4SPN/C_ADDI16SP across HwModes
instead of duplicating the instruction definitions for each mode.
This commit was created with the help of AI tools
Added:
llvm/test/TableGen/RegClassByHwModeCompressPat4Modes.td
llvm/test/TableGen/RegClassByHwModeCompressPatError.td
Modified:
llvm/test/TableGen/RegClassByHwModeCompressPat.td
llvm/utils/TableGen/Common/CodeGenHwModes.cpp
llvm/utils/TableGen/Common/CodeGenHwModes.h
llvm/utils/TableGen/Common/SubtargetFeatureInfo.cpp
llvm/utils/TableGen/Common/SubtargetFeatureInfo.h
llvm/utils/TableGen/CompressInstEmitter.cpp
Removed:
################################################################################
diff --git a/llvm/test/TableGen/RegClassByHwModeCompressPat.td b/llvm/test/TableGen/RegClassByHwModeCompressPat.td
index 36441d0d9a6e9..ea1cf1352f467 100644
--- a/llvm/test/TableGen/RegClassByHwModeCompressPat.td
+++ b/llvm/test/TableGen/RegClassByHwModeCompressPat.td
@@ -2,10 +2,20 @@
include "Common/RegClassByHwModeCommon.td"
-def IsPtr64 : Predicate<"Subtarget->isPtr64()">;
+def FeaturePtr64 : SubtargetFeature<"ptr64", "IsPtr64", "true", "Ptr64 mode">;
+
+def IsPtr64 : Predicate<"Subtarget->isPtr64()">,
+ AssemblerPredicate<(all_of FeaturePtr64)>;
+def NotPtr64 : Predicate<"!Subtarget->isPtr64()">,
+ AssemblerPredicate<(all_of (not FeaturePtr64))>;
defvar Ptr32 = DefaultMode;
def Ptr64 : HwMode<[IsPtr64]>;
def PtrRC : RegClassByHwMode<[Ptr32, Ptr64], [XRegs, YRegs]>;
+// Define a subclass of PtrRC to test type compatibility check for HwMode-dependent register classes.
+// PtrRCSub is a subclass of PtrRC in all modes because XRegsSub is subclass of XRegs, and YRegsSub is subclass of YRegs.
+def XRegsSub : RegisterClass<"MyTarget", [i64], 64, (add X1, X2)>;
+def YRegsSub : RegisterClass<"MyTarget", [i64], 64, (add Y1, Y2)>;
+def PtrRCSub : RegClassByHwMode<[Ptr32, Ptr64], [XRegsSub, YRegsSub]>;
def NullReg : RegisterByHwMode<PtrRC, [Ptr32, Ptr64], [X0, Y0]>;
def X_MOV : TestInstruction {
@@ -38,6 +48,13 @@ def X_MOV_TIED : TestInstruction {
let Size = 1;
}
+def Y_MOV : TestInstruction {
+ let OutOperandList = (outs YRegs:$dst);
+ let InOperandList = (ins YRegs:$src);
+ let AsmString = "y_mov $dst, $src";
+ let opcode = 5;
+}
+
def PTR_MOV : TestInstruction {
let OutOperandList = (outs PtrRC:$dst);
let InOperandList = (ins PtrRC:$src);
@@ -80,6 +97,22 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$dst),
(PTR_MOV_TIED PtrRC:$dst)>;
def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
(PTR_MOV_SMALL PtrRC:$dst, PtrRC:$src)>;
+// Test type compatibility when both pattern and instruction operands are mode-dependent
+// but
diff erent register classes. PTR_MOV expects PtrRC, but the pattern uses PtrRCSub.
+// This is allowed because PtrRCSub is a subclass of PtrRC in all modes.
+def : CompressPat<(PTR_MOV PtrRCSub:$dst, PtrRCSub:$src),
+ (PTR_MOV_SMALL PtrRCSub:$dst, PtrRCSub:$src)>;
+
+// Test compression/uncompression when HwMode can be resolved by pattern predicates.
+// X_MOV (XRegs) -> PTR_MOV_SMALL (PtrRC) is valid in 32-bit mode (NotPtr64).
+let Predicates = [NotPtr64] in
+def : CompressPat<(X_MOV XRegs:$dst, XRegs:$src),
+ (PTR_MOV_SMALL XRegs:$dst, XRegs:$src)>;
+
+// Y_MOV (YRegs) -> PTR_MOV_SMALL (PtrRC) is valid in 64-bit mode (IsPtr64).
+let Predicates = [IsPtr64] in
+def : CompressPat<(Y_MOV YRegs:$dst, YRegs:$src),
+ (PTR_MOV_SMALL YRegs:$dst, YRegs:$src)>;
// CHECK: static bool compressInst(MCInst &OutInst,
// CHECK-NEXT: const MCInst &MI,
@@ -125,6 +158,19 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
// CHECK-NEXT: return true;
// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRCSub]).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRCSub]).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV_SMALL);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
// CHECK-NEXT: break;
// CHECK-NEXT: } // case PTR_MOV
// CHECK-NEXT: case MyTarget::X_MOV: {
@@ -165,8 +211,39 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
// CHECK-NEXT: return true;
// CHECK-NEXT: } // if
+// CHECK-NEXT: if (!STI.getFeatureBits()[MyTarget::FeaturePtr64] &&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV_SMALL);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
// CHECK-NEXT: break;
// CHECK-NEXT: } // case X_MOV
+// CHECK-NEXT: case MyTarget::Y_MOV: {
+// CHECK-NEXT: if (STI.getFeatureBits()[MyTarget::FeaturePtr64] &&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::YRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::YRegsRegClassID).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV_SMALL);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case Y_MOV
// CHECK-NEXT: } // switch
// CHECK-NEXT: return false;
// CHECK-NEXT: }
@@ -191,6 +268,47 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
// CHECK-NEXT: return true;
// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRCSub]).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRCSub]).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (!STI.getFeatureBits()[MyTarget::FeaturePtr64] &&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // x_mov $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::X_MOV);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (STI.getFeatureBits()[MyTarget::FeaturePtr64] &&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::YRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::YRegsRegClassID).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // y_mov $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::Y_MOV);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
// CHECK-NEXT: break;
// CHECK-NEXT: } // case PTR_MOV_SMALL
// CHECK-NEXT: case MyTarget::PTR_MOV_TIED: {
@@ -288,6 +406,16 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
// CHECK-NEXT: // Operand: dst
// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: return 1;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(0).isReg() && MI.getOperand(0).getReg().isPhysical() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRCSub]).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() && MI.getOperand(1).getReg().isPhysical() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTargetRegClassByHwModeTables[HwModeId][MyTarget::PtrRCSub]).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: // Operand: src
+
// CHECK-NEXT: return 1;
// CHECK-NEXT: } // if
// CHECK-NEXT: if (MI.getOperand(1).isReg() && MI.getOperand(0).isReg() &&
@@ -312,6 +440,17 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
// CHECK-NEXT: break;
// CHECK-NEXT: } // case PTR_MOV
// CHECK-NEXT: case MyTarget::X_MOV: {
+// CHECK-NEXT: if (!STI.getFeatureBits()[MyTarget::FeaturePtr64] &&
+// CHECK-NEXT: MI.getOperand(0).isReg() && MI.getOperand(0).getReg().isPhysical() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() && MI.getOperand(1).getReg().isPhysical() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: // Operand: src
+
+// CHECK-NEXT: return 1;
+// CHECK-NEXT: } // if
// CHECK-NEXT: if (MI.getOperand(0).isReg() && MI.getOperand(0).getReg().isPhysical() &&
// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
// CHECK-NEXT: MI.getOperand(1).isReg() && MI.getOperand(1).getReg().isPhysical() &&
@@ -343,6 +482,20 @@ def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
// CHECK-NEXT: } // if
// CHECK-NEXT: break;
// CHECK-NEXT: } // case X_MOV
+// CHECK-NEXT: case MyTarget::Y_MOV: {
+// CHECK-NEXT: if (STI.getFeatureBits()[MyTarget::FeaturePtr64] &&
+// CHECK-NEXT: MI.getOperand(0).isReg() && MI.getOperand(0).getReg().isPhysical() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::YRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() && MI.getOperand(1).getReg().isPhysical() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::YRegsRegClassID).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: // Operand: src
+
+// CHECK-NEXT: return 1;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case Y_MOV
// CHECK-NEXT: } // switch
// CHECK-NEXT: return 0;
// CHECK-NEXT: }
diff --git a/llvm/test/TableGen/RegClassByHwModeCompressPat4Modes.td b/llvm/test/TableGen/RegClassByHwModeCompressPat4Modes.td
new file mode 100644
index 0000000000000..aa0156c6ac0ac
--- /dev/null
+++ b/llvm/test/TableGen/RegClassByHwModeCompressPat4Modes.td
@@ -0,0 +1,116 @@
+// RUN: llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %S %s -o - | FileCheck %s
+
+include "Common/RegClassByHwModeCommon.td"
+
+// Define features
+def Feature64Bit : SubtargetFeature<"64bit", "Is64Bit", "true", "64-bit mode">;
+def FeatureCap : SubtargetFeature<"cap", "IsCap", "true", "Capability mode">;
+
+// Define predicates
+def Is64Bit : Predicate<"Subtarget->is64Bit()">,
+ AssemblerPredicate<(all_of Feature64Bit)>;
+def Not64Bit : Predicate<"!Subtarget->is64Bit()">,
+ AssemblerPredicate<(all_of (not Feature64Bit))>;
+def IsCap : Predicate<"Subtarget->isCap()">,
+ AssemblerPredicate<(all_of FeatureCap)>;
+def NotCap : Predicate<"!Subtarget->isCap()">,
+ AssemblerPredicate<(all_of (not FeatureCap))>;
+
+// Define 4 modes (similar to RISC-V RV32I, RV64I, RV32Y, RV64Y)
+def Mode32I : HwMode<[Not64Bit, NotCap]>;
+def Mode64I : HwMode<[Is64Bit, NotCap]>;
+def Mode32C : HwMode<[Not64Bit, IsCap]>;
+def Mode64C : HwMode<[Is64Bit, IsCap]>;
+
+// Define mode-dependent register class (SP-like)
+// Resolves to XRegs in Integer modes, and YRegs in Capability modes
+def PtrRC : RegClassByHwMode<[Mode32I, Mode64I, Mode32C, Mode64C],
+ [XRegs, XRegs, YRegs, YRegs]>;
+
+def X_MOV : TestInstruction {
+ let OutOperandList = (outs XRegs:$dst);
+ let InOperandList = (ins XRegs:$src);
+ let AsmString = "x_mov $dst, $src";
+ let opcode = 0;
+}
+
+def Y_MOV : TestInstruction {
+ let OutOperandList = (outs YRegs:$dst);
+ let InOperandList = (ins YRegs:$src);
+ let AsmString = "y_mov $dst, $src";
+ let opcode = 5;
+}
+
+def PTR_MOV_SMALL : TestInstruction {
+ let OutOperandList = (outs PtrRC:$dst);
+ let InOperandList = (ins PtrRC:$src);
+ let AsmString = "ptr_mov.small $dst, $src";
+ let opcode = 4;
+ let Size = 1;
+}
+
+// Test partial resolution with 4 modes.
+// NotCap only resolves the 'Type' dimension (to Integer), leaving 32-bit and 64-bit modes active.
+// Both active modes (Mode32I, Mode64I) resolve PtrRC to XRegs, so it should resolve successfully.
+let Predicates = [NotCap] in
+def : CompressPat<(X_MOV XRegs:$dst, XRegs:$src),
+ (PTR_MOV_SMALL XRegs:$dst, XRegs:$src)>;
+
+// IsCap only resolves the 'Type' dimension (to Capability), leaving 32-bit and 64-bit modes active.
+// Both active modes (Mode32C, Mode64C) resolve PtrRC to YRegs, so it should resolve successfully.
+let Predicates = [IsCap] in
+def : CompressPat<(Y_MOV YRegs:$dst, YRegs:$src),
+ (PTR_MOV_SMALL YRegs:$dst, YRegs:$src)>;
+
+// CHECK: static bool compressInst(MCInst &OutInst,
+// CHECK-NEXT: const MCInst &MI,
+// CHECK-NEXT: const MCSubtargetInfo &STI) {
+// CHECK-NEXT: {{\[\[}}maybe_unused]] unsigned HwModeId = STI.getHwMode(MCSubtargetInfo::HwMode_RegInfo);
+// CHECK-NEXT: switch (MI.getOpcode()) {
+// CHECK-NEXT: default: return false;
+// CHECK-NEXT: case MyTarget::X_MOV: {
+// CHECK-NEXT: if (!STI.getFeatureBits()[MyTarget::FeatureCap] &&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::XRegsRegClassID).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV_SMALL);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case X_MOV
+// CHECK-NEXT: case MyTarget::Y_MOV: {
+// CHECK-NEXT: if (STI.getFeatureBits()[MyTarget::FeatureCap] &&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::YRegsRegClassID).contains(MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT: getMyTargetMCRegisterClass(MyTarget::YRegsRegClassID).contains(MI.getOperand(1).getReg())) {
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV_SMALL);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case Y_MOV
+// CHECK-NEXT: } // switch
+// CHECK-NEXT: return false;
+// CHECK-NEXT: }
+
+def MyTargetISA : InstrInfo;
+def MyTargetAsmWriter : AsmWriter {
+ let PassSubtarget = 1;
+}
+def MyTarget : Target {
+ let InstructionSet = MyTargetISA;
+ let AssemblyWriters = [MyTargetAsmWriter];
+}
diff --git a/llvm/test/TableGen/RegClassByHwModeCompressPatError.td b/llvm/test/TableGen/RegClassByHwModeCompressPatError.td
new file mode 100644
index 0000000000000..94270f9fa5232
--- /dev/null
+++ b/llvm/test/TableGen/RegClassByHwModeCompressPatError.td
@@ -0,0 +1,129 @@
+// Test that TableGen emits an error when RegClassByHwMode cannot be resolved
+// due to missing or contradictory predicates, leading to a type mismatch.
+// RUN: not llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %S %s -DERROR_UNRESOLVED 2>&1 | FileCheck %s --check-prefix=UNRESOLVED
+// RUN: not llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %S %s -DERROR_CONTRADICTORY_PAT 2>&1 | FileCheck %s --check-prefix=CONTRADICTORY_PAT
+// RUN: not llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %S %s -DERROR_WRONG_PRED 2>&1 | FileCheck %s --check-prefix=WRONG_PRED
+// RUN: not llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %S %s -DERROR_CONTRADICTORY_MODE 2>&1 | FileCheck %s --check-prefix=CONTRADICTORY_MODE
+// RUN: not llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %S %s -DERROR_INCOMPATIBLE_HWMODE 2>&1 | FileCheck %s --check-prefix=INCOMPATIBLE_HWMODE
+// RUN: not llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %S %s -DERROR_NON_ASM_PRED_MODE 2>&1 | FileCheck %s --check-prefix=NON_ASM_PRED_MODE
+
+include "Common/RegClassByHwModeCommon.td"
+
+def FeaturePtr64 : SubtargetFeature<"ptr64", "IsPtr64", "true", "Ptr64 mode">;
+
+def IsPtr64 : Predicate<"Subtarget->isPtr64()">,
+ AssemblerPredicate<(all_of FeaturePtr64)>;
+def NotPtr64 : Predicate<"!Subtarget->isPtr64()">,
+ AssemblerPredicate<(all_of (not FeaturePtr64))>;
+
+defvar Ptr32 = DefaultMode;
+#ifdef ERROR_NON_ASM_PRED_MODE
+/// NonAsmPred has no AssemblerPredicate, so NonAsmMode is only active when both
+/// IsPtr64 and NonAsmPred hold. DefaultMode can still be active when IsPtr64
+/// is true and NonAsmPred is false, so a pattern predicated only on IsPtr64
+/// cannot uniquely resolve PtrRC to YRegs.
+def NonAsmPred : Predicate<"Subtarget->someOtherCheck()">;
+def NonAsmMode : HwMode<[IsPtr64, NonAsmPred]>;
+def PtrRC : RegClassByHwMode<[Ptr32, NonAsmMode], [XRegs, YRegs]>;
+#else
+def Ptr64 : HwMode<[IsPtr64]>;
+#ifdef ERROR_CONTRADICTORY_MODE
+// BadMode has contradictory predicates, so it can never be active.
+def BadMode : HwMode<[IsPtr64, NotPtr64]>;
+def PtrRC : RegClassByHwMode<[Ptr32, BadMode], [XRegs, YRegs]>;
+#else
+def PtrRC : RegClassByHwMode<[Ptr32, Ptr64], [XRegs, YRegs]>;
+#endif
+def OtherRC : RegClassByHwMode<[Ptr32, Ptr64], [YRegs, XRegs]>;
+#endif
+
+def X_MOV : TestInstruction {
+ let OutOperandList = (outs XRegs:$dst);
+ let InOperandList = (ins XRegs:$src);
+ let AsmString = "x_mov $dst, $src";
+ let opcode = 0;
+}
+
+def Y_MOV : TestInstruction {
+ let OutOperandList = (outs YRegs:$dst);
+ let InOperandList = (ins YRegs:$src);
+ let AsmString = "y_mov $dst, $src";
+ let opcode = 5;
+}
+
+def PTR_MOV : TestInstruction {
+ let OutOperandList = (outs PtrRC:$dst);
+ let InOperandList = (ins PtrRC:$src);
+ let AsmString = "ptr_mov $dst, $src";
+ let opcode = 3;
+}
+
+def PTR_MOV_SMALL : TestInstruction {
+ let OutOperandList = (outs PtrRC:$dst);
+ let InOperandList = (ins PtrRC:$src);
+ let AsmString = "ptr_mov.small $dst, $src";
+ let opcode = 4;
+ let Size = 1;
+}
+
+#ifdef ERROR_UNRESOLVED
+// X_MOV uses XRegs, but PTR_MOV_SMALL uses PtrRC.
+// Since there are no predicates, PtrRC cannot be resolved.
+def : CompressPat<(X_MOV XRegs:$dst, XRegs:$src),
+ (PTR_MOV_SMALL XRegs:$dst, XRegs:$src)>;
+// UNRESOLVED: {{.*}}RegClassByHwModeCompressPatError.td:[[@LINE-2]]:{{[0-9]+}}: error: Error in Dag '{{.*}}'. Operand 'dst' has type 'XRegs' which does not match the type 'PtrRC' in the corresponding instruction operand!
+#endif
+
+#ifdef ERROR_CONTRADICTORY_PAT
+// Pattern has contradictory predicates, so no modes can be active.
+let Predicates = [IsPtr64, NotPtr64] in
+def : CompressPat<(X_MOV XRegs:$dst, XRegs:$src),
+ (PTR_MOV_SMALL XRegs:$dst, XRegs:$src)>;
+// CONTRADICTORY_PAT: {{.*}}RegClassByHwModeCompressPatError.td:[[@LINE-2]]:{{[0-9]+}}: error: Error in Dag '{{.*}}'. Operand 'dst' has type 'XRegs' which does not match the type 'PtrRC' in the corresponding instruction operand!
+#endif
+
+#ifdef ERROR_WRONG_PRED
+// Pattern has NotPtr64 predicate, so PtrRC resolves to XRegs.
+// But Y_MOV uses YRegs, which is incompatible with XRegs.
+let Predicates = [NotPtr64] in
+def : CompressPat<(Y_MOV YRegs:$dst, YRegs:$src),
+ (PTR_MOV_SMALL YRegs:$dst, YRegs:$src)>;
+// WRONG_PRED: {{.*}}RegClassByHwModeCompressPatError.td:[[@LINE-2]]:{{[0-9]+}}: error: Error in Dag '{{.*}}'. Operand 'dst' has type 'YRegs' which does not match the type 'PtrRC' in the corresponding instruction operand!
+#endif
+
+#ifdef ERROR_CONTRADICTORY_MODE
+// Pattern has IsPtr64 predicate, which should resolve PtrRC to YRegs (from BadMode).
+// But BadMode is incompatible because it has contradictory predicates.
+// So PtrRC cannot be resolved.
+let Predicates = [IsPtr64] in
+def : CompressPat<(Y_MOV YRegs:$dst, YRegs:$src),
+ (PTR_MOV_SMALL YRegs:$dst, YRegs:$src)>;
+// CONTRADICTORY_MODE: {{.*}}RegClassByHwModeCompressPatError.td:[[@LINE-2]]:{{[0-9]+}}: error: Error in Dag '{{.*}}'. Operand 'dst' has type 'YRegs' which does not match the type 'PtrRC' in the corresponding instruction operand!
+#endif
+
+#ifdef ERROR_INCOMPATIBLE_HWMODE
+// OtherRC has YRegs for Ptr32 and XRegs for Ptr64.
+// PtrRC has XRegs for Ptr32 and YRegs for Ptr64.
+// They are incompatible because they don't have subclass relationship in all modes.
+def : CompressPat<(PTR_MOV OtherRC:$dst, OtherRC:$src),
+ (PTR_MOV_SMALL PtrRC:$dst, PtrRC:$src)>;
+// INCOMPATIBLE_HWMODE: {{.*}}RegClassByHwModeCompressPatError.td:[[@LINE-2]]:{{[0-9]+}}: error: Error in Dag '{{.*}}'. Operand 'dst' has type 'OtherRC' which does not match the type 'PtrRC' in the corresponding instruction operand!
+#endif
+
+#ifdef ERROR_NON_ASM_PRED_MODE
+/// Pattern has IsPtr64 predicate, but NonAsmMode also requires NonAsmPred, so
+/// DefaultMode (XRegs) is still possible when IsPtr64 holds.
+let Predicates = [IsPtr64] in
+def : CompressPat<(Y_MOV YRegs:$dst, YRegs:$src),
+ (PTR_MOV_SMALL YRegs:$dst, YRegs:$src)>;
+// NON_ASM_PRED_MODE: {{.*}}RegClassByHwModeCompressPatError.td:[[@LINE-2]]:{{[0-9]+}}: error: Error in Dag '{{.*}}'. Operand 'dst' has type 'YRegs' which does not match the type 'PtrRC' in the corresponding instruction operand!
+#endif
+
+def MyTargetISA : InstrInfo;
+def MyTargetAsmWriter : AsmWriter {
+ let PassSubtarget = 1;
+}
+def MyTarget : Target {
+ let InstructionSet = MyTargetISA;
+ let AssemblyWriters = [MyTargetAsmWriter];
+}
diff --git a/llvm/utils/TableGen/Common/CodeGenHwModes.cpp b/llvm/utils/TableGen/Common/CodeGenHwModes.cpp
index 5a93c83be5933..2c44cc06ea1b3 100644
--- a/llvm/utils/TableGen/Common/CodeGenHwModes.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenHwModes.cpp
@@ -9,13 +9,18 @@
//===----------------------------------------------------------------------===//
#include "CodeGenHwModes.h"
+#include "SubtargetFeatureInfo.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
+#include <algorithm>
+#include <set>
using namespace llvm;
+#define DEBUG_TYPE "codegen-hwmodes"
+
StringRef CodeGenHwModes::DefaultModeName = "DefaultMode";
HwMode::HwMode(const Record *R) {
@@ -48,6 +53,134 @@ void HwModeSelect::dump() const {
dbgs() << " (" << P.first << ',' << P.second->getName() << ')';
dbgs() << " }\n";
}
+// Helper to represent predicates semantically.
+HwModePredicates::HwModePredicates(ArrayRef<const Record *> Preds) {
+ HasNonAsmPredicates =
+ !getRequiredFeatures(FeaturesSet, AnyOfFeatureSets, Preds);
+}
+
+// DefaultMode is implicitly active only when *none* of the other target
+// HwModes are active.
+//
+// Mathematically: DefaultMode = !(Mode_1 || Mode_2 || ... || Mode_N)
+// = !Mode_1 && !Mode_2 && ... && !Mode_N
+//
+// If Mode_1 requires (FeatureA && FeatureB), then !Mode_1 requires
+// (!FeatureA || !FeatureB). We construct these implicit OR-sets for all
+// other modes and add them to DefaultMode's requirements.
+HwModePredicates
+HwModePredicates::createForDefaultMode(const CodeGenHwModes &CGH) {
+ HwModePredicates SP;
+ for (unsigned M = 1; M < CGH.getNumModeIds(); ++M) {
+ const HwMode &Mode = CGH.getMode(M);
+ HwModePredicates ModePreds(Mode.Predicates);
+ if (ModePreds.HasNonAsmPredicates) {
+ LLVM_DEBUG(dbgs().indent(2)
+ << "Warning: HwMode '" << Mode.Name
+ << "' has non-assembler predicates. Ignoring for "
+ "DefaultMode negation.\n");
+ SP.HasNonAsmPredicates = true;
+ continue;
+ }
+
+ // Negate the features in FeaturesSet: !(F1 && F2) = !F1 || !F2
+ std::set<SubtargetFeatureLiteral> NegatedFeatures;
+ for (const auto &Op : ModePreds.FeaturesSet) {
+ NegatedFeatures.insert({Op.Feature, !Op.IsNot});
+ }
+
+ if (ModePreds.AnyOfFeatureSets.empty()) {
+ if (!NegatedFeatures.empty())
+ SP.AnyOfFeatureSets.insert(std::move(NegatedFeatures));
+ } else if (ModePreds.AnyOfFeatureSets.size() == 1) {
+ // Negate a mode with a single OR-set:
+ // !(F1 && F2 && (L1 || L2)) = !F1 || !F2 || (!L1 && !L2)
+ // In CNF: (!F1 || !F2 || !L1) && (!F1 || !F2 || !L2)
+ const auto &OrSet = *ModePreds.AnyOfFeatureSets.begin();
+ for (const auto &Op : OrSet) {
+ std::set<SubtargetFeatureLiteral> NewOrSet = NegatedFeatures;
+ NewOrSet.insert({Op.Feature, !Op.IsNot});
+ SP.AnyOfFeatureSets.insert(std::move(NewOrSet));
+ }
+ } else {
+ LLVM_DEBUG(dbgs().indent(2)
+ << "Warning: HwMode '" << Mode.Name
+ << "' has multiple complex predicates. Ignoring for "
+ "DefaultMode negation.\n");
+ }
+ }
+ return SP;
+}
+
+void HwModePredicates::add(const HwModePredicates &Other) {
+ FeaturesSet.insert(Other.FeaturesSet.begin(), Other.FeaturesSet.end());
+ AnyOfFeatureSets.insert(Other.AnyOfFeatureSets.begin(),
+ Other.AnyOfFeatureSets.end());
+ HasNonAsmPredicates |= Other.HasNonAsmPredicates;
+}
+
+// Evaluates if the current set of predicates contains a contradiction.
+// Performs unit propagation: if we have a known feature F, we can simplify
+// OR-sets (A || B) containing F or !F.
+// Returns true if the OrSet is satisfied, false otherwise.
+// If not satisfied, fills SimplifiedSet with remaining active literals.
+static bool simplifyOrSet(const std::set<SubtargetFeatureLiteral> &OrSet,
+ const std::set<SubtargetFeatureLiteral> &FeaturesSet,
+ std::set<SubtargetFeatureLiteral> &SimplifiedSet) {
+ for (const auto &Lit : OrSet) {
+ if (FeaturesSet.count(Lit))
+ return true; // Satisfied
+ if (!FeaturesSet.count({Lit.Feature, !Lit.IsNot}))
+ SimplifiedSet.insert(Lit);
+ }
+ return false;
+}
+
+// Evaluates if the current set of predicates contains a contradiction.
+// Performs unit propagation: if we have a known feature F, we can simplify
+// OR-sets (A || B) containing F or !F.
+bool HwModePredicates::isSelfContradictory() {
+ // Check for immediate contradictions (e.g. requiring both F and !F).
+ for (const auto &Lit : FeaturesSet) {
+ if (FeaturesSet.count({Lit.Feature, !Lit.IsNot}))
+ return true;
+ }
+
+ std::set<std::set<SubtargetFeatureLiteral>> NewAnyOfs;
+ bool FeaturesChanged = false;
+
+ for (const auto &OrSet : AnyOfFeatureSets) {
+ std::set<SubtargetFeatureLiteral> SimplifiedSet;
+ if (simplifyOrSet(OrSet, FeaturesSet, SimplifiedSet))
+ continue; // Discard satisfied set
+
+ // All literals in this OR-set are false, causing a contradiction.
+ if (SimplifiedSet.empty())
+ return true;
+
+ if (SimplifiedSet.size() == 1) {
+ // Unit clause: only one choice remains, so it must be true.
+ FeaturesSet.insert(*SimplifiedSet.begin());
+ FeaturesChanged = true;
+ } else {
+ NewAnyOfs.insert(std::move(SimplifiedSet));
+ }
+ }
+
+ if (FeaturesChanged) {
+ AnyOfFeatureSets = std::move(NewAnyOfs);
+ return isSelfContradictory();
+ }
+
+ return false;
+}
+
+// Two predicate sets conflict if their union is self-contradictory.
+bool HwModePredicates::conflictsWith(const HwModePredicates &Other) const {
+ HwModePredicates Combined(*this);
+ Combined.add(Other);
+ return Combined.isSelfContradictory();
+}
CodeGenHwModes::CodeGenHwModes(const RecordKeeper &RK) : Records(RK) {
for (const Record *R : Records.getAllDerivedDefinitions("HwMode")) {
@@ -68,6 +201,18 @@ CodeGenHwModes::CodeGenHwModes(const RecordKeeper &RK) : Records(RK) {
}
}
+const HwModePredicates &
+CodeGenHwModes::getModePredicates(unsigned ModeId) const {
+ if (PredicatesByMode.empty()) {
+ PredicatesByMode.resize(getNumModeIds());
+ PredicatesByMode[DefaultMode] =
+ HwModePredicates::createForDefaultMode(*this);
+ for (unsigned M = 1; M < getNumModeIds(); ++M)
+ PredicatesByMode[M] = HwModePredicates(getMode(M).Predicates);
+ }
+ return PredicatesByMode[ModeId];
+}
+
unsigned CodeGenHwModes::getHwModeId(const Record *R) const {
if (R->getName() == DefaultModeName)
return DefaultMode;
@@ -103,3 +248,52 @@ void CodeGenHwModes::dump() const {
}
dbgs() << "}\n";
}
+
+// Resolves a HwModeSelect record based on pattern predicates.
+// Returns a unique resolved record if compatible, or nullptr if ambiguous.
+const Record *
+CodeGenHwModes::resolveModeSelect(const Record *SelectRec,
+ ArrayRef<const Record *> PatPreds) const {
+ if (!SelectRec->isSubClassOf("HwModeSelect"))
+ return SelectRec;
+
+ LLVM_DEBUG(dbgs() << "Trying to resolve HwModeSelect '"
+ << SelectRec->getName() << "'\n");
+
+ const HwModeSelect &MS = getHwModeSelect(SelectRec);
+
+ std::set<const Record *> ResolvedObjects;
+
+ // Construct and parse the pattern predicates ONCE here
+ HwModePredicates PatPredsSet(PatPreds);
+
+ for (const auto &[ModeId, Obj] : MS.Items) {
+ const HwModePredicates &ModePreds = getModePredicates(ModeId);
+
+ if (!ModePreds.conflictsWith(PatPredsSet)) {
+ LLVM_DEBUG(dbgs() << " HwMode '" << getModeName(ModeId, true)
+ << "' is compatible -> " << Obj->getName() << "\n");
+ ResolvedObjects.insert(Obj);
+ } else {
+ LLVM_DEBUG(dbgs() << " HwMode '" << getModeName(ModeId, true)
+ << "' is incompatible due to semantic conflict\n");
+ }
+ }
+
+ if (ResolvedObjects.size() == 1) {
+ const Record *Resolved = *ResolvedObjects.begin();
+ LLVM_DEBUG(dbgs() << " Resolved to unique object: " << Resolved->getName()
+ << "\n");
+ return Resolved;
+ }
+
+ if (ResolvedObjects.empty()) {
+ LLVM_DEBUG(dbgs() << " No active modes resolved for '"
+ << SelectRec->getName() << "'\n");
+ } else {
+ LLVM_DEBUG(
+ dbgs() << " Multiple active modes resolved to
diff erent objects for '"
+ << SelectRec->getName() << "'\n");
+ }
+ return nullptr;
+}
diff --git a/llvm/utils/TableGen/Common/CodeGenHwModes.h b/llvm/utils/TableGen/Common/CodeGenHwModes.h
index 7e45f8ef1de49..e605c22357533 100644
--- a/llvm/utils/TableGen/Common/CodeGenHwModes.h
+++ b/llvm/utils/TableGen/Common/CodeGenHwModes.h
@@ -11,10 +11,13 @@
#ifndef LLVM_UTILS_TABLEGEN_COMMON_CODEGENHWMODES_H
#define LLVM_UTILS_TABLEGEN_COMMON_CODEGENHWMODES_H
+#include "SubtargetFeatureInfo.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include <cassert>
#include <map>
+#include <set>
#include <utility>
#include <vector>
@@ -26,6 +29,20 @@ class RecordKeeper;
struct CodeGenHwModes;
+struct HwModePredicates {
+ std::set<SubtargetFeatureLiteral> FeaturesSet;
+ std::set<std::set<SubtargetFeatureLiteral>> AnyOfFeatureSets;
+ bool HasNonAsmPredicates = false;
+
+ HwModePredicates() = default;
+ HwModePredicates(ArrayRef<const Record *> Preds);
+
+ static HwModePredicates createForDefaultMode(const CodeGenHwModes &CGH);
+ void add(const HwModePredicates &Other);
+ bool isSelfContradictory();
+ bool conflictsWith(const HwModePredicates &Other) const;
+};
+
struct HwMode {
HwMode(const Record *R);
StringRef Name;
@@ -56,6 +73,9 @@ struct CodeGenHwModes {
return getMode(Id).Name;
}
const HwModeSelect &getHwModeSelect(const Record *R) const;
+ const HwModePredicates &getModePredicates(unsigned ModeId) const;
+ const Record *resolveModeSelect(const Record *SelectRec,
+ ArrayRef<const Record *> PatPreds) const;
const std::map<const Record *, HwModeSelect> &getHwModeSelects() const {
return ModeSelects;
}
@@ -67,6 +87,7 @@ struct CodeGenHwModes {
DenseMap<const Record *, unsigned> ModeIds; // HwMode Record -> HwModeId
std::vector<HwMode> Modes;
std::map<const Record *, HwModeSelect> ModeSelects;
+ mutable std::vector<HwModePredicates> PredicatesByMode;
};
} // namespace llvm
diff --git a/llvm/utils/TableGen/Common/SubtargetFeatureInfo.cpp b/llvm/utils/TableGen/Common/SubtargetFeatureInfo.cpp
index f0afbd1b6ddd1..62a7f086ca1ae 100644
--- a/llvm/utils/TableGen/Common/SubtargetFeatureInfo.cpp
+++ b/llvm/utils/TableGen/Common/SubtargetFeatureInfo.cpp
@@ -210,3 +210,53 @@ void SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures(
OS << " return Features;\n";
OS << "}\n\n";
}
+
+bool llvm::getRequiredFeatures(
+ std::set<SubtargetFeatureLiteral> &FeaturesSet,
+ std::set<std::set<SubtargetFeatureLiteral>> &AnyOfFeatureSets,
+ ArrayRef<const Record *> ReqPredicates) {
+ bool AllPredicatesParsed = true;
+ for (const Record *R : ReqPredicates) {
+ if (!R->getValueAsBit("AssemblerMatcherPredicate")) {
+ AllPredicatesParsed = false;
+ continue;
+ }
+ const RecordVal *V = R->getValue("AssemblerCondDag");
+ if (!V || !V->getValue() || !isa<DagInit>(V->getValue())) {
+ // Ignore this predicate for semantic analysis, as it has no subtarget
+ // features.
+ AllPredicatesParsed = false;
+ continue;
+ }
+ const DagInit *D = cast<DagInit>(V->getValue());
+ std::string CombineType = D->getOperator()->getAsString();
+ if (CombineType != "any_of" && CombineType != "all_of")
+ PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
+ if (D->getNumArgs() == 0)
+ PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
+ bool IsOr = CombineType == "any_of";
+ std::set<SubtargetFeatureLiteral> AnyOfSet;
+
+ for (auto *Arg : D->getArgs()) {
+ bool IsNot = false;
+ if (auto *NotArg = dyn_cast<DagInit>(Arg)) {
+ if (NotArg->getOperator()->getAsString() != "not" ||
+ NotArg->getNumArgs() != 1)
+ PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
+ Arg = NotArg->getArg(0);
+ IsNot = true;
+ }
+ if (!isa<DefInit>(Arg) ||
+ !cast<DefInit>(Arg)->getDef()->isSubClassOf("SubtargetFeature"))
+ PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
+ if (IsOr)
+ AnyOfSet.insert({cast<DefInit>(Arg)->getDef()->getName(), IsNot});
+ else
+ FeaturesSet.insert({cast<DefInit>(Arg)->getDef()->getName(), IsNot});
+ }
+
+ if (IsOr)
+ AnyOfFeatureSets.insert(std::move(AnyOfSet));
+ }
+ return AllPredicatesParsed;
+}
diff --git a/llvm/utils/TableGen/Common/SubtargetFeatureInfo.h b/llvm/utils/TableGen/Common/SubtargetFeatureInfo.h
index 2b2577b93c284..b74cdc4903e47 100644
--- a/llvm/utils/TableGen/Common/SubtargetFeatureInfo.h
+++ b/llvm/utils/TableGen/Common/SubtargetFeatureInfo.h
@@ -12,7 +12,9 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/TableGen/Record.h"
#include <map>
+#include <set>
#include <string>
+#include <tuple>
#include <utility>
#include <vector>
@@ -108,6 +110,26 @@ struct SubtargetFeatureInfo {
static void emitMCPredicateCheck(raw_ostream &OS, StringRef TargetName,
ArrayRef<const Record *> Predicates);
};
+
+struct SubtargetFeatureLiteral {
+ StringRef Feature;
+ bool IsNot;
+
+ bool operator<(const SubtargetFeatureLiteral &Other) const {
+ return std::tie(Feature, IsNot) < std::tie(Other.Feature, Other.IsNot);
+ }
+ bool operator==(const SubtargetFeatureLiteral &Other) const {
+ return Feature == Other.Feature && IsNot == Other.IsNot;
+ }
+};
+
+/// Populates \p FeaturesSet and \p AnyOfFeatureSets from \p ReqPredicates.
+/// Returns true if all predicates were assembler predicates with a valid
+/// AssemblerCondDag, or false if any non-assembler predicates were skipped.
+bool getRequiredFeatures(
+ std::set<SubtargetFeatureLiteral> &FeaturesSet,
+ std::set<std::set<SubtargetFeatureLiteral>> &AnyOfFeatureSets,
+ ArrayRef<const Record *> ReqPredicates);
} // end namespace llvm
#endif // LLVM_UTIL_TABLEGEN_COMMON_SUBTARGETFEATUREINFO_H
diff --git a/llvm/utils/TableGen/CompressInstEmitter.cpp b/llvm/utils/TableGen/CompressInstEmitter.cpp
index 3fe54dd97ab53..3bf1e84a7f0e3 100644
--- a/llvm/utils/TableGen/CompressInstEmitter.cpp
+++ b/llvm/utils/TableGen/CompressInstEmitter.cpp
@@ -65,9 +65,12 @@
//===----------------------------------------------------------------------===//
+#include "Common/CodeGenHwModes.h"
#include "Common/CodeGenInstruction.h"
#include "Common/CodeGenRegisters.h"
#include "Common/CodeGenTarget.h"
+#include "Common/InfoByHwMode.h"
+#include "Common/SubtargetFeatureInfo.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
@@ -139,13 +142,14 @@ class CompressInstEmitter {
void addDagOperandMapping(const Record *Rec, const DagInit *Dag,
const CodeGenInstruction &Inst,
IndexedMap<OpData> &OperandMap,
- StringMap<ArgData> &Operands, bool IsSourceInst);
+ StringMap<ArgData> &Operands, bool IsSourceInst,
+ ArrayRef<const Record *> PatPreds);
void evaluateCompressPat(const Record *Compress);
void emitCompressInstEmitter(raw_ostream &OS, EmitterType EType);
bool validateTypes(const Record *DagOpType, const Record *InstOpType,
- bool IsSourceInst);
+ bool IsSourceInst, ArrayRef<const Record *> PatPreds);
bool validateRegister(const Record *Reg, const Record *RegClass,
- ArrayRef<SMLoc> Loc);
+ ArrayRef<SMLoc> Loc, ArrayRef<const Record *> PatPreds);
void checkDagOperandMapping(const Record *Rec,
const StringMap<ArgData> &DestOperands,
const DagInit *SourceDag, const DagInit *DestDag);
@@ -166,18 +170,68 @@ class CompressInstEmitter {
bool CompressInstEmitter::validateRegister(const Record *Reg,
const Record *RegClass,
- ArrayRef<SMLoc> Loc) {
+ ArrayRef<SMLoc> Loc,
+ ArrayRef<const Record *> PatPreds) {
assert((Reg->isSubClassOf("Register") ||
Reg->isSubClassOf("RegisterByHwMode")) &&
"Reg record should be a Register");
assert(RegClass->isSubClassOf("RegisterClassLike") &&
"RegClass record should be RegisterClassLike");
+
+ if (const Record *RC =
+ Target.getHwModes().resolveModeSelect(RegClass, PatPreds))
+ RegClass = RC;
+ if (const Record *R = Target.getHwModes().resolveModeSelect(Reg, PatPreds))
+ Reg = R;
+
return Target.getRegBank().regClassContainsReg(RegClass, Reg, Loc);
}
bool CompressInstEmitter::validateTypes(const Record *DagOpType,
const Record *InstOpType,
- bool IsSourceInst) {
+ bool IsSourceInst,
+ ArrayRef<const Record *> PatPreds) {
+ if (DagOpType == InstOpType)
+ return true;
+
+ const Record *ResolvedDag =
+ Target.getHwModes().resolveModeSelect(DagOpType, PatPreds);
+ const Record *ResolvedInst =
+ Target.getHwModes().resolveModeSelect(InstOpType, PatPreds);
+
+ if (ResolvedDag && ResolvedInst) {
+ DagOpType = ResolvedDag;
+ InstOpType = ResolvedInst;
+ } else if (DagOpType->isSubClassOf("RegClassByHwMode") &&
+ InstOpType->isSubClassOf("RegClassByHwMode")) {
+ RegClassByHwMode DagRC(DagOpType, Target.getRegBank());
+ RegClassByHwMode InstRC(InstOpType, Target.getRegBank());
+ const CodeGenHwModes &CGH = Target.getHwModes();
+ HwModePredicates PatPredsSet(PatPreds);
+ bool HasCompatibleMode = false;
+ for (const auto &[Mode, DagClass] : DagRC) {
+ if (CGH.getModePredicates(Mode).conflictsWith(PatPredsSet))
+ continue;
+ if (!InstRC.hasMode(Mode))
+ return false;
+ const CodeGenRegisterClass *InstClass = InstRC.get(Mode);
+ if (!InstClass->hasSubClass(DagClass))
+ return false;
+ HasCompatibleMode = true;
+ }
+ for (const auto &[Mode, InstClass] : InstRC) {
+ if (CGH.getModePredicates(Mode).conflictsWith(PatPredsSet))
+ continue;
+ if (!DagRC.hasMode(Mode))
+ return false;
+ }
+ return HasCompatibleMode;
+ } else if (DagOpType->isSubClassOf("HwModeSelect") ||
+ InstOpType->isSubClassOf("HwModeSelect")) {
+ return false;
+ }
+
+ // Concrete check (when resolved uniquely).
if (DagOpType == InstOpType)
return true;
@@ -214,12 +268,10 @@ static bool validateArgsTypes(const Init *Arg1, const Init *Arg2) {
/// operands and fixed registers it expects the Dag operand type to be contained
/// in the instantiated instruction operand type. For immediate operands and
/// immediates no validation checks are enforced at pattern validation time.
-void CompressInstEmitter::addDagOperandMapping(const Record *Rec,
- const DagInit *Dag,
- const CodeGenInstruction &Inst,
- IndexedMap<OpData> &OperandMap,
- StringMap<ArgData> &Operands,
- bool IsSourceInst) {
+void CompressInstEmitter::addDagOperandMapping(
+ const Record *Rec, const DagInit *Dag, const CodeGenInstruction &Inst,
+ IndexedMap<OpData> &OperandMap, StringMap<ArgData> &Operands,
+ bool IsSourceInst, ArrayRef<const Record *> PatPreds) {
unsigned NumMIOperands = 0;
if (!Inst.Operands.empty())
NumMIOperands =
@@ -259,7 +311,7 @@ void CompressInstEmitter::addDagOperandMapping(const Record *Rec,
if (DI->getDef()->isSubClassOf("Register") ||
DI->getDef()->isSubClassOf("RegisterByHwMode")) {
// Check if the fixed register belongs to the Register class.
- if (!validateRegister(DI->getDef(), OpndRec, Rec->getLoc()))
+ if (!validateRegister(DI->getDef(), OpndRec, Rec->getLoc(), PatPreds))
PrintFatalError(Rec->getLoc(),
"Error in Dag '" + Dag->getAsString() +
"': Register '" + DI->getDef()->getName() +
@@ -273,7 +325,7 @@ void CompressInstEmitter::addDagOperandMapping(const Record *Rec,
// corresponding instruction. Operands in the input and output Dag
// patterns are allowed to be a subclass of the type specified in the
// corresponding instruction operand instead of being an exact match.
- if (!validateTypes(DI->getDef(), OpndRec, IsSourceInst))
+ if (!validateTypes(DI->getDef(), OpndRec, IsSourceInst, PatPreds))
PrintFatalError(Rec->getLoc(),
"Error in Dag '" + Dag->getAsString() +
"'. Operand '" + Dag->getArgNameStr(DAGOpNo) +
@@ -466,6 +518,13 @@ void CompressInstEmitter::evaluateCompressPat(const Record *Rec) {
"'is not strictly smaller than the uncompressed instruction '" +
SourceOperator->getName() + "' !");
+ // Get the target features for the CompressPat.
+ std::vector<const Record *> PatReqFeatures;
+ std::vector<const Record *> RF = Rec->getValueAsListOfDefs("Predicates");
+ copy_if(RF, std::back_inserter(PatReqFeatures), [](const Record *R) {
+ return R->getValueAsBit("AssemblerMatcherPredicate");
+ });
+
// Fill the mapping from the source to destination instructions.
IndexedMap<OpData> SourceOperandMap;
@@ -473,7 +532,7 @@ void CompressInstEmitter::evaluateCompressPat(const Record *Rec) {
StringMap<ArgData> SourceOperands;
// Create a mapping between source Dag operands and source Inst operands.
addDagOperandMapping(Rec, SourceDag, SourceInst, SourceOperandMap,
- SourceOperands, /*IsSourceInst*/ true);
+ SourceOperands, /*IsSourceInst*/ true, PatReqFeatures);
IndexedMap<OpData> DestOperandMap;
// Map from arg name to DAG operand number and MI operand number.
@@ -481,62 +540,18 @@ void CompressInstEmitter::evaluateCompressPat(const Record *Rec) {
// Create a mapping between destination Dag operands and destination Inst
// operands.
addDagOperandMapping(Rec, DestDag, DestInst, DestOperandMap, DestOperands,
- /*IsSourceInst*/ false);
+ /*IsSourceInst*/ false, PatReqFeatures);
checkDagOperandMapping(Rec, DestOperands, SourceDag, DestDag);
// Create operand mapping between the source and destination instructions.
createInstOperandMapping(Rec, SourceDag, DestDag, SourceOperandMap,
DestOperandMap, SourceOperands, DestInst);
- // Get the target features for the CompressPat.
- std::vector<const Record *> PatReqFeatures;
- std::vector<const Record *> RF = Rec->getValueAsListOfDefs("Predicates");
- copy_if(RF, std::back_inserter(PatReqFeatures), [](const Record *R) {
- return R->getValueAsBit("AssemblerMatcherPredicate");
- });
-
CompressPatterns.emplace_back(SourceInst, DestInst, std::move(PatReqFeatures),
SourceOperandMap, DestOperandMap,
Rec->getValueAsBit("isCompressOnly"));
}
-static void
-getReqFeatures(std::set<std::pair<bool, StringRef>> &FeaturesSet,
- std::set<std::set<std::pair<bool, StringRef>>> &AnyOfFeatureSets,
- ArrayRef<const Record *> ReqFeatures) {
- for (const Record *R : ReqFeatures) {
- const DagInit *D = R->getValueAsDag("AssemblerCondDag");
- std::string CombineType = D->getOperator()->getAsString();
- if (CombineType != "any_of" && CombineType != "all_of")
- PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
- if (D->getNumArgs() == 0)
- PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
- bool IsOr = CombineType == "any_of";
- std::set<std::pair<bool, StringRef>> AnyOfSet;
-
- for (auto *Arg : D->getArgs()) {
- bool IsNot = false;
- if (auto *NotArg = dyn_cast<DagInit>(Arg)) {
- if (NotArg->getOperator()->getAsString() != "not" ||
- NotArg->getNumArgs() != 1)
- PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
- Arg = NotArg->getArg(0);
- IsNot = true;
- }
- if (!isa<DefInit>(Arg) ||
- !cast<DefInit>(Arg)->getDef()->isSubClassOf("SubtargetFeature"))
- PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
- if (IsOr)
- AnyOfSet.emplace(IsNot, cast<DefInit>(Arg)->getDef()->getName());
- else
- FeaturesSet.emplace(IsNot, cast<DefInit>(Arg)->getDef()->getName());
- }
-
- if (IsOr)
- AnyOfFeatureSets.insert(std::move(AnyOfSet));
- }
-}
-
static unsigned getPredicates(DenseMap<const Record *, unsigned> &PredicateMap,
std::vector<const Record *> &Predicates,
const Record *Rec, StringRef Name) {
@@ -703,10 +718,11 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS,
CaseStream.indent(2) << "case " + TargetName + "::" + CurOp + ": {\n";
}
- std::set<std::pair<bool, StringRef>> FeaturesSet;
- std::set<std::set<std::pair<bool, StringRef>>> AnyOfFeatureSets;
+ std::set<SubtargetFeatureLiteral> FeaturesSet;
+ std::set<std::set<SubtargetFeatureLiteral>> AnyOfFeatureSets;
// Add CompressPat required features.
- getReqFeatures(FeaturesSet, AnyOfFeatureSets, CompressPat.PatReqFeatures);
+ getRequiredFeatures(FeaturesSet, AnyOfFeatureSets,
+ CompressPat.PatReqFeatures);
// Add Dest instruction required features.
std::vector<const Record *> ReqFeatures;
@@ -715,25 +731,25 @@ void CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS,
copy_if(RF, std::back_inserter(ReqFeatures), [](const Record *R) {
return R->getValueAsBit("AssemblerMatcherPredicate");
});
- getReqFeatures(FeaturesSet, AnyOfFeatureSets, ReqFeatures);
+ getRequiredFeatures(FeaturesSet, AnyOfFeatureSets, ReqFeatures);
ListSeparator CondSep(" &&\n ");
// Emit checks for all required features.
- for (auto &Op : FeaturesSet) {
- StringRef Not = Op.first ? "!" : "";
+ for (const auto &Op : FeaturesSet) {
+ StringRef Not = Op.IsNot ? "!" : "";
CondStream << CondSep << Not << "STI.getFeatureBits()[" << TargetName
- << "::" << Op.second << "]";
+ << "::" << Op.Feature << "]";
}
// Emit checks for all required feature groups.
- for (auto &Set : AnyOfFeatureSets) {
+ for (const auto &Set : AnyOfFeatureSets) {
CondStream << CondSep << "(";
- for (auto &Op : Set) {
+ for (const auto &Op : Set) {
bool IsLast = &Op == &*Set.rbegin();
- StringRef Not = Op.first ? "!" : "";
+ StringRef Not = Op.IsNot ? "!" : "";
CondStream << Not << "STI.getFeatureBits()[" << TargetName
- << "::" << Op.second << "]";
+ << "::" << Op.Feature << "]";
if (!IsLast)
CondStream << " || ";
}
More information about the llvm-commits
mailing list