[llvm] 7092467 - [RFC][GISel] Add a way to ignore COPY instructions in InstructionSelector
via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 9 23:37:48 PST 2023
Author: Pierre van Houtryve
Date: 2023-02-10T08:37:42+01:00
New Revision: 70924673af680303d64e540d7767c0eeda5217fc
URL: https://github.com/llvm/llvm-project/commit/70924673af680303d64e540d7767c0eeda5217fc
DIFF: https://github.com/llvm/llvm-project/commit/70924673af680303d64e540d7767c0eeda5217fc.diff
LOG: [RFC][GISel] Add a way to ignore COPY instructions in InstructionSelector
RFC to add a way to ignore COPY instructions when pattern-matching MIR in GISel.
- Add a new "GISelFlags" class to TableGen. Both `Pattern` and `PatFrags` defs can use it to alter matching behaviour.
- Flags start at zero and are scoped: the setter returns a `SaveAndRestore` object so that when the current scope ends, the flags are restored to their previous values. This allows child patterns to modify the flags without affecting the parent pattern.
- Child patterns always reuse the parent's pattern, but they can override its values. For more examples, see `GlobalISelEmitterFlags.td` tests.
- [AMDGPU] Use the IgnoreCopies flag in BFI patterns, which are known to be bothered by cross-regbank copies.
Reviewed By: arsenm
Differential Revision: https://reviews.llvm.org/D136234
Added:
llvm/test/TableGen/GlobalISelEmitterFlags.td
Modified:
llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
llvm/include/llvm/Target/TargetSelectionDAG.td
llvm/lib/Target/AMDGPU/AMDGPUInstructions.td
llvm/lib/Target/AMDGPU/SIInstructions.td
llvm/test/CodeGen/AMDGPU/bfi_int.ll
llvm/utils/TableGen/CodeGenDAGPatterns.cpp
llvm/utils/TableGen/CodeGenDAGPatterns.h
llvm/utils/TableGen/GlobalISelEmitter.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
index db1a5473e45ae..7e9a601f7221f 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h
@@ -103,11 +103,13 @@ enum {
/// - JumpTable... - (UpperBound - LowerBound) (at least 2) jump targets
GIM_SwitchType,
- /// Record the specified instruction
+ /// Record the specified instruction.
+ /// The IgnoreCopies variant ignores COPY instructions.
/// - NewInsnID - Instruction ID to define
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
GIM_RecordInsn,
+ GIM_RecordInsnIgnoreCopies,
/// Check the feature bits
/// - Expected features
@@ -262,11 +264,14 @@ enum {
GIM_CheckIsSafeToFold,
/// Check the specified operands are identical.
+ /// The IgnoreCopies variant looks through COPY instructions before
+ /// comparing the operands.
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - OtherInsnID - Other instruction ID
/// - OtherOpIdx - Other operand index
GIM_CheckIsSameOperand,
+ GIM_CheckIsSameOperandIgnoreCopies,
/// Predicates with 'let PredicateCodeUsesOperands = 1' need to examine some
/// named operands that will be recorded in RecordedOperands. Names of these
@@ -364,7 +369,8 @@ enum {
/// reading from a specific operand.
/// - InsnID - Instruction ID to modify
/// - OldInsnID - Instruction ID to get the matched operand from
- /// - OpIdx - Operand index in OldInsnID the render function should read from..
+ /// - OpIdx - Operand index in OldInsnID the render function should read
+ /// from..
/// - RendererFnID - Custom renderer function to call
GIR_CustomOperandRenderer,
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
index fc4e94929d410..1db2fa2b34cb3 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h
@@ -101,7 +101,8 @@ bool InstructionSelector::executeMatchTable(
break;
}
- case GIM_RecordInsn: {
+ case GIM_RecordInsn:
+ case GIM_RecordInsnIgnoreCopies: {
int64_t NewInsnID = MatchTable[CurrentIdx++];
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t OpIdx = MatchTable[CurrentIdx++];
@@ -126,7 +127,12 @@ bool InstructionSelector::executeMatchTable(
break;
}
- MachineInstr *NewMI = MRI.getVRegDef(MO.getReg());
+ MachineInstr *NewMI;
+ if (MatcherOpcode == GIM_RecordInsnIgnoreCopies)
+ NewMI = getDefIgnoringCopies(MO.getReg(), MRI);
+ else
+ NewMI = MRI.getVRegDef(MO.getReg());
+
if ((size_t)NewInsnID < State.MIs.size())
State.MIs[NewInsnID] = NewMI;
else {
@@ -816,7 +822,8 @@ bool InstructionSelector::executeMatchTable(
}
break;
}
- case GIM_CheckIsSameOperand: {
+ case GIM_CheckIsSameOperand:
+ case GIM_CheckIsSameOperandIgnoreCopies: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t OpIdx = MatchTable[CurrentIdx++];
int64_t OtherInsnID = MatchTable[CurrentIdx++];
@@ -827,8 +834,20 @@ bool InstructionSelector::executeMatchTable(
<< OtherInsnID << "][" << OtherOpIdx << "])\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
assert(State.MIs[OtherInsnID] != nullptr && "Used insn before defined");
- if (!State.MIs[InsnID]->getOperand(OpIdx).isIdenticalTo(
- State.MIs[OtherInsnID]->getOperand(OtherOpIdx))) {
+
+ MachineOperand &Op = State.MIs[InsnID]->getOperand(OpIdx);
+ MachineOperand &OtherOp = State.MIs[OtherInsnID]->getOperand(OtherOpIdx);
+
+ if (MatcherOpcode == GIM_CheckIsSameOperandIgnoreCopies) {
+ if (Op.isReg() && OtherOp.isReg()) {
+ MachineInstr *MI = getDefIgnoringCopies(Op.getReg(), MRI);
+ MachineInstr *OtherMI = getDefIgnoringCopies(OtherOp.getReg(), MRI);
+ if (MI && MI == OtherMI)
+ break;
+ }
+ }
+
+ if (!Op.isIdenticalTo(OtherOp)) {
if (handleReject() == RejectAndGiveUp)
return false;
}
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index a841cf7eb0707..210aa8fc494dc 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -890,6 +890,12 @@ class PatFrags<dag ops, list<dag> frags, code pred = [{}],
ValueType ScalarMemoryVT = ?;
}
+// Patterns and PatFrags can also subclass GISelFlags to set flags that affect
+// how GlobalISel behaves when matching them.
+class GISelFlags {
+ bit GIIgnoreCopies = ?;
+}
+
// PatFrag - A version of PatFrags matching only a single fragment.
class PatFrag<dag ops, dag frag, code pred = [{}],
SDNodeXForm xform = NOOP_SDNodeXForm>
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUInstructions.td b/llvm/lib/Target/AMDGPU/AMDGPUInstructions.td
index 15b97a797f363..998fca791f9f3 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUInstructions.td
+++ b/llvm/lib/Target/AMDGPU/AMDGPUInstructions.td
@@ -104,7 +104,10 @@ class PredicateControl {
}
class AMDGPUPat<dag pattern, dag result> : Pat<pattern, result>,
- PredicateControl;
+ PredicateControl, GISelFlags;
+
+let GIIgnoreCopies = 1 in
+class AMDGPUPatIgnoreCopies<dag pattern, dag result> : AMDGPUPat<pattern, result>;
let RecomputePerFunction = 1 in {
def FP16Denormals : Predicate<"MF->getInfo<SIMachineFunctionInfo>()->getMode().allFP64FP16Denormals()">;
diff --git a/llvm/lib/Target/AMDGPU/SIInstructions.td b/llvm/lib/Target/AMDGPU/SIInstructions.td
index 012459fe97c94..0683deb71a8b7 100644
--- a/llvm/lib/Target/AMDGPU/SIInstructions.td
+++ b/llvm/lib/Target/AMDGPU/SIInstructions.td
@@ -2051,52 +2051,53 @@ def BFIImm32 : PatFrag<
}]
>;
+
// Definition from ISA doc:
// (y & x) | (z & ~x)
-def : AMDGPUPat <
+def : AMDGPUPatIgnoreCopies <
(DivergentBinFrag<or> (and i32:$y, i32:$x), (and i32:$z, (not i32:$x))),
(V_BFI_B32_e64 (COPY_TO_REGCLASS VSrc_b32:$x, VGPR_32),
- (COPY_TO_REGCLASS VSrc_b32:$y, VGPR_32),
- (COPY_TO_REGCLASS VSrc_b32:$z, VGPR_32))
+ (COPY_TO_REGCLASS VSrc_b32:$y, VGPR_32),
+ (COPY_TO_REGCLASS VSrc_b32:$z, VGPR_32))
>;
// (y & C) | (z & ~C)
-def : AMDGPUPat <
+def : AMDGPUPatIgnoreCopies <
(BFIImm32 i32:$x, i32:$y, i32:$z),
(V_BFI_B32_e64 VSrc_b32:$x, VSrc_b32:$y, VSrc_b32:$z)
>;
// 64-bit version
-def : AMDGPUPat <
+def : AMDGPUPatIgnoreCopies <
(DivergentBinFrag<or> (and i64:$y, i64:$x), (and i64:$z, (not i64:$x))),
(REG_SEQUENCE VReg_64,
(V_BFI_B32_e64 (i32 (EXTRACT_SUBREG VReg_64:$x, sub0)),
- (i32 (EXTRACT_SUBREG VReg_64:$y, sub0)),
- (i32 (EXTRACT_SUBREG VReg_64:$z, sub0))), sub0,
+ (i32 (EXTRACT_SUBREG VReg_64:$y, sub0)),
+ (i32 (EXTRACT_SUBREG VReg_64:$z, sub0))), sub0,
(V_BFI_B32_e64 (i32 (EXTRACT_SUBREG VReg_64:$x, sub1)),
- (i32 (EXTRACT_SUBREG VReg_64:$y, sub1)),
- (i32 (EXTRACT_SUBREG VReg_64:$z, sub1))), sub1)
+ (i32 (EXTRACT_SUBREG VReg_64:$y, sub1)),
+ (i32 (EXTRACT_SUBREG VReg_64:$z, sub1))), sub1)
>;
// SHA-256 Ch function
// z ^ (x & (y ^ z))
-def : AMDGPUPat <
+def : AMDGPUPatIgnoreCopies <
(DivergentBinFrag<xor> i32:$z, (and i32:$x, (xor i32:$y, i32:$z))),
(V_BFI_B32_e64 (COPY_TO_REGCLASS VSrc_b32:$x, VGPR_32),
- (COPY_TO_REGCLASS VSrc_b32:$y, VGPR_32),
- (COPY_TO_REGCLASS VSrc_b32:$z, VGPR_32))
+ (COPY_TO_REGCLASS VSrc_b32:$y, VGPR_32),
+ (COPY_TO_REGCLASS VSrc_b32:$z, VGPR_32))
>;
// 64-bit version
-def : AMDGPUPat <
+def : AMDGPUPatIgnoreCopies <
(DivergentBinFrag<xor> i64:$z, (and i64:$x, (xor i64:$y, i64:$z))),
(REG_SEQUENCE VReg_64,
(V_BFI_B32_e64 (i32 (EXTRACT_SUBREG VReg_64:$x, sub0)),
- (i32 (EXTRACT_SUBREG VReg_64:$y, sub0)),
- (i32 (EXTRACT_SUBREG VReg_64:$z, sub0))), sub0,
+ (i32 (EXTRACT_SUBREG VReg_64:$y, sub0)),
+ (i32 (EXTRACT_SUBREG VReg_64:$z, sub0))), sub0,
(V_BFI_B32_e64 (i32 (EXTRACT_SUBREG VReg_64:$x, sub1)),
- (i32 (EXTRACT_SUBREG VReg_64:$y, sub1)),
- (i32 (EXTRACT_SUBREG VReg_64:$z, sub1))), sub1)
+ (i32 (EXTRACT_SUBREG VReg_64:$y, sub1)),
+ (i32 (EXTRACT_SUBREG VReg_64:$z, sub1))), sub1)
>;
def : AMDGPUPat <
@@ -3197,27 +3198,27 @@ def : AMDGPUPat <
// SHA-256 Ma patterns
// ((x & z) | (y & (x | z))) -> BFI (XOR x, y), z, y
-def : AMDGPUPat <
+def : AMDGPUPatIgnoreCopies <
(DivergentBinFrag<or> (and i32:$x, i32:$z),
(and i32:$y, (or i32:$x, i32:$z))),
(V_BFI_B32_e64 (V_XOR_B32_e64 (COPY_TO_REGCLASS VSrc_b32:$x, VGPR_32),
(COPY_TO_REGCLASS VSrc_b32:$y, VGPR_32)),
- (COPY_TO_REGCLASS VSrc_b32:$z, VGPR_32),
- (COPY_TO_REGCLASS VSrc_b32:$y, VGPR_32))
+ (COPY_TO_REGCLASS VSrc_b32:$z, VGPR_32),
+ (COPY_TO_REGCLASS VSrc_b32:$y, VGPR_32))
>;
-def : AMDGPUPat <
+def : AMDGPUPatIgnoreCopies <
(DivergentBinFrag<or> (and i64:$x, i64:$z),
(and i64:$y, (or i64:$x, i64:$z))),
(REG_SEQUENCE VReg_64,
(V_BFI_B32_e64 (V_XOR_B32_e64 (i32 (EXTRACT_SUBREG VReg_64:$x, sub0)),
(i32 (EXTRACT_SUBREG VReg_64:$y, sub0))),
- (i32 (EXTRACT_SUBREG VReg_64:$z, sub0)),
- (i32 (EXTRACT_SUBREG VReg_64:$y, sub0))), sub0,
+ (i32 (EXTRACT_SUBREG VReg_64:$z, sub0)),
+ (i32 (EXTRACT_SUBREG VReg_64:$y, sub0))), sub0,
(V_BFI_B32_e64 (V_XOR_B32_e64 (i32 (EXTRACT_SUBREG VReg_64:$x, sub1)),
(i32 (EXTRACT_SUBREG VReg_64:$y, sub1))),
- (i32 (EXTRACT_SUBREG VReg_64:$z, sub1)),
- (i32 (EXTRACT_SUBREG VReg_64:$y, sub1))), sub1)
+ (i32 (EXTRACT_SUBREG VReg_64:$z, sub1)),
+ (i32 (EXTRACT_SUBREG VReg_64:$y, sub1))), sub1)
>;
multiclass IntMed3Pat<Instruction med3Inst,
diff --git a/llvm/test/CodeGen/AMDGPU/bfi_int.ll b/llvm/test/CodeGen/AMDGPU/bfi_int.ll
index 0b73a5c919795..eaf1c35dc0c79 100644
--- a/llvm/test/CodeGen/AMDGPU/bfi_int.ll
+++ b/llvm/test/CodeGen/AMDGPU/bfi_int.ll
@@ -270,16 +270,13 @@ define amdgpu_ps float @v_s_s_bfi_sha256_ch(i32 %x, i32 inreg %y, i32 inreg %z)
;
; GFX8-GISEL-LABEL: v_s_s_bfi_sha256_ch:
; GFX8-GISEL: ; %bb.0: ; %entry
-; GFX8-GISEL-NEXT: s_xor_b32 s0, s0, s1
-; GFX8-GISEL-NEXT: v_and_b32_e32 v0, s0, v0
-; GFX8-GISEL-NEXT: v_xor_b32_e32 v0, s1, v0
+; GFX8-GISEL-NEXT: v_mov_b32_e32 v1, s0
+; GFX8-GISEL-NEXT: v_bfi_b32 v0, v0, v1, s1
; GFX8-GISEL-NEXT: ; return to shader part epilog
;
; GFX10-GISEL-LABEL: v_s_s_bfi_sha256_ch:
; GFX10-GISEL: ; %bb.0: ; %entry
-; GFX10-GISEL-NEXT: s_xor_b32 s0, s0, s1
-; GFX10-GISEL-NEXT: v_and_b32_e32 v0, s0, v0
-; GFX10-GISEL-NEXT: v_xor_b32_e32 v0, s1, v0
+; GFX10-GISEL-NEXT: v_bfi_b32 v0, v0, s0, s1
; GFX10-GISEL-NEXT: ; return to shader part epilog
entry:
%xor0 = xor i32 %y, %z
@@ -309,16 +306,13 @@ define amdgpu_ps float @s_v_s_bfi_sha256_ch(i32 inreg %x, i32 %y, i32 inreg %z)
;
; GFX8-GISEL-LABEL: s_v_s_bfi_sha256_ch:
; GFX8-GISEL: ; %bb.0: ; %entry
-; GFX8-GISEL-NEXT: v_xor_b32_e32 v0, s1, v0
-; GFX8-GISEL-NEXT: v_and_b32_e32 v0, s0, v0
-; GFX8-GISEL-NEXT: v_xor_b32_e32 v0, s1, v0
+; GFX8-GISEL-NEXT: v_mov_b32_e32 v1, s1
+; GFX8-GISEL-NEXT: v_bfi_b32 v0, s0, v0, v1
; GFX8-GISEL-NEXT: ; return to shader part epilog
;
; GFX10-GISEL-LABEL: s_v_s_bfi_sha256_ch:
; GFX10-GISEL: ; %bb.0: ; %entry
-; GFX10-GISEL-NEXT: v_xor_b32_e32 v0, s1, v0
-; GFX10-GISEL-NEXT: v_and_b32_e32 v0, s0, v0
-; GFX10-GISEL-NEXT: v_xor_b32_e32 v0, s1, v0
+; GFX10-GISEL-NEXT: v_bfi_b32 v0, s0, v0, s1
; GFX10-GISEL-NEXT: ; return to shader part epilog
entry:
%xor0 = xor i32 %y, %z
@@ -448,16 +442,12 @@ define amdgpu_ps float @v_v_s_bfi_sha256_ch(i32 %x, i32 %y, i32 inreg %z) {
;
; GFX8-GISEL-LABEL: v_v_s_bfi_sha256_ch:
; GFX8-GISEL: ; %bb.0: ; %entry
-; GFX8-GISEL-NEXT: v_xor_b32_e32 v1, s0, v1
-; GFX8-GISEL-NEXT: v_and_b32_e32 v0, v0, v1
-; GFX8-GISEL-NEXT: v_xor_b32_e32 v0, s0, v0
+; GFX8-GISEL-NEXT: v_bfi_b32 v0, v0, v1, s0
; GFX8-GISEL-NEXT: ; return to shader part epilog
;
; GFX10-GISEL-LABEL: v_v_s_bfi_sha256_ch:
; GFX10-GISEL: ; %bb.0: ; %entry
-; GFX10-GISEL-NEXT: v_xor_b32_e32 v1, s0, v1
-; GFX10-GISEL-NEXT: v_and_b32_e32 v0, v0, v1
-; GFX10-GISEL-NEXT: v_xor_b32_e32 v0, s0, v0
+; GFX10-GISEL-NEXT: v_bfi_b32 v0, v0, v1, s0
; GFX10-GISEL-NEXT: ; return to shader part epilog
entry:
%xor0 = xor i32 %y, %z
diff --git a/llvm/test/TableGen/GlobalISelEmitterFlags.td b/llvm/test/TableGen/GlobalISelEmitterFlags.td
new file mode 100644
index 0000000000000..1c53e1a45c26b
--- /dev/null
+++ b/llvm/test/TableGen/GlobalISelEmitterFlags.td
@@ -0,0 +1,60 @@
+// RUN: llvm-tblgen %s -gen-global-isel -optimize-match-table=false -I %p/../../include -I %p/Common -o - | FileCheck %s
+
+include "llvm/Target/Target.td"
+include "GlobalISelEmitterCommon.td"
+
+def InstThreeOperands : I<(outs GPR32:$dst), (ins GPR32:$cond, GPR32:$src,GPR32:$src2), []>;
+
+class SrlPF<bit IC>: PatFrag<
+ (ops node:$PATFRAG_Src0, node:$src1),
+ (srl $PATFRAG_Src0, $src1)>, GISelFlags {
+ let GIIgnoreCopies = IC;
+}
+
+// GIIgnoreCopies on Pattern
+// MIs[1] should be using IgnoreCopies variants.
+let GIIgnoreCopies = 1 in
+def : Pat<
+ (i32 (sub (mul i32:$src0, i32:$src0), i32:$src1)),
+ (InstThreeOperands GPR32:$src0, GPR32:$src1, GPR32:$src1)
+>, GISelFlags;
+
+// GIIgnoreCopies set on "root" PatFrag.
+// MIs[1] and MIs[2] should be using IgnoreCopies variants.
+def : Pat<
+ (i32 (SrlPF<1> (shl (mul i32:$src0, i32:$src0), i32:$src1), i32:$src0)),
+ (InstThreeOperands GPR32:$src0, GPR32:$src1, GPR32:$src1)
+>;
+
+// GIIgnoreCopies set on "root" PatFrag, but a children PatFrag forces it back to zero.
+// MIs[1] should be using IgnoreCopies variants.
+// MIs[2] should NOT be using them.
+def : Pat<
+ (i32 (SrlPF<1> (SrlPF<0> (add i32:$src0, i32:$src0), i32:$src1), i32:$src0)),
+ (InstThreeOperands GPR32:$src0, GPR32:$src1, GPR32:$src1)
+>;
+
+// CHECK: GIM_Try
+// CHECK: GIM_RecordInsnIgnoreCopies, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
+// CHECK: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_LSHR
+// CHECK: GIM_RecordInsn, /*DefineMI*/2, /*MI*/1, /*OpIdx*/1, // MIs[2]
+// CHECK: GIM_CheckOpcode, /*MI*/2, TargetOpcode::G_ADD
+// CHECK: GIM_CheckIsSameOperand, /*MI*/2, /*OpIdx*/2, /*OtherMI*/2, /*OtherOpIdx*/1
+// CHECK: GIM_CheckIsSameOperandIgnoreCopies, /*MI*/0, /*OpIdx*/2, /*OtherMI*/2, /*OtherOpIdx*/1
+// CHECK: // (srl:{ *:[i32] } (srl:{ *:[i32] } (add:{ *:[i32] } i32:{ *:[i32] }:$src0, i32:{ *:[i32] }:$src0), i32:{ *:[i32] }:$src1), i32:{ *:[i32] }:$src0)
+// CHECK: GIR_Done
+// CHECK: GIM_Try
+// CHECK: GIM_RecordInsnIgnoreCopies, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
+// CHECK: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_SHL
+// CHECK: GIM_RecordInsnIgnoreCopies, /*DefineMI*/2, /*MI*/1, /*OpIdx*/1, // MIs[2]
+// CHECK: GIM_CheckOpcode, /*MI*/2, TargetOpcode::G_MUL
+// CHECK: GIM_CheckIsSameOperandIgnoreCopies, /*MI*/2, /*OpIdx*/2, /*OtherMI*/2, /*OtherOpIdx*/1
+// CHECK: GIM_CheckIsSameOperandIgnoreCopies, /*MI*/0, /*OpIdx*/2, /*OtherMI*/2, /*OtherOpIdx*/1
+// CHECK: // (srl:{ *:[i32] } (shl:{ *:[i32] } (mul:{ *:[i32] } i32:{ *:[i32] }:$src0, i32:{ *:[i32] }:$src0), i32:{ *:[i32] }:$src1), i32:{ *:[i32] }:$src0)
+// CHECK: GIR_Done
+// CHECK: GIM_Try
+// CHECK: GIM_RecordInsnIgnoreCopies, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
+// CHECK: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_MUL
+// CHECK: GIM_CheckIsSameOperandIgnoreCopies, /*MI*/1, /*OpIdx*/2, /*OtherMI*/1, /*OtherOpIdx*/1
+// CHECK: // (sub:{ *:[i32] } (mul:{ *:[i32] } i32:{ *:[i32] }:$src0, i32:{ *:[i32] }:$src0), i32:{ *:[i32] }:$src1) => (InstThreeOperands:{ *:[i32] } GPR32:{ *:[i32] }:$src0, GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src1)
+// CHECK: GIR_Done
\ No newline at end of file
diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp
index dd04778e2dbe7..b9eb20dd6edbc 100644
--- a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp
@@ -2038,6 +2038,7 @@ TreePatternNodePtr TreePatternNode::clone() const {
New->setNamesAsPredicateArg(getNamesAsPredicateArg());
New->Types = Types;
New->setPredicateCalls(getPredicateCalls());
+ New->setGISelFlagsRecord(getGISelFlagsRecord());
New->setTransformFn(getTransformFn());
return New;
}
@@ -2140,6 +2141,7 @@ void TreePatternNode::InlinePatternFragments(
R->setName(getName());
R->setNamesAsPredicateArg(getNamesAsPredicateArg());
R->setPredicateCalls(getPredicateCalls());
+ R->setGISelFlagsRecord(getGISelFlagsRecord());
R->setTransformFn(getTransformFn());
for (unsigned i = 0, e = getNumTypes(); i != e; ++i)
R->setType(i, getExtType(i));
@@ -2209,6 +2211,9 @@ void TreePatternNode::InlinePatternFragments(
for (unsigned i = 0, e = FragTree->getNumTypes(); i != e; ++i)
FragTree->UpdateNodeType(i, getExtType(i), TP);
+ if (Op->isSubClassOf("GISelFlags"))
+ FragTree->setGISelFlagsRecord(Op);
+
// Transfer in the old predicates.
for (const TreePredicateCall &Pred : getPredicateCalls())
FragTree->addPredicateCall(Pred);
@@ -4542,6 +4547,7 @@ static void CombineChildVariants(
R->setName(Orig->getName());
R->setNamesAsPredicateArg(Orig->getNamesAsPredicateArg());
R->setPredicateCalls(Orig->getPredicateCalls());
+ R->setGISelFlagsRecord(Orig->getGISelFlagsRecord());
R->setTransformFn(Orig->getTransformFn());
for (unsigned i = 0, e = Orig->getNumTypes(); i != e; ++i)
R->setType(i, Orig->getExtType(i));
diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.h b/llvm/utils/TableGen/CodeGenDAGPatterns.h
index ec35e66800886..221532fbe63d9 100644
--- a/llvm/utils/TableGen/CodeGenDAGPatterns.h
+++ b/llvm/utils/TableGen/CodeGenDAGPatterns.h
@@ -665,6 +665,10 @@ class TreePatternNode {
std::vector<TreePatternNodePtr> Children;
+ /// If this was instantiated from a PatFrag node, and the PatFrag was derived
+ /// from "GISelFlags": the original Record derived from GISelFlags.
+ const Record *GISelFlags = nullptr;
+
public:
TreePatternNode(Record *Op, std::vector<TreePatternNodePtr> Ch,
unsigned NumResults)
@@ -794,6 +798,9 @@ class TreePatternNode {
/// marked isCommutative.
bool isCommutativeIntrinsic(const CodeGenDAGPatterns &CDP) const;
+ void setGISelFlagsRecord(const Record *R) { GISelFlags = R; }
+ const Record *getGISelFlagsRecord() const { return GISelFlags; }
+
void print(raw_ostream &OS) const;
void dump() const;
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index 480dadd297c07..66e1429121789 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -38,6 +38,7 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/LowLevelTypeImpl.h"
#include "llvm/Support/MachineValueType.h"
+#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
@@ -673,6 +674,12 @@ class OperandMatcher;
class MatchAction;
class PredicateMatcher;
+enum {
+ GISF_IgnoreCopies = 0x1,
+};
+
+using GISelFlags = std::uint16_t;
+
class Matcher {
public:
virtual ~Matcher() = default;
@@ -864,6 +871,9 @@ class RuleMatcher : public Matcher {
/// ID for the next temporary register ID allocated with allocateTempRegID()
unsigned NextTempRegID;
+ /// Current GISelFlags
+ GISelFlags Flags = 0;
+
std::vector<Record *> RequiredFeatures;
std::vector<std::unique_ptr<PredicateMatcher>> EpilogueMatchers;
@@ -884,6 +894,18 @@ class RuleMatcher : public Matcher {
uint64_t RuleID;
static uint64_t NextRuleID;
+ GISelFlags updateGISelFlag(GISelFlags CurFlags, const Record *R,
+ StringRef FlagName, GISelFlags FlagBit) {
+ // If the value of a flag is unset, ignore it.
+ // If it's set, it always takes precedence over the existing value so
+ // clear/set the corresponding bit.
+ bool Unset = false;
+ bool Value = R->getValueAsBitOrUnset("GIIgnoreCopies", Unset);
+ if (!Unset)
+ return Value ? (CurFlags | FlagBit) : (CurFlags & ~FlagBit);
+ return CurFlags;
+ }
+
public:
RuleMatcher(ArrayRef<SMLoc> SrcLoc)
: NextInsnVarID(0), NextOutputInsnID(0), NextTempRegID(0), SrcLoc(SrcLoc),
@@ -901,6 +923,23 @@ class RuleMatcher : public Matcher {
template <class Kind, class... Args>
action_iterator insertAction(action_iterator InsertPt, Args &&... args);
+ // Update the active GISelFlags based on the GISelFlags Record R.
+ // A SaveAndRestore object is returned so the old GISelFlags are restored
+ // at the end of the scope.
+ SaveAndRestore<GISelFlags> setGISelFlags(const Record *R) {
+ if (!R || !R->isSubClassOf("GISelFlags"))
+ return {Flags, Flags};
+
+ assert((R->isSubClassOf("PatFrags") || R->isSubClassOf("Pattern")) &&
+ "GISelFlags is only expected on Pattern/PatFrags!");
+
+ GISelFlags NewFlags =
+ updateGISelFlag(Flags, R, "GIIgnoreCopies", GISF_IgnoreCopies);
+ return {Flags, NewFlags};
+ }
+
+ GISelFlags getGISelFlags() const { return Flags; }
+
/// Define an instruction without emitting any code to do so.
unsigned implicitlyDefineInsnVar(InstructionMatcher &Matcher);
@@ -1216,11 +1255,13 @@ class SameOperandMatcher : public OperandPredicateMatcher {
std::string MatchingName;
unsigned OrigOpIdx;
+ GISelFlags Flags;
+
public:
SameOperandMatcher(unsigned InsnVarID, unsigned OpIdx, StringRef MatchingName,
- unsigned OrigOpIdx)
+ unsigned OrigOpIdx, GISelFlags Flags)
: OperandPredicateMatcher(OPM_SameOperand, InsnVarID, OpIdx),
- MatchingName(MatchingName), OrigOpIdx(OrigOpIdx) {}
+ MatchingName(MatchingName), OrigOpIdx(OrigOpIdx), Flags(Flags) {}
static bool classof(const PredicateMatcher *P) {
return P->getKind() == OPM_SameOperand;
@@ -2481,12 +2522,15 @@ class InstructionOperandMatcher : public OperandPredicateMatcher {
protected:
std::unique_ptr<InstructionMatcher> InsnMatcher;
+ GISelFlags Flags;
+
public:
InstructionOperandMatcher(unsigned InsnVarID, unsigned OpIdx,
RuleMatcher &Rule, StringRef SymbolicName,
bool NumOpsCheck = true)
: OperandPredicateMatcher(OPM_Instruction, InsnVarID, OpIdx),
- InsnMatcher(new InstructionMatcher(Rule, SymbolicName, NumOpsCheck)) {}
+ InsnMatcher(new InstructionMatcher(Rule, SymbolicName, NumOpsCheck)),
+ Flags(Rule.getGISelFlags()) {}
static bool classof(const PredicateMatcher *P) {
return P->getKind() == OPM_Instruction;
@@ -2496,7 +2540,9 @@ class InstructionOperandMatcher : public OperandPredicateMatcher {
void emitCaptureOpcodes(MatchTable &Table, RuleMatcher &Rule) const {
const unsigned NewInsnVarID = InsnMatcher->getInsnVarID();
- Table << MatchTable::Opcode("GIM_RecordInsn")
+ const bool IgnoreCopies = Flags & GISF_IgnoreCopies;
+ Table << MatchTable::Opcode(IgnoreCopies ? "GIM_RecordInsnIgnoreCopies"
+ : "GIM_RecordInsn")
<< MatchTable::Comment("DefineMI")
<< MatchTable::IntValue(NewInsnVarID) << MatchTable::Comment("MI")
<< MatchTable::IntValue(getInsnVarID())
@@ -3325,8 +3371,10 @@ void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) {
// If the operand is already defined, then we must ensure both references in
// the matcher have the exact same node.
+ RuleMatcher &RM = OM.getInstructionMatcher().getRuleMatcher();
OM.addPredicate<SameOperandMatcher>(
- OM.getSymbolicName(), getOperandMatcher(OM.getSymbolicName()).getOpIdx());
+ OM.getSymbolicName(), getOperandMatcher(OM.getSymbolicName()).getOpIdx(),
+ RM.getGISelFlags());
}
void RuleMatcher::definePhysRegOperand(Record *Reg, OperandMatcher &OM) {
@@ -3534,15 +3582,16 @@ void SameOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
const OperandMatcher &OtherOM = Rule.getOperandMatcher(MatchingName);
unsigned OtherInsnVarID = Rule.getInsnVarID(OtherOM.getInstructionMatcher());
assert(OtherInsnVarID == OtherOM.getInstructionMatcher().getInsnVarID());
-
- Table << MatchTable::Opcode("GIM_CheckIsSameOperand")
+ const bool IgnoreCopies = Flags & GISF_IgnoreCopies;
+ Table << MatchTable::Opcode(IgnoreCopies
+ ? "GIM_CheckIsSameOperandIgnoreCopies"
+ : "GIM_CheckIsSameOperand")
<< MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID)
<< MatchTable::Comment("OpIdx") << MatchTable::IntValue(OpIdx)
<< MatchTable::Comment("OtherMI")
<< MatchTable::IntValue(OtherInsnVarID)
<< MatchTable::Comment("OtherOpIdx")
- << MatchTable::IntValue(OtherOM.getOpIdx())
- << MatchTable::LineBreak;
+ << MatchTable::IntValue(OtherOM.getOpIdx()) << MatchTable::LineBreak;
}
//===- GlobalISelEmitter class --------------------------------------------===//
@@ -3984,6 +4033,8 @@ Expected<InstructionMatcher &> GlobalISelEmitter::addBuiltinPredicates(
Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
RuleMatcher &Rule, InstructionMatcher &InsnMatcher,
const TreePatternNode *Src, unsigned &TempOpIdx) {
+ const auto SavedFlags = Rule.setGISelFlags(Src->getGISelFlagsRecord());
+
Record *SrcGIEquivOrNull = nullptr;
const CodeGenInstruction *SrcGIOrNull = nullptr;
@@ -5208,6 +5259,9 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
// before their first use.)
InstructionMatcher &InsnMatcherTemp = M.addInstructionMatcher(Src->getName());
unsigned TempOpIdx = 0;
+
+ const auto SavedFlags = M.setGISelFlags(P.getSrcRecord());
+
auto InsnMatcherOrError =
createAndImportSelDAGMatcher(M, InsnMatcherTemp, Src, TempOpIdx);
if (auto Error = InsnMatcherOrError.takeError())
More information about the llvm-commits
mailing list