Index: llvm/test/TableGen/DefaultOpsGlobalISel.td =================================================================== --- /dev/null +++ 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; +def SelectOMod : ComplexPattern; +def SelectClampOMod : ComplexPattern; + +def gi_SelectClamp : + GIComplexOperandMatcher, + GIComplexPatternEquiv; + +def gi_SelectOMod : + GIComplexOperandMatcher, + GIComplexPatternEquiv; + +def gi_SelectClampOMod : + GIComplexOperandMatcher, + GIComplexPatternEquiv; + + +def omod : OperandWithDefaultOps ; +def clamp : OperandWithDefaultOps ; + + +// 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) +>; Index: llvm/utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- llvm/utils/TableGen/GlobalISelEmitter.cpp +++ llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -4309,18 +4309,48 @@ 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))