diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h --- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -264,6 +264,9 @@ void applyCombineShlOfExtend(MachineInstr &MI, const RegisterImmPair &MatchData); + /// Fold away a merge of an unmerge of the corresponding values. + bool matchCombineMergeUnmerge(MachineInstr &MI, Register &MatchInfo); + /// Reduce a shift by a constant to an unmerge and a shift on a half sized /// type. This will not produce a shift smaller than \p TargetShiftSize. bool matchCombineShiftToUnmerge(MachineInstr &MI, unsigned TargetShiftSize, diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td --- a/llvm/include/llvm/Target/GlobalISel/Combine.td +++ b/llvm/include/llvm/Target/GlobalISel/Combine.td @@ -490,6 +490,14 @@ (apply [{ Helper.applyCombineUnmergeMergeToPlainValues(*${d}, ${info}); }]) >; +// Fold merge(unmerge). +def merge_unmerge : GICombineRule< + (defs root:$d, register_matchinfo:$matchinfo), + (match (wip_match_opcode G_MERGE_VALUES):$d, + [{ return Helper.matchCombineMergeUnmerge(*${d}, ${matchinfo}); }]), + (apply [{ Helper.replaceSingleDefInstWithReg(*${d}, ${matchinfo}); }]) +>; + // Fold (fabs (fabs x)) -> (fabs x). def fabs_fabs_fold: GICombineRule< (defs root:$root, register_matchinfo:$matchinfo), @@ -694,7 +702,7 @@ known_bits_simplifications, ext_ext_fold, not_cmp_fold, opt_brcond_by_inverting_cond, unmerge_merge, fabs_fabs_fold, unmerge_cst, unmerge_dead_to_trunc, - unmerge_zext_to_zext, trunc_ext_fold, trunc_shl, + unmerge_zext_to_zext, merge_unmerge, trunc_ext_fold, trunc_shl, const_combines, xor_of_and_with_same_reg, ptr_add_with_zero, shift_immed_chain, shift_of_shifted_logic_chain, load_or_combine, div_rem_to_divrem, funnel_shift_combines, form_bitfield_extract]>; diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp --- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -2014,6 +2014,25 @@ MI.eraseFromParent(); } +bool CombinerHelper::matchCombineMergeUnmerge(MachineInstr &MI, + Register &MatchInfo) { + GMerge &Merge = cast(MI); + SmallVector MergedValues; + for (unsigned I = 0; I < Merge.getNumSources(); ++I) + MergedValues.emplace_back(Merge.getSourceReg(I)); + + auto *Unmerge = getOpcodeDef(MergedValues[0], MRI); + if (!Unmerge || Unmerge->getNumDefs() != Merge.getNumSources()) + return false; + + for (unsigned I = 0; I < MergedValues.size(); ++I) + if (MergedValues[I] != Unmerge->getReg(I)) + return false; + + MatchInfo = Unmerge->getSourceReg(); + return true; +} + static Register peekThroughBitcast(Register Reg, const MachineRegisterInfo &MRI) { while (mi_match(Reg, MRI, m_GBitcast(m_Reg(Reg)))) diff --git a/llvm/lib/Target/AArch64/AArch64Combine.td b/llvm/lib/Target/AArch64/AArch64Combine.td --- a/llvm/lib/Target/AArch64/AArch64Combine.td +++ b/llvm/lib/Target/AArch64/AArch64Combine.td @@ -203,6 +203,6 @@ extractvecelt_pairwise_add, redundant_or, mul_const, redundant_sext_inreg, form_bitfield_extract, rotate_out_of_range, - icmp_to_true_false_known_bits]> { + icmp_to_true_false_known_bits, merge_unmerge]> { let DisableRuleOption = "aarch64postlegalizercombiner-disable-rule"; } diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/postlegalizer-combiner-merge.mir b/llvm/test/CodeGen/AArch64/GlobalISel/postlegalizer-combiner-merge.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/postlegalizer-combiner-merge.mir @@ -0,0 +1,70 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple aarch64 -run-pass=aarch64-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s + +--- +name: merge_unmerge +alignment: 4 +legalized: true +liveins: + - { reg: '$w0' } +body: | + bb.1.entry: + liveins: $x0 + + ; CHECK-LABEL: name: merge_unmerge + ; CHECK: [[COPY:%[0-9]+]]:_(s64) = COPY $x0 + ; CHECK: $x0 = COPY [[COPY]](s64) + ; CHECK: RET_ReallyLR implicit $x0 + %0:_(s64) = COPY $x0 + %a:_(s32), %b:_(s32) = G_UNMERGE_VALUES %0 + %merge:_(s64) = G_MERGE_VALUES %a, %b + $x0 = COPY %merge(s64) + RET_ReallyLR implicit $x0 + +... +--- +name: merge_unmerge_mismatched_order +alignment: 4 +legalized: true +liveins: + - { reg: '$w0' } +body: | + bb.1.entry: + liveins: $x0 + + ; CHECK-LABEL: name: merge_unmerge_mismatched_order + ; CHECK: [[COPY:%[0-9]+]]:_(s64) = COPY $x0 + ; CHECK: %b:_(s32), %a:_(s32) = G_UNMERGE_VALUES [[COPY]](s64) + ; CHECK: %merge:_(s64) = G_MERGE_VALUES %a(s32), %b(s32) + ; CHECK: $x0 = COPY %merge(s64) + ; CHECK: RET_ReallyLR implicit $x0 + %0:_(s64) = COPY $x0 + %b:_(s32), %a:_(s32) = G_UNMERGE_VALUES %0 + %merge:_(s64) = G_MERGE_VALUES %a, %b + $x0 = COPY %merge(s64) + RET_ReallyLR implicit $x0 + +... +--- +name: merge_unmerge_mismatched_num_defs +alignment: 4 +legalized: true +liveins: + - { reg: '$w0' } +body: | + bb.1.entry: + liveins: $q0 + + ; CHECK-LABEL: name: merge_unmerge_mismatched_num_defs + ; CHECK: [[COPY:%[0-9]+]]:_(s128) = COPY $q0 + ; CHECK: %a:_(s32), %b:_(s32), %c:_(s32), %d:_(s32) = G_UNMERGE_VALUES [[COPY]](s128) + ; CHECK: %merge:_(s64) = G_MERGE_VALUES %a(s32), %b(s32) + ; CHECK: $x0 = COPY %merge(s64) + ; CHECK: RET_ReallyLR implicit $x0 + %0:_(s128) = COPY $q0 + %a:_(s32), %b:_(s32), %c:_(s32), %d:_(s32) = G_UNMERGE_VALUES %0 + %merge:_(s64) = G_MERGE_VALUES %a, %b + $x0 = COPY %merge(s64) + RET_ReallyLR implicit $x0 + +...