[llvm] [GISel][RISCV] Add G_CTLS Opcode and combines, lower to cls(w) (PR #175069)

Stefan Weigl-Bosker via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 8 14:51:50 PST 2026


https://github.com/sweiglbosker updated https://github.com/llvm/llvm-project/pull/175069

>From 1fcb146fb73a2844143b936784a2b969a48b9615 Mon Sep 17 00:00:00 2001
From: Stefan Weigl-Bosker <stefan at s00.xyz>
Date: Thu, 8 Jan 2026 11:07:10 -0500
Subject: [PATCH 1/2] precommit tests

---
 llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll | 166 ++++++++++++++++++++
 llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll | 146 +++++++++++++++++
 2 files changed, 312 insertions(+)
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll

diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll b/llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll
new file mode 100644
index 0000000000000..974729423d403
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll
@@ -0,0 +1,166 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -global-isel -mattr=+experimental-p,+zbb -verify-machineinstrs \
+; RUN:   < %s | FileCheck %s
+
+define i8 @cls_i8(i8 %x) {
+; CHECK-LABEL: cls_i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sext.b a1, a0
+; CHECK-NEXT:    srai a1, a1, 7
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    zext.b a0, a0
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    addi a0, a0, -24
+; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    ret
+  %a = ashr i8 %x, 7
+  %b = xor i8 %x, %a
+  %c = call i8 @llvm.ctlz.i8(i8 %b, i1 false)
+  %d = sub i8 %c, 1
+  ret i8 %d
+}
+
+define i8 @cls_i8_2(i8 %x) {
+; CHECK-LABEL: cls_i8_2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sext.b a1, a0
+; CHECK-NEXT:    srai a1, a1, 7
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    slli a0, a0, 1
+; CHECK-NEXT:    ori a0, a0, 1
+; CHECK-NEXT:    zext.b a0, a0
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    addi a0, a0, -24
+; CHECK-NEXT:    ret
+  %a = ashr i8 %x, 7
+  %b = xor i8 %x, %a
+  %c = shl i8 %b, 1
+  %d = or i8 %c, 1
+  %e = call i8 @llvm.ctlz.i8(i8 %d, i1 true)
+  ret i8 %e
+}
+
+define i16 @cls_i16(i16 %x) {
+; CHECK-LABEL: cls_i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sext.h a1, a0
+; CHECK-NEXT:    srai a1, a1, 15
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    zext.h a0, a0
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    addi a0, a0, -16
+; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    ret
+  %a = ashr i16 %x, 15
+  %b = xor i16 %x, %a
+  %c = call i16 @llvm.ctlz.i16(i16 %b, i1 false)
+  %d = sub i16 %c, 1
+  ret i16 %d
+}
+
+define i16 @cls_i16_2(i16 %x) {
+; CHECK-LABEL: cls_i16_2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sext.h a1, a0
+; CHECK-NEXT:    srai a1, a1, 15
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    slli a0, a0, 1
+; CHECK-NEXT:    ori a0, a0, 1
+; CHECK-NEXT:    zext.h a0, a0
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    addi a0, a0, -16
+; CHECK-NEXT:    ret
+  %a = ashr i16 %x, 15
+  %b = xor i16 %x, %a
+  %c = shl i16 %b, 1
+  %d = or i16 %c, 1
+  %e = call i16 @llvm.ctlz.i16(i16 %d, i1 true)
+  ret i16 %e
+}
+
+define i32 @cls_i32(i32 %x) {
+; CHECK-LABEL: cls_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    srai a1, a0, 31
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    ret
+  %a = ashr i32 %x, 31
+  %b = xor i32 %x, %a
+  %c = call i32 @llvm.ctlz.i32(i32 %b, i1 false)
+  %d = sub i32 %c, 1
+  ret i32 %d
+}
+
+define i32 @cls_i32_2(i32 %x) {
+; CHECK-LABEL: cls_i32_2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    srai a1, a0, 31
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    slli a0, a0, 1
+; CHECK-NEXT:    ori a0, a0, 1
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    ret
+  %a = ashr i32 %x, 31
+  %b = xor i32 %x, %a
+  %c = shl i32 %b, 1
+  %d = or i32 %c, 1
+  %e = call i32 @llvm.ctlz.i32(i32 %d, i1 true)
+  ret i32 %e
+}
+
+define i64 @cls_i64(i64 %x) {
+; CHECK-LABEL: cls_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    srai a2, a1, 31
+; CHECK-NEXT:    xor a1, a1, a2
+; CHECK-NEXT:    beqz a1, .LBB6_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    clz a0, a1
+; CHECK-NEXT:    j .LBB6_3
+; CHECK-NEXT:  .LBB6_2:
+; CHECK-NEXT:    xor a0, a0, a2
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    addi a0, a0, 32
+; CHECK-NEXT:  .LBB6_3:
+; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    sltiu a1, a0, -1
+; CHECK-NEXT:    li a2, -1
+; CHECK-NEXT:    add a1, a2, a1
+; CHECK-NEXT:    ret
+  %a = ashr i64 %x, 63
+  %b = xor i64 %x, %a
+  %c = call i64 @llvm.ctlz.i64(i64 %b, i1 false)
+  %d = sub i64 %c, 1
+  ret i64 %d
+}
+
+define i64 @cls_i64_2(i64 %x) {
+; CHECK-LABEL: cls_i64_2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    srai a2, a1, 31
+; CHECK-NEXT:    xor a0, a0, a2
+; CHECK-NEXT:    xor a1, a1, a2
+; CHECK-NEXT:    slli a1, a1, 1
+; CHECK-NEXT:    srli a2, a0, 31
+; CHECK-NEXT:    or a1, a1, a2
+; CHECK-NEXT:    beqz a1, .LBB7_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    clz a0, a1
+; CHECK-NEXT:    li a1, 0
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  .LBB7_2:
+; CHECK-NEXT:    slli a0, a0, 1
+; CHECK-NEXT:    ori a0, a0, 1
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    addi a0, a0, 32
+; CHECK-NEXT:    li a1, 0
+; CHECK-NEXT:    ret
+  %a = ashr i64 %x, 63
+  %b = xor i64 %x, %a
+  %c = shl i64 %b, 1
+  %d = or i64 %c, 1
+  %e = call i64 @llvm.ctlz.i64(i64 %d, i1 true)
+  ret i64 %e
+}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll b/llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll
new file mode 100644
index 0000000000000..673e91dad7126
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll
@@ -0,0 +1,146 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 -global-isel -mattr=+experimental-p,+zbb -verify-machineinstrs < %s | FileCheck %s
+
+define i8 @cls_i8(i8 %x) {
+; CHECK-LABEL: cls_i8:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sext.b a1, a0
+; CHECK-NEXT:    srai a1, a1, 7
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    zext.b a0, a0
+; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    li a1, 24
+; CHECK-NEXT:    subw a0, a0, a1
+; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    ret
+  %a = ashr i8 %x, 7
+  %b = xor i8 %x, %a
+  %c = call i8 @llvm.ctlz.i8(i8 %b, i1 false)
+  %d = sub i8 %c, 1
+  ret i8 %d
+}
+
+define i8 @cls_i8_2(i8 %x) {
+; CHECK-LABEL: cls_i8_2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sext.b a1, a0
+; CHECK-NEXT:    srai a1, a1, 7
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    slli a0, a0, 1
+; CHECK-NEXT:    ori a0, a0, 1
+; CHECK-NEXT:    zext.b a0, a0
+; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    li a1, 24
+; CHECK-NEXT:    subw a0, a0, a1
+; CHECK-NEXT:    ret
+  %a = ashr i8 %x, 7
+  %b = xor i8 %x, %a
+  %c = shl i8 %b, 1
+  %d = or i8 %c, 1
+  %e = call i8 @llvm.ctlz.i8(i8 %d, i1 true)
+  ret i8 %e
+}
+
+define i16 @cls_i16(i16 %x) {
+; CHECK-LABEL: cls_i16:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sext.h a1, a0
+; CHECK-NEXT:    srai a1, a1, 15
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    zext.h a0, a0
+; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    li a1, 16
+; CHECK-NEXT:    subw a0, a0, a1
+; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    ret
+  %a = ashr i16 %x, 15
+  %b = xor i16 %x, %a
+  %c = call i16 @llvm.ctlz.i16(i16 %b, i1 false)
+  %d = sub i16 %c, 1
+  ret i16 %d
+}
+
+define i16 @cls_i16_2(i16 %x) {
+; CHECK-LABEL: cls_i16_2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sext.h a1, a0
+; CHECK-NEXT:    srai a1, a1, 15
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    slli a0, a0, 1
+; CHECK-NEXT:    ori a0, a0, 1
+; CHECK-NEXT:    zext.h a0, a0
+; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    li a1, 16
+; CHECK-NEXT:    subw a0, a0, a1
+; CHECK-NEXT:    ret
+  %a = ashr i16 %x, 15
+  %b = xor i16 %x, %a
+  %c = shl i16 %b, 1
+  %d = or i16 %c, 1
+  %e = call i16 @llvm.ctlz.i16(i16 %d, i1 true)
+  ret i16 %e
+}
+
+define i32 @cls_i32(i32 %x) {
+; CHECK-LABEL: cls_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sraiw a1, a0, 31
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    addiw a0, a0, -1
+; CHECK-NEXT:    ret
+  %a = ashr i32 %x, 31
+  %b = xor i32 %x, %a
+  %c = call i32 @llvm.ctlz.i32(i32 %b, i1 false)
+  %d = sub i32 %c, 1
+  ret i32 %d
+}
+
+define i32 @cls_i32_2(i32 %x) {
+; CHECK-LABEL: cls_i32_2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    sraiw a1, a0, 31
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    slli a0, a0, 1
+; CHECK-NEXT:    ori a0, a0, 1
+; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    ret
+  %a = ashr i32 %x, 31
+  %b = xor i32 %x, %a
+  %c = shl i32 %b, 1
+  %d = or i32 %c, 1
+  %e = call i32 @llvm.ctlz.i32(i32 %d, i1 true)
+  ret i32 %e
+}
+
+define i64 @cls_i64(i64 %x) {
+; CHECK-LABEL: cls_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    srai a1, a0, 63
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    ret
+  %a = ashr i64 %x, 63
+  %b = xor i64 %x, %a
+  %c = call i64 @llvm.ctlz.i64(i64 %b, i1 false)
+  %d = sub i64 %c, 1
+  ret i64 %d
+}
+
+define i64 @cls_i64_2(i64 %x) {
+; CHECK-LABEL: cls_i64_2:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    srai a1, a0, 63
+; CHECK-NEXT:    xor a0, a0, a1
+; CHECK-NEXT:    slli a0, a0, 1
+; CHECK-NEXT:    ori a0, a0, 1
+; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    ret
+  %a = ashr i64 %x, 63
+  %b = xor i64 %x, %a
+  %c = shl i64 %b, 1
+  %d = or i64 %c, 1
+  %e = call i64 @llvm.ctlz.i64(i64 %d, i1 true)
+  ret i64 %e
+}

>From 8f7ac6baffe6af5c079c10c0b213b963482f1698 Mon Sep 17 00:00:00 2001
From: Stefan Weigl-Bosker <stefan at s00.xyz>
Date: Thu, 8 Jan 2026 15:53:55 -0500
Subject: [PATCH 2/2] [GISel][RISCV] Add G_CTLS Opcode and combines, lower to
 cls(w)

---
 llvm/docs/GlobalISel/GenericOpcode.rst        |  11 +
 .../llvm/CodeGen/GlobalISel/CombinerHelper.h  |   4 +
 .../CodeGen/GlobalISel/GenericMachineInstrs.h |   8 +
 .../llvm/CodeGen/GlobalISel/LegalizerHelper.h |   2 +
 .../CodeGen/GlobalISel/MachineIRBuilder.h     |   5 +
 llvm/include/llvm/Support/TargetOpcodes.def   |   3 +
 llvm/include/llvm/Target/GenericOpcodes.td    |   6 +
 .../include/llvm/Target/GlobalISel/Combine.td |  10 +-
 .../Target/GlobalISel/SelectionDAGCompat.td   |   1 +
 .../lib/CodeGen/GlobalISel/CombinerHelper.cpp |  97 ++++
 .../CodeGen/GlobalISel/LegalizerHelper.cpp    |  87 +++-
 llvm/lib/CodeGen/GlobalISel/Utils.cpp         |   1 +
 .../Target/RISCV/GISel/RISCVLegalizerInfo.cpp |  16 +-
 llvm/lib/Target/RISCV/RISCVInstrGISel.td      |   7 +
 .../GlobalISel/legalizer-info-validation.mir  |   3 +
 .../Inputs/reference_x86_vocab_print.txt      |   1 +
 .../reference_x86_vocab_wo=0.5_print.txt      |   1 +
 .../GlobalISel/legalizer-info-validation.mir  |   3 +
 .../legalizer/legalize-ctls-rv32.mir          | 431 ++++++++++++++++++
 .../legalizer/legalize-ctls-rv64.mir          | 395 ++++++++++++++++
 llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll   |  45 +-
 llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll   |  56 +--
 22 files changed, 1107 insertions(+), 86 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-ctls-rv32.mir
 create mode 100644 llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-ctls-rv64.mir

diff --git a/llvm/docs/GlobalISel/GenericOpcode.rst b/llvm/docs/GlobalISel/GenericOpcode.rst
index 329d9d13ebddd..871a03fd1152d 100644
--- a/llvm/docs/GlobalISel/GenericOpcode.rst
+++ b/llvm/docs/GlobalISel/GenericOpcode.rst
@@ -501,6 +501,17 @@ undefined.
   %2:_(s33) = G_CTLZ_ZERO_UNDEF %1
   %2:_(s33) = G_CTTZ_ZERO_UNDEF %1
 
+G_CTLS
+^^^^^^
+
+Count leading redundant sign bits. If the value is positive then the result is
+the number of extra leading zeros. If the value is negative then the result is
+the number of extra leading ones.
+
+.. code-block:: none
+
+  %2:_(s33) = G_CTLS %1
+
 G_ABDS, G_ABDU
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
index 9de1a643f1000..5d4347066a40c 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
@@ -1046,6 +1046,10 @@ class CombinerHelper {
   bool matchRedundantSextInReg(MachineInstr &Root, MachineInstr &Other,
                                BuildFnTy &MatchInfo) const;
 
+  // (ctlz (xor x, (sra x, bitwidth-1))) -> (add (ctls x), 1) or
+  // (ctlz (or (shl (xor x, (sra x, bitwidth-1)), 1), 1) -> (ctls x)
+  bool matchCtls(MachineInstr &CtlzMI, 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/CodeGen/GlobalISel/GenericMachineInstrs.h b/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h
index 5faf57fd06228..75d95a71f5973 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h
@@ -797,6 +797,14 @@ class GOr : public GLogicalBinOp {
   };
 };
 
+/// Represents a logical xor.
+class GXor : public GLogicalBinOp {
+public:
+  static bool classof(const MachineInstr *MI) {
+    return MI->getOpcode() == TargetOpcode::G_XOR;
+  };
+};
+
 /// Represents an extract vector element.
 class GExtractVectorElement : public GenericMachineInstr {
 public:
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
index a458cbd94ccb1..dade270bc8b5d 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h
@@ -443,6 +443,8 @@ class LegalizerHelper {
                                            LLT Ty);
   LLVM_ABI LegalizeResult narrowScalarCTTZ(MachineInstr &MI, unsigned TypeIdx,
                                            LLT Ty);
+  LLVM_ABI LegalizeResult narrowScalarCTLS(MachineInstr &MI, unsigned TypeIdx,
+                                           LLT Ty);
   LLVM_ABI LegalizeResult narrowScalarCTPOP(MachineInstr &MI, unsigned TypeIdx,
                                             LLT Ty);
   LLVM_ABI LegalizeResult narrowScalarFLDEXP(MachineInstr &MI, unsigned TypeIdx,
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
index 5f3f1d386569c..30bf7e3a4a3d0 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
@@ -2063,6 +2063,11 @@ class LLVM_ABI MachineIRBuilder {
     return buildInstr(TargetOpcode::G_CTTZ_ZERO_UNDEF, {Dst}, {Src0});
   }
 
+  /// Build and insert \p Res = G_CTLS \p Op0, \p Src0
+  MachineInstrBuilder buildCTLS(const DstOp &Dst, const SrcOp &Src0) {
+    return buildInstr(TargetOpcode::G_CTLS, {Dst}, {Src0});
+  }
+
   /// Build and insert \p Dst = G_BSWAP \p Src0
   MachineInstrBuilder buildBSwap(const DstOp &Dst, const SrcOp &Src0) {
     return buildInstr(TargetOpcode::G_BSWAP, {Dst}, {Src0});
diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def
index 0d92f50a09d38..3217ffafc235a 100644
--- a/llvm/include/llvm/Support/TargetOpcodes.def
+++ b/llvm/include/llvm/Support/TargetOpcodes.def
@@ -839,6 +839,9 @@ HANDLE_TARGET_OPCODE(G_CTLZ)
 /// Same as above, undefined for zero inputs.
 HANDLE_TARGET_OPCODE(G_CTLZ_ZERO_UNDEF)
 
+/// Generic count extra sign bits.
+HANDLE_TARGET_OPCODE(G_CTLS)
+
 /// Generic count bits.
 HANDLE_TARGET_OPCODE(G_CTPOP)
 
diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index 1b65b8b73527d..b785847b53f0f 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -214,6 +214,12 @@ def G_CTTZ_ZERO_UNDEF : GenericInstruction {
   let hasSideEffects = false;
 }
 
+def G_CTLS : GenericInstruction {
+  let OutOperandList = (outs type0:$dst);
+  let InOperandList = (ins type1:$src);
+  let hasSideEffects = false;
+}
+
 def G_CTPOP : GenericInstruction {
   let OutOperandList = (outs type0:$dst);
   let InOperandList = (ins type1:$src);
diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index 0ab2d9487a295..20779600d1afa 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -2004,6 +2004,14 @@ class narrow_binop_opcode<Instruction binopOpcode> : GICombineRule <
          [{ return Helper.matchNarrowBinop(*${Trunc}, *${Binop}, ${matchinfo}); }]),
   (apply [{ Helper.applyBuildFn(*${Trunc}, ${matchinfo}); }])>;
 
+// Fold (ctlz (xor x, (sra x, bitwidth-1))) -> (add (ctls x), 1).
+// Fold (ctlz (or (shl (xor x, (sra x, bitwidth-1)), 1), 1) -> (ctls x)
+def ctlz_to_ctls : GICombineRule <
+  (defs root:$root, build_fn_matchinfo:$matchinfo),
+  (match (wip_match_opcode G_CTLZ, G_CTLZ_ZERO_UNDEF):$root,
+         [{ return Helper.matchCtls(*${root}, ${matchinfo}); }]),
+  (apply [{Helper.applyBuildFn(*${root}, ${matchinfo});}])>;
+
 def narrow_binop_add : narrow_binop_opcode<G_ADD>;
 def narrow_binop_sub : narrow_binop_opcode<G_SUB>;
 def narrow_binop_mul : narrow_binop_opcode<G_MUL>;
@@ -2166,7 +2174,7 @@ def all_combines : GICombineGroup<[integer_reassoc_combines, trivial_combines,
     simplify_neg_minmax, combine_concat_vector,
     sext_trunc, zext_trunc, prefer_sign_combines, shuffle_combines,
     combine_use_vector_truncate, merge_combines, overflow_combines, 
-    truncsat_combines, lshr_of_trunc_of_lshr]>;
+    truncsat_combines, lshr_of_trunc_of_lshr, ctlz_to_ctls]>;
 
 // 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/include/llvm/Target/GlobalISel/SelectionDAGCompat.td b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
index a69e089779315..4b0c89b780c6f 100644
--- a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
+++ b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
@@ -154,6 +154,7 @@ def : GINodeEquiv<G_CTLZ, ctlz>;
 def : GINodeEquiv<G_CTTZ, cttz>;
 def : GINodeEquiv<G_CTLZ_ZERO_UNDEF, ctlz_zero_undef>;
 def : GINodeEquiv<G_CTTZ_ZERO_UNDEF, cttz_zero_undef>;
+def : GINodeEquiv<G_CTLS, ctls>;
 def : GINodeEquiv<G_CTPOP, ctpop>;
 def : GINodeEquiv<G_EXTRACT_VECTOR_ELT, extractelt>;
 def : GINodeEquiv<G_INSERT_VECTOR_ELT, vector_insert>;
diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
index c58969144dc2b..9b7273f048b82 100644
--- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
@@ -8512,3 +8512,100 @@ bool CombinerHelper::matchSuboCarryOut(const MachineInstr &MI,
 
   return false;
 }
+
+// Fold (ctlz (xor x, (sra x, bitwidth-1))) -> (add (ctls x), 1).
+// Fold (ctlz (or (shl (xor x, (sra x, bitwidth-1)), 1), 1) -> (ctls x)
+bool CombinerHelper::matchCtls(MachineInstr &CtlzMI,
+                               BuildFnTy &MatchInfo) const {
+  assert((CtlzMI.getOpcode() == TargetOpcode::G_CTLZ ||
+          CtlzMI.getOpcode() == TargetOpcode::G_CTLZ_ZERO_UNDEF) &&
+         "Expected G_CTLZ variant");
+
+  const Register Dst = CtlzMI.getOperand(0).getReg();
+  Register Src = CtlzMI.getOperand(1).getReg();
+
+  LLT Ty = MRI.getType(Dst);
+
+  if (!(Ty.isValid() && Ty.isScalar()))
+    return false;
+
+  LegalityQuery Query = {TargetOpcode::G_CTLS, {Ty, MRI.getType(Src)}};
+
+  switch (LI->getAction(Query).Action) {
+  default:
+    return false;
+  case LegalizeActions::Legal:
+  case LegalizeActions::Custom:
+  case LegalizeActions::WidenScalar:
+    break;
+  }
+
+  const MachineInstr *RhsMI = MRI.getVRegDef(Src);
+  if (!RhsMI)
+    return false;
+
+  bool NeedAdd = true;
+  //  Src = or(shl(V, 1), 1) -> Src=V; NeedAdd = False
+  if (auto *OrOp = dyn_cast<GOr>(RhsMI)) {
+    Register ShlReg = OrOp->getLHSReg();
+
+    auto MaybeOne = getIConstantVRegSExtVal(OrOp->getRHSReg(), MRI);
+    if (!MaybeOne || MaybeOne.value() != 1)
+      return false;
+
+    MachineInstr *ShlMI = MRI.getVRegDef(ShlReg);
+    auto *Shl = dyn_cast<GShl>(ShlMI);
+
+    if (!Shl || !MRI.hasOneNonDBGUse(ShlReg))
+      return false;
+
+    auto ShAmt = getIConstantVRegSExtVal(Shl->getShiftReg(), MRI);
+    if (!ShAmt || ShAmt.value() != 1)
+      return false;
+
+    NeedAdd = false;
+    Src = Shl->getSrcReg();
+  }
+
+  if (!MRI.hasOneNonDBGUse(Src))
+    return false;
+
+  //  (xor x, (ashr x, bitwidth-1))
+  auto *Xor = dyn_cast<GXor>(MRI.getVRegDef(Src));
+  if (!Xor || !MRI.hasOneNonDBGUse(Src))
+    return false;
+
+  Register X = Xor->getLHSReg();
+  Register AshrReg = Xor->getRHSReg();
+
+  MachineInstr *AshrMI = MRI.getVRegDef(AshrReg);
+  if (!AshrMI || AshrMI->getOpcode() != TargetOpcode::G_ASHR ||
+      !MRI.hasOneNonDBGUse(AshrReg))
+    return false;
+
+  unsigned BitWidth = Ty.getScalarSizeInBits();
+
+  auto ShAmt = getIConstantVRegSExtVal(AshrMI->getOperand(2).getReg(), MRI);
+  if (!ShAmt || ShAmt.value() != BitWidth - 1)
+    return false;
+
+  Register MaybeX = AshrMI->getOperand(1).getReg();
+  if (MaybeX != X)
+    return false;
+
+  MatchInfo = [=](MachineIRBuilder &B) {
+    if (!NeedAdd) {
+      B.buildCTLS(Dst, X);
+      return;
+    }
+
+    Register Tmp = MRI.createGenericVirtualRegister(Ty);
+    B.buildCTLS(Tmp, X);
+
+    LLT STy = Ty.getScalarType();
+    Register One = B.buildConstant(STy, 1).getReg(0);
+    B.buildAdd(Dst, Tmp, One);
+  };
+
+  return true;
+}
diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
index e40b34b0b306f..1af6cc33f1a3f 100644
--- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
@@ -1761,6 +1761,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
   case TargetOpcode::G_CTLZ_ZERO_UNDEF:
   case TargetOpcode::G_CTTZ:
   case TargetOpcode::G_CTTZ_ZERO_UNDEF:
+  case TargetOpcode::G_CTLS:
   case TargetOpcode::G_CTPOP:
     if (TypeIdx == 1)
       switch (MI.getOpcode()) {
@@ -1772,6 +1773,8 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI,
         return narrowScalarCTTZ(MI, TypeIdx, NarrowTy);
       case TargetOpcode::G_CTPOP:
         return narrowScalarCTPOP(MI, TypeIdx, NarrowTy);
+      case TargetOpcode::G_CTLS:
+        return narrowScalarCTLS(MI, TypeIdx, NarrowTy);
       default:
         return UnableToLegalize;
       }
@@ -2799,6 +2802,7 @@ LegalizerHelper::widenScalar(MachineInstr &MI, unsigned TypeIdx, LLT WideTy) {
   case TargetOpcode::G_CTTZ_ZERO_UNDEF:
   case TargetOpcode::G_CTLZ:
   case TargetOpcode::G_CTLZ_ZERO_UNDEF:
+  case TargetOpcode::G_CTLS:
   case TargetOpcode::G_CTPOP: {
     if (TypeIdx == 0) {
       Observer.changingInstr(MI);
@@ -2810,10 +2814,19 @@ LegalizerHelper::widenScalar(MachineInstr &MI, unsigned TypeIdx, LLT WideTy) {
     Register SrcReg = MI.getOperand(1).getReg();
 
     // First extend the input.
-    unsigned ExtOpc = Opcode == TargetOpcode::G_CTTZ ||
-                              Opcode == TargetOpcode::G_CTTZ_ZERO_UNDEF
-                          ? TargetOpcode::G_ANYEXT
-                          : TargetOpcode::G_ZEXT;
+    unsigned ExtOpc;
+    switch (Opcode) {
+    case TargetOpcode::G_CTTZ:
+    case TargetOpcode::G_CTTZ_ZERO_UNDEF:
+      ExtOpc = TargetOpcode::G_ANYEXT;
+      break;
+    case TargetOpcode::G_CTLS:
+      ExtOpc = TargetOpcode::G_SEXT;
+      break;
+    default:
+      ExtOpc = TargetOpcode::G_ZEXT;
+    }
+
     auto MIBSrc = MIRBuilder.buildInstr(ExtOpc, {WideTy}, {SrcReg});
     LLT CurTy = MRI.getType(SrcReg);
     unsigned NewOpc = Opcode;
@@ -2843,7 +2856,7 @@ LegalizerHelper::widenScalar(MachineInstr &MI, unsigned TypeIdx, LLT WideTy) {
     // Perform the operation at the larger size.
     auto MIBNewOp = MIRBuilder.buildInstr(NewOpc, {WideTy}, {MIBSrc});
     // This is already the correct result for CTPOP and CTTZs
-    if (Opcode == TargetOpcode::G_CTLZ) {
+    if (Opcode == TargetOpcode::G_CTLZ || Opcode == TargetOpcode::G_CTLS) {
       // The correct result is NewOp - (Difference in widety and current ty).
       MIBNewOp = MIRBuilder.buildSub(
           WideTy, MIBNewOp, MIRBuilder.buildConstant(WideTy, SizeDiff));
@@ -4656,6 +4669,7 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
   case TargetOpcode::G_CTLZ:
   case TargetOpcode::G_CTTZ:
   case TargetOpcode::G_CTPOP:
+  case TargetOpcode::G_CTLS:
     return lowerBitCount(MI);
   case G_UADDO: {
     auto [Res, CarryOut, LHS, RHS] = MI.getFirst4Regs();
@@ -7547,6 +7561,52 @@ LegalizerHelper::narrowScalarCTTZ(MachineInstr &MI, unsigned TypeIdx,
   return UnableToLegalize;
 }
 
+LegalizerHelper::LegalizeResult
+LegalizerHelper::narrowScalarCTLS(MachineInstr &MI, unsigned TypeIdx,
+                                  LLT NarrowTy) {
+  if (TypeIdx != 1)
+    return UnableToLegalize;
+
+  auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
+  unsigned NarrowSize = NarrowTy.getSizeInBits();
+
+  if (!(SrcTy.isScalar() && SrcTy.getSizeInBits() == 2 * NarrowSize))
+    return UnableToLegalize;
+
+  MachineIRBuilder &B = MIRBuilder;
+
+  auto UnmergeSrc = B.buildUnmerge(NarrowTy, SrcReg);
+  Register Lo = UnmergeSrc.getReg(0);
+  Register Hi = UnmergeSrc.getReg(1);
+
+  auto ShAmt = B.buildConstant(NarrowTy, NarrowSize - 1);
+  auto Sign = B.buildAShr(NarrowTy, Hi, ShAmt);
+
+  auto LoSign = B.buildAShr(NarrowTy, Lo, ShAmt);
+  auto LoSameSign = B.buildICmp(CmpInst::ICMP_EQ, LLT::scalar(1),
+                                LoSign.getReg(0), Sign.getReg(0));
+
+  auto HiIsSign =
+      B.buildICmp(CmpInst::ICMP_EQ, LLT::scalar(1), Hi, Sign.getReg(0));
+
+  auto LoCTLS = B.buildCTLS(DstTy, Lo);
+  auto GNarrowSize = B.buildConstant(DstTy, NarrowSize);
+  auto HiIsSignCTLS = B.buildAdd(DstTy, LoCTLS, GNarrowSize);
+
+  // If the low half flips sign, the run of redundant bits stops at the
+  // boundary, so use (NarrowSize - 1) instead of extending into Lo.
+  auto GNarrowSizeMinus1 = B.buildConstant(DstTy, NarrowSize - 1);
+  auto HiSignResult =
+      B.buildSelect(DstTy, LoSameSign, HiIsSignCTLS, GNarrowSizeMinus1);
+
+  auto HiCTLS = B.buildCTLS(DstTy, Hi);
+
+  B.buildSelect(DstReg, HiIsSign, HiSignResult, HiCTLS);
+
+  MI.eraseFromParent();
+  return Legalized;
+}
+
 LegalizerHelper::LegalizeResult
 LegalizerHelper::narrowScalarCTPOP(MachineInstr &MI, unsigned TypeIdx,
                                    LLT NarrowTy) {
@@ -7769,6 +7829,23 @@ LegalizerHelper::lowerBitCount(MachineInstr &MI) {
     MI.eraseFromParent();
     return Legalized;
   }
+  case TargetOpcode::G_CTLS: {
+    auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
+
+    // ctls(x) -> ctlz(x ^ (x >> (N - 1))) - 1
+    auto SignIdxC =
+        MIRBuilder.buildConstant(SrcTy, SrcTy.getScalarSizeInBits() - 1);
+    auto OneC = MIRBuilder.buildConstant(DstTy, 1);
+
+    auto Shr = MIRBuilder.buildAShr(SrcTy, SrcReg, SignIdxC);
+
+    auto Xor = MIRBuilder.buildXor(SrcTy, SrcReg, Shr);
+    auto Ctlz = MIRBuilder.buildCTLZ(DstTy, Xor);
+
+    MIRBuilder.buildSub(DstReg, Ctlz, OneC);
+    MI.eraseFromParent();
+    return Legalized;
+  }
   }
 }
 
diff --git a/llvm/lib/CodeGen/GlobalISel/Utils.cpp b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
index 35e89cae9e929..658774ec3fcb9 100644
--- a/llvm/lib/CodeGen/GlobalISel/Utils.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/Utils.cpp
@@ -1877,6 +1877,7 @@ static bool canCreateUndefOrPoison(Register Reg, const MachineRegisterInfo &MRI,
     return true;
   case TargetOpcode::G_CTLZ:
   case TargetOpcode::G_CTTZ:
+  case TargetOpcode::G_CTLS:
   case TargetOpcode::G_ABS:
   case TargetOpcode::G_CTPOP:
   case TargetOpcode::G_BSWAP:
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index ae43fccf8e818..7766a4540f2d4 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -233,6 +233,17 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
   }
   CountZerosUndefActions.lower();
 
+  auto &CountSignActions = getActionDefinitionsBuilder(G_CTLS);
+  if (ST.hasStdExtP()) {
+    CountSignActions.legalFor({{sXLen, sXLen}})
+        .customFor({{s32, s32}})
+        .clampScalar(0, s32, sXLen)
+        .widenScalarToNextPow2(0)
+        .scalarSameSizeAs(1, 0);
+  } else {
+    CountSignActions.maxScalar(0, sXLen).scalarSameSizeAs(1, 0).lower();
+  }
+
   auto &CTPOPActions = getActionDefinitionsBuilder(G_CTPOP);
   if (ST.hasStdExtZbb()) {
     CTPOPActions.legalFor({{sXLen, sXLen}})
@@ -1385,6 +1396,8 @@ static unsigned getRISCVWOpcode(unsigned Opcode) {
     return RISCV::G_CLZW;
   case TargetOpcode::G_CTTZ:
     return RISCV::G_CTZW;
+  case TargetOpcode::G_CTLS:
+    return RISCV::G_CLSW;
   case TargetOpcode::G_FPTOSI:
     return RISCV::G_FCVT_W_RV64;
   case TargetOpcode::G_FPTOUI:
@@ -1497,7 +1510,8 @@ bool RISCVLegalizerInfo::legalizeCustom(
     return true;
   }
   case TargetOpcode::G_CTLZ:
-  case TargetOpcode::G_CTTZ: {
+  case TargetOpcode::G_CTTZ:
+  case TargetOpcode::G_CTLS: {
     Helper.Observer.changingInstr(MI);
     Helper.widenScalarSrc(MI, sXLen, 1, TargetOpcode::G_ANYEXT);
     Helper.widenScalarDst(MI, sXLen);
diff --git a/llvm/lib/Target/RISCV/RISCVInstrGISel.td b/llvm/lib/Target/RISCV/RISCVInstrGISel.td
index bd3ecd737bede..386ec1259f41d 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrGISel.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrGISel.td
@@ -178,3 +178,10 @@ def G_VSLIDEUP_VL : RISCVGenericInstruction {
 }
 def : GINodeEquiv<G_VSLIDEUP_VL, riscv_slideup_vl>;
 
+// Psuedo equivalent to RISCVISD::CLSW
+def G_CLSW : RISCVGenericInstruction {
+  let OutOperandList = (outs type0:$dst);
+  let InOperandList = (ins type0:$src);
+  let hasSideEffects = false;
+}
+def : GINodeEquiv<G_CLSW , riscv_clsw>;
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
index 8a0071c9ea5c1..7e728218a0b34 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
@@ -746,6 +746,9 @@
 # DEBUG-NEXT: G_CTLZ_ZERO_UNDEF (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
 # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
+# DEBUG-NEXT: G_CTLS (opcode {{[0-9]+}}): 2 type indices, 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_CTPOP (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
 # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
diff --git a/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_print.txt b/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_print.txt
index 62e07445ad12e..15d4589580b7b 100644
--- a/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_print.txt
+++ b/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_print.txt
@@ -369,6 +369,7 @@ Key: G_CONCAT_VECTORS:  [ 0.00  0.00 ]
 Key: G_CONSTANT:  [ 0.00  0.00 ]
 Key: G_CONSTANT_FOLD_BARRIER:  [ 0.00  0.00 ]
 Key: G_CONSTANT_POOL:  [ 0.00  0.00 ]
+Key: G_CTLS:  [ 0.00  0.00 ]
 Key: G_CTLZ:  [ 0.00  0.00 ]
 Key: G_CTLZ_ZERO_UNDEF:  [ 0.00  0.00 ]
 Key: G_CTPOP:  [ 0.00  0.00 ]
diff --git a/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_wo=0.5_print.txt b/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_wo=0.5_print.txt
index 03a3fafc6b801..3cc3890216565 100644
--- a/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_wo=0.5_print.txt
+++ b/llvm/test/CodeGen/MIR2Vec/Inputs/reference_x86_vocab_wo=0.5_print.txt
@@ -369,6 +369,7 @@ Key: G_CONCAT_VECTORS:  [ 0.00  0.00 ]
 Key: G_CONSTANT:  [ 0.00  0.00 ]
 Key: G_CONSTANT_FOLD_BARRIER:  [ 0.00  0.00 ]
 Key: G_CONSTANT_POOL:  [ 0.00  0.00 ]
+Key: G_CTLS:  [ 0.00  0.00 ]
 Key: G_CTLZ:  [ 0.00  0.00 ]
 Key: G_CTLZ_ZERO_UNDEF:  [ 0.00  0.00 ]
 Key: G_CTPOP:  [ 0.00  0.00 ]
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
index a823a4ccf3e46..43ef36da6a11a 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
@@ -736,6 +736,9 @@
 # DEBUG-NEXT: G_CTLZ_ZERO_UNDEF (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
 # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
+# DEBUG-NEXT: G_CTLS (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
+# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
+# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: G_CTPOP (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
 # DEBUG-NEXT: .. the first uncovered type index: 2, OK
 # DEBUG-NEXT: .. the first uncovered imm index: 0, OK
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-ctls-rv32.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-ctls-rv32.mir
new file mode 100644
index 0000000000000..924415f85b099
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-ctls-rv32.mir
@@ -0,0 +1,431 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=riscv32 -run-pass=legalizer %s -o - \
+# RUN:   | FileCheck %s --check-prefix=RV32I
+# RUN: llc -mtriple=riscv32 -mattr=+experimental-p -run-pass=legalizer %s -o - \
+# RUN:   | FileCheck %s --check-prefix=RV32P
+# RUN: llc -mtriple=riscv32 -mattr=+zbb -run-pass=legalizer %s -o - \
+# RUN:   | FileCheck %s --check-prefix=RV32ZBB
+
+---
+name:            cls_i8
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; RV32I-LABEL: name: cls_i8
+    ; RV32I: liveins: $x10
+    ; RV32I-NEXT: {{  $}}
+    ; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 7
+    ; RV32I-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; RV32I-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 24
+    ; RV32I-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[COPY]], [[C2]](s32)
+    ; RV32I-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[SHL]], [[C2]](s32)
+    ; RV32I-NEXT: [[ASHR1:%[0-9]+]]:_(s32) = G_ASHR [[ASHR]], [[C]](s32)
+    ; RV32I-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[ASHR1]]
+    ; RV32I-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 255
+    ; RV32I-NEXT: [[AND:%[0-9]+]]:_(s32) = G_AND [[XOR]], [[C3]]
+    ; RV32I-NEXT: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[AND]], [[C1]](s32)
+    ; RV32I-NEXT: [[OR:%[0-9]+]]:_(s32) = G_OR [[XOR]], [[LSHR]]
+    ; RV32I-NEXT: [[C4:%[0-9]+]]:_(s32) = G_CONSTANT i32 2
+    ; RV32I-NEXT: [[AND1:%[0-9]+]]:_(s32) = G_AND [[OR]], [[C3]]
+    ; RV32I-NEXT: [[LSHR1:%[0-9]+]]:_(s32) = G_LSHR [[AND1]], [[C4]](s32)
+    ; RV32I-NEXT: [[OR1:%[0-9]+]]:_(s32) = G_OR [[OR]], [[LSHR1]]
+    ; RV32I-NEXT: [[C5:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+    ; RV32I-NEXT: [[AND2:%[0-9]+]]:_(s32) = G_AND [[OR1]], [[C3]]
+    ; RV32I-NEXT: [[LSHR2:%[0-9]+]]:_(s32) = G_LSHR [[AND2]], [[C5]](s32)
+    ; RV32I-NEXT: [[OR2:%[0-9]+]]:_(s32) = G_OR [[OR1]], [[LSHR2]]
+    ; RV32I-NEXT: [[AND3:%[0-9]+]]:_(s32) = G_AND [[OR2]], [[C3]]
+    ; RV32I-NEXT: [[LSHR3:%[0-9]+]]:_(s32) = G_LSHR [[AND3]], [[C1]](s32)
+    ; RV32I-NEXT: [[C6:%[0-9]+]]:_(s32) = G_CONSTANT i32 85
+    ; RV32I-NEXT: [[AND4:%[0-9]+]]:_(s32) = G_AND [[LSHR3]], [[C6]]
+    ; RV32I-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[OR2]], [[AND4]]
+    ; RV32I-NEXT: [[AND5:%[0-9]+]]:_(s32) = G_AND [[SUB]], [[C3]]
+    ; RV32I-NEXT: [[LSHR4:%[0-9]+]]:_(s32) = G_LSHR [[AND5]], [[C4]](s32)
+    ; RV32I-NEXT: [[C7:%[0-9]+]]:_(s32) = G_CONSTANT i32 51
+    ; RV32I-NEXT: [[AND6:%[0-9]+]]:_(s32) = G_AND [[LSHR4]], [[C7]]
+    ; RV32I-NEXT: [[AND7:%[0-9]+]]:_(s32) = G_AND [[SUB]], [[C7]]
+    ; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[AND6]], [[AND7]]
+    ; RV32I-NEXT: [[LSHR5:%[0-9]+]]:_(s32) = G_LSHR [[ADD]], [[C5]](s32)
+    ; RV32I-NEXT: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[LSHR5]], [[ADD]]
+    ; RV32I-NEXT: [[C8:%[0-9]+]]:_(s32) = G_CONSTANT i32 15
+    ; RV32I-NEXT: [[AND8:%[0-9]+]]:_(s32) = G_AND [[ADD1]], [[C8]]
+    ; RV32I-NEXT: [[C9:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
+    ; RV32I-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+    ; RV32I-NEXT: $x10 = COPY [[AND8]](s32)
+    ; RV32I-NEXT: $x11 = COPY [[C1]](s32)
+    ; RV32I-NEXT: PseudoCALL target-flags(riscv-call) &__mulsi3, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    ; RV32I-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+    ; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32I-NEXT: [[AND9:%[0-9]+]]:_(s32) = G_AND [[COPY1]], [[C3]]
+    ; RV32I-NEXT: [[LSHR6:%[0-9]+]]:_(s32) = G_LSHR [[AND9]], [[C9]](s32)
+    ; RV32I-NEXT: [[C10:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; RV32I-NEXT: [[SUB1:%[0-9]+]]:_(s32) = G_SUB [[C10]], [[LSHR6]]
+    ; RV32I-NEXT: [[SUB2:%[0-9]+]]:_(s32) = G_SUB [[SUB1]], [[C1]]
+    ; RV32I-NEXT: $x10 = COPY [[SUB2]](s32)
+    ; RV32I-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV32P-LABEL: name: cls_i8
+    ; RV32P: liveins: $x10
+    ; RV32P-NEXT: {{  $}}
+    ; RV32P-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32P-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 24
+    ; RV32P-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[COPY]], [[C]](s32)
+    ; RV32P-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[SHL]], [[C]](s32)
+    ; RV32P-NEXT: [[CTLS:%[0-9]+]]:_(s32) = G_CTLS [[ASHR]](s32)
+    ; RV32P-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[CTLS]], [[C]]
+    ; RV32P-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY [[SUB]](s32)
+    ; RV32P-NEXT: $x10 = COPY [[COPY1]](s32)
+    ; RV32P-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV32ZBB-LABEL: name: cls_i8
+    ; RV32ZBB: liveins: $x10
+    ; RV32ZBB-NEXT: {{  $}}
+    ; RV32ZBB-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32ZBB-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 7
+    ; RV32ZBB-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; RV32ZBB-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s32) = G_SEXT_INREG [[COPY]], 8
+    ; RV32ZBB-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[SEXT_INREG]], [[C]](s32)
+    ; RV32ZBB-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[ASHR]]
+    ; RV32ZBB-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 255
+    ; RV32ZBB-NEXT: [[AND:%[0-9]+]]:_(s32) = G_AND [[XOR]], [[C2]]
+    ; RV32ZBB-NEXT: [[CTLZ:%[0-9]+]]:_(s32) = G_CTLZ [[AND]](s32)
+    ; RV32ZBB-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 24
+    ; RV32ZBB-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[CTLZ]], [[C3]]
+    ; RV32ZBB-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY [[SUB]](s32)
+    ; RV32ZBB-NEXT: [[SUB1:%[0-9]+]]:_(s32) = G_SUB [[COPY1]], [[C1]]
+    ; RV32ZBB-NEXT: $x10 = COPY [[SUB1]](s32)
+    ; RV32ZBB-NEXT: PseudoRET implicit $x10
+    %1:_(s32) = COPY $x10
+    %0:_(s8) = G_TRUNC %1(s32)
+    %2:_(s8) = G_CTLS %0(s8)
+    %3:_(s32) = G_ANYEXT %2(s8)
+    $x10 = COPY %3(s32)
+    PseudoRET implicit $x10
+...
+
+---
+name:            cls_i16
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; RV32I-LABEL: name: cls_i16
+    ; RV32I: liveins: $x10
+    ; RV32I-NEXT: {{  $}}
+    ; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 15
+    ; RV32I-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; RV32I-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; RV32I-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[COPY]], [[C2]](s32)
+    ; RV32I-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[SHL]], [[C2]](s32)
+    ; RV32I-NEXT: [[ASHR1:%[0-9]+]]:_(s32) = G_ASHR [[ASHR]], [[C]](s32)
+    ; RV32I-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[ASHR1]]
+    ; RV32I-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 65535
+    ; RV32I-NEXT: [[AND:%[0-9]+]]:_(s32) = G_AND [[XOR]], [[C3]]
+    ; RV32I-NEXT: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[AND]], [[C1]](s32)
+    ; RV32I-NEXT: [[OR:%[0-9]+]]:_(s32) = G_OR [[XOR]], [[LSHR]]
+    ; RV32I-NEXT: [[C4:%[0-9]+]]:_(s32) = G_CONSTANT i32 2
+    ; RV32I-NEXT: [[AND1:%[0-9]+]]:_(s32) = G_AND [[OR]], [[C3]]
+    ; RV32I-NEXT: [[LSHR1:%[0-9]+]]:_(s32) = G_LSHR [[AND1]], [[C4]](s32)
+    ; RV32I-NEXT: [[OR1:%[0-9]+]]:_(s32) = G_OR [[OR]], [[LSHR1]]
+    ; RV32I-NEXT: [[C5:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+    ; RV32I-NEXT: [[AND2:%[0-9]+]]:_(s32) = G_AND [[OR1]], [[C3]]
+    ; RV32I-NEXT: [[LSHR2:%[0-9]+]]:_(s32) = G_LSHR [[AND2]], [[C5]](s32)
+    ; RV32I-NEXT: [[OR2:%[0-9]+]]:_(s32) = G_OR [[OR1]], [[LSHR2]]
+    ; RV32I-NEXT: [[C6:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; RV32I-NEXT: [[AND3:%[0-9]+]]:_(s32) = G_AND [[OR2]], [[C3]]
+    ; RV32I-NEXT: [[LSHR3:%[0-9]+]]:_(s32) = G_LSHR [[AND3]], [[C6]](s32)
+    ; RV32I-NEXT: [[OR3:%[0-9]+]]:_(s32) = G_OR [[OR2]], [[LSHR3]]
+    ; RV32I-NEXT: [[AND4:%[0-9]+]]:_(s32) = G_AND [[OR3]], [[C3]]
+    ; RV32I-NEXT: [[LSHR4:%[0-9]+]]:_(s32) = G_LSHR [[AND4]], [[C1]](s32)
+    ; RV32I-NEXT: [[C7:%[0-9]+]]:_(s32) = G_CONSTANT i32 21845
+    ; RV32I-NEXT: [[AND5:%[0-9]+]]:_(s32) = G_AND [[LSHR4]], [[C7]]
+    ; RV32I-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[OR3]], [[AND5]]
+    ; RV32I-NEXT: [[AND6:%[0-9]+]]:_(s32) = G_AND [[SUB]], [[C3]]
+    ; RV32I-NEXT: [[LSHR5:%[0-9]+]]:_(s32) = G_LSHR [[AND6]], [[C4]](s32)
+    ; RV32I-NEXT: [[C8:%[0-9]+]]:_(s32) = G_CONSTANT i32 13107
+    ; RV32I-NEXT: [[AND7:%[0-9]+]]:_(s32) = G_AND [[LSHR5]], [[C8]]
+    ; RV32I-NEXT: [[AND8:%[0-9]+]]:_(s32) = G_AND [[SUB]], [[C8]]
+    ; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[AND7]], [[AND8]]
+    ; RV32I-NEXT: [[LSHR6:%[0-9]+]]:_(s32) = G_LSHR [[ADD]], [[C5]](s32)
+    ; RV32I-NEXT: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[LSHR6]], [[ADD]]
+    ; RV32I-NEXT: [[C9:%[0-9]+]]:_(s32) = G_CONSTANT i32 3855
+    ; RV32I-NEXT: [[AND9:%[0-9]+]]:_(s32) = G_AND [[ADD1]], [[C9]]
+    ; RV32I-NEXT: [[C10:%[0-9]+]]:_(s32) = G_CONSTANT i32 257
+    ; RV32I-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+    ; RV32I-NEXT: $x10 = COPY [[AND9]](s32)
+    ; RV32I-NEXT: $x11 = COPY [[C10]](s32)
+    ; RV32I-NEXT: PseudoCALL target-flags(riscv-call) &__mulsi3, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    ; RV32I-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+    ; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32I-NEXT: [[AND10:%[0-9]+]]:_(s32) = G_AND [[COPY1]], [[C3]]
+    ; RV32I-NEXT: [[LSHR7:%[0-9]+]]:_(s32) = G_LSHR [[AND10]], [[C6]](s32)
+    ; RV32I-NEXT: [[SUB1:%[0-9]+]]:_(s32) = G_SUB [[C2]], [[LSHR7]]
+    ; RV32I-NEXT: [[SUB2:%[0-9]+]]:_(s32) = G_SUB [[SUB1]], [[C1]]
+    ; RV32I-NEXT: $x10 = COPY [[SUB2]](s32)
+    ; RV32I-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV32P-LABEL: name: cls_i16
+    ; RV32P: liveins: $x10
+    ; RV32P-NEXT: {{  $}}
+    ; RV32P-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32P-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; RV32P-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[COPY]], [[C]](s32)
+    ; RV32P-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[SHL]], [[C]](s32)
+    ; RV32P-NEXT: [[CTLS:%[0-9]+]]:_(s32) = G_CTLS [[ASHR]](s32)
+    ; RV32P-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[CTLS]], [[C]]
+    ; RV32P-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY [[SUB]](s32)
+    ; RV32P-NEXT: $x10 = COPY [[COPY1]](s32)
+    ; RV32P-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV32ZBB-LABEL: name: cls_i16
+    ; RV32ZBB: liveins: $x10
+    ; RV32ZBB-NEXT: {{  $}}
+    ; RV32ZBB-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32ZBB-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 15
+    ; RV32ZBB-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; RV32ZBB-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s32) = G_SEXT_INREG [[COPY]], 16
+    ; RV32ZBB-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[SEXT_INREG]], [[C]](s32)
+    ; RV32ZBB-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[ASHR]]
+    ; RV32ZBB-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 65535
+    ; RV32ZBB-NEXT: [[AND:%[0-9]+]]:_(s32) = G_AND [[XOR]], [[C2]]
+    ; RV32ZBB-NEXT: [[CTLZ:%[0-9]+]]:_(s32) = G_CTLZ [[AND]](s32)
+    ; RV32ZBB-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; RV32ZBB-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[CTLZ]], [[C3]]
+    ; RV32ZBB-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY [[SUB]](s32)
+    ; RV32ZBB-NEXT: [[SUB1:%[0-9]+]]:_(s32) = G_SUB [[COPY1]], [[C1]]
+    ; RV32ZBB-NEXT: $x10 = COPY [[SUB1]](s32)
+    ; RV32ZBB-NEXT: PseudoRET implicit $x10
+    %1:_(s32) = COPY $x10
+    %0:_(s16) = G_TRUNC %1(s32)
+    %2:_(s16) = G_CTLS %0(s16)
+    %3:_(s32) = G_ANYEXT %2(s16)
+    $x10 = COPY %3(s32)
+    PseudoRET implicit $x10
+...
+
+---
+name:            cls_i32
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; RV32I-LABEL: name: cls_i32
+    ; RV32I: liveins: $x10
+    ; RV32I-NEXT: {{  $}}
+    ; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 31
+    ; RV32I-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; RV32I-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[COPY]], [[C]](s32)
+    ; RV32I-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[ASHR]]
+    ; RV32I-NEXT: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[XOR]], [[C1]](s32)
+    ; RV32I-NEXT: [[OR:%[0-9]+]]:_(s32) = G_OR [[XOR]], [[LSHR]]
+    ; RV32I-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 2
+    ; RV32I-NEXT: [[LSHR1:%[0-9]+]]:_(s32) = G_LSHR [[OR]], [[C2]](s32)
+    ; RV32I-NEXT: [[OR1:%[0-9]+]]:_(s32) = G_OR [[OR]], [[LSHR1]]
+    ; RV32I-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+    ; RV32I-NEXT: [[LSHR2:%[0-9]+]]:_(s32) = G_LSHR [[OR1]], [[C3]](s32)
+    ; RV32I-NEXT: [[OR2:%[0-9]+]]:_(s32) = G_OR [[OR1]], [[LSHR2]]
+    ; RV32I-NEXT: [[C4:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; RV32I-NEXT: [[LSHR3:%[0-9]+]]:_(s32) = G_LSHR [[OR2]], [[C4]](s32)
+    ; RV32I-NEXT: [[OR3:%[0-9]+]]:_(s32) = G_OR [[OR2]], [[LSHR3]]
+    ; RV32I-NEXT: [[C5:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; RV32I-NEXT: [[LSHR4:%[0-9]+]]:_(s32) = G_LSHR [[OR3]], [[C5]](s32)
+    ; RV32I-NEXT: [[OR4:%[0-9]+]]:_(s32) = G_OR [[OR3]], [[LSHR4]]
+    ; RV32I-NEXT: [[LSHR5:%[0-9]+]]:_(s32) = G_LSHR [[OR4]], [[C1]](s32)
+    ; RV32I-NEXT: [[C6:%[0-9]+]]:_(s32) = G_CONSTANT i32 1431655765
+    ; RV32I-NEXT: [[AND:%[0-9]+]]:_(s32) = G_AND [[LSHR5]], [[C6]]
+    ; RV32I-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[OR4]], [[AND]]
+    ; RV32I-NEXT: [[LSHR6:%[0-9]+]]:_(s32) = G_LSHR [[SUB]], [[C2]](s32)
+    ; RV32I-NEXT: [[C7:%[0-9]+]]:_(s32) = G_CONSTANT i32 858993459
+    ; RV32I-NEXT: [[AND1:%[0-9]+]]:_(s32) = G_AND [[LSHR6]], [[C7]]
+    ; RV32I-NEXT: [[AND2:%[0-9]+]]:_(s32) = G_AND [[SUB]], [[C7]]
+    ; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[AND1]], [[AND2]]
+    ; RV32I-NEXT: [[LSHR7:%[0-9]+]]:_(s32) = G_LSHR [[ADD]], [[C3]](s32)
+    ; RV32I-NEXT: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[LSHR7]], [[ADD]]
+    ; RV32I-NEXT: [[C8:%[0-9]+]]:_(s32) = G_CONSTANT i32 252645135
+    ; RV32I-NEXT: [[AND3:%[0-9]+]]:_(s32) = G_AND [[ADD1]], [[C8]]
+    ; RV32I-NEXT: [[C9:%[0-9]+]]:_(s32) = G_CONSTANT i32 24
+    ; RV32I-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[AND3]], [[C4]](s32)
+    ; RV32I-NEXT: [[ADD2:%[0-9]+]]:_(s32) = G_ADD [[AND3]], [[SHL]]
+    ; RV32I-NEXT: [[SHL1:%[0-9]+]]:_(s32) = G_SHL [[ADD2]], [[C5]](s32)
+    ; RV32I-NEXT: [[ADD3:%[0-9]+]]:_(s32) = G_ADD [[ADD2]], [[SHL1]]
+    ; RV32I-NEXT: [[LSHR8:%[0-9]+]]:_(s32) = G_LSHR [[ADD3]], [[C9]](s32)
+    ; RV32I-NEXT: [[C10:%[0-9]+]]:_(s32) = G_CONSTANT i32 32
+    ; RV32I-NEXT: [[SUB1:%[0-9]+]]:_(s32) = G_SUB [[C10]], [[LSHR8]]
+    ; RV32I-NEXT: [[SUB2:%[0-9]+]]:_(s32) = G_SUB [[SUB1]], [[C1]]
+    ; RV32I-NEXT: $x10 = COPY [[SUB2]](s32)
+    ; RV32I-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV32P-LABEL: name: cls_i32
+    ; RV32P: liveins: $x10
+    ; RV32P-NEXT: {{  $}}
+    ; RV32P-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32P-NEXT: [[CTLS:%[0-9]+]]:_(s32) = G_CTLS [[COPY]](s32)
+    ; RV32P-NEXT: $x10 = COPY [[CTLS]](s32)
+    ; RV32P-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV32ZBB-LABEL: name: cls_i32
+    ; RV32ZBB: liveins: $x10
+    ; RV32ZBB-NEXT: {{  $}}
+    ; RV32ZBB-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32ZBB-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 31
+    ; RV32ZBB-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; RV32ZBB-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[COPY]], [[C]](s32)
+    ; RV32ZBB-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[ASHR]]
+    ; RV32ZBB-NEXT: [[CTLZ:%[0-9]+]]:_(s32) = G_CTLZ [[XOR]](s32)
+    ; RV32ZBB-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[CTLZ]], [[C1]]
+    ; RV32ZBB-NEXT: $x10 = COPY [[SUB]](s32)
+    ; RV32ZBB-NEXT: PseudoRET implicit $x10
+    %0:_(s32) = COPY $x10
+    %1:_(s32) = G_CTLS %0(s32)
+    $x10 = COPY %1(s32)
+    PseudoRET implicit $x10
+...
+
+---
+name:            cls_i64
+body:             |
+  bb.1:
+    liveins: $x10, $x11
+
+    ; RV32I-LABEL: name: cls_i64
+    ; RV32I: liveins: $x10, $x11
+    ; RV32I-NEXT: {{  $}}
+    ; RV32I-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32I-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; RV32I-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 31
+    ; RV32I-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[COPY1]], [[C]](s32)
+    ; RV32I-NEXT: [[ASHR1:%[0-9]+]]:_(s32) = G_ASHR [[COPY]], [[C]](s32)
+    ; RV32I-NEXT: [[ICMP:%[0-9]+]]:_(s32) = G_ICMP intpred(eq), [[ASHR1]](s32), [[ASHR]]
+    ; RV32I-NEXT: [[ICMP1:%[0-9]+]]:_(s32) = G_ICMP intpred(eq), [[COPY1]](s32), [[ASHR]]
+    ; RV32I-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; RV32I-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[ASHR1]]
+    ; RV32I-NEXT: [[LSHR:%[0-9]+]]:_(s32) = G_LSHR [[XOR]], [[C1]](s32)
+    ; RV32I-NEXT: [[OR:%[0-9]+]]:_(s32) = G_OR [[XOR]], [[LSHR]]
+    ; RV32I-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 2
+    ; RV32I-NEXT: [[LSHR1:%[0-9]+]]:_(s32) = G_LSHR [[OR]], [[C2]](s32)
+    ; RV32I-NEXT: [[OR1:%[0-9]+]]:_(s32) = G_OR [[OR]], [[LSHR1]]
+    ; RV32I-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+    ; RV32I-NEXT: [[LSHR2:%[0-9]+]]:_(s32) = G_LSHR [[OR1]], [[C3]](s32)
+    ; RV32I-NEXT: [[OR2:%[0-9]+]]:_(s32) = G_OR [[OR1]], [[LSHR2]]
+    ; RV32I-NEXT: [[C4:%[0-9]+]]:_(s32) = G_CONSTANT i32 8
+    ; RV32I-NEXT: [[LSHR3:%[0-9]+]]:_(s32) = G_LSHR [[OR2]], [[C4]](s32)
+    ; RV32I-NEXT: [[OR3:%[0-9]+]]:_(s32) = G_OR [[OR2]], [[LSHR3]]
+    ; RV32I-NEXT: [[C5:%[0-9]+]]:_(s32) = G_CONSTANT i32 16
+    ; RV32I-NEXT: [[LSHR4:%[0-9]+]]:_(s32) = G_LSHR [[OR3]], [[C5]](s32)
+    ; RV32I-NEXT: [[OR4:%[0-9]+]]:_(s32) = G_OR [[OR3]], [[LSHR4]]
+    ; RV32I-NEXT: [[LSHR5:%[0-9]+]]:_(s32) = G_LSHR [[OR4]], [[C1]](s32)
+    ; RV32I-NEXT: [[C6:%[0-9]+]]:_(s32) = G_CONSTANT i32 1431655765
+    ; RV32I-NEXT: [[AND:%[0-9]+]]:_(s32) = G_AND [[LSHR5]], [[C6]]
+    ; RV32I-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[OR4]], [[AND]]
+    ; RV32I-NEXT: [[LSHR6:%[0-9]+]]:_(s32) = G_LSHR [[SUB]], [[C2]](s32)
+    ; RV32I-NEXT: [[C7:%[0-9]+]]:_(s32) = G_CONSTANT i32 858993459
+    ; RV32I-NEXT: [[AND1:%[0-9]+]]:_(s32) = G_AND [[LSHR6]], [[C7]]
+    ; RV32I-NEXT: [[AND2:%[0-9]+]]:_(s32) = G_AND [[SUB]], [[C7]]
+    ; RV32I-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[AND1]], [[AND2]]
+    ; RV32I-NEXT: [[LSHR7:%[0-9]+]]:_(s32) = G_LSHR [[ADD]], [[C3]](s32)
+    ; RV32I-NEXT: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[LSHR7]], [[ADD]]
+    ; RV32I-NEXT: [[C8:%[0-9]+]]:_(s32) = G_CONSTANT i32 252645135
+    ; RV32I-NEXT: [[AND3:%[0-9]+]]:_(s32) = G_AND [[ADD1]], [[C8]]
+    ; RV32I-NEXT: [[C9:%[0-9]+]]:_(s32) = G_CONSTANT i32 24
+    ; RV32I-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL [[AND3]], [[C4]](s32)
+    ; RV32I-NEXT: [[ADD2:%[0-9]+]]:_(s32) = G_ADD [[AND3]], [[SHL]]
+    ; RV32I-NEXT: [[SHL1:%[0-9]+]]:_(s32) = G_SHL [[ADD2]], [[C5]](s32)
+    ; RV32I-NEXT: [[ADD3:%[0-9]+]]:_(s32) = G_ADD [[ADD2]], [[SHL1]]
+    ; RV32I-NEXT: [[LSHR8:%[0-9]+]]:_(s32) = G_LSHR [[ADD3]], [[C9]](s32)
+    ; RV32I-NEXT: [[C10:%[0-9]+]]:_(s32) = G_CONSTANT i32 32
+    ; RV32I-NEXT: [[SUB1:%[0-9]+]]:_(s32) = G_SUB [[C10]], [[LSHR8]]
+    ; RV32I-NEXT: [[SUB2:%[0-9]+]]:_(s32) = G_SUB [[SUB1]], [[C1]]
+    ; RV32I-NEXT: [[ADD4:%[0-9]+]]:_(s32) = G_ADD [[SUB2]], [[C10]]
+    ; RV32I-NEXT: [[SELECT:%[0-9]+]]:_(s32) = G_SELECT [[ICMP]](s32), [[ADD4]], [[C]]
+    ; RV32I-NEXT: [[XOR1:%[0-9]+]]:_(s32) = G_XOR [[COPY1]], [[ASHR]]
+    ; RV32I-NEXT: [[LSHR9:%[0-9]+]]:_(s32) = G_LSHR [[XOR1]], [[C1]](s32)
+    ; RV32I-NEXT: [[OR5:%[0-9]+]]:_(s32) = G_OR [[XOR1]], [[LSHR9]]
+    ; RV32I-NEXT: [[LSHR10:%[0-9]+]]:_(s32) = G_LSHR [[OR5]], [[C2]](s32)
+    ; RV32I-NEXT: [[OR6:%[0-9]+]]:_(s32) = G_OR [[OR5]], [[LSHR10]]
+    ; RV32I-NEXT: [[LSHR11:%[0-9]+]]:_(s32) = G_LSHR [[OR6]], [[C3]](s32)
+    ; RV32I-NEXT: [[OR7:%[0-9]+]]:_(s32) = G_OR [[OR6]], [[LSHR11]]
+    ; RV32I-NEXT: [[LSHR12:%[0-9]+]]:_(s32) = G_LSHR [[OR7]], [[C4]](s32)
+    ; RV32I-NEXT: [[OR8:%[0-9]+]]:_(s32) = G_OR [[OR7]], [[LSHR12]]
+    ; RV32I-NEXT: [[LSHR13:%[0-9]+]]:_(s32) = G_LSHR [[OR8]], [[C5]](s32)
+    ; RV32I-NEXT: [[OR9:%[0-9]+]]:_(s32) = G_OR [[OR8]], [[LSHR13]]
+    ; RV32I-NEXT: [[LSHR14:%[0-9]+]]:_(s32) = G_LSHR [[OR9]], [[C1]](s32)
+    ; RV32I-NEXT: [[AND4:%[0-9]+]]:_(s32) = G_AND [[LSHR14]], [[C6]]
+    ; RV32I-NEXT: [[SUB3:%[0-9]+]]:_(s32) = G_SUB [[OR9]], [[AND4]]
+    ; RV32I-NEXT: [[LSHR15:%[0-9]+]]:_(s32) = G_LSHR [[SUB3]], [[C2]](s32)
+    ; RV32I-NEXT: [[AND5:%[0-9]+]]:_(s32) = G_AND [[LSHR15]], [[C7]]
+    ; RV32I-NEXT: [[AND6:%[0-9]+]]:_(s32) = G_AND [[SUB3]], [[C7]]
+    ; RV32I-NEXT: [[ADD5:%[0-9]+]]:_(s32) = G_ADD [[AND5]], [[AND6]]
+    ; RV32I-NEXT: [[LSHR16:%[0-9]+]]:_(s32) = G_LSHR [[ADD5]], [[C3]](s32)
+    ; RV32I-NEXT: [[ADD6:%[0-9]+]]:_(s32) = G_ADD [[LSHR16]], [[ADD5]]
+    ; RV32I-NEXT: [[AND7:%[0-9]+]]:_(s32) = G_AND [[ADD6]], [[C8]]
+    ; RV32I-NEXT: [[SHL2:%[0-9]+]]:_(s32) = G_SHL [[AND7]], [[C4]](s32)
+    ; RV32I-NEXT: [[ADD7:%[0-9]+]]:_(s32) = G_ADD [[AND7]], [[SHL2]]
+    ; RV32I-NEXT: [[SHL3:%[0-9]+]]:_(s32) = G_SHL [[ADD7]], [[C5]](s32)
+    ; RV32I-NEXT: [[ADD8:%[0-9]+]]:_(s32) = G_ADD [[ADD7]], [[SHL3]]
+    ; RV32I-NEXT: [[LSHR17:%[0-9]+]]:_(s32) = G_LSHR [[ADD8]], [[C9]](s32)
+    ; RV32I-NEXT: [[SUB4:%[0-9]+]]:_(s32) = G_SUB [[C10]], [[LSHR17]]
+    ; RV32I-NEXT: [[SUB5:%[0-9]+]]:_(s32) = G_SUB [[SUB4]], [[C1]]
+    ; RV32I-NEXT: [[SELECT1:%[0-9]+]]:_(s32) = G_SELECT [[ICMP1]](s32), [[SELECT]], [[SUB5]]
+    ; RV32I-NEXT: [[C11:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
+    ; RV32I-NEXT: $x10 = COPY [[SELECT1]](s32)
+    ; RV32I-NEXT: $x11 = COPY [[C11]](s32)
+    ; RV32I-NEXT: PseudoRET implicit $x10, implicit $x11
+    ;
+    ; RV32P-LABEL: name: cls_i64
+    ; RV32P: liveins: $x10, $x11
+    ; RV32P-NEXT: {{  $}}
+    ; RV32P-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32P-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; RV32P-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 31
+    ; RV32P-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[COPY1]], [[C]](s32)
+    ; RV32P-NEXT: [[ASHR1:%[0-9]+]]:_(s32) = G_ASHR [[COPY]], [[C]](s32)
+    ; RV32P-NEXT: [[ICMP:%[0-9]+]]:_(s32) = G_ICMP intpred(eq), [[ASHR1]](s32), [[ASHR]]
+    ; RV32P-NEXT: [[ICMP1:%[0-9]+]]:_(s32) = G_ICMP intpred(eq), [[COPY1]](s32), [[ASHR]]
+    ; RV32P-NEXT: [[CTLS:%[0-9]+]]:_(s32) = G_CTLS [[COPY]](s32)
+    ; RV32P-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 32
+    ; RV32P-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[CTLS]], [[C1]]
+    ; RV32P-NEXT: [[SELECT:%[0-9]+]]:_(s32) = G_SELECT [[ICMP]](s32), [[ADD]], [[C]]
+    ; RV32P-NEXT: [[CTLS1:%[0-9]+]]:_(s32) = G_CTLS [[COPY1]](s32)
+    ; RV32P-NEXT: [[SELECT1:%[0-9]+]]:_(s32) = G_SELECT [[ICMP1]](s32), [[SELECT]], [[CTLS1]]
+    ; RV32P-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
+    ; RV32P-NEXT: $x10 = COPY [[SELECT1]](s32)
+    ; RV32P-NEXT: $x11 = COPY [[C2]](s32)
+    ; RV32P-NEXT: PseudoRET implicit $x10, implicit $x11
+    ;
+    ; RV32ZBB-LABEL: name: cls_i64
+    ; RV32ZBB: liveins: $x10, $x11
+    ; RV32ZBB-NEXT: {{  $}}
+    ; RV32ZBB-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $x10
+    ; RV32ZBB-NEXT: [[COPY1:%[0-9]+]]:_(s32) = COPY $x11
+    ; RV32ZBB-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 31
+    ; RV32ZBB-NEXT: [[ASHR:%[0-9]+]]:_(s32) = G_ASHR [[COPY1]], [[C]](s32)
+    ; RV32ZBB-NEXT: [[ASHR1:%[0-9]+]]:_(s32) = G_ASHR [[COPY]], [[C]](s32)
+    ; RV32ZBB-NEXT: [[ICMP:%[0-9]+]]:_(s32) = G_ICMP intpred(eq), [[ASHR1]](s32), [[ASHR]]
+    ; RV32ZBB-NEXT: [[ICMP1:%[0-9]+]]:_(s32) = G_ICMP intpred(eq), [[COPY1]](s32), [[ASHR]]
+    ; RV32ZBB-NEXT: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1
+    ; RV32ZBB-NEXT: [[XOR:%[0-9]+]]:_(s32) = G_XOR [[COPY]], [[ASHR1]]
+    ; RV32ZBB-NEXT: [[CTLZ:%[0-9]+]]:_(s32) = G_CTLZ [[XOR]](s32)
+    ; RV32ZBB-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[CTLZ]], [[C1]]
+    ; RV32ZBB-NEXT: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 32
+    ; RV32ZBB-NEXT: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[SUB]], [[C2]]
+    ; RV32ZBB-NEXT: [[SELECT:%[0-9]+]]:_(s32) = G_SELECT [[ICMP]](s32), [[ADD]], [[C]]
+    ; RV32ZBB-NEXT: [[XOR1:%[0-9]+]]:_(s32) = G_XOR [[COPY1]], [[ASHR]]
+    ; RV32ZBB-NEXT: [[CTLZ1:%[0-9]+]]:_(s32) = G_CTLZ [[XOR1]](s32)
+    ; RV32ZBB-NEXT: [[SUB1:%[0-9]+]]:_(s32) = G_SUB [[CTLZ1]], [[C1]]
+    ; RV32ZBB-NEXT: [[SELECT1:%[0-9]+]]:_(s32) = G_SELECT [[ICMP1]](s32), [[SELECT]], [[SUB1]]
+    ; RV32ZBB-NEXT: [[C3:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
+    ; RV32ZBB-NEXT: $x10 = COPY [[SELECT1]](s32)
+    ; RV32ZBB-NEXT: $x11 = COPY [[C3]](s32)
+    ; RV32ZBB-NEXT: PseudoRET implicit $x10, implicit $x11
+    %1:_(s32) = COPY $x10
+    %2:_(s32) = COPY $x11
+    %0:_(s64) = G_MERGE_VALUES %1(s32), %2(s32)
+    %3:_(s64) = G_CTLS %0(s64)
+    %4:_(s32), %5:_(s32) = G_UNMERGE_VALUES %3(s64)
+    $x10 = COPY %4(s32)
+    $x11 = COPY %5(s32)
+    PseudoRET implicit $x10, implicit $x11
+...
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-ctls-rv64.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-ctls-rv64.mir
new file mode 100644
index 0000000000000..3ff9c44fd4ca3
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer/legalize-ctls-rv64.mir
@@ -0,0 +1,395 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=riscv64 -run-pass=legalizer %s -o - \
+# RUN:   | FileCheck %s --check-prefix=RV64I
+# RUN: llc -mtriple=riscv64 -mattr=+experimental-p -run-pass=legalizer %s -o - \
+# RUN:   | FileCheck %s --check-prefix=RV64P
+# RUN: llc -mtriple=riscv64 -mattr=+zbb -run-pass=legalizer %s -o - \
+# RUN:   | FileCheck %s --check-prefix=RV64ZBB
+
+---
+name:            cls_i8
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; RV64I-LABEL: name: cls_i8
+    ; RV64I: liveins: $x10
+    ; RV64I-NEXT: {{  $}}
+    ; RV64I-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64I-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 7
+    ; RV64I-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
+    ; RV64I-NEXT: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 56
+    ; RV64I-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[COPY]], [[C2]](s64)
+    ; RV64I-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[SHL]], [[C2]](s64)
+    ; RV64I-NEXT: [[ASHR1:%[0-9]+]]:_(s64) = G_ASHR [[ASHR]], [[C]](s64)
+    ; RV64I-NEXT: [[XOR:%[0-9]+]]:_(s64) = G_XOR [[COPY]], [[ASHR1]]
+    ; RV64I-NEXT: [[C3:%[0-9]+]]:_(s64) = G_CONSTANT i64 255
+    ; RV64I-NEXT: [[AND:%[0-9]+]]:_(s64) = G_AND [[XOR]], [[C3]]
+    ; RV64I-NEXT: [[LSHR:%[0-9]+]]:_(s64) = G_LSHR [[AND]], [[C1]](s64)
+    ; RV64I-NEXT: [[OR:%[0-9]+]]:_(s64) = G_OR [[XOR]], [[LSHR]]
+    ; RV64I-NEXT: [[C4:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
+    ; RV64I-NEXT: [[AND1:%[0-9]+]]:_(s64) = G_AND [[OR]], [[C3]]
+    ; RV64I-NEXT: [[LSHR1:%[0-9]+]]:_(s64) = G_LSHR [[AND1]], [[C4]](s64)
+    ; RV64I-NEXT: [[OR1:%[0-9]+]]:_(s64) = G_OR [[OR]], [[LSHR1]]
+    ; RV64I-NEXT: [[C5:%[0-9]+]]:_(s64) = G_CONSTANT i64 4
+    ; RV64I-NEXT: [[AND2:%[0-9]+]]:_(s64) = G_AND [[OR1]], [[C3]]
+    ; RV64I-NEXT: [[LSHR2:%[0-9]+]]:_(s64) = G_LSHR [[AND2]], [[C5]](s64)
+    ; RV64I-NEXT: [[OR2:%[0-9]+]]:_(s64) = G_OR [[OR1]], [[LSHR2]]
+    ; RV64I-NEXT: [[AND3:%[0-9]+]]:_(s64) = G_AND [[OR2]], [[C3]]
+    ; RV64I-NEXT: [[LSHR3:%[0-9]+]]:_(s64) = G_LSHR [[AND3]], [[C1]](s64)
+    ; RV64I-NEXT: [[C6:%[0-9]+]]:_(s64) = G_CONSTANT i64 85
+    ; RV64I-NEXT: [[AND4:%[0-9]+]]:_(s64) = G_AND [[LSHR3]], [[C6]]
+    ; RV64I-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[OR2]], [[AND4]]
+    ; RV64I-NEXT: [[AND5:%[0-9]+]]:_(s64) = G_AND [[SUB]], [[C3]]
+    ; RV64I-NEXT: [[LSHR4:%[0-9]+]]:_(s64) = G_LSHR [[AND5]], [[C4]](s64)
+    ; RV64I-NEXT: [[C7:%[0-9]+]]:_(s64) = G_CONSTANT i64 51
+    ; RV64I-NEXT: [[AND6:%[0-9]+]]:_(s64) = G_AND [[LSHR4]], [[C7]]
+    ; RV64I-NEXT: [[AND7:%[0-9]+]]:_(s64) = G_AND [[SUB]], [[C7]]
+    ; RV64I-NEXT: [[ADD:%[0-9]+]]:_(s64) = G_ADD [[AND6]], [[AND7]]
+    ; RV64I-NEXT: [[LSHR5:%[0-9]+]]:_(s64) = G_LSHR [[ADD]], [[C5]](s64)
+    ; RV64I-NEXT: [[ADD1:%[0-9]+]]:_(s64) = G_ADD [[LSHR5]], [[ADD]]
+    ; RV64I-NEXT: [[C8:%[0-9]+]]:_(s64) = G_CONSTANT i64 15
+    ; RV64I-NEXT: [[AND8:%[0-9]+]]:_(s64) = G_AND [[ADD1]], [[C8]]
+    ; RV64I-NEXT: [[C9:%[0-9]+]]:_(s64) = G_CONSTANT i64 0
+    ; RV64I-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+    ; RV64I-NEXT: $x10 = COPY [[AND8]](s64)
+    ; RV64I-NEXT: $x11 = COPY [[C1]](s64)
+    ; RV64I-NEXT: PseudoCALL target-flags(riscv-call) &__muldi3, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    ; RV64I-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+    ; RV64I-NEXT: [[COPY1:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64I-NEXT: [[AND9:%[0-9]+]]:_(s64) = G_AND [[COPY1]], [[C3]]
+    ; RV64I-NEXT: [[LSHR6:%[0-9]+]]:_(s64) = G_LSHR [[AND9]], [[C9]](s64)
+    ; RV64I-NEXT: [[C10:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
+    ; RV64I-NEXT: [[SUB1:%[0-9]+]]:_(s64) = G_SUB [[C10]], [[LSHR6]]
+    ; RV64I-NEXT: [[SUB2:%[0-9]+]]:_(s64) = G_SUB [[SUB1]], [[C1]]
+    ; RV64I-NEXT: $x10 = COPY [[SUB2]](s64)
+    ; RV64I-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV64P-LABEL: name: cls_i8
+    ; RV64P: liveins: $x10
+    ; RV64P-NEXT: {{  $}}
+    ; RV64P-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64P-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 56
+    ; RV64P-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[COPY]], [[C]](s64)
+    ; RV64P-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[SHL]], [[C]](s64)
+    ; RV64P-NEXT: [[CLSW:%[0-9]+]]:_(s64) = G_CLSW [[ASHR]]
+    ; RV64P-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 24
+    ; RV64P-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[CLSW]], [[C1]]
+    ; RV64P-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s64) = G_SEXT_INREG [[SUB]], 32
+    ; RV64P-NEXT: $x10 = COPY [[SEXT_INREG]](s64)
+    ; RV64P-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV64ZBB-LABEL: name: cls_i8
+    ; RV64ZBB: liveins: $x10
+    ; RV64ZBB-NEXT: {{  $}}
+    ; RV64ZBB-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64ZBB-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 7
+    ; RV64ZBB-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
+    ; RV64ZBB-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s64) = G_SEXT_INREG [[COPY]], 8
+    ; RV64ZBB-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[SEXT_INREG]], [[C]](s64)
+    ; RV64ZBB-NEXT: [[XOR:%[0-9]+]]:_(s64) = G_XOR [[COPY]], [[ASHR]]
+    ; RV64ZBB-NEXT: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 255
+    ; RV64ZBB-NEXT: [[AND:%[0-9]+]]:_(s64) = G_AND [[XOR]], [[C2]]
+    ; RV64ZBB-NEXT: [[CLZW:%[0-9]+]]:_(s64) = G_CLZW [[AND]]
+    ; RV64ZBB-NEXT: [[C3:%[0-9]+]]:_(s64) = G_CONSTANT i64 24
+    ; RV64ZBB-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[CLZW]], [[C3]]
+    ; RV64ZBB-NEXT: [[SEXT_INREG1:%[0-9]+]]:_(s64) = G_SEXT_INREG [[SUB]], 32
+    ; RV64ZBB-NEXT: [[SUB1:%[0-9]+]]:_(s64) = G_SUB [[SEXT_INREG1]], [[C1]]
+    ; RV64ZBB-NEXT: $x10 = COPY [[SUB1]](s64)
+    ; RV64ZBB-NEXT: PseudoRET implicit $x10
+    %1:_(s64) = COPY $x10
+    %0:_(s8) = G_TRUNC %1(s64)
+    %2:_(s8) = G_CTLS %0(s8)
+    %3:_(s64) = G_ANYEXT %2(s8)
+    $x10 = COPY %3(s64)
+    PseudoRET implicit $x10
+...
+
+---
+name:            cls_i16
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; RV64I-LABEL: name: cls_i16
+    ; RV64I: liveins: $x10
+    ; RV64I-NEXT: {{  $}}
+    ; RV64I-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64I-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 15
+    ; RV64I-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
+    ; RV64I-NEXT: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 48
+    ; RV64I-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[COPY]], [[C2]](s64)
+    ; RV64I-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[SHL]], [[C2]](s64)
+    ; RV64I-NEXT: [[ASHR1:%[0-9]+]]:_(s64) = G_ASHR [[ASHR]], [[C]](s64)
+    ; RV64I-NEXT: [[XOR:%[0-9]+]]:_(s64) = G_XOR [[COPY]], [[ASHR1]]
+    ; RV64I-NEXT: [[C3:%[0-9]+]]:_(s64) = G_CONSTANT i64 65535
+    ; RV64I-NEXT: [[AND:%[0-9]+]]:_(s64) = G_AND [[XOR]], [[C3]]
+    ; RV64I-NEXT: [[LSHR:%[0-9]+]]:_(s64) = G_LSHR [[AND]], [[C1]](s64)
+    ; RV64I-NEXT: [[OR:%[0-9]+]]:_(s64) = G_OR [[XOR]], [[LSHR]]
+    ; RV64I-NEXT: [[C4:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
+    ; RV64I-NEXT: [[AND1:%[0-9]+]]:_(s64) = G_AND [[OR]], [[C3]]
+    ; RV64I-NEXT: [[LSHR1:%[0-9]+]]:_(s64) = G_LSHR [[AND1]], [[C4]](s64)
+    ; RV64I-NEXT: [[OR1:%[0-9]+]]:_(s64) = G_OR [[OR]], [[LSHR1]]
+    ; RV64I-NEXT: [[C5:%[0-9]+]]:_(s64) = G_CONSTANT i64 4
+    ; RV64I-NEXT: [[AND2:%[0-9]+]]:_(s64) = G_AND [[OR1]], [[C3]]
+    ; RV64I-NEXT: [[LSHR2:%[0-9]+]]:_(s64) = G_LSHR [[AND2]], [[C5]](s64)
+    ; RV64I-NEXT: [[OR2:%[0-9]+]]:_(s64) = G_OR [[OR1]], [[LSHR2]]
+    ; RV64I-NEXT: [[C6:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
+    ; RV64I-NEXT: [[AND3:%[0-9]+]]:_(s64) = G_AND [[OR2]], [[C3]]
+    ; RV64I-NEXT: [[LSHR3:%[0-9]+]]:_(s64) = G_LSHR [[AND3]], [[C6]](s64)
+    ; RV64I-NEXT: [[OR3:%[0-9]+]]:_(s64) = G_OR [[OR2]], [[LSHR3]]
+    ; RV64I-NEXT: [[AND4:%[0-9]+]]:_(s64) = G_AND [[OR3]], [[C3]]
+    ; RV64I-NEXT: [[LSHR4:%[0-9]+]]:_(s64) = G_LSHR [[AND4]], [[C1]](s64)
+    ; RV64I-NEXT: [[C7:%[0-9]+]]:_(s64) = G_CONSTANT i64 21845
+    ; RV64I-NEXT: [[AND5:%[0-9]+]]:_(s64) = G_AND [[LSHR4]], [[C7]]
+    ; RV64I-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[OR3]], [[AND5]]
+    ; RV64I-NEXT: [[AND6:%[0-9]+]]:_(s64) = G_AND [[SUB]], [[C3]]
+    ; RV64I-NEXT: [[LSHR5:%[0-9]+]]:_(s64) = G_LSHR [[AND6]], [[C4]](s64)
+    ; RV64I-NEXT: [[C8:%[0-9]+]]:_(s64) = G_CONSTANT i64 13107
+    ; RV64I-NEXT: [[AND7:%[0-9]+]]:_(s64) = G_AND [[LSHR5]], [[C8]]
+    ; RV64I-NEXT: [[AND8:%[0-9]+]]:_(s64) = G_AND [[SUB]], [[C8]]
+    ; RV64I-NEXT: [[ADD:%[0-9]+]]:_(s64) = G_ADD [[AND7]], [[AND8]]
+    ; RV64I-NEXT: [[LSHR6:%[0-9]+]]:_(s64) = G_LSHR [[ADD]], [[C5]](s64)
+    ; RV64I-NEXT: [[ADD1:%[0-9]+]]:_(s64) = G_ADD [[LSHR6]], [[ADD]]
+    ; RV64I-NEXT: [[C9:%[0-9]+]]:_(s64) = G_CONSTANT i64 3855
+    ; RV64I-NEXT: [[AND9:%[0-9]+]]:_(s64) = G_AND [[ADD1]], [[C9]]
+    ; RV64I-NEXT: [[C10:%[0-9]+]]:_(s64) = G_CONSTANT i64 257
+    ; RV64I-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+    ; RV64I-NEXT: $x10 = COPY [[AND9]](s64)
+    ; RV64I-NEXT: $x11 = COPY [[C10]](s64)
+    ; RV64I-NEXT: PseudoCALL target-flags(riscv-call) &__muldi3, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    ; RV64I-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+    ; RV64I-NEXT: [[COPY1:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64I-NEXT: [[AND10:%[0-9]+]]:_(s64) = G_AND [[COPY1]], [[C3]]
+    ; RV64I-NEXT: [[LSHR7:%[0-9]+]]:_(s64) = G_LSHR [[AND10]], [[C6]](s64)
+    ; RV64I-NEXT: [[C11:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
+    ; RV64I-NEXT: [[SUB1:%[0-9]+]]:_(s64) = G_SUB [[C11]], [[LSHR7]]
+    ; RV64I-NEXT: [[SUB2:%[0-9]+]]:_(s64) = G_SUB [[SUB1]], [[C1]]
+    ; RV64I-NEXT: $x10 = COPY [[SUB2]](s64)
+    ; RV64I-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV64P-LABEL: name: cls_i16
+    ; RV64P: liveins: $x10
+    ; RV64P-NEXT: {{  $}}
+    ; RV64P-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64P-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 48
+    ; RV64P-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[COPY]], [[C]](s64)
+    ; RV64P-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[SHL]], [[C]](s64)
+    ; RV64P-NEXT: [[CLSW:%[0-9]+]]:_(s64) = G_CLSW [[ASHR]]
+    ; RV64P-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
+    ; RV64P-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[CLSW]], [[C1]]
+    ; RV64P-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s64) = G_SEXT_INREG [[SUB]], 32
+    ; RV64P-NEXT: $x10 = COPY [[SEXT_INREG]](s64)
+    ; RV64P-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV64ZBB-LABEL: name: cls_i16
+    ; RV64ZBB: liveins: $x10
+    ; RV64ZBB-NEXT: {{  $}}
+    ; RV64ZBB-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64ZBB-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 15
+    ; RV64ZBB-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
+    ; RV64ZBB-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s64) = G_SEXT_INREG [[COPY]], 16
+    ; RV64ZBB-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[SEXT_INREG]], [[C]](s64)
+    ; RV64ZBB-NEXT: [[XOR:%[0-9]+]]:_(s64) = G_XOR [[COPY]], [[ASHR]]
+    ; RV64ZBB-NEXT: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 65535
+    ; RV64ZBB-NEXT: [[AND:%[0-9]+]]:_(s64) = G_AND [[XOR]], [[C2]]
+    ; RV64ZBB-NEXT: [[CLZW:%[0-9]+]]:_(s64) = G_CLZW [[AND]]
+    ; RV64ZBB-NEXT: [[C3:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
+    ; RV64ZBB-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[CLZW]], [[C3]]
+    ; RV64ZBB-NEXT: [[SEXT_INREG1:%[0-9]+]]:_(s64) = G_SEXT_INREG [[SUB]], 32
+    ; RV64ZBB-NEXT: [[SUB1:%[0-9]+]]:_(s64) = G_SUB [[SEXT_INREG1]], [[C1]]
+    ; RV64ZBB-NEXT: $x10 = COPY [[SUB1]](s64)
+    ; RV64ZBB-NEXT: PseudoRET implicit $x10
+    %1:_(s64) = COPY $x10
+    %0:_(s16) = G_TRUNC %1(s64)
+    %2:_(s16) = G_CTLS %0(s16)
+    %3:_(s64) = G_ANYEXT %2(s16)
+    $x10 = COPY %3(s64)
+    PseudoRET implicit $x10
+...
+
+---
+name:            cls_i32
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; RV64I-LABEL: name: cls_i32
+    ; RV64I: liveins: $x10
+    ; RV64I-NEXT: {{  $}}
+    ; RV64I-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64I-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 31
+    ; RV64I-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
+    ; RV64I-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s64) = G_SEXT_INREG [[COPY]], 32
+    ; RV64I-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[SEXT_INREG]], [[C]](s64)
+    ; RV64I-NEXT: [[XOR:%[0-9]+]]:_(s64) = G_XOR [[COPY]], [[ASHR]]
+    ; RV64I-NEXT: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 4294967295
+    ; RV64I-NEXT: [[AND:%[0-9]+]]:_(s64) = G_AND [[XOR]], [[C2]]
+    ; RV64I-NEXT: [[LSHR:%[0-9]+]]:_(s64) = G_LSHR [[AND]], [[C1]](s64)
+    ; RV64I-NEXT: [[OR:%[0-9]+]]:_(s64) = G_OR [[XOR]], [[LSHR]]
+    ; RV64I-NEXT: [[C3:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
+    ; RV64I-NEXT: [[AND1:%[0-9]+]]:_(s64) = G_AND [[OR]], [[C2]]
+    ; RV64I-NEXT: [[LSHR1:%[0-9]+]]:_(s64) = G_LSHR [[AND1]], [[C3]](s64)
+    ; RV64I-NEXT: [[OR1:%[0-9]+]]:_(s64) = G_OR [[OR]], [[LSHR1]]
+    ; RV64I-NEXT: [[C4:%[0-9]+]]:_(s64) = G_CONSTANT i64 4
+    ; RV64I-NEXT: [[AND2:%[0-9]+]]:_(s64) = G_AND [[OR1]], [[C2]]
+    ; RV64I-NEXT: [[LSHR2:%[0-9]+]]:_(s64) = G_LSHR [[AND2]], [[C4]](s64)
+    ; RV64I-NEXT: [[OR2:%[0-9]+]]:_(s64) = G_OR [[OR1]], [[LSHR2]]
+    ; RV64I-NEXT: [[C5:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
+    ; RV64I-NEXT: [[AND3:%[0-9]+]]:_(s64) = G_AND [[OR2]], [[C2]]
+    ; RV64I-NEXT: [[LSHR3:%[0-9]+]]:_(s64) = G_LSHR [[AND3]], [[C5]](s64)
+    ; RV64I-NEXT: [[OR3:%[0-9]+]]:_(s64) = G_OR [[OR2]], [[LSHR3]]
+    ; RV64I-NEXT: [[C6:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
+    ; RV64I-NEXT: [[AND4:%[0-9]+]]:_(s64) = G_AND [[OR3]], [[C2]]
+    ; RV64I-NEXT: [[LSHR4:%[0-9]+]]:_(s64) = G_LSHR [[AND4]], [[C6]](s64)
+    ; RV64I-NEXT: [[OR4:%[0-9]+]]:_(s64) = G_OR [[OR3]], [[LSHR4]]
+    ; RV64I-NEXT: [[AND5:%[0-9]+]]:_(s64) = G_AND [[OR4]], [[C2]]
+    ; RV64I-NEXT: [[LSHR5:%[0-9]+]]:_(s64) = G_LSHR [[AND5]], [[C1]](s64)
+    ; RV64I-NEXT: [[C7:%[0-9]+]]:_(s64) = G_CONSTANT i64 1431655765
+    ; RV64I-NEXT: [[AND6:%[0-9]+]]:_(s64) = G_AND [[LSHR5]], [[C7]]
+    ; RV64I-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[OR4]], [[AND6]]
+    ; RV64I-NEXT: [[SEXT_INREG1:%[0-9]+]]:_(s64) = G_SEXT_INREG [[SUB]], 32
+    ; RV64I-NEXT: [[AND7:%[0-9]+]]:_(s64) = G_AND [[SEXT_INREG1]], [[C2]]
+    ; RV64I-NEXT: [[LSHR6:%[0-9]+]]:_(s64) = G_LSHR [[AND7]], [[C3]](s64)
+    ; RV64I-NEXT: [[C8:%[0-9]+]]:_(s64) = G_CONSTANT i64 858993459
+    ; RV64I-NEXT: [[AND8:%[0-9]+]]:_(s64) = G_AND [[LSHR6]], [[C8]]
+    ; RV64I-NEXT: [[AND9:%[0-9]+]]:_(s64) = G_AND [[SEXT_INREG1]], [[C8]]
+    ; RV64I-NEXT: [[ADD:%[0-9]+]]:_(s64) = G_ADD [[AND8]], [[AND9]]
+    ; RV64I-NEXT: [[SEXT_INREG2:%[0-9]+]]:_(s64) = G_SEXT_INREG [[ADD]], 32
+    ; RV64I-NEXT: [[LSHR7:%[0-9]+]]:_(s64) = G_LSHR [[SEXT_INREG2]], [[C4]](s64)
+    ; RV64I-NEXT: [[ADD1:%[0-9]+]]:_(s64) = G_ADD [[LSHR7]], [[SEXT_INREG2]]
+    ; RV64I-NEXT: [[SEXT_INREG3:%[0-9]+]]:_(s64) = G_SEXT_INREG [[ADD1]], 32
+    ; RV64I-NEXT: [[C9:%[0-9]+]]:_(s64) = G_CONSTANT i64 252645135
+    ; RV64I-NEXT: [[AND10:%[0-9]+]]:_(s64) = G_AND [[SEXT_INREG3]], [[C9]]
+    ; RV64I-NEXT: [[C10:%[0-9]+]]:_(s64) = G_CONSTANT i64 16843009
+    ; RV64I-NEXT: [[C11:%[0-9]+]]:_(s64) = G_CONSTANT i64 24
+    ; RV64I-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $x2, implicit $x2
+    ; RV64I-NEXT: $x10 = COPY [[AND10]](s64)
+    ; RV64I-NEXT: $x11 = COPY [[C10]](s64)
+    ; RV64I-NEXT: PseudoCALL target-flags(riscv-call) &__muldi3, csr_ilp32_lp64, implicit-def $x1, implicit $x10, implicit $x11, implicit-def $x10
+    ; RV64I-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $x2, implicit $x2
+    ; RV64I-NEXT: [[COPY1:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64I-NEXT: [[AND11:%[0-9]+]]:_(s64) = G_AND [[COPY1]], [[C2]]
+    ; RV64I-NEXT: [[LSHR8:%[0-9]+]]:_(s64) = G_LSHR [[AND11]], [[C11]](s64)
+    ; RV64I-NEXT: [[C12:%[0-9]+]]:_(s64) = G_CONSTANT i64 32
+    ; RV64I-NEXT: [[SUB1:%[0-9]+]]:_(s64) = G_SUB [[C12]], [[LSHR8]]
+    ; RV64I-NEXT: [[SEXT_INREG4:%[0-9]+]]:_(s64) = G_SEXT_INREG [[SUB1]], 32
+    ; RV64I-NEXT: [[SUB2:%[0-9]+]]:_(s64) = G_SUB [[SEXT_INREG4]], [[C1]]
+    ; RV64I-NEXT: [[SEXT_INREG5:%[0-9]+]]:_(s64) = G_SEXT_INREG [[SUB2]], 32
+    ; RV64I-NEXT: $x10 = COPY [[SEXT_INREG5]](s64)
+    ; RV64I-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV64P-LABEL: name: cls_i32
+    ; RV64P: liveins: $x10
+    ; RV64P-NEXT: {{  $}}
+    ; RV64P-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64P-NEXT: [[CLSW:%[0-9]+]]:_(s64) = G_CLSW [[COPY]]
+    ; RV64P-NEXT: $x10 = COPY [[CLSW]](s64)
+    ; RV64P-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV64ZBB-LABEL: name: cls_i32
+    ; RV64ZBB: liveins: $x10
+    ; RV64ZBB-NEXT: {{  $}}
+    ; RV64ZBB-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64ZBB-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 31
+    ; RV64ZBB-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
+    ; RV64ZBB-NEXT: [[SEXT_INREG:%[0-9]+]]:_(s64) = G_SEXT_INREG [[COPY]], 32
+    ; RV64ZBB-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[SEXT_INREG]], [[C]](s64)
+    ; RV64ZBB-NEXT: [[XOR:%[0-9]+]]:_(s64) = G_XOR [[COPY]], [[ASHR]]
+    ; RV64ZBB-NEXT: [[CLZW:%[0-9]+]]:_(s64) = G_CLZW [[XOR]]
+    ; RV64ZBB-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[CLZW]], [[C1]]
+    ; RV64ZBB-NEXT: [[SEXT_INREG1:%[0-9]+]]:_(s64) = G_SEXT_INREG [[SUB]], 32
+    ; RV64ZBB-NEXT: $x10 = COPY [[SEXT_INREG1]](s64)
+    ; RV64ZBB-NEXT: PseudoRET implicit $x10
+    %1:_(s64) = COPY $x10
+    %0:_(s32) = G_TRUNC %1(s64)
+    %2:_(s32) = G_CTLS %0(s32)
+    %3:_(s64) = G_ANYEXT %2(s32)
+    $x10 = COPY %3(s64)
+    PseudoRET implicit $x10
+...
+
+---
+name:            cls_i64
+body:             |
+  bb.1:
+    liveins: $x10
+
+    ; RV64I-LABEL: name: cls_i64
+    ; RV64I: liveins: $x10
+    ; RV64I-NEXT: {{  $}}
+    ; RV64I-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64I-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 63
+    ; RV64I-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
+    ; RV64I-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[COPY]], [[C]](s64)
+    ; RV64I-NEXT: [[XOR:%[0-9]+]]:_(s64) = G_XOR [[COPY]], [[ASHR]]
+    ; RV64I-NEXT: [[LSHR:%[0-9]+]]:_(s64) = G_LSHR [[XOR]], [[C1]](s64)
+    ; RV64I-NEXT: [[OR:%[0-9]+]]:_(s64) = G_OR [[XOR]], [[LSHR]]
+    ; RV64I-NEXT: [[C2:%[0-9]+]]:_(s64) = G_CONSTANT i64 2
+    ; RV64I-NEXT: [[LSHR1:%[0-9]+]]:_(s64) = G_LSHR [[OR]], [[C2]](s64)
+    ; RV64I-NEXT: [[OR1:%[0-9]+]]:_(s64) = G_OR [[OR]], [[LSHR1]]
+    ; RV64I-NEXT: [[C3:%[0-9]+]]:_(s64) = G_CONSTANT i64 4
+    ; RV64I-NEXT: [[LSHR2:%[0-9]+]]:_(s64) = G_LSHR [[OR1]], [[C3]](s64)
+    ; RV64I-NEXT: [[OR2:%[0-9]+]]:_(s64) = G_OR [[OR1]], [[LSHR2]]
+    ; RV64I-NEXT: [[C4:%[0-9]+]]:_(s64) = G_CONSTANT i64 8
+    ; RV64I-NEXT: [[LSHR3:%[0-9]+]]:_(s64) = G_LSHR [[OR2]], [[C4]](s64)
+    ; RV64I-NEXT: [[OR3:%[0-9]+]]:_(s64) = G_OR [[OR2]], [[LSHR3]]
+    ; RV64I-NEXT: [[C5:%[0-9]+]]:_(s64) = G_CONSTANT i64 16
+    ; RV64I-NEXT: [[LSHR4:%[0-9]+]]:_(s64) = G_LSHR [[OR3]], [[C5]](s64)
+    ; RV64I-NEXT: [[OR4:%[0-9]+]]:_(s64) = G_OR [[OR3]], [[LSHR4]]
+    ; RV64I-NEXT: [[C6:%[0-9]+]]:_(s64) = G_CONSTANT i64 32
+    ; RV64I-NEXT: [[LSHR5:%[0-9]+]]:_(s64) = G_LSHR [[OR4]], [[C6]](s64)
+    ; RV64I-NEXT: [[OR5:%[0-9]+]]:_(s64) = G_OR [[OR4]], [[LSHR5]]
+    ; RV64I-NEXT: [[LSHR6:%[0-9]+]]:_(s64) = G_LSHR [[OR5]], [[C1]](s64)
+    ; RV64I-NEXT: [[C7:%[0-9]+]]:_(s64) = G_CONSTANT i64 6148914691236517205
+    ; RV64I-NEXT: [[AND:%[0-9]+]]:_(s64) = G_AND [[LSHR6]], [[C7]]
+    ; RV64I-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[OR5]], [[AND]]
+    ; RV64I-NEXT: [[LSHR7:%[0-9]+]]:_(s64) = G_LSHR [[SUB]], [[C2]](s64)
+    ; RV64I-NEXT: [[C8:%[0-9]+]]:_(s64) = G_CONSTANT i64 3689348814741910323
+    ; RV64I-NEXT: [[AND1:%[0-9]+]]:_(s64) = G_AND [[LSHR7]], [[C8]]
+    ; RV64I-NEXT: [[AND2:%[0-9]+]]:_(s64) = G_AND [[SUB]], [[C8]]
+    ; RV64I-NEXT: [[ADD:%[0-9]+]]:_(s64) = G_ADD [[AND1]], [[AND2]]
+    ; RV64I-NEXT: [[LSHR8:%[0-9]+]]:_(s64) = G_LSHR [[ADD]], [[C3]](s64)
+    ; RV64I-NEXT: [[ADD1:%[0-9]+]]:_(s64) = G_ADD [[LSHR8]], [[ADD]]
+    ; RV64I-NEXT: [[C9:%[0-9]+]]:_(s64) = G_CONSTANT i64 1085102592571150095
+    ; RV64I-NEXT: [[AND3:%[0-9]+]]:_(s64) = G_AND [[ADD1]], [[C9]]
+    ; RV64I-NEXT: [[C10:%[0-9]+]]:_(s64) = G_CONSTANT i64 56
+    ; RV64I-NEXT: [[SHL:%[0-9]+]]:_(s64) = G_SHL [[AND3]], [[C4]](s64)
+    ; RV64I-NEXT: [[ADD2:%[0-9]+]]:_(s64) = G_ADD [[AND3]], [[SHL]]
+    ; RV64I-NEXT: [[SHL1:%[0-9]+]]:_(s64) = G_SHL [[ADD2]], [[C5]](s64)
+    ; RV64I-NEXT: [[ADD3:%[0-9]+]]:_(s64) = G_ADD [[ADD2]], [[SHL1]]
+    ; RV64I-NEXT: [[SHL2:%[0-9]+]]:_(s64) = G_SHL [[ADD3]], [[C6]](s64)
+    ; RV64I-NEXT: [[ADD4:%[0-9]+]]:_(s64) = G_ADD [[ADD3]], [[SHL2]]
+    ; RV64I-NEXT: [[LSHR9:%[0-9]+]]:_(s64) = G_LSHR [[ADD4]], [[C10]](s64)
+    ; RV64I-NEXT: [[C11:%[0-9]+]]:_(s64) = G_CONSTANT i64 64
+    ; RV64I-NEXT: [[SUB1:%[0-9]+]]:_(s64) = G_SUB [[C11]], [[LSHR9]]
+    ; RV64I-NEXT: [[SUB2:%[0-9]+]]:_(s64) = G_SUB [[SUB1]], [[C1]]
+    ; RV64I-NEXT: $x10 = COPY [[SUB2]](s64)
+    ; RV64I-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV64P-LABEL: name: cls_i64
+    ; RV64P: liveins: $x10
+    ; RV64P-NEXT: {{  $}}
+    ; RV64P-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64P-NEXT: [[CTLS:%[0-9]+]]:_(s64) = G_CTLS [[COPY]](s64)
+    ; RV64P-NEXT: $x10 = COPY [[CTLS]](s64)
+    ; RV64P-NEXT: PseudoRET implicit $x10
+    ;
+    ; RV64ZBB-LABEL: name: cls_i64
+    ; RV64ZBB: liveins: $x10
+    ; RV64ZBB-NEXT: {{  $}}
+    ; RV64ZBB-NEXT: [[COPY:%[0-9]+]]:_(s64) = COPY $x10
+    ; RV64ZBB-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 63
+    ; RV64ZBB-NEXT: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 1
+    ; RV64ZBB-NEXT: [[ASHR:%[0-9]+]]:_(s64) = G_ASHR [[COPY]], [[C]](s64)
+    ; RV64ZBB-NEXT: [[XOR:%[0-9]+]]:_(s64) = G_XOR [[COPY]], [[ASHR]]
+    ; RV64ZBB-NEXT: [[CTLZ:%[0-9]+]]:_(s64) = G_CTLZ [[XOR]](s64)
+    ; RV64ZBB-NEXT: [[SUB:%[0-9]+]]:_(s64) = G_SUB [[CTLZ]], [[C1]]
+    ; RV64ZBB-NEXT: $x10 = COPY [[SUB]](s64)
+    ; RV64ZBB-NEXT: PseudoRET implicit $x10
+    %0:_(s64) = COPY $x10
+    %1:_(s64) = G_CTLS %0(s64)
+    $x10 = COPY %1(s64)
+    PseudoRET implicit $x10
+...
+
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll b/llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll
index 974729423d403..3f403fd8cb9e5 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/rv32p.ll
@@ -5,13 +5,9 @@
 define i8 @cls_i8(i8 %x) {
 ; CHECK-LABEL: cls_i8:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sext.b a1, a0
-; CHECK-NEXT:    srai a1, a1, 7
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    zext.b a0, a0
-; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    sext.b a0, a0
+; CHECK-NEXT:    cls a0, a0
 ; CHECK-NEXT:    addi a0, a0, -24
-; CHECK-NEXT:    addi a0, a0, -1
 ; CHECK-NEXT:    ret
   %a = ashr i8 %x, 7
   %b = xor i8 %x, %a
@@ -23,13 +19,8 @@ define i8 @cls_i8(i8 %x) {
 define i8 @cls_i8_2(i8 %x) {
 ; CHECK-LABEL: cls_i8_2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sext.b a1, a0
-; CHECK-NEXT:    srai a1, a1, 7
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    slli a0, a0, 1
-; CHECK-NEXT:    ori a0, a0, 1
-; CHECK-NEXT:    zext.b a0, a0
-; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    sext.b a0, a0
+; CHECK-NEXT:    cls a0, a0
 ; CHECK-NEXT:    addi a0, a0, -24
 ; CHECK-NEXT:    ret
   %a = ashr i8 %x, 7
@@ -43,13 +34,9 @@ define i8 @cls_i8_2(i8 %x) {
 define i16 @cls_i16(i16 %x) {
 ; CHECK-LABEL: cls_i16:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sext.h a1, a0
-; CHECK-NEXT:    srai a1, a1, 15
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    zext.h a0, a0
-; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    sext.h a0, a0
+; CHECK-NEXT:    cls a0, a0
 ; CHECK-NEXT:    addi a0, a0, -16
-; CHECK-NEXT:    addi a0, a0, -1
 ; CHECK-NEXT:    ret
   %a = ashr i16 %x, 15
   %b = xor i16 %x, %a
@@ -61,13 +48,8 @@ define i16 @cls_i16(i16 %x) {
 define i16 @cls_i16_2(i16 %x) {
 ; CHECK-LABEL: cls_i16_2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sext.h a1, a0
-; CHECK-NEXT:    srai a1, a1, 15
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    slli a0, a0, 1
-; CHECK-NEXT:    ori a0, a0, 1
-; CHECK-NEXT:    zext.h a0, a0
-; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    sext.h a0, a0
+; CHECK-NEXT:    cls a0, a0
 ; CHECK-NEXT:    addi a0, a0, -16
 ; CHECK-NEXT:    ret
   %a = ashr i16 %x, 15
@@ -81,10 +63,7 @@ define i16 @cls_i16_2(i16 %x) {
 define i32 @cls_i32(i32 %x) {
 ; CHECK-LABEL: cls_i32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    srai a1, a0, 31
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    clz a0, a0
-; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    cls a0, a0
 ; CHECK-NEXT:    ret
   %a = ashr i32 %x, 31
   %b = xor i32 %x, %a
@@ -96,11 +75,7 @@ define i32 @cls_i32(i32 %x) {
 define i32 @cls_i32_2(i32 %x) {
 ; CHECK-LABEL: cls_i32_2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    srai a1, a0, 31
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    slli a0, a0, 1
-; CHECK-NEXT:    ori a0, a0, 1
-; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    cls a0, a0
 ; CHECK-NEXT:    ret
   %a = ashr i32 %x, 31
   %b = xor i32 %x, %a
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll b/llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll
index 673e91dad7126..5faf1079a7804 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/rv64p.ll
@@ -4,14 +4,10 @@
 define i8 @cls_i8(i8 %x) {
 ; CHECK-LABEL: cls_i8:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sext.b a1, a0
-; CHECK-NEXT:    srai a1, a1, 7
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    zext.b a0, a0
-; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    sext.b a0, a0
+; CHECK-NEXT:    clsw a0, a0
 ; CHECK-NEXT:    li a1, 24
 ; CHECK-NEXT:    subw a0, a0, a1
-; CHECK-NEXT:    addi a0, a0, -1
 ; CHECK-NEXT:    ret
   %a = ashr i8 %x, 7
   %b = xor i8 %x, %a
@@ -23,13 +19,8 @@ define i8 @cls_i8(i8 %x) {
 define i8 @cls_i8_2(i8 %x) {
 ; CHECK-LABEL: cls_i8_2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sext.b a1, a0
-; CHECK-NEXT:    srai a1, a1, 7
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    slli a0, a0, 1
-; CHECK-NEXT:    ori a0, a0, 1
-; CHECK-NEXT:    zext.b a0, a0
-; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    sext.b a0, a0
+; CHECK-NEXT:    clsw a0, a0
 ; CHECK-NEXT:    li a1, 24
 ; CHECK-NEXT:    subw a0, a0, a1
 ; CHECK-NEXT:    ret
@@ -44,14 +35,10 @@ define i8 @cls_i8_2(i8 %x) {
 define i16 @cls_i16(i16 %x) {
 ; CHECK-LABEL: cls_i16:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sext.h a1, a0
-; CHECK-NEXT:    srai a1, a1, 15
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    zext.h a0, a0
-; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    sext.h a0, a0
+; CHECK-NEXT:    clsw a0, a0
 ; CHECK-NEXT:    li a1, 16
 ; CHECK-NEXT:    subw a0, a0, a1
-; CHECK-NEXT:    addi a0, a0, -1
 ; CHECK-NEXT:    ret
   %a = ashr i16 %x, 15
   %b = xor i16 %x, %a
@@ -63,13 +50,8 @@ define i16 @cls_i16(i16 %x) {
 define i16 @cls_i16_2(i16 %x) {
 ; CHECK-LABEL: cls_i16_2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sext.h a1, a0
-; CHECK-NEXT:    srai a1, a1, 15
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    slli a0, a0, 1
-; CHECK-NEXT:    ori a0, a0, 1
-; CHECK-NEXT:    zext.h a0, a0
-; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    sext.h a0, a0
+; CHECK-NEXT:    clsw a0, a0
 ; CHECK-NEXT:    li a1, 16
 ; CHECK-NEXT:    subw a0, a0, a1
 ; CHECK-NEXT:    ret
@@ -84,10 +66,7 @@ define i16 @cls_i16_2(i16 %x) {
 define i32 @cls_i32(i32 %x) {
 ; CHECK-LABEL: cls_i32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sraiw a1, a0, 31
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    clzw a0, a0
-; CHECK-NEXT:    addiw a0, a0, -1
+; CHECK-NEXT:    clsw a0, a0
 ; CHECK-NEXT:    ret
   %a = ashr i32 %x, 31
   %b = xor i32 %x, %a
@@ -99,11 +78,7 @@ define i32 @cls_i32(i32 %x) {
 define i32 @cls_i32_2(i32 %x) {
 ; CHECK-LABEL: cls_i32_2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    sraiw a1, a0, 31
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    slli a0, a0, 1
-; CHECK-NEXT:    ori a0, a0, 1
-; CHECK-NEXT:    clzw a0, a0
+; CHECK-NEXT:    clsw a0, a0
 ; CHECK-NEXT:    ret
   %a = ashr i32 %x, 31
   %b = xor i32 %x, %a
@@ -116,10 +91,7 @@ define i32 @cls_i32_2(i32 %x) {
 define i64 @cls_i64(i64 %x) {
 ; CHECK-LABEL: cls_i64:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    srai a1, a0, 63
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    clz a0, a0
-; CHECK-NEXT:    addi a0, a0, -1
+; CHECK-NEXT:    cls a0, a0
 ; CHECK-NEXT:    ret
   %a = ashr i64 %x, 63
   %b = xor i64 %x, %a
@@ -131,11 +103,7 @@ define i64 @cls_i64(i64 %x) {
 define i64 @cls_i64_2(i64 %x) {
 ; CHECK-LABEL: cls_i64_2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    srai a1, a0, 63
-; CHECK-NEXT:    xor a0, a0, a1
-; CHECK-NEXT:    slli a0, a0, 1
-; CHECK-NEXT:    ori a0, a0, 1
-; CHECK-NEXT:    clz a0, a0
+; CHECK-NEXT:    cls a0, a0
 ; CHECK-NEXT:    ret
   %a = ashr i64 %x, 63
   %b = xor i64 %x, %a



More information about the llvm-commits mailing list