[llvm] 26f714f - TableGen/GlobalISel: Handle default operands that are used

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 6 15:27:06 PST 2020


Author: Matt Arsenault
Date: 2020-01-06T18:26:42-05:00
New Revision: 26f714ff43e3498ae2528ad8c9875de77a529472

URL: https://github.com/llvm/llvm-project/commit/26f714ff43e3498ae2528ad8c9875de77a529472
DIFF: https://github.com/llvm/llvm-project/commit/26f714ff43e3498ae2528ad8c9875de77a529472.diff

LOG: TableGen/GlobalISel: Handle default operands that are used

Copy the logic from the existing handling in the DAG matcher emittter.

This will enable some AMDGPU pattern cleanups without breaking
GlobalISel tests, and eventually handle importing more patterns.

The test is a bit annoying since the sections seem to randomly sort
themselves if anything else is added in the future.

Added: 
    llvm/test/TableGen/DefaultOpsGlobalISel.td

Modified: 
    llvm/utils/TableGen/GlobalISelEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/TableGen/DefaultOpsGlobalISel.td b/llvm/test/TableGen/DefaultOpsGlobalISel.td
new file mode 100644
index 000000000000..9ea05e0c3bef
--- /dev/null
+++ b/llvm/test/TableGen/DefaultOpsGlobalISel.td
@@ -0,0 +1,144 @@
+// 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 SelectClamp  : ComplexPattern<untyped, 2, "SelectClamp">;
+def SelectOMod  : ComplexPattern<untyped, 2, "SelectOMod">;
+def SelectClampOMod  : ComplexPattern<untyped, 3, "SelectClampOMod">;
+
+def gi_SelectClamp :
+    GIComplexOperandMatcher<s32, "selectClamp">,
+    GIComplexPatternEquiv<SelectClamp>;
+
+def gi_SelectOMod :
+    GIComplexOperandMatcher<s32, "selectOMod">,
+    GIComplexPatternEquiv<SelectOMod>;
+
+def gi_SelectClampOMod :
+    GIComplexOperandMatcher<s32, "selectClampOMod">,
+    GIComplexPatternEquiv<SelectClampOMod>;
+
+
+def omod : OperandWithDefaultOps <i32, (ops (i32 0))>;
+def clamp : OperandWithDefaultOps <i1, (ops (i1 0))>;
+
+
+// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_FFLOOR,
+// CHECK: GIM_CheckComplexPattern, /*MI*/0, /*Op*/1, /*Renderer*/0, GICP_gi_SelectClampOMod,
+// CHECK: // (ffloor:{ *:[f32] } (SelectClampOMod:{ *:[f32] } f32:{ *:[f32] }:$src0, omod:{ *:[i32] }:$omod, i1:{ *:[i1] }:$clamp))  =>  (FLOMP:{ *:[f32] } f32:{ *:[f32] }:$src0, i1:{ *:[i1] }:$clamp, omod:{ *:[i32] }:$omod)
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::FLOMP,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/0, // src0
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/2, // clamp
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/1, // omod
+
+
+// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_FCOS,
+// CHECK: // (fcos:{ *:[f32] } (SelectOMod:{ *:[f32] } f32:{ *:[f32] }:$src0, i32:{ *:[i32] }:$omod))  =>  (FLAMP:{ *:[f32] } FPR32:{ *:[f32] }:$src0, omod:{ *:[i32] }:$omod)
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::FLAMP,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/0, // src0
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/1, // omod
+// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/0,
+
+
+// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_FEXP2,
+// CHECK: // (fexp2:{ *:[f32] } (SelectClamp:{ *:[f32] } f32:{ *:[f32] }:$src0, i1:{ *:[i1] }:$clamp))  =>  (FEEPLE:{ *:[f32] } FPR32:{ *:[f32] }:$src0, (FFOO:{ *:[f32] } FPR32:{ *:[f32] }:$src0), clamp:{ *:[i1] }:$clamp)
+
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/MyTarget::FFOO,
+// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define,
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/1, /*RendererID*/0, /*SubOperand*/0, // src0
+// CHECK-NEXT: GIR_AddImm, /*InsnID*/1, /*Imm*/0,
+// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/1,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::FEEPLE,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/0, // src0
+// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0,
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/1, // clamp
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
+// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
+
+
+// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_FSIN,
+// CHECK: // (fsin:{ *:[f32] } (SelectClamp:{ *:[f32] } f32:{ *:[f32] }:$src0, i1:{ *:[i1] }:$clamp))  =>  (FFOO:{ *:[f32] } f32:{ *:[f32] }:$src0, i1:{ *:[i1] }:$clamp)
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::FFOO,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/0, // src0
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/1, // clamp
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
+
+
+// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_FSQRT,
+// CHECK: // (fsqrt:{ *:[f32] } (SelectClamp:{ *:[f32] } f32:{ *:[f32] }:$src0, i1:{ *:[i1] }:$clamp))  =>  (FLAMP:{ *:[f32] } FPR32:{ *:[f32] }:$src0, 93:{ *:[i32] }, clamp:{ *:[i1] }:$clamp)
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::FLAMP,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/0, // src0
+// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/93,
+// CHECK-NEXT: GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/1, // clamp
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
+
+// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_INTRINSIC_ROUND,
+// CHECK: // (fround:{ *:[f32] } f32:{ *:[f32] }:$src0)  =>  (FBAR:{ *:[f32] } f32:{ *:[f32] }:$src0)
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::FBAR,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src0
+// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/0,
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
+
+// CHECK: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_INTRINSIC_TRUNC,
+// CHECK: // (ftrunc:{ *:[f32] } f32:{ *:[f32] }:$src0)  =>  (FFOO:{ *:[f32] } FPR32:{ *:[f32] }:$src0)
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::FFOO,
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src0
+// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/0,
+// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
+
+
+// Have default operand with explicit value from complex pattern.
+def FFOO : I<(outs FPR32:$dst), (ins FPR32:$src0, clamp:$clamp),
+            [(set FPR32:$dst, (fsin (SelectClamp f32:$src0, i1:$clamp)))]>;
+
+
+// Have default operand, not explicitly specified in a standalone
+// pattern.
+def : Pat <
+  (ftrunc f32:$src0),
+  (FFOO FPR32:$src0)
+>;
+
+// Have default operand, not explicitly specified in an instruction
+// definition pattern.
+def FBAR : I<(outs FPR32:$dst), (ins FPR32:$src0, clamp:$clamp),
+            [(set FPR32:$dst, (fround f32:$src0))]>;
+
+
+// // Swapped order in instruction from pattern
+def FLOMP : I<
+  (outs FPR32:$dst), (ins FPR32:$src0, clamp:$clamp, omod:$omod),
+  [(set FPR32:$dst, (ffloor (SelectClampOMod f32:$src0, omod:$omod, i1:$clamp)))]>;
+
+def FLAMP : I<(outs FPR32:$dst), (ins FPR32:$src0, omod:$omod, clamp:$clamp), []>;
+
+// // Have 2 default operands, and the first is specified
+def : Pat <
+  (fcos (SelectOMod f32:$src0, i32:$omod)),
+  (FLAMP FPR32:$src0, omod:$omod)
+>;
+
+// Immediate used for first defaulted operand
+def : Pat <
+  (fsqrt (SelectClamp f32:$src0, i1:$clamp)),
+  (FLAMP FPR32:$src0, 93, clamp:$clamp)
+>;
+
+def FEEPLE : I<(outs FPR32:$dst),
+               (ins FPR32:$src0, FPR32:$src1, clamp:$clamp), []>;
+
+// Default operand isn't on the root ouput instruction
+def : Pat <
+  (fexp2 (SelectClamp f32:$src0, i1:$clamp)),
+  (FEEPLE FPR32:$src0, (FFOO FPR32:$src0), clamp:$clamp)
+>;

diff  --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index 07da34cc566c..b2e3903eda8b 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -4309,18 +4309,48 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderers(
     ExpectedDstINumUses--;
   }
 
+  // NumResults - This is the number of results produced by the instruction in
+  // the "outs" list.
+  unsigned NumResults = OrigDstI->Operands.NumDefs;
+
+  // Number of operands we know the output instruction must have. If it is
+  // variadic, we could have more operands.
+  unsigned NumFixedOperands = DstI->Operands.size();
+
+  // Loop over all of the fixed operands of the instruction pattern, emitting
+  // code to fill them all in. The node 'N' usually has number children equal to
+  // the number of input operands of the instruction.  However, in cases where
+  // there are predicate operands for an instruction, we need to fill in the
+  // 'execute always' values. Match up the node operands to the instruction
+  // operands to do this.
   unsigned Child = 0;
+
+  // Similarly to the code in TreePatternNode::ApplyTypeConstraints, count the
+  // number of operands at the end of the list which have default values.
+  // Those can come from the pattern if it provides enough arguments, or be
+  // filled in with the default if the pattern hasn't provided them. But any
+  // operand with a default value _before_ the last mandatory one will be
+  // filled in with their defaults unconditionally.
+  unsigned NonOverridableOperands = NumFixedOperands;
+  while (NonOverridableOperands > NumResults &&
+         CGP.operandHasDefault(DstI->Operands[NonOverridableOperands - 1].Rec))
+    --NonOverridableOperands;
+
   unsigned NumDefaultOps = 0;
   for (unsigned I = 0; I != DstINumUses; ++I) {
-    const CGIOperandList::OperandInfo &DstIOperand =
-        DstI->Operands[DstI->Operands.NumDefs + I];
+    unsigned InstOpNo = DstI->Operands.NumDefs + I;
+
+    // Determine what to emit for this operand.
+    Record *OperandNode = DstI->Operands[InstOpNo].Rec;
 
     // If the operand has default values, introduce them now.
-    // FIXME: Until we have a decent test case that dictates we should do
-    // otherwise, we're going to assume that operands with default values cannot
-    // be specified in the patterns. Therefore, adding them will not cause us to
-    // end up with too many rendered operands.
-    if (DstIOperand.Rec->isSubClassOf("OperandWithDefaultOps")) {
+    if (CGP.operandHasDefault(OperandNode) &&
+        (InstOpNo < NonOverridableOperands || Child >= Dst->getNumChildren())) {
+      // This is a predicate or optional def operand which the pattern has not
+      // overridden, or which we aren't letting it override; emit the 'default
+      // ops' operands.
+
+      const CGIOperandList::OperandInfo &DstIOperand = DstI->Operands[InstOpNo];
       DagInit *DefaultOps = DstIOperand.Rec->getValueAsDag("DefaultOps");
       if (auto Error = importDefaultOperandRenderers(
             InsertPt, M, DstMIBuilder, DefaultOps))


        


More information about the llvm-commits mailing list