Index: llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h =================================================================== --- llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -194,6 +194,13 @@ /// G_IMPLICIT_DEF. bool matchAnyExplicitUseIsUndef(MachineInstr &MI); + /// Return true if all register explicit use operands on \p MI are defined by + /// a G_IMPLICIT_DEF. + bool matchAllExplicitUsesAreUndef(MachineInstr &MI); + + /// Return true if a G_SHUFFLE_VECTOR instruction \p MI has an undef mask. + bool matchUndefShuffleVectorMask(MachineInstr &MI); + /// Replace an instruction with a G_FCONSTANT with value \p C. bool replaceInstWithFConstant(MachineInstr &MI, double C); Index: llvm/include/llvm/Target/GlobalISel/Combine.td =================================================================== --- llvm/include/llvm/Target/GlobalISel/Combine.td +++ llvm/include/llvm/Target/GlobalISel/Combine.td @@ -161,14 +161,35 @@ [{ return Helper.matchAnyExplicitUseIsUndef(*${root}); }]), (apply [{ Helper.replaceInstWithConstant(*${root}, -1); }])>; -def propagate_undef: GICombineRule< +// Instructions where if any source operand is undef, the instruction can be +// replaced with undef. +def propagate_undef_any_op: GICombineRule< (defs root:$root), (match (wip_match_opcode G_ADD, G_FPTOSI, G_FPTOUI, G_SUB, G_XOR):$root, [{ return Helper.matchAnyExplicitUseIsUndef(*${root}); }]), (apply [{ Helper.replaceInstWithUndef(*${root}); }])>; +// Instructions where if all source operands are undef, the instruction can be +// replaced with undef. +def propagate_undef_all_ops: GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_SHUFFLE_VECTOR):$root, + [{ return Helper.matchAllExplicitUsesAreUndef(*${root}); }]), + (apply [{ Helper.replaceInstWithUndef(*${root}); }])>; + +// Replace a G_SHUFFLE_VECTOR with an undef mask with a G_IMPLICIT_DEF. +def propagate_undef_shuffle_mask: GICombineRule< + (defs root:$root), + (match (wip_match_opcode G_SHUFFLE_VECTOR):$root, + [{ return Helper.matchUndefShuffleVectorMask(*${root}); }]), + (apply [{ Helper.replaceInstWithUndef(*${root}); }])>; + +// FIXME: These should use the custom predicate feature once it lands. def undef_combines : GICombineGroup<[undef_to_fp_zero, undef_to_int_zero, - undef_to_negative_one, propagate_undef]>; + undef_to_negative_one, + propagate_undef_any_op, + propagate_undef_all_ops, + propagate_undef_shuffle_mask]>; def trivial_combines : GICombineGroup<[copy_prop, mul_to_shl]>; def all_combines : GICombineGroup<[trivial_combines, ptr_add_immed_chain, Index: llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -1490,6 +1490,19 @@ }); } +bool CombinerHelper::matchAllExplicitUsesAreUndef(MachineInstr &MI) { + return all_of(MI.explicit_uses(), [this](const MachineOperand &MO) { + return !MO.isReg() || + getOpcodeDef(TargetOpcode::G_IMPLICIT_DEF, MO.getReg(), MRI); + }); +} + +bool CombinerHelper::matchUndefShuffleVectorMask(MachineInstr &MI) { + assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR); + ArrayRef Mask = MI.getOperand(3).getShuffleMask(); + return all_of(Mask, [](int Elt) { return Elt < 0; }); +} + bool CombinerHelper::replaceInstWithFConstant(MachineInstr &MI, double C) { assert(MI.getNumDefs() == 1 && "Expected only one def?"); Builder.setInstr(MI); Index: llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-undef.mir =================================================================== --- llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-undef.mir +++ llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-undef.mir @@ -166,3 +166,61 @@ RET_ReallyLR implicit $w0 ... +--- +name: shufflevector_undef_ops_to_undef +alignment: 4 +tracksRegLiveness: true +body: | + bb.0: + ; CHECK-LABEL: name: shufflevector_undef_ops_to_undef + ; CHECK: [[DEF:%[0-9]+]]:_(<2 x s32>) = G_IMPLICIT_DEF + ; CHECK: $d0 = COPY [[DEF]](<2 x s32>) + ; CHECK: RET_ReallyLR implicit $d0 + %1:_(<2 x s32>) = G_IMPLICIT_DEF + %2:_(<2 x s32>) = G_IMPLICIT_DEF + %0:_(<2 x s32>) = G_SHUFFLE_VECTOR %1(<2 x s32>), %2(<2 x s32>), shufflemask(0, 1) + $d0 = COPY %0(<2 x s32>) + RET_ReallyLR implicit $d0 + +... +--- +name: shufflevector_undef_mask_to_undef +alignment: 4 +tracksRegLiveness: true +body: | + bb.0: + liveins: $d0, $d1 + ; CHECK-LABEL: name: shufflevector_undef_mask_to_undef + ; CHECK: liveins: $d0, $d1 + ; CHECK: [[DEF:%[0-9]+]]:_(<2 x s32>) = G_IMPLICIT_DEF + ; CHECK: $d0 = COPY [[DEF]](<2 x s32>) + ; CHECK: RET_ReallyLR implicit $d0 + %0:_(<2 x s32>) = COPY $d0 + %1:_(<2 x s32>) = COPY $d1 + %2:_(<2 x s32>) = G_SHUFFLE_VECTOR %0(<2 x s32>), %1, shufflemask(undef, undef) + $d0 = COPY %2(<2 x s32>) + RET_ReallyLR implicit $d0 + +... +--- +name: shufflevector_not_all_ops_undef +alignment: 4 +tracksRegLiveness: true +body: | + bb.0: + liveins: $d0 + ; Show that we don't do the combine when one of the vectors is not a + ; G_IMPLICIT_DEF. + ; + ; CHECK-LABEL: name: shufflevector_not_all_ops_undef + ; CHECK: liveins: $d0 + ; CHECK: [[DEF:%[0-9]+]]:_(<2 x s32>) = G_IMPLICIT_DEF + ; CHECK: [[COPY:%[0-9]+]]:_(<2 x s32>) = COPY $d0 + ; CHECK: [[SHUF:%[0-9]+]]:_(<2 x s32>) = G_SHUFFLE_VECTOR [[DEF]](<2 x s32>), [[COPY]], shufflemask(0, 1) + ; CHECK: $d0 = COPY [[SHUF]](<2 x s32>) + ; CHECK: RET_ReallyLR implicit $d0 + %1:_(<2 x s32>) = G_IMPLICIT_DEF + %2:_(<2 x s32>) = COPY $d0 + %0:_(<2 x s32>) = G_SHUFFLE_VECTOR %1(<2 x s32>), %2(<2 x s32>), shufflemask(0, 1) + $d0 = COPY %0(<2 x s32>) + RET_ReallyLR implicit $d0