[llvm] [AArch64][GlobalISel] Add pre-legalizer combines for AVGFLOOR and AVGCEIL (PR #192866)

via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 19 12:09:33 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-globalisel

Author: Atafid (Atafid)

<details>
<summary>Changes</summary>

This patch adds GlobalISel pre-legalizer combines to pattern-match and optimize average operations, bringing GlobalISel on par with SelectionDAG.

Specifically, it matches:
- `(a + b) >> 1` into `G_UAVGFLOOR` / `G_SAVGFLOOR`
- `(a + b + 1) >> 1` into `G_UAVGCEIL` / `G_SAVGCEIL`

Support is included for both scalar and vector types, correctly handling constants and splat vectors via `isOneOrOneSplat()`. This builds upon the generic opcodes introduced for AArch64 intrinsics lowering and enables optimal emission of Neon instructions (e.g., `urhadd`, `shadd`) directly from generic IR.

Fixes #<!-- -->118083

---

Patch is 51.46 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/192866.diff


5 Files Affected:

- (modified) llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h (+8) 
- (modified) llvm/include/llvm/Target/GlobalISel/Combine.td (+29-15) 
- (modified) llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp (+145) 
- (added) llvm/test/CodeGen/AArch64/GlobalISel/combine-avg.mir (+319) 
- (modified) llvm/test/CodeGen/AArch64/arm64-vhadd.ll (+154-440) 


``````````diff
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
index 365bbaacfe055..294de3747b9bc 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
@@ -1054,6 +1054,14 @@ class CombinerHelper {
   // (ctlz (or (shl (xor x, (sra x, bitwidth-1)), 1), 1) -> (ctls x)
   bool matchCtls(MachineInstr &CtlzMI, BuildFnTy &MatchInfo) const;
 
+  // shr ( add ( ext X, ext Y ), 1 ) -> avgfloor ( x, y )
+  bool matchAVGFloor(MachineInstr &MI, MachineRegisterInfo &MRI,
+                     BuildFnTy &MatchInfo) const;
+
+  // shr ( add ( ext X, ext Y, 1 ), 1 ) -> avgceil ( x, y )
+  bool matchAVGCeil(MachineInstr &MI, MachineRegisterInfo &MRI,
+                    BuildFnTy &MatchInfo) const;
+
 private:
   /// Checks for legality of an indexed variant of \p LdSt.
   bool isIndexedLoadStoreLegal(GLoadStore &LdSt) const;
diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index df3955909fe85..ea90410eb03c3 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -1174,29 +1174,29 @@ def funnel_shift_overshift: GICombineRule<
 
 // Transform: fshl x, z, y | shl x, y -> fshl x, z, y
 // Transform: shl x, y | fshl x, z, y  -> fshl x, z, y
-// FIXME: TableGen didn't handle G_OR commutativity on its own, 
+// FIXME: TableGen didn't handle G_OR commutativity on its own,
 //        necessitating the use of !foreach to handle it manually.
 def funnel_shift_or_shift_to_funnel_shift_left_frags : GICombinePatFrag<
   (outs root: $dst, $out1, $out2), (ins),
-  !foreach(inst, [(G_OR $dst, $out1, $out2), (G_OR $dst, $out2, $out1)], 
+  !foreach(inst, [(G_OR $dst, $out1, $out2), (G_OR $dst, $out2, $out1)],
            (pattern (G_FSHL $out1, $x, $z, $y), (G_SHL $out2, $x, $y), inst))>;
 def funnel_shift_or_shift_to_funnel_shift_left: GICombineRule<
-  (defs root:$root), 
+  (defs root:$root),
   (match (funnel_shift_or_shift_to_funnel_shift_left_frags $root, $out1, $out2)),
   (apply (GIReplaceReg $root, $out1))
 >;
 
 // Transform: fshr z, x, y | srl x, y -> fshr z, x, y
 // Transform: srl x, y | fshr z, x, y -> fshr z, x, y
-// FIXME: TableGen didn't handle G_OR commutativity on its own, 
+// FIXME: TableGen didn't handle G_OR commutativity on its own,
 //        necessitating the use of !foreach to handle it manually.
 def funnel_shift_or_shift_to_funnel_shift_right_frags : GICombinePatFrag<
   (outs root: $dst, $out1, $out2), (ins),
-  !foreach(inst, [(G_OR $dst, $out1, $out2), (G_OR $dst, $out2, $out1)], 
+  !foreach(inst, [(G_OR $dst, $out1, $out2), (G_OR $dst, $out2, $out1)],
            (pattern (G_FSHR $out1, $z, $x, $y), (G_LSHR $out2, $x, $y), inst))>;
 def funnel_shift_or_shift_to_funnel_shift_right: GICombineRule<
-  (defs root:$root), 
-  (match (funnel_shift_or_shift_to_funnel_shift_right_frags $root, $out1, $out2)), 
+  (defs root:$root),
+  (match (funnel_shift_or_shift_to_funnel_shift_right_frags $root, $out1, $out2)),
   (apply (GIReplaceReg $root, $out1))
 >;
 
@@ -1323,7 +1323,7 @@ def udiv_by_pow2 : GICombineRule<
    [{ return Helper.matchDivByPow2(*${root}, /*IsSigned=*/false); }]),
   (apply [{ Helper.applyUDivByPow2(*${root}); }])>;
 
-def intdiv_combines : GICombineGroup<[udiv_by_pow2, sdiv_by_pow2, 
+def intdiv_combines : GICombineGroup<[udiv_by_pow2, sdiv_by_pow2,
                                       udiv_by_const, sdiv_by_const,]>;
 
 def urem_by_const : GICombineRule<
@@ -1434,7 +1434,7 @@ def trunc_ssatu : GICombineRule<
 
 def trunc_usatu : GICombineRule<
   (defs root:$root),
-  (match (G_UMIN $min, $x, $y):$Min, 
+  (match (G_UMIN $min, $x, $y):$Min,
          (G_TRUNC $dst, $min):$root,
     [{ return Helper.matchTruncUSatU(*${root}, *${Min}); }]),
   (apply (G_TRUNC_USAT_U $dst, $x))>;
@@ -1982,14 +1982,14 @@ def APlusBMinusAplusC : GICombineRule<
           (G_SUB $sub1, $B, $add1),
           (G_ADD $root, $A, $sub1)),
    (apply (G_SUB $root, $B, $C))>;
-   
+
 // fold (A-(B-C)) to A+(C-B)
 def AMinusBMinusC : GICombineRule<
    (defs root:$root),
    (match (G_SUB $sub1, $B, $C),
-          (G_SUB $root, $A, $sub1), 
+          (G_SUB $root, $A, $sub1),
           [{ return MRI.hasOneNonDBGUse(${sub1}.getReg()); }]),
-   (apply (G_SUB $add, $C, $B), 
+   (apply (G_SUB $add, $C, $B),
           (G_ADD $root, $A, $add))>;
 
 // fold (A+(B-(C+A))) to (B-C)
@@ -1999,7 +1999,7 @@ def APlusBMinusCPlusA : GICombineRule<
           (G_SUB $sub1, $B, $add1),
           (G_ADD $root, $A, $sub1)),
    (apply (G_SUB $root, $B, $C))>;
-  
+
 // fold (A - (0 - B)) to (A + B)
 def AMinusZeroMinusB : GICombineRule<
    (defs root:$root),
@@ -2013,7 +2013,7 @@ def AMinusBMinusA: GICombineRule<
    (match (G_SUB $add, $A, $B),
           (G_SUB $root, $A, $add)),
    (apply (GIReplaceReg $root, $B))>;
-   
+
 // fold (not (add X, -1)) -> (sub 0, X)
 def NotAPlusNegOne: GICombineRule<
    (defs root:$root),
@@ -2287,6 +2287,20 @@ def overflow_combines: GICombineGroup<[
   match_subo_no_overflow
 ]>;
 
+def avgfloor_match : GICombineRule<
+  (defs root:$root, build_fn_matchinfo:$matchinfo),
+  (match (wip_match_opcode G_LSHR, G_ASHR):$root,
+         [{ return Helper.matchAVGFloor(*${root}, MRI, ${matchinfo}); }]),
+  (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])
+>;
+
+def avgceil_match : GICombineRule<
+  (defs root:$root, build_fn_matchinfo:$matchinfo),
+  (match (wip_match_opcode G_LSHR, G_ASHR):$root,
+         [{ return Helper.matchAVGCeil(*${root}, MRI, ${matchinfo}); }]),
+  (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])
+>;
+
 // 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,
@@ -2376,7 +2390,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
     sext_trunc, zext_trunc, prefer_sign_combines, shuffle_combines,
     combine_use_vector_truncate, merge_combines, overflow_combines,
     truncsat_combines, lshr_of_trunc_of_lshr, ctls_combines, add_shift, sub_one_from_sub,
-    binop_with_neg, sub_minus_one]>;
+    binop_with_neg, sub_minus_one, avgfloor_match, avgceil_match]>;
 
 // A combine group used to for prelegalizer combiners at -O0. The combines in
 // this group have been selected based on experiments to balance code size and
diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index 0a7d682c44049..6288be25c1c6f 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -8692,3 +8692,148 @@ bool CombinerHelper::matchCtls(MachineInstr &CtlzMI,
 
   return true;
 }
+
+static bool matchAnySExt(MachineRegisterInfo &MRI, Register Reg,
+                         Register &ExtSrc) {
+  if (mi_match(Reg, MRI, m_GSExt(m_Reg(ExtSrc))))
+    return true;
+
+  MachineInstr *ExtInst = MRI.getVRegDef(Reg);
+  if (ExtInst && ExtInst->getOpcode() == TargetOpcode::G_SEXT_INREG) {
+    ExtSrc = ExtInst->getOperand(1).getReg();
+    return true;
+  }
+  return false;
+};
+
+// Fold shr ( add ( ext X, ext Y ), 1 ) -> avgfloor ( x, y )
+bool CombinerHelper::matchAVGFloor(MachineInstr &MI, MachineRegisterInfo &MRI,
+                                   BuildFnTy &MatchInfo) const {
+  assert((MI.getOpcode() == TargetOpcode::G_LSHR ||
+          MI.getOpcode() == TargetOpcode::G_ASHR) &&
+         "Expected G_LSHR/G_ASHR");
+
+  Register Dst = MI.getOperand(0).getReg();
+  Register Src = MI.getOperand(1).getReg();
+  Register ShiftAmtReg = MI.getOperand(2).getReg();
+
+  Register X, Y;
+  bool IsSigned = (MI.getOpcode() == TargetOpcode::G_ASHR);
+
+  Register ExtReg1, ExtReg2;
+  if (!mi_match(Src, MRI, m_OneUse(m_GAdd(m_Reg(ExtReg1), m_Reg(ExtReg2)))))
+    return false;
+
+  if (IsSigned) {
+    if (!matchAnySExt(MRI, ExtReg1, X) || !matchAnySExt(MRI, ExtReg2, Y)) {
+      return false;
+    }
+  } else {
+    if (!mi_match(ExtReg1, MRI, m_GZExt(m_Reg(X))) ||
+        !mi_match(ExtReg2, MRI, m_GZExt(m_Reg(Y))))
+      return false;
+  }
+
+  if (!isOneOrOneSplat(ShiftAmtReg, false))
+    return false;
+
+  LLT XTy = MRI.getType(X);
+  LLT YTy = MRI.getType(Y);
+
+  if (XTy != YTy)
+    return false;
+
+  LLT DstTy = MRI.getType(Dst);
+  bool NeedExt = DstTy.getSizeInBits() > XTy.getSizeInBits();
+
+  MatchInfo = [=](MachineIRBuilder &B) {
+    unsigned TargetOpc =
+        IsSigned ? TargetOpcode::G_SAVGFLOOR : TargetOpcode::G_UAVGFLOOR;
+
+    auto Avg = B.buildInstr(TargetOpc, {XTy}, {X, Y});
+
+    if (NeedExt) {
+      if (IsSigned)
+        B.buildSExt(Dst, Avg);
+      else
+        B.buildZExt(Dst, Avg);
+    } else {
+      B.buildCopy(Dst, Avg);
+    }
+  };
+
+  return true;
+}
+
+// Fold shr ( add ( ext X, ext Y, 1 ), 1 ) -> avgceil ( x, y )
+bool CombinerHelper::matchAVGCeil(MachineInstr &MI, MachineRegisterInfo &MRI,
+                                  BuildFnTy &MatchInfo) const {
+  assert((MI.getOpcode() == TargetOpcode::G_LSHR ||
+          MI.getOpcode() == TargetOpcode::G_ASHR) &&
+         "Expected G_LSHR/G_ASHR");
+
+  Register Dst = MI.getOperand(0).getReg();
+  Register Src = MI.getOperand(1).getReg();
+  Register ShiftAmtReg = MI.getOperand(2).getReg();
+
+  Register X, Y;
+  bool IsSigned = (MI.getOpcode() == TargetOpcode::G_ASHR);
+
+  // check for ((A + B) + 1) or ((A + 1) + B)
+  Register SumLHS, SumRHS;
+  if (!mi_match(Src, MRI, m_GAdd(m_Reg(SumLHS), m_Reg(SumRHS))))
+    return false;
+
+  Register InnerAddReg;
+  if (isOneOrOneSplat(SumLHS, false)) {
+    InnerAddReg = SumRHS;
+  } else if (isOneOrOneSplat(SumRHS, false)) {
+    InnerAddReg = SumLHS;
+  } else {
+    return false;
+  }
+
+  Register ExtReg1, ExtReg2;
+  if (!mi_match(InnerAddReg, MRI, m_GAdd(m_Reg(ExtReg1), m_Reg(ExtReg2))))
+    return false;
+
+  if (IsSigned) {
+    if (!matchAnySExt(MRI, ExtReg1, X) || !matchAnySExt(MRI, ExtReg2, Y)) {
+      return false;
+    }
+  } else {
+    if (!mi_match(ExtReg1, MRI, m_GZExt(m_Reg(X))) ||
+        !mi_match(ExtReg2, MRI, m_GZExt(m_Reg(Y))))
+      return false;
+  }
+
+  if (!isOneOrOneSplat(ShiftAmtReg, false))
+    return false;
+
+  LLT XTy = MRI.getType(X);
+  LLT YTy = MRI.getType(Y);
+
+  if (XTy != YTy)
+    return false;
+
+  LLT DstTy = MRI.getType(Dst);
+  bool NeedExt = DstTy.getSizeInBits() > XTy.getSizeInBits();
+
+  MatchInfo = [=](MachineIRBuilder &B) {
+    unsigned TargetOpc =
+        IsSigned ? TargetOpcode::G_SAVGCEIL : TargetOpcode::G_UAVGCEIL;
+
+    auto Avg = B.buildInstr(TargetOpc, {XTy}, {X, Y});
+
+    if (NeedExt) {
+      if (IsSigned)
+        B.buildSExt(Dst, Avg);
+      else
+        B.buildZExt(Dst, Avg);
+    } else {
+      B.buildCopy(Dst, Avg);
+    }
+  };
+
+  return true;
+}
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/combine-avg.mir b/llvm/test/CodeGen/AArch64/GlobalISel/combine-avg.mir
new file mode 100644
index 0000000000000..65bd0901087df
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/combine-avg.mir
@@ -0,0 +1,319 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=aarch64 -run-pass=aarch64-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+...
+---
+name:            test_uavgfloor_i16
+alignment:       4
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $w0, $w1
+    ; CHECK-LABEL: name: test_uavgfloor_i16
+    ; CHECK: liveins: $w0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %w0_copy:_(s32) = COPY $w0
+    ; CHECK-NEXT: %w1_copy:_(s32) = COPY $w1
+    ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC %w0_copy(s32)
+    ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(s16) = G_TRUNC %w1_copy(s32)
+    ; CHECK-NEXT: [[UAVGFLOOR:%[0-9]+]]:_(s16) = G_UAVGFLOOR [[TRUNC]], [[TRUNC1]]
+    ; CHECK-NEXT: %ret_ext:_(s32) = G_ANYEXT [[UAVGFLOOR]](s16)
+    ; CHECK-NEXT: $w0 = COPY %ret_ext(s32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %w0_copy:_(s32) = COPY $w0
+    %w1_copy:_(s32) = COPY $w1
+
+    %0:_(s16) = G_TRUNC %w0_copy(s32)
+    %1:_(s16) = G_TRUNC %w1_copy(s32)
+
+    %2:_(s32) = G_ZEXT %0(s16)
+    %3:_(s32) = G_ZEXT %1(s16)
+
+    %4:_(s32) = G_ADD %2, %3
+
+    %5:_(s32) = G_CONSTANT i32 1
+    %6:_(s32) = G_LSHR %4, %5(s32)
+
+    %7:_(s16) = G_TRUNC %6(s32)
+
+    %ret_ext:_(s32) = G_ANYEXT %7(s16)
+    $w0 = COPY %ret_ext(s32)
+    RET_ReallyLR implicit $w0
+
+...
+---
+name:            test_uavgfloor_v8s16
+alignment:       4
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $q0, $q1
+    ; CHECK-LABEL: name: test_uavgfloor_v8s16
+    ; CHECK: liveins: $q0, $q1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(<8 x s16>) = COPY $q0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(<8 x s16>) = COPY $q1
+    ; CHECK-NEXT: [[UAVGFLOOR:%[0-9]+]]:_(<8 x s16>) = G_UAVGFLOOR [[COPY]], [[COPY1]]
+    ; CHECK-NEXT: $q0 = COPY [[UAVGFLOOR]](<8 x s16>)
+    ; CHECK-NEXT: RET_ReallyLR implicit $q0
+    %0:_(<8 x s16>) = COPY $q0
+    %1:_(<8 x s16>) = COPY $q1
+
+    %ext0:_(<8 x s32>) = G_ZEXT %0
+    %ext1:_(<8 x s32>) = G_ZEXT %1
+
+    %sum:_(<8 x s32>) = G_ADD %ext0, %ext1
+
+    %shift_cst:_(s32) = G_CONSTANT i32 1
+    %v_shift_cst:_(<8 x s32>) = G_BUILD_VECTOR %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst
+    %shift:_(<8 x s32>) = G_LSHR %sum, %v_shift_cst
+
+    %res:_(<8 x s16>) = G_TRUNC %shift
+    $q0 = COPY %res
+    RET_ReallyLR implicit $q0
+
+...
+---
+name:            test_savgfloor_i16
+alignment:       4
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $w0, $w1
+    ; CHECK-LABEL: name: test_savgfloor_i16
+    ; CHECK: liveins: $w0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %w0_copy:_(s32) = COPY $w0
+    ; CHECK-NEXT: %w1_copy:_(s32) = COPY $w1
+    ; CHECK-NEXT: [[SAVGFLOOR:%[0-9]+]]:_(s32) = G_SAVGFLOOR %w0_copy, %w1_copy
+    ; CHECK-NEXT: $w0 = COPY [[SAVGFLOOR]](s32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %w0_copy:_(s32) = COPY $w0
+    %w1_copy:_(s32) = COPY $w1
+
+    %0:_(s16) = G_TRUNC %w0_copy(s32)
+    %1:_(s16) = G_TRUNC %w1_copy(s32)
+
+    %2:_(s32) = G_SEXT %0(s16)
+    %3:_(s32) = G_SEXT %1(s16)
+
+    %4:_(s32) = G_ADD %2, %3
+
+    %5:_(s32) = G_CONSTANT i32 1
+    %6:_(s32) = G_ASHR %4, %5(s32)
+
+    %7:_(s16) = G_TRUNC %6(s32)
+
+    %ret_ext:_(s32) = G_ANYEXT %7(s16)
+    $w0 = COPY %ret_ext(s32)
+    RET_ReallyLR implicit $w0
+
+...
+---
+name:            test_savgfloor_v8s16
+alignment:       4
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $q0, $q1
+    ; CHECK-LABEL: name: test_savgfloor_v8s16
+    ; CHECK: liveins: $q0, $q1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(<8 x s16>) = COPY $q0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(<8 x s16>) = COPY $q1
+    ; CHECK-NEXT: [[SAVGFLOOR:%[0-9]+]]:_(<8 x s16>) = G_SAVGFLOOR [[COPY]], [[COPY1]]
+    ; CHECK-NEXT: $q0 = COPY [[SAVGFLOOR]](<8 x s16>)
+    ; CHECK-NEXT: RET_ReallyLR implicit $q0
+    %0:_(<8 x s16>) = COPY $q0
+    %1:_(<8 x s16>) = COPY $q1
+
+    %ext0:_(<8 x s32>) = G_SEXT %0
+    %ext1:_(<8 x s32>) = G_SEXT %1
+
+    %sum:_(<8 x s32>) = G_ADD %ext0, %ext1
+
+    %shift_cst:_(s32) = G_CONSTANT i32 1
+    %v_shift_cst:_(<8 x s32>) = G_BUILD_VECTOR %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst
+    %shift:_(<8 x s32>) = G_ASHR %sum, %v_shift_cst
+
+    %res:_(<8 x s16>) = G_TRUNC %shift
+    $q0 = COPY %res
+    RET_ReallyLR implicit $q0
+
+...
+---
+name:            test_negative_floor
+alignment:       4
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $w0, $w1
+    ; CHECK-LABEL: name: test_negative_floor
+    ; CHECK: liveins: $w0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %w0_copy:_(s32) = COPY $w0
+    ; CHECK-NEXT: %w1_copy:_(s32) = COPY $w1
+    ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC %w0_copy(s32)
+    ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(s16) = G_TRUNC %w1_copy(s32)
+    ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[TRUNC]](s16)
+    ; CHECK-NEXT: [[ZEXT1:%[0-9]+]]:_(s32) = G_ZEXT [[TRUNC1]](s16)
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[ZEXT]], [[ZEXT1]]
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 2
+    ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[ADD]], [[C]](s32)
+    ; CHECK-NEXT: $w0 = COPY [[LSHR]](s32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %w0_copy:_(s32) = COPY $w0
+    %w1_copy:_(s32) = COPY $w1
+
+    %0:_(s16) = G_TRUNC %w0_copy(s32)
+    %1:_(s16) = G_TRUNC %w1_copy(s32)
+
+    %2:_(s32) = G_ZEXT %0(s16)
+    %3:_(s32) = G_ZEXT %1(s16)
+
+    %4:_(s32) = G_ADD %2, %3
+
+    %5:_(s32) = G_CONSTANT i32 2
+    %6:_(s32) = G_LSHR %4, %5(s32)
+
+    %7:_(s16) = G_TRUNC %6(s32)
+
+    %ret_ext:_(s32) = G_ANYEXT %7(s16)
+    $w0 = COPY %ret_ext(s32)
+    RET_ReallyLR implicit $w0
+
+...
+---
+name:            test_uavgceil_s32
+alignment:       4
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $w0, $w1
+    ; CHECK-LABEL: name: test_uavgceil_s32
+    ; CHECK: liveins: $w0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %w0_copy:_(s32) = COPY $w0
+    ; CHECK-NEXT: %w1_copy:_(s32) = COPY $w1
+    ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(s16) = G_TRUNC %w0_copy(s32)
+    ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(s16) = G_TRUNC %w1_copy(s32)
+    ; CHECK-NEXT: [[UAVGCEIL:%[0-9]+]]:_(s16) = G_UAVGCEIL [[TRUNC]], [[TRUNC1]]
+    ; CHECK-NEXT: %res:_(s32) = G_ZEXT [[UAVGCEIL]](s16)
+    ; CHECK-NEXT: $w0 = COPY %res(s32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %w0_copy:_(s32) = COPY $w0
+    %w1_copy:_(s32) = COPY $w1
+    %0:_(s16) = G_TRUNC %w0_copy(s32)
+    %1:_(s16) = G_TRUNC %w1_copy(s32)
+    %2:_(s32) = G_ZEXT %0(s16)
+    %3:_(s32) = G_ZEXT %1(s16)
+
+    %sum:_(s32) = G_ADD %2, %3
+    %cst1:_(s32) = G_CONSTANT i32 1
+    %add_one:_(s32) = G_ADD %sum, %cst1
+    %cst_shift:_(s32) = G_CONSTANT i32 1
+    %res:_(s32) = G_LSHR %add_one, %cst_shift
+
+    $w0 = COPY %res(s32)
+    RET_ReallyLR implicit $w0
+
+...
+---
+name:            test_savgceil_s32_associative
+alignment:       4
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $w0, $w1
+    ; CHECK-LABEL: name: test_savgceil_s32_associative
+    ; CHECK: liveins: $w0, $w1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: %w0_copy:_(s32) = COPY $w0
+    ; CHECK-NEXT: %w1_copy:_(s32) = COPY $w1
+    ; CHECK-NEXT: [[SAVGCEIL:%[0-9]+]]:_(s32) = G_SAVGCEIL %w0_copy, %w1_copy
+    ; CHECK-NEXT: $w0 = COPY [[SAVGCEIL]](s32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %w0_copy:_(s32) = COPY $w0
+    %w1_copy:_(s32) = COPY $w1
+    %0:_(s16) = G_TRUNC %w0_copy(s32)
+    %1:_(s16) = G_TRUNC %w1_copy(s32)
+    %2:_(s32) = G_SEXT %0(s16)
+    %3:_(s32) = G_SEXT %1(s16)
+
+    %cst1:_(s32) = G_CONSTANT i32 1
+    %add_one:_(s32) = G_ADD %2, %cst1
+    %sum:_(s32) = G_ADD %add_one, %3
+    %cst_shift:_(s32) = G_CONSTANT i32 1
+    %res:_(s32) = G_ASHR %sum, %cst_shift
+
+    $w0 = COPY %res(s32)
+    RET_ReallyLR implicit $w0
+
+...
+---
+name:            test_uavgceil_v8s16
+alignment:       4
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $q0, $q1
+    ; CHECK-LABEL: name: test_uavgceil_v8s16
+    ; CHECK: liveins: $q0, $q1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(<8 x s16>) = COPY $q0
+    ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(<8 x s16>) = COPY $q1
+    ; CHECK-NEXT: [[UAVGCEIL:%[0-9]+]]:_(<8 x s16>) = G_UAVGCEIL [[COPY]], [[COPY1]]
+    ; CHECK-NEXT: $q0 = COPY [[UAVGCEIL]](<8 x s16>)
+    ; CHECK-NEXT: RET_ReallyLR implicit $q0
+    %0:_(<8 x s16>) = COPY $q0
+    %1:_(<8 x s16>) = COPY $q1
+    %ext0:_(<8 x s32>) = G_ZEXT %0
+    %ext1:_(<8 x s32>) = G_ZEXT %1
+    %sum:_(<8 x s32>) = G_ADD %ext0, %ext1
+    %cst1:_(s32) = G_CONSTANT i32 1
+    %v_cst1:_(<8 x s32>) = G_BUILD_VECTOR %cst1, %cst1, %cst1, %cst1, %cst1, %cst1, %cst1, %cst1
+    %add_one:_(<8 x s32>) = G_ADD %sum, %v_cst1
+    %shift_cst:_(s32) = G_CONSTANT i32 1
+    %v_shift_cst:_(<8 x s32>) = G_BUILD_VECTOR %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst, %shift_cst
+    %shift:_(<8 x s32>) = G_LSHR %add_one, %v_shift_cst
+    %res:_(<8 x s16>) = G_TRUNC %shift
+    $q0 = COPY %res
+    RET_Really...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/192866


More information about the llvm-commits mailing list