[llvm] 4c6ab48 - GlobalISel: Try to combine G_[SU]DIV and G_[SU]REM

Christudasan Devadasan via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 10 05:16:40 PST 2021


Author: Christudasan Devadasan
Date: 2021-03-10T18:46:07+05:30
New Revision: 4c6ab48fb1149c4f1f1d2953f5d232211b3a6bce

URL: https://github.com/llvm/llvm-project/commit/4c6ab48fb1149c4f1f1d2953f5d232211b3a6bce
DIFF: https://github.com/llvm/llvm-project/commit/4c6ab48fb1149c4f1f1d2953f5d232211b3a6bce.diff

LOG: GlobalISel: Try to combine G_[SU]DIV and G_[SU]REM

It is good to have a combined `divrem` instruction when the
`div` and `rem` are computed from identical input operands.
Some targets can lower them through a single expansion that
computes both division and remainder. It effectively reduces
the number of instructions than individually expanding them.

Reviewed By: arsenm, paquette

Differential Revision: https://reviews.llvm.org/D96013

Added: 
    llvm/test/CodeGen/AMDGPU/GlobalISel/postlegalizer-combiner-divrem.mir
    llvm/test/CodeGen/AMDGPU/GlobalISel/prelegalizer-combiner-divrem.mir

Modified: 
    llvm/docs/GlobalISel/GenericOpcode.rst
    llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
    llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
    llvm/include/llvm/Support/TargetOpcodes.def
    llvm/include/llvm/Target/GenericOpcodes.td
    llvm/include/llvm/Target/GlobalISel/Combine.td
    llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
    llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
    llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
    llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/GlobalISel/GenericOpcode.rst b/llvm/docs/GlobalISel/GenericOpcode.rst
index 41ee1f795768..15a28e5afd17 100644
--- a/llvm/docs/GlobalISel/GenericOpcode.rst
+++ b/llvm/docs/GlobalISel/GenericOpcode.rst
@@ -245,6 +245,15 @@ These each perform their respective integer arithmetic on a scalar.
 
   %2:_(s32) = G_ADD %0:_(s32), %1:_(s32)
 
+G_SDIVREM, G_UDIVREM
+^^^^^^^^^^^^^^^^^^^^
+
+Perform integer division and remainder thereby producing two results.
+
+.. code-block:: none
+
+  %div:_(s32), %rem:_(s32) = G_SDIVREM %0:_(s32), %1:_(s32)
+
 G_SADDSAT, G_UADDSAT, G_SSUBSAT, G_USUBSAT, G_SSHLSAT, G_USHLSAT
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

diff  --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
index 5856b7800881..3a65d8424109 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
@@ -156,6 +156,11 @@ class CombinerHelper {
   bool matchSextInRegOfLoad(MachineInstr &MI, std::tuple<Register, unsigned> &MatchInfo);
   bool applySextInRegOfLoad(MachineInstr &MI, std::tuple<Register, unsigned> &MatchInfo);
 
+  /// Try to combine G_[SU]DIV and G_[SU]REM into a single G_[SU]DIVREM
+  /// when their source operands are identical.
+  bool matchCombineDivRem(MachineInstr &MI, MachineInstr *&OtherMI);
+  void applyCombineDivRem(MachineInstr &MI, MachineInstr *&OtherMI);
+
   /// If a brcond's true block is not the fallthrough, make it so by inverting
   /// the condition and swapping operands.
   bool matchOptBrCondByInvertingCond(MachineInstr &MI);

diff  --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
index 16d5343d2601..d8e79d573ebc 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
@@ -373,7 +373,7 @@ class LegalizerHelper {
   LegalizeResult lowerReadWriteRegister(MachineInstr &MI);
   LegalizeResult lowerSMULH_UMULH(MachineInstr &MI);
   LegalizeResult lowerSelect(MachineInstr &MI);
-
+  LegalizeResult lowerDIVREM(MachineInstr &MI);
 };
 
 /// Helper function that creates a libcall to the given \p Name using the given

diff  --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def
index 63964b4b9503..2fc1de2d8551 100644
--- a/llvm/include/llvm/Support/TargetOpcodes.def
+++ b/llvm/include/llvm/Support/TargetOpcodes.def
@@ -248,6 +248,12 @@ HANDLE_TARGET_OPCODE(G_SREM)
 // Generic unsigned remainder instruction.
 HANDLE_TARGET_OPCODE(G_UREM)
 
+// Generic signed divrem instruction.
+HANDLE_TARGET_OPCODE(G_SDIVREM)
+
+// Generic unsigned divrem instruction.
+HANDLE_TARGET_OPCODE(G_UDIVREM)
+
 /// Generic bitwise and instruction.
 HANDLE_TARGET_OPCODE(G_AND)
 

diff  --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index a4d5ffd77166..1732c9577a35 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -285,6 +285,22 @@ def G_UREM : GenericInstruction {
   let isCommutable = false;
 }
 
+// Generic signed division and remainder.
+def G_SDIVREM : GenericInstruction {
+  let OutOperandList = (outs type0:$div, type0:$rem);
+  let InOperandList = (ins type0:$src1, type0:$src2);
+  let hasSideEffects = false;
+  let isCommutable = false;
+}
+
+// Generic unsigned division and remainder.
+def G_UDIVREM : GenericInstruction {
+  let OutOperandList = (outs type0:$div, type0:$rem);
+  let InOperandList = (ins type0:$src1, type0:$src2);
+  let hasSideEffects = false;
+  let isCommutable = false;
+}
+
 // Generic bitwise and.
 def G_AND : GenericInstruction {
   let OutOperandList = (outs type0:$dst);

diff  --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 982a2fd010ba..517b0ff87dd3 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -306,6 +306,15 @@ def urem_pow2_to_mask : GICombineRule<
   (apply [{ return Helper.applySimplifyURemByPow2(*${root}); }])
 >;
 
+// Transform d = [su]div(x, y) and r = [su]rem(x, y) - > d, r = [su]divrem(x, y)
+def div_rem_to_divrem_matchdata : GIDefMatchData<"MachineInstr *">;
+def div_rem_to_divrem : GICombineRule<
+  (defs root:$root, div_rem_to_divrem_matchdata:$matchinfo),
+  (match (wip_match_opcode G_SDIV, G_UDIV, G_SREM, G_UREM):$root,
+    [{ return Helper.matchCombineDivRem(*${root}, ${matchinfo}); }]),
+  (apply [{ Helper.applyCombineDivRem(*${root}, ${matchinfo}); }])
+>;
+
 // Fold (x op 0) - > 0
 def binop_right_to_zero: GICombineRule<
   (defs root:$root),
@@ -630,4 +639,5 @@ def all_combines : GICombineGroup<[trivial_combines, insert_vec_elt_combines,
     unmerge_merge, fabs_fabs_fold, unmerge_cst, unmerge_dead_to_trunc,
     unmerge_zext_to_zext, 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]>;
+    shift_immed_chain, shift_of_shifted_logic_chain, load_or_combine,
+    div_rem_to_divrem]>;

diff  --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index 58226f55361c..cf6512872b77 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -946,6 +946,93 @@ void CombinerHelper::applyCombineIndexedLoadStore(
   LLVM_DEBUG(dbgs() << "    Combinined to indexed operation");
 }
 
+bool CombinerHelper::matchCombineDivRem(MachineInstr &MI,
+                                        MachineInstr *&OtherMI) {
+  unsigned Opcode = MI.getOpcode();
+  bool IsDiv, IsSigned;
+
+  switch (Opcode) {
+  default:
+    llvm_unreachable("Unexpected opcode!");
+  case TargetOpcode::G_SDIV:
+  case TargetOpcode::G_UDIV: {
+    IsDiv = true;
+    IsSigned = Opcode == TargetOpcode::G_SDIV;
+    break;
+  }
+  case TargetOpcode::G_SREM:
+  case TargetOpcode::G_UREM: {
+    IsDiv = false;
+    IsSigned = Opcode == TargetOpcode::G_SREM;
+    break;
+  }
+  }
+
+  Register Src1 = MI.getOperand(1).getReg();
+  unsigned DivOpcode, RemOpcode, DivremOpcode;
+  if (IsSigned) {
+    DivOpcode = TargetOpcode::G_SDIV;
+    RemOpcode = TargetOpcode::G_SREM;
+    DivremOpcode = TargetOpcode::G_SDIVREM;
+  } else {
+    DivOpcode = TargetOpcode::G_UDIV;
+    RemOpcode = TargetOpcode::G_UREM;
+    DivremOpcode = TargetOpcode::G_UDIVREM;
+  }
+
+  if (!isLegalOrBeforeLegalizer({DivremOpcode, {MRI.getType(Src1)}}))
+    return false;
+
+  // Combine:
+  //   %div:_ = G_[SU]DIV %src1:_, %src2:_
+  //   %rem:_ = G_[SU]REM %src1:_, %src2:_
+  // into:
+  //  %div:_, %rem:_ = G_[SU]DIVREM %src1:_, %src2:_
+
+  // Combine:
+  //   %rem:_ = G_[SU]REM %src1:_, %src2:_
+  //   %div:_ = G_[SU]DIV %src1:_, %src2:_
+  // into:
+  //  %div:_, %rem:_ = G_[SU]DIVREM %src1:_, %src2:_
+
+  for (auto &UseMI : MRI.use_nodbg_instructions(Src1)) {
+    if (MI.getParent() == UseMI.getParent() &&
+        ((IsDiv && UseMI.getOpcode() == RemOpcode) ||
+         (!IsDiv && UseMI.getOpcode() == DivOpcode)) &&
+        matchEqualDefs(MI.getOperand(2), UseMI.getOperand(2))) {
+      OtherMI = &UseMI;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void CombinerHelper::applyCombineDivRem(MachineInstr &MI,
+                                        MachineInstr *&OtherMI) {
+  unsigned Opcode = MI.getOpcode();
+  assert(OtherMI && "OtherMI shouldn't be empty.");
+
+  Register DestDivReg, DestRemReg;
+  if (Opcode == TargetOpcode::G_SDIV || Opcode == TargetOpcode::G_UDIV) {
+    DestDivReg = MI.getOperand(0).getReg();
+    DestRemReg = OtherMI->getOperand(0).getReg();
+  } else {
+    DestDivReg = OtherMI->getOperand(0).getReg();
+    DestRemReg = MI.getOperand(0).getReg();
+  }
+
+  bool IsSigned =
+      Opcode == TargetOpcode::G_SDIV || Opcode == TargetOpcode::G_SREM;
+  Builder.setInstrAndDebugLoc(MI);
+  Builder.buildInstr(IsSigned ? TargetOpcode::G_SDIVREM
+                              : TargetOpcode::G_UDIVREM,
+                     {DestDivReg, DestRemReg},
+                     {MI.getOperand(1).getReg(), MI.getOperand(2).getReg()});
+  MI.eraseFromParent();
+  OtherMI->eraseFromParent();
+}
+
 bool CombinerHelper::matchOptBrCondByInvertingCond(MachineInstr &MI) {
   if (MI.getOpcode() != TargetOpcode::G_BR)
     return false;

diff  --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index 5fef8537d1a2..f74ef8e7877a 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -3148,6 +3148,9 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
   }
   case G_SELECT:
     return lowerSelect(MI);
+  case G_SDIVREM:
+  case G_UDIVREM:
+    return lowerDIVREM(MI);
   }
 }
 
@@ -6341,3 +6344,19 @@ LegalizerHelper::LegalizeResult LegalizerHelper::lowerSelect(MachineInstr &MI) {
   MI.eraseFromParent();
   return Legalized;
 }
+
+LegalizerHelper::LegalizeResult LegalizerHelper::lowerDIVREM(MachineInstr &MI) {
+  // Split DIVREM into individual instructions.
+  unsigned Opcode = MI.getOpcode();
+
+  MIRBuilder.buildInstr(
+      Opcode == TargetOpcode::G_SDIVREM ? TargetOpcode::G_SDIV
+                                        : TargetOpcode::G_UDIV,
+      {MI.getOperand(0).getReg()}, {MI.getOperand(2), MI.getOperand(3)});
+  MIRBuilder.buildInstr(
+      Opcode == TargetOpcode::G_SDIVREM ? TargetOpcode::G_SREM
+                                        : TargetOpcode::G_UREM,
+      {MI.getOperand(1).getReg()}, {MI.getOperand(2), MI.getOperand(3)});
+  MI.eraseFromParent();
+  return Legalized;
+}

diff  --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
index 91a29f90c9f8..69d8cdec866b 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
@@ -45,6 +45,14 @@
 # DEBUG-NEXT: .. the first uncovered type index: 1, OK
 # DEBUG-NEXT: .. the first uncovered imm index: 0, OK
 #
+# DEBUG-NEXT: G_SDIVREM (opcode {{[0-9]+}}): 1 type index, 0 imm indices
+# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
+#
+# DEBUG-NEXT: G_UDIVREM (opcode {{[0-9]+}}): 1 type index, 0 imm indices
+# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
+#
 # DEBUG-NEXT: G_AND (opcode {{[0-9]+}}): 1 type index, 0 imm indices
 # DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
 # DEBUG-NEXT: .. the first uncovered type index: 1, OK

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/postlegalizer-combiner-divrem.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/postlegalizer-combiner-divrem.mir
new file mode 100644
index 000000000000..b886d8f4023d
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/postlegalizer-combiner-divrem.mir
@@ -0,0 +1,224 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -global-isel -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -run-pass=amdgpu-postlegalizer-combiner -verify-machineinstrs -o - %s | FileCheck %s
+
+# Post-legalizer should not generate divrem instruction.
+---
+name: test_sdiv_srem
+tracksRegLiveness: true
+legalized: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_sdiv_srem
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem:_(s32) = G_SREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_srem_sdiv
+tracksRegLiveness: true
+legalized: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_srem_sdiv
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %rem:_(s32) = G_SREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %div:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_udiv_urem
+tracksRegLiveness: true
+legalized: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_udiv_urem
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32) = G_UDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem:_(s32) = G_UREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %div:_(s32) = G_UDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_UREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_urem_udiv
+tracksRegLiveness: true
+legalized: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_urem_udiv
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %rem:_(s32) = G_UREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %div:_(s32) = G_UDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %rem:_(s32) = G_UREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %div:_(s32) = G_UDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_v2
+tracksRegLiveness: true
+legalized: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK-LABEL: name: test_sdiv_srem_v2
+    ; CHECK: liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    ; CHECK: %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(<2 x s32>) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div(<2 x s32>), %ptr1(p1) :: (store 8, align 4, addrspace 1)
+    ; CHECK: %rem:_(<2 x s32>) = G_SREM %src1, %src2
+    ; CHECK: G_STORE %rem(<2 x s32>), %ptr2(p1) :: (store 8, align 4, addrspace 1)
+    %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    %div:_(<2 x s32>) = G_SDIV %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %div:_(<2 x s32>), %ptr1:_(p1) :: (store 8, addrspace 1, align 4)
+    %rem:_(<2 x s32>) = G_SREM %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %rem:_(<2 x s32>), %ptr2:_(p1) :: (store 8, addrspace 1, align 4)
+...
+---
+name: test_udiv_urem_v2
+tracksRegLiveness: true
+legalized: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK-LABEL: name: test_udiv_urem_v2
+    ; CHECK: liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    ; CHECK: %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(<2 x s32>) = G_UDIV %src1, %src2
+    ; CHECK: G_STORE %div(<2 x s32>), %ptr1(p1) :: (store 8, align 4, addrspace 1)
+    ; CHECK: %rem:_(<2 x s32>) = G_UREM %src1, %src2
+    ; CHECK: G_STORE %rem(<2 x s32>), %ptr2(p1) :: (store 8, align 4, addrspace 1)
+    %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    %div:_(<2 x s32>) = G_UDIV %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %div:_(<2 x s32>), %ptr1:_(p1) :: (store 8, addrspace 1, align 4)
+    %rem:_(<2 x s32>) = G_UREM %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %rem:_(<2 x s32>), %ptr2:_(p1) :: (store 8, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_extra_sdiv
+tracksRegLiveness: true
+legalized: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK-LABEL: name: test_sdiv_srem_extra_sdiv
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr3:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem:_(s32) = G_SREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    ; CHECK: %div2:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div2(s32), %ptr3(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %ptr3:_(p1) = COPY $vgpr6_vgpr7
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+    %div2:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div2:_(s32), %ptr3:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_extra_srem
+tracksRegLiveness: true
+legalized: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK-LABEL: name: test_sdiv_srem_extra_srem
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr3:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem:_(s32) = G_SREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem2:_(s32) = G_SREM %src1, %src2
+    ; CHECK: G_STORE %rem2(s32), %ptr3(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %ptr3:_(p1) = COPY $vgpr6_vgpr7
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem2:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem2:_(s32), %ptr3:_(p1) :: (store 4, addrspace 1, align 4)
+...

diff  --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/prelegalizer-combiner-divrem.mir b/llvm/test/CodeGen/AMDGPU/GlobalISel/prelegalizer-combiner-divrem.mir
new file mode 100644
index 000000000000..bc595ea0f890
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/prelegalizer-combiner-divrem.mir
@@ -0,0 +1,523 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -global-isel -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -run-pass=amdgpu-prelegalizer-combiner -verify-machineinstrs -o - %s | FileCheck %s
+
+---
+name: test_sdiv_srem
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_sdiv_srem
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_v2
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK-LABEL: name: test_sdiv_srem_v2
+    ; CHECK: liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    ; CHECK: %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(<2 x s32>), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(<2 x s32>), %ptr1(p1) :: (store 8, align 4, addrspace 1)
+    ; CHECK: G_STORE %rem(<2 x s32>), %ptr2(p1) :: (store 8, align 4, addrspace 1)
+    %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    %div:_(<2 x s32>) = G_SDIV %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %div:_(<2 x s32>), %ptr1:_(p1) :: (store 8, addrspace 1, align 4)
+    %rem:_(<2 x s32>) = G_SREM %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %rem:_(<2 x s32>), %ptr2:_(p1) :: (store 8, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_v4
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1_vgpr2_vgpr3, $vgpr4_vgpr5_vgpr6_vgpr7, $vgpr8_vgpr9, $vgpr10_vgpr11
+    ; CHECK-LABEL: name: test_sdiv_srem_v4
+    ; CHECK: liveins: $vgpr0_vgpr1_vgpr2_vgpr3, $vgpr4_vgpr5_vgpr6_vgpr7, $vgpr8_vgpr9, $vgpr10_vgpr11
+    ; CHECK: %src1:_(<4 x s32>) = COPY $vgpr0_vgpr1_vgpr2_vgpr3
+    ; CHECK: %src2:_(<4 x s32>) = COPY $vgpr4_vgpr5_vgpr6_vgpr7
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr8_vgpr9
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr10_vgpr11
+    ; CHECK: %div:_(<4 x s32>), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(<4 x s32>), %ptr1(p1) :: (store 16, align 4, addrspace 1)
+    ; CHECK: G_STORE %rem(<4 x s32>), %ptr2(p1) :: (store 16, align 4, addrspace 1)
+    %src1:_(<4 x s32>) = COPY $vgpr0_vgpr1_vgpr2_vgpr3
+    %src2:_(<4 x s32>) = COPY $vgpr4_vgpr5_vgpr6_vgpr7
+    %ptr1:_(p1) = COPY $vgpr8_vgpr9
+    %ptr2:_(p1) = COPY $vgpr10_vgpr11
+    %div:_(<4 x s32>) = G_SDIV %src1:_(<4 x s32>), %src2:_(<4 x s32>)
+    G_STORE %div:_(<4 x s32>), %ptr1:_(p1) :: (store 16, addrspace 1, align 4)
+    %rem:_(<4 x s32>) = G_SREM %src1:_(<4 x s32>), %src2:_(<4 x s32>)
+    G_STORE %rem:_(<4 x s32>), %ptr2:_(p1) :: (store 16, addrspace 1, align 4)
+...
+---
+name: test_srem_sdiv
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_srem_sdiv
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: G_STORE %div(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_srem_sdiv_v2
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK-LABEL: name: test_srem_sdiv_v2
+    ; CHECK: liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    ; CHECK: %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(<2 x s32>), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %rem(<2 x s32>), %ptr1(p1) :: (store 8, align 4, addrspace 1)
+    ; CHECK: G_STORE %div(<2 x s32>), %ptr2(p1) :: (store 8, align 4, addrspace 1)
+    %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    %rem:_(<2 x s32>) = G_SREM %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %rem:_(<2 x s32>), %ptr1:_(p1) :: (store 8, addrspace 1, align 4)
+    %div:_(<2 x s32>) = G_SDIV %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %div:_(<2 x s32>), %ptr2:_(p1) :: (store 8, addrspace 1, align 4)
+...
+---
+name: test_srem_sdiv_v4
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1_vgpr2_vgpr3, $vgpr4_vgpr5_vgpr6_vgpr7, $vgpr8_vgpr9, $vgpr10_vgpr11
+    ; CHECK-LABEL: name: test_srem_sdiv_v4
+    ; CHECK: liveins: $vgpr0_vgpr1_vgpr2_vgpr3, $vgpr4_vgpr5_vgpr6_vgpr7, $vgpr8_vgpr9, $vgpr10_vgpr11
+    ; CHECK: %src1:_(<4 x s32>) = COPY $vgpr0_vgpr1_vgpr2_vgpr3
+    ; CHECK: %src2:_(<4 x s32>) = COPY $vgpr4_vgpr5_vgpr6_vgpr7
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr8_vgpr9
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr10_vgpr11
+    ; CHECK: %div:_(<4 x s32>), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %rem(<4 x s32>), %ptr1(p1) :: (store 16, align 4, addrspace 1)
+    ; CHECK: G_STORE %div(<4 x s32>), %ptr2(p1) :: (store 16, align 4, addrspace 1)
+    %src1:_(<4 x s32>) = COPY $vgpr0_vgpr1_vgpr2_vgpr3
+    %src2:_(<4 x s32>) = COPY $vgpr4_vgpr5_vgpr6_vgpr7
+    %ptr1:_(p1) = COPY $vgpr8_vgpr9
+    %ptr2:_(p1) = COPY $vgpr10_vgpr11
+    %rem:_(<4 x s32>) = G_SREM %src1:_(<4 x s32>), %src2:_(<4 x s32>)
+    G_STORE %rem:_(<4 x s32>), %ptr1:_(p1) :: (store 16, addrspace 1, align 4)
+    %div:_(<4 x s32>) = G_SDIV %src1:_(<4 x s32>), %src2:_(<4 x s32>)
+    G_STORE %div:_(<4 x s32>), %ptr2:_(p1) :: (store 16, addrspace 1, align 4)
+...
+---
+name: test_udiv_urem
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_udiv_urem
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32), %rem:_ = G_UDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %div:_(s32) = G_UDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_UREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_udiv_urem_v2
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK-LABEL: name: test_udiv_urem_v2
+    ; CHECK: liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    ; CHECK: %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(<2 x s32>), %rem:_ = G_UDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(<2 x s32>), %ptr1(p1) :: (store 8, align 4, addrspace 1)
+    ; CHECK: G_STORE %rem(<2 x s32>), %ptr2(p1) :: (store 8, align 4, addrspace 1)
+    %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    %div:_(<2 x s32>) = G_UDIV %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %div:_(<2 x s32>), %ptr1:_(p1) :: (store 8, addrspace 1, align 4)
+    %rem:_(<2 x s32>) = G_UREM %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %rem:_(<2 x s32>), %ptr2:_(p1) :: (store 8, addrspace 1, align 4)
+...
+---
+name: test_udiv_urem_v4
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1_vgpr2_vgpr3, $vgpr4_vgpr5_vgpr6_vgpr7, $vgpr8_vgpr9, $vgpr10_vgpr11
+    ; CHECK-LABEL: name: test_udiv_urem_v4
+    ; CHECK: liveins: $vgpr0_vgpr1_vgpr2_vgpr3, $vgpr4_vgpr5_vgpr6_vgpr7, $vgpr8_vgpr9, $vgpr10_vgpr11
+    ; CHECK: %src1:_(<4 x s32>) = COPY $vgpr0_vgpr1_vgpr2_vgpr3
+    ; CHECK: %src2:_(<4 x s32>) = COPY $vgpr4_vgpr5_vgpr6_vgpr7
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr8_vgpr9
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr10_vgpr11
+    ; CHECK: %div:_(<4 x s32>), %rem:_ = G_UDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(<4 x s32>), %ptr1(p1) :: (store 16, align 4, addrspace 1)
+    ; CHECK: G_STORE %rem(<4 x s32>), %ptr2(p1) :: (store 16, align 4, addrspace 1)
+    %src1:_(<4 x s32>) = COPY $vgpr0_vgpr1_vgpr2_vgpr3
+    %src2:_(<4 x s32>) = COPY $vgpr4_vgpr5_vgpr6_vgpr7
+    %ptr1:_(p1) = COPY $vgpr8_vgpr9
+    %ptr2:_(p1) = COPY $vgpr10_vgpr11
+    %div:_(<4 x s32>) = G_UDIV %src1:_(<4 x s32>), %src2:_(<4 x s32>)
+    G_STORE %div:_(<4 x s32>), %ptr1:_(p1) :: (store 16, addrspace 1, align 4)
+    %rem:_(<4 x s32>) = G_UREM %src1:_(<4 x s32>), %src2:_(<4 x s32>)
+    G_STORE %rem:_(<4 x s32>), %ptr2:_(p1) :: (store 16, addrspace 1, align 4)
+...
+---
+name: test_urem_udiv
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_urem_udiv
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32), %rem:_ = G_UDIVREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: G_STORE %div(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %rem:_(s32) = G_UREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %div:_(s32) = G_UDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_urem_udiv_v2
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK-LABEL: name: test_urem_udiv_v2
+    ; CHECK: liveins: $vgpr0_vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    ; CHECK: %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(<2 x s32>), %rem:_ = G_UDIVREM %src1, %src2
+    ; CHECK: G_STORE %rem(<2 x s32>), %ptr1(p1) :: (store 8, align 4, addrspace 1)
+    ; CHECK: G_STORE %div(<2 x s32>), %ptr2(p1) :: (store 8, align 4, addrspace 1)
+    %src1:_(<2 x s32>) = COPY $vgpr0_vgpr1
+    %src2:_(<2 x s32>) = COPY $vgpr2_vgpr3
+    %ptr1:_(p1) = COPY $vgpr4_vgpr5
+    %ptr2:_(p1) = COPY $vgpr6_vgpr7
+    %rem:_(<2 x s32>) = G_UREM %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %rem:_(<2 x s32>), %ptr1:_(p1) :: (store 8, addrspace 1, align 4)
+    %div:_(<2 x s32>) = G_UDIV %src1:_(<2 x s32>), %src2:_(<2 x s32>)
+    G_STORE %div:_(<2 x s32>), %ptr2:_(p1) :: (store 8, addrspace 1, align 4)
+...
+---
+name: test_urem_udiv_v4
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1_vgpr2_vgpr3, $vgpr4_vgpr5_vgpr6_vgpr7, $vgpr8_vgpr9, $vgpr10_vgpr11
+    ; CHECK-LABEL: name: test_urem_udiv_v4
+    ; CHECK: liveins: $vgpr0_vgpr1_vgpr2_vgpr3, $vgpr4_vgpr5_vgpr6_vgpr7, $vgpr8_vgpr9, $vgpr10_vgpr11
+    ; CHECK: %src1:_(<4 x s32>) = COPY $vgpr0_vgpr1_vgpr2_vgpr3
+    ; CHECK: %src2:_(<4 x s32>) = COPY $vgpr4_vgpr5_vgpr6_vgpr7
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr8_vgpr9
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr10_vgpr11
+    ; CHECK: %div:_(<4 x s32>), %rem:_ = G_UDIVREM %src1, %src2
+    ; CHECK: G_STORE %rem(<4 x s32>), %ptr1(p1) :: (store 16, align 4, addrspace 1)
+    ; CHECK: G_STORE %div(<4 x s32>), %ptr2(p1) :: (store 16, align 4, addrspace 1)
+    %src1:_(<4 x s32>) = COPY $vgpr0_vgpr1_vgpr2_vgpr3
+    %src2:_(<4 x s32>) = COPY $vgpr4_vgpr5_vgpr6_vgpr7
+    %ptr1:_(p1) = COPY $vgpr8_vgpr9
+    %ptr2:_(p1) = COPY $vgpr10_vgpr11
+    %rem:_(<4 x s32>) = G_UREM %src1:_(<4 x s32>), %src2:_(<4 x s32>)
+    G_STORE %rem:_(<4 x s32>), %ptr1:_(p1) :: (store 16, addrspace 1, align 4)
+    %div:_(<4 x s32>) = G_UDIV %src1:_(<4 x s32>), %src2:_(<4 x s32>)
+    G_STORE %div:_(<4 x s32>), %ptr2:_(p1) :: (store 16, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_extra_use
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_sdiv_srem_extra_use
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = G_IMPLICIT_DEF
+    ; CHECK: %ptr2:_(p1) = G_IMPLICIT_DEF
+    ; CHECK: %ptr3:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr4:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: G_STORE %src1(s32), %ptr1(p1) :: (volatile store 4 into `i32 addrspace(1)* undef`, addrspace 1)
+    ; CHECK: G_STORE %src2(s32), %ptr2(p1) :: (volatile store 4 into `i32 addrspace(1)* undef`, addrspace 1)
+    ; CHECK: %div:_(s32), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr3(p1) :: (store 4, addrspace 1)
+    ; CHECK: G_STORE %rem(s32), %ptr4(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = G_IMPLICIT_DEF
+    %ptr2:_(p1) = G_IMPLICIT_DEF
+    %ptr3:_(p1) = COPY $vgpr2_vgpr3
+    %ptr4:_(p1) = COPY $vgpr4_vgpr5
+    G_STORE %src1:_(s32), %ptr1:_(p1) :: (volatile store 4 into `i32 addrspace(1)* undef`, addrspace 1)
+    G_STORE %src2:_(s32), %ptr2:_(p1) :: (volatile store 4 into `i32 addrspace(1)* undef`, addrspace 1)
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr3:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr4:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_extra_sdiv
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; Combine the first sdiv/srem pair into sdivrem and retain the extra
+    ; sdiv instruction.
+    ; CHECK-LABEL: name: test_sdiv_srem_extra_sdiv
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr3:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(s32), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    ; CHECK: %div2:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div2(s32), %ptr3(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %ptr3:_(p1) = COPY $vgpr6_vgpr7
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+    %div2:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div2:_(s32), %ptr3:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_extra_srem
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; Combine the first sdiv/srem pair into sdivrem and retain the extra
+    ; srem instruction.
+    ; CHECK-LABEL: name: test_sdiv_srem_extra_srem
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5, $vgpr6_vgpr7
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %ptr3:_(p1) = COPY $vgpr6_vgpr7
+    ; CHECK: %div:_(s32), %rem:_ = G_SDIVREM %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem2:_(s32) = G_SREM %src1, %src2
+    ; CHECK: G_STORE %rem2(s32), %ptr3(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %ptr3:_(p1) = COPY $vgpr6_vgpr7
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem2:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem2:_(s32), %ptr3:_(p1) :: (store 4, addrspace 1, align 4)
+...
+# Some negative tests.
+---
+name: test_sdiv_srem_
diff erent_src_opnd2
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2, $vgpr3_vgpr4, $vgpr5_vgpr6
+    ; CHECK-LABEL: name: test_sdiv_srem_
diff erent_src_opnd2
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2, $vgpr3_vgpr4, $vgpr5_vgpr6
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %src3:_(s32) = COPY $vgpr2
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr3_vgpr4
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr5_vgpr6
+    ; CHECK: %div:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem:_(s32) = G_SREM %src1, %src3
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %src3:_(s32) = COPY $vgpr2
+    %ptr1:_(p1) = COPY $vgpr3_vgpr4
+    %ptr2:_(p1) = COPY $vgpr5_vgpr6
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src3:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_src_opnds_swapped
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_sdiv_srem_src_opnds_swapped
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem:_(s32) = G_SREM %src2, %src1
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src2:_(s32), %src1:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_sdiv_urem
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_sdiv_urem
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32) = G_SDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem:_(s32) = G_UREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_UREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_udiv_srem
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK-LABEL: name: test_udiv_srem
+    ; CHECK: liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    ; CHECK: %src1:_(s32) = COPY $vgpr0
+    ; CHECK: %src2:_(s32) = COPY $vgpr1
+    ; CHECK: %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    ; CHECK: %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    ; CHECK: %div:_(s32) = G_UDIV %src1, %src2
+    ; CHECK: G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+    ; CHECK: %rem:_(s32) = G_SREM %src1, %src2
+    ; CHECK: G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %div:_(s32) = G_UDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...
+---
+name: test_sdiv_srem_
diff erent_blocks
+tracksRegLiveness: true
+body: |
+  ; CHECK-LABEL: name: test_sdiv_srem_
diff erent_blocks
+  ; CHECK: bb.0:
+  ; CHECK:   successors: %bb.1(0x80000000)
+  ; CHECK:   liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+  ; CHECK:   %src1:_(s32) = COPY $vgpr0
+  ; CHECK:   %src2:_(s32) = COPY $vgpr1
+  ; CHECK:   %ptr1:_(p1) = COPY $vgpr2_vgpr3
+  ; CHECK:   %div:_(s32) = G_SDIV %src1, %src2
+  ; CHECK:   G_STORE %div(s32), %ptr1(p1) :: (store 4, addrspace 1)
+  ; CHECK:   S_BRANCH %bb.1
+  ; CHECK: bb.1:
+  ; CHECK:   liveins: $vgpr4_vgpr5
+  ; CHECK:   %ptr2:_(p1) = COPY $vgpr4_vgpr5
+  ; CHECK:   %rem:_(s32) = G_SREM %src1, %src2
+  ; CHECK:   G_STORE %rem(s32), %ptr2(p1) :: (store 4, addrspace 1)
+  bb.0:
+    liveins: $vgpr0, $vgpr1, $vgpr2_vgpr3, $vgpr4_vgpr5
+    %src1:_(s32) = COPY $vgpr0
+    %src2:_(s32) = COPY $vgpr1
+    %ptr1:_(p1) = COPY $vgpr2_vgpr3
+    %div:_(s32) = G_SDIV %src1:_(s32), %src2:_(s32)
+    G_STORE %div:_(s32), %ptr1:_(p1) :: (store 4, addrspace 1, align 4)
+    S_BRANCH %bb.1
+  bb.1:
+    liveins: $vgpr4_vgpr5
+    %ptr2:_(p1) = COPY $vgpr4_vgpr5
+    %rem:_(s32) = G_SREM %src1:_(s32), %src2:_(s32)
+    G_STORE %rem:_(s32), %ptr2:_(p1) :: (store 4, addrspace 1, align 4)
+...

diff  --git a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
index 19e7f096e587..07fc53915efc 100644
--- a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
+++ b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp
@@ -3105,6 +3105,68 @@ TEST_F(AArch64GISelMITest, LowerBSWAP) {
   EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
 }
 
+// Test lowering of G_SDIVREM into G_SDIV and G_SREM
+TEST_F(AArch64GISelMITest, LowerSDIVREM) {
+  setUp();
+  if (!TM)
+    return;
+
+  // Declare your legalization info
+  DefineLegalizerInfo(
+      A, { getActionDefinitionsBuilder(G_SDIVREM).lowerFor({s64}); });
+
+  LLT S64{LLT::scalar(64)};
+
+  // Build Instr
+  auto SDivrem =
+      B.buildInstr(TargetOpcode::G_SDIVREM, {S64, S64}, {Copies[0], Copies[1]});
+  AInfo Info(MF->getSubtarget());
+  DummyGISelObserver Observer;
+  LegalizerHelper Helper(*MF, Info, Observer, B);
+  // Perform Legalization
+  EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
+            Helper.lower(*SDivrem, 0, S64));
+
+  const auto *CheckStr = R"(
+  CHECK: [[DIV:%[0-9]+]]:_(s64) = G_SDIV %0:_, %1:_
+  CHECK: [[REM:%[0-9]+]]:_(s64) = G_SREM %0:_, %1:_
+  )";
+
+  // Check
+  EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
+}
+
+// Test lowering of G_UDIVREM into G_UDIV and G_UREM
+TEST_F(AArch64GISelMITest, LowerUDIVREM) {
+  setUp();
+  if (!TM)
+    return;
+
+  // Declare your legalization info
+  DefineLegalizerInfo(
+      A, { getActionDefinitionsBuilder(G_UDIVREM).lowerFor({s64}); });
+
+  LLT S64{LLT::scalar(64)};
+
+  // Build Instr
+  auto UDivrem =
+      B.buildInstr(TargetOpcode::G_UDIVREM, {S64, S64}, {Copies[0], Copies[1]});
+  AInfo Info(MF->getSubtarget());
+  DummyGISelObserver Observer;
+  LegalizerHelper Helper(*MF, Info, Observer, B);
+  // Perform Legalization
+  EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized,
+            Helper.lower(*UDivrem, 0, S64));
+
+  const auto *CheckStr = R"(
+  CHECK: [[DIV:%[0-9]+]]:_(s64) = G_UDIV %0:_, %1:_
+  CHECK: [[REM:%[0-9]+]]:_(s64) = G_UREM %0:_, %1:_
+  )";
+
+  // Check
+  EXPECT_TRUE(CheckMachineFunction(*MF, CheckStr)) << *MF;
+}
+
 // Test widening of G_UNMERGE_VALUES
 TEST_F(AArch64GISelMITest, WidenUnmerge) {
   setUp();


        


More information about the llvm-commits mailing list