Skip to content

[RISCV][GISEL] legalize, regbankselect, and instruction-select for G_… #73061

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 30, 2023

Conversation

michaelmaitland
Copy link
Contributor

…[UN]MERGE_VALUES

When MERGE or UNMERGE s64 on a subtarget that is non-64bit, it must have the D extension and use FPR in order to be legal.

All other instances of MERGE and UNMERGE that can be made legal should be narrowed, widend, or replaced by the combiner.

@llvmbot
Copy link
Member

llvmbot commented Nov 22, 2023

@llvm/pr-subscribers-backend-risc-v

@llvm/pr-subscribers-llvm-globalisel

Author: Michael Maitland (michaelmaitland)

Changes

…[UN]MERGE_VALUES

When MERGE or UNMERGE s64 on a subtarget that is non-64bit, it must have the D extension and use FPR in order to be legal.

All other instances of MERGE and UNMERGE that can be made legal should be narrowed, widend, or replaced by the combiner.


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

9 Files Affected:

  • (modified) llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp (+59)
  • (modified) llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp (+4)
  • (modified) llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp (+28)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/merge-unmerge-rv32.mir (+44)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/bitcast-between-f64-and-i64.ll (+31)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/legalizer/merge-unmerge-d.mir (+38)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/legalizer/rv32/merge-unmerge.mir (+131)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/legalizer/rv64/merge-unmerge.mir (+119)
  • (added) llvm/test/CodeGen/RISCV/GlobalISel/regbankselect/merge-unmerge-rv32.mir (+40)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
index 3c72269d1e00c2f..1ad26bf05784e83 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVInstructionSelector.cpp
@@ -48,6 +48,9 @@ class RISCVInstructionSelector : public InstructionSelector {
   const TargetRegisterClass *
   getRegClassForTypeOnBank(LLT Ty, const RegisterBank &RB) const;
 
+  bool isRegInGprb(Register Reg, MachineRegisterInfo &MRI) const;
+  bool isRegInFprb(Register Reg, MachineRegisterInfo &MRI) const;
+
   // tblgen-erated 'select' implementation, used as the initial selector for
   // the patterns that don't require complex C++.
   bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const;
@@ -72,6 +75,10 @@ class RISCVInstructionSelector : public InstructionSelector {
                     MachineRegisterInfo &MRI) const;
   bool selectFPCompare(MachineInstr &MI, MachineIRBuilder &MIB,
                        MachineRegisterInfo &MRI) const;
+  bool selectMergeValues(MachineInstr &MI, MachineIRBuilder &MIB,
+                           MachineRegisterInfo &MRI) const;
+  bool selectUnmergeValues(MachineInstr &MI, MachineIRBuilder &MIB,
+                           MachineRegisterInfo &MRI) const;
 
   ComplexRendererFns selectShiftMask(MachineOperand &Root) const;
   ComplexRendererFns selectAddrRegImm(MachineOperand &Root) const;
@@ -564,11 +571,53 @@ bool RISCVInstructionSelector::select(MachineInstr &MI) {
     return selectSelect(MI, MIB, MRI);
   case TargetOpcode::G_FCMP:
     return selectFPCompare(MI, MIB, MRI);
+  case TargetOpcode::G_MERGE_VALUES:
+    return selectMergeValues(MI, MIB, MRI);
+  case TargetOpcode::G_UNMERGE_VALUES:
+    return selectUnmergeValues(MI, MIB, MRI);
   default:
     return false;
   }
 }
 
+bool RISCVInstructionSelector::selectMergeValues(
+    MachineInstr &MI, MachineIRBuilder &MIB, MachineRegisterInfo &MRI) const {
+  assert(MI.getOpcode() == TargetOpcode::G_MERGE_VALUES);
+
+  // Build a F64 Pair from operands
+  if (MI.getNumOperands() != 3)
+    return false;
+  Register Dst = MI.getOperand(0).getReg();
+  Register Lo = MI.getOperand(1).getReg();
+  Register Hi = MI.getOperand(2).getReg();
+  if (!isRegInFprb(Dst, MRI) || !(isRegInGprb(Lo, MRI) && isRegInGprb(Hi, MRI)))
+    return false;
+  MachineInstr *Result =
+      MIB.buildInstr(RISCV::BuildPairF64Pseudo, {Dst}, {Lo, Hi});
+
+  MI.eraseFromParent();
+  return constrainSelectedInstRegOperands(*Result, TII, TRI, RBI);
+}
+
+bool RISCVInstructionSelector::selectUnmergeValues(
+    MachineInstr &MI, MachineIRBuilder &MIB, MachineRegisterInfo &MRI) const {
+  assert(MI.getOpcode() == TargetOpcode::G_UNMERGE_VALUES);
+
+  // Split F64 Src into two s32 parts
+  if (MI.getNumOperands() != 3)
+    return false;
+  Register Src = MI.getOperand(2).getReg();
+  Register Lo = MI.getOperand(0).getReg();
+  Register Hi = MI.getOperand(1).getReg();
+  if (!isRegInFprb(Src, MRI) ||
+      !(isRegInGprb(Lo, MRI) && isRegInGprb(Hi, MRI)))
+    return false;
+  MachineInstr *Result = MIB.buildInstr(RISCV::SplitF64Pseudo, {Lo, Hi}, {Src});
+
+  MI.eraseFromParent();
+  return constrainSelectedInstRegOperands(*Result, TII, TRI, RBI);
+}
+
 bool RISCVInstructionSelector::replacePtrWithInt(MachineOperand &Op,
                                                  MachineIRBuilder &MIB,
                                                  MachineRegisterInfo &MRI) {
@@ -652,6 +701,16 @@ const TargetRegisterClass *RISCVInstructionSelector::getRegClassForTypeOnBank(
   return nullptr;
 }
 
+bool RISCVInstructionSelector::isRegInGprb(Register Reg,
+                                          MachineRegisterInfo &MRI) const {
+  return RBI.getRegBank(Reg, MRI, TRI)->getID() == RISCV::GPRBRegBankID;
+}
+
+bool RISCVInstructionSelector::isRegInFprb(Register Reg,
+                                          MachineRegisterInfo &MRI) const {
+  return RBI.getRegBank(Reg, MRI, TRI)->getID() == RISCV::FPRBRegBankID;
+}
+
 bool RISCVInstructionSelector::selectCopy(MachineInstr &MI,
                                           MachineRegisterInfo &MRI) const {
   Register DstReg = MI.getOperand(0).getReg();
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index 9eb5812e024b915..6155e94b58e7343 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -86,6 +86,10 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST) {
     unsigned BigTyIdx = Op == G_MERGE_VALUES ? 0 : 1;
     unsigned LitTyIdx = Op == G_MERGE_VALUES ? 1 : 0;
     getActionDefinitionsBuilder(Op)
+        .legalIf([=, &ST](const LegalityQuery &Query) -> bool {
+          return ST.hasStdExtD() && typeIs(LitTyIdx, s32)(Query) &&
+                 typeIs(BigTyIdx, s64)(Query);
+        })
         .widenScalarToNextPow2(LitTyIdx, XLen)
         .widenScalarToNextPow2(BigTyIdx, XLen)
         .clampScalar(LitTyIdx, sXLen, sXLen)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
index 6d77e2b7edd9010..cf5f67ec80fd691 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVRegisterBankInfo.cpp
@@ -353,6 +353,34 @@ RISCVRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
     OpdsMapping[2] = OpdsMapping[3] = getFPValueMapping(Size);
     break;
   }
+  case TargetOpcode::G_MERGE_VALUES: {
+    // Use FPR64 for s64 merge on rv32.
+    assert(MI.getNumOperands() == 3 && "Unsupported G_MERGE_VALUES");
+    LLT Ty = MRI.getType(MI.getOperand(0).getReg());
+    if (GPRSize == 32 && Ty.getSizeInBits() == 64) {
+      assert(MF.getSubtarget<RISCVSubtarget>().hasStdExtD());
+      // FIXME: OpdsMapping[0, 1] should probably visit their uses to determine
+      // if GPRValueMapping or FPRValueMapping
+      OpdsMapping[0] = getFPValueMapping(Ty.getSizeInBits());
+      OpdsMapping[1] = GPRValueMapping;
+      OpdsMapping[2] = GPRValueMapping;
+    }
+    break;
+  }
+  case TargetOpcode::G_UNMERGE_VALUES: {
+    // Use FPR64 for s64 unmerge on rv32.
+    assert(MI.getNumOperands() == 3 && "Unsupported G_UNMERGE_VALUES");
+    LLT Ty = MRI.getType(MI.getOperand(2).getReg());
+    if (GPRSize == 32 && Ty.getSizeInBits() == 64) {
+      assert(MF.getSubtarget<RISCVSubtarget>().hasStdExtD());
+      // FIXME: OpdsMapping[0, 1] should probably visit their uses to determine
+      // if GPRValueMapping or FPRValueMapping
+      OpdsMapping[0] = GPRValueMapping;
+      OpdsMapping[1] = GPRValueMapping;
+      OpdsMapping[2] = getFPValueMapping(Ty.getSizeInBits());
+    }
+    break;
+  }
   default:
     // By default map all scalars to GPR.
     for (unsigned Idx = 0; Idx < NumOperands; ++Idx) {
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/merge-unmerge-rv32.mir b/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/merge-unmerge-rv32.mir
new file mode 100644
index 000000000000000..11d3bf6a4cd0e6e
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/instruction-select/merge-unmerge-rv32.mir
@@ -0,0 +1,44 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv32 -mattr=+d -run-pass=instruction-select \
+# RUN: -simplify-mir -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            merge_i64
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: merge_i64
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:gpr = COPY $x10
+    ; CHECK-NEXT: [[BuildPairF64Pseudo:%[0-9]+]]:fpr64 = BuildPairF64Pseudo [[COPY]], [[COPY]]
+    ; CHECK-NEXT: $f10_d = COPY [[BuildPairF64Pseudo]]
+    ; CHECK-NEXT: PseudoRET implicit $f10_d
+    %0:gprb(s32) = COPY $x10
+    %1:fprb(s64) = G_MERGE_VALUES %0(s32), %0(s32)
+    $f10_d = COPY %1(s64)
+    PseudoRET implicit $f10_d
+...
+---
+name:            unmerge_i32
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body:             |
+  bb.0.entry:
+    liveins: $f10_d
+    ; CHECK-LABEL: name: unmerge_i32
+    ; CHECK: liveins: $f10_d
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:fpr64 = COPY $f10_d
+    ; CHECK-NEXT: [[SplitF64Pseudo:%[0-9]+]]:gpr, [[SplitF64Pseudo1:%[0-9]+]]:gpr = SplitF64Pseudo [[COPY]]
+    ; CHECK-NEXT: $x10 = COPY [[SplitF64Pseudo]]
+    ; CHECK-NEXT: PseudoRET implicit $x10
+    %0:fprb(s64) = COPY $f10_d
+    %1:gprb(s32), %2:gprb(s32) = G_UNMERGE_VALUES %0(s64)
+    $x10 = COPY %1(s32)
+    PseudoRET implicit $x10
+...
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/bitcast-between-f64-and-i64.ll b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/bitcast-between-f64-and-i64.ll
new file mode 100644
index 000000000000000..5461f7366c523a4
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/irtranslator/bitcast-between-f64-and-i64.ll
@@ -0,0 +1,31 @@
+; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4
+; RUN: llc -mtriple=riscv32 -global-isel -mattr=+d -stop-after=legalizer -verify-machineinstrs < %s \
+; RUN:   | FileCheck -check-prefixes=CHECK %s
+
+define i64 @double_to_i64(double %a) {
+  ; CHECK-LABEL: name: double_to_i64
+  ; CHECK: bb.1 (%ir-block.0):
+  ; CHECK-NEXT:   liveins: $f10_d
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:_(s64) = COPY $f10_d
+  ; CHECK-NEXT:   [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[COPY]](s64)
+  ; CHECK-NEXT:   $x10 = COPY [[UV]](s32)
+  ; CHECK-NEXT:   $x11 = COPY [[UV1]](s32)
+  ; CHECK-NEXT:   PseudoRET implicit $x10, implicit $x11
+  %1 = bitcast double %a to i64
+  ret i64 %1
+}
+
+define double @i64_to_double(i64 %a) {
+  ; CHECK-LABEL: name: i64_to_double
+  ; CHECK: bb.1 (%ir-block.0):
+  ; CHECK-NEXT:   liveins: $x10, $x11
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+  ; CHECK-NEXT:   [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY1]](s32)
+  ; CHECK-NEXT:   $f10_d = COPY [[MV]](s64)
+  ; CHECK-NEXT:   PseudoRET implicit $f10_d
+  %1 = bitcast i64 %a to double
+  ret double %1
+}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/merge-unmerge-d.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/merge-unmerge-d.mir
new file mode 100644
index 000000000000000..10c775cd9ecfe1a
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/merge-unmerge-d.mir
@@ -0,0 +1,38 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv32 -mattr=+d -run-pass=legalizer %s -o - \
+# RUN:   | FileCheck %s
+
+---
+name:            merge_i64
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: merge_i64
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; CHECK-NEXT: [[MV:%[0-9]+]]:_(s64) = G_MERGE_VALUES [[COPY]](s32), [[COPY]](s32)
+    ; CHECK-NEXT: $f10_d = COPY [[MV]](s64)
+    ; CHECK-NEXT: PseudoRET implicit $f10_d
+    %0:_(s32) = COPY $x10
+    %1:_(s64) = G_MERGE_VALUES %0(s32), %0(s32)
+    $f10_d = COPY %1(s64)
+    PseudoRET implicit $f10_d
+...
+---
+name:            unmerge_i32
+body:             |
+  bb.0.entry:
+    liveins: $f10_d
+    ; CHECK-LABEL: name: unmerge_i32
+    ; CHECK: liveins: $f10_d
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $f10_d
+    ; CHECK-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[COPY]](s64)
+    ; CHECK-NEXT: $x10 = COPY [[UV]](s32)
+    ; CHECK-NEXT: PseudoRET implicit $x10
+    %0:_(s64) = COPY $f10_d
+    %1:_(s32), %2:_(s32) = G_UNMERGE_VALUES %0(s64)
+    $x10 = COPY %1(s32)
+    PseudoRET implicit $x10
+...
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/rv32/merge-unmerge.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/rv32/merge-unmerge.mir
new file mode 100644
index 000000000000000..2e4a39c468111f2
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/rv32/merge-unmerge.mir
@@ -0,0 +1,131 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv32 -run-pass=legalizer %s -o - \
+# RUN:   | FileCheck --check-prefix=RV32 %s
+
+---
+name:            merge_i32
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; RV32-LABEL: name: merge_i32
+    ; RV32: liveins: $x10
+    ; RV32-NEXT: {{  $}}
+    ; RV32-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32-NEXT: [[ASSERT_ZEXT:%[0-9]+]]:_(s32) = G_ASSERT_ZEXT [[COPY]], 16
+    ; RV32-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; RV32-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[ASSERT_ZEXT]], [[C]](s32)
+    ; RV32-NEXT: [[OR:%[0-9]+]]:_(s32) = G_OR [[ASSERT_ZEXT]], [[SHL]]
+    ; RV32-NEXT: $x10 = COPY [[OR]](s32)
+    ; RV32-NEXT: PseudoRET implicit $x10
+    %0:_(s32) = COPY $x10
+    %1:_(s32) = G_ASSERT_ZEXT %0, 16
+    %2:_(s16) = G_TRUNC %1(s32)
+    %3:_(s32) = G_MERGE_VALUES %2(s16), %2(s16)
+    $x10 = COPY %3(s32)
+    PseudoRET implicit $x10
+...
+---
+name:            merge_i64
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; RV32-LABEL: name: merge_i64
+    ; RV32: liveins: $x10
+    ; RV32-NEXT: {{  $}}
+    ; RV32-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32-NEXT: $x10 = COPY [[COPY]](s32)
+    ; RV32-NEXT: PseudoRET implicit $x10
+    %0:_(s32) = COPY $x10
+    %1:_(s64) = G_MERGE_VALUES %0(s32), %0(s32)
+    %2:_(s32) = G_TRUNC %1(s64)
+    $x10 = COPY %2(s32)
+    PseudoRET implicit $x10
+...
+---
+name:            merge_i128
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; RV32-LABEL: name: merge_i128
+    ; RV32: liveins: $x10
+    ; RV32-NEXT: {{  $}}
+    ; RV32-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32-NEXT: $x10 = COPY [[COPY]](s32)
+    ; RV32-NEXT: PseudoRET implicit $x10
+    %1:_(s32) = COPY $x10
+    %2:_(s64) = G_ZEXT %1(s32)
+    %0:_(s128) = G_MERGE_VALUES %2(s64), %2(s64)
+    %3:_(s32) = G_TRUNC %0(s128)
+    $x10 = COPY %3(s32)
+    PseudoRET implicit $x10
+...
+---
+name:            unmerge_i32
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; RV32-LABEL: name: unmerge_i32
+    ; RV32: liveins: $x10
+    ; RV32-NEXT: {{  $}}
+    ; RV32-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32-NEXT: $x10 = COPY [[COPY]](s32)
+    ; RV32-NEXT: PseudoRET implicit $x10
+    %0:_(s32) = COPY $x10
+    %1:_(s64) = G_ZEXT %0(s32)
+    %2:_(s32), %3:_(s32) = G_UNMERGE_VALUES %1(s64)
+    $x10 = COPY %2(s32)
+    PseudoRET implicit $x10
+...
+---
+name:            unmerge_i64
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; RV32-LABEL: name: unmerge_i64
+    ; RV32: liveins: $x10
+    ; RV32-NEXT: {{  $}}
+    ; RV32-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32-NEXT: $x10 = COPY [[COPY]](s32)
+    ; RV32-NEXT: PseudoRET implicit $x10
+    %0:_(s32) = COPY $x10
+    %1:_(s64) = G_ZEXT %0(s32)
+    %2:_(s32), %3:_(s32) = G_UNMERGE_VALUES %1(s64)
+    $x10 = COPY %2(s32)
+    PseudoRET implicit $x10
+...
+---
+name:            unmerge_i128
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; RV32-LABEL: name: unmerge_i128
+    ; RV32: liveins: $x10
+    ; RV32-NEXT: {{  $}}
+    ; RV32-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
+    ; RV32-NEXT: $x10 = COPY [[C]](s32)
+    ; RV32-NEXT: PseudoRET implicit $x10
+    %0:_(s32) = COPY $x10
+    %1:_(s128) = G_ZEXT %0(s32)
+    %2:_(s64), %3:_(s64) = G_UNMERGE_VALUES %1(s128)
+    %4:_(s32) = G_TRUNC %3(s64)
+    $x10 = COPY %4(s32)
+    PseudoRET implicit $x10
+...
+---
+name:            unmerge_i256
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; RV32-LABEL: name: unmerge_i256
+    ; RV32: liveins: $x10
+    ; RV32-NEXT: {{  $}}
+    ; RV32-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32-NEXT: $x10 = COPY [[COPY]](s32)
+    ; RV32-NEXT: PseudoRET implicit $x10
+    %0:_(s32) = COPY $x10
+    %1:_(s256) = G_ZEXT %0(s32)
+    %2:_(s128), %3:_(s128) = G_UNMERGE_VALUES %1(s256)
+    %4:_(s32) = G_TRUNC %2(s128)
+    $x10 = COPY %4(s32)
+    PseudoRET implicit $x10
+...
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/rv64/merge-unmerge.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/rv64/merge-unmerge.mir
new file mode 100644
index 000000000000000..3cf3a7c24863936
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/rv64/merge-unmerge.mir
@@ -0,0 +1,119 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=riscv64 -run-pass=legalizer %s -o - | FileCheck %s
+
+---
+name:            merge_i32
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: merge_i32
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; CHECK-NEXT: [[ASSERT_ZEXT:%[0-9]+]]:_(s64) = G_ASSERT_ZEXT [[COPY]], 16
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[ASSERT_ZEXT]], [[C]](s64)
+    ; CHECK-NEXT: [[OR:%[0-9]+]]:_(s64) = G_OR [[ASSERT_ZEXT]], [[SHL]]
+    ; CHECK-NEXT: $x10 = COPY [[OR]](s64)
+    ; CHECK-NEXT: PseudoRET implicit $x10
+    %0:_(s64) = COPY $x10
+    %1:_(s64) = G_ASSERT_ZEXT %0, 16
+    %2:_(s16) = G_TRUNC %1(s64)
+    %3:_(s32) = G_MERGE_VALUES %2(s16), %2(s16)
+    %4:_(s64) = G_ZEXT %3(s32)
+    $x10 = COPY %4(s64)
+    PseudoRET implicit $x10
+...
+---
+name:            merge_i64
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: merge_i64
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; CHECK-NEXT: [[ASSERT_ZEXT:%[0-9]+]]:_(s64) = G_ASSERT_ZEXT [[COPY]], 32
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 32
+    ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[ASSERT_ZEXT]], [[C]](s64)
+    ; CHECK-NEXT: [[OR:%[0-9]+]]:_(s64) = G_OR [[ASSERT_ZEXT]], [[SHL]]
+    ; CHECK-NEXT: $x10 = COPY [[OR]](s64)
+    ; CHECK-NEXT: PseudoRET implicit $x10
+    %0:_(s64) = COPY $x10
+    %1:_(s64) = G_ASSERT_ZEXT %0, 32
+    %2:_(s32) = G_TRUNC %1(s64)
+    %3:_(s64) = G_MERGE_VALUES %2(s32), %2(s32)
+    $x10 = COPY %3(s64)
+    PseudoRET implicit $x10
+...
+---
+name:            merge_i128
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: merge_i128
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; CHECK-NEXT: $x10 = COPY [[COPY]](s64)
+    ; CHECK-NEXT: PseudoRET implicit $x10
+    %0:_(s64) = COPY $x10
+    %1:_(s128) = G_MERGE_VALUES %0(s64), %0(s64)
+    %2:_(s64) = G_TRUNC %1(s128)
+    $x10 = COPY %2(s64)
+    PseudoRET implicit $x10
+...
+---
+name:            unmerge_i32
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: unmerge_i32
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 4294967295
+    ; CHECK-NEXT: [[AND:%[0-9]+]]:_(s64) = G_AND [[COPY]], [[C]]
+    ; CHECK-NEXT: $x10 = COPY [[AND]](s64)
+    ; CHECK-NEXT: PseudoRET implicit $x10
+    %0:_(s64) = COPY $x10
+    %1:_(s32), %2:_(s32) = G_UNMERGE_VALUES %0(s64)
+    %3:_(s64) = G_ZEXT %1(s32)
+    $x10 = COPY %3(s64)
+    PseudoRET implicit $x10
+...
+---
+name:            unmerge_i64
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: unmerge_i64
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; CHECK-NEXT: $x10 = COPY [[COPY]](s64)
+    ; CHECK-NEXT: PseudoRET implicit $x10
+    %0:_(s64) = COPY $x10
+    %1:_(s128) = G_ZEXT %0(s64)
+    %2:_(s64), %3:_(s64) = G_UNMERGE_VALUES %1(s128)
+    $x10 = COPY %2(s64)
+    PseudoRET implicit $x10
+...
+---
+name:            unmerge_i128
+body:             |
+  bb.0.entry:
+    liveins: $x10
+    ; CHECK-LABEL: name: unmerge_i128
+    ; CHECK: liveins: $x10
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; CHECK-NEXT: $x10 = COPY [[COPY]](s64)
+    ; CHECK-NEXT: PseudoRET implicit $x10
+    %0:_(s64) = COPY...
[truncated]

Copy link

github-actions bot commented Nov 22, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Collaborator

@topperc topperc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

…[UN]MERGE_VALUES

When MERGE or UNMERGE s64 on a subtarget that is non-64bit, it must have
the D extension and use FPR in order to be legal.

All other instances of MERGE and UNMERGE that can be made legal should be
narrowed, widend, or replaced by the combiner.
@michaelmaitland michaelmaitland merged commit dbb9043 into llvm:main Nov 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants