summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/test/TableGen/DefaultOpsGlobalISel.td144
-rw-r--r--llvm/utils/TableGen/GlobalISelEmitter.cpp44
2 files changed, 181 insertions, 7 deletions
diff --git a/llvm/test/TableGen/DefaultOpsGlobalISel.td b/llvm/test/TableGen/DefaultOpsGlobalISel.td
new file mode 100644
index 00000000000..9ea05e0c3be
--- /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 07da34cc566..b2e3903eda8 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))
OpenPOWER on IntegriCloud