Skip to content

Conversation

@yingopq
Copy link
Contributor

@yingopq yingopq commented Feb 4, 2026

MIPS16 cannot handle constant pools created by CTTZ table lookup expansion. This causes "Cannot select" errors when trying to select MipsISD::Lo nodes for constant pool addresses.

The fix adds a virtual method canUseConstantPoolForCTTZ() to TargetLowering. MIPS16 overrides this to return false, using ISD::CTPOP.

Fix #61055.

MIPS16 cannot handle constant pools created by CTTZ table lookup
expansion. This causes "Cannot select" errors when trying to select
MipsISD::Lo nodes for constant pool addresses.

The fix adds a virtual method `canUseConstantPoolForCTTZ()` to
TargetLowering. MIPS16 overrides this to return false, using ISD::CTPOP.

Fix llvm#61055.
@llvmbot
Copy link
Member

llvmbot commented Feb 4, 2026

@llvm/pr-subscribers-llvm-selectiondag
@llvm/pr-subscribers-llvm-globalisel

@llvm/pr-subscribers-backend-mips

Author: None (yingopq)

Changes

MIPS16 cannot handle constant pools created by CTTZ table lookup expansion. This causes "Cannot select" errors when trying to select MipsISD::Lo nodes for constant pool addresses.

The fix adds a virtual method canUseConstantPoolForCTTZ() to TargetLowering. MIPS16 overrides this to return false, using ISD::CTPOP.

Fix #61055.


Full diff: https://github.com/llvm/llvm-project/pull/179633.diff

5 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/TargetLowering.h (+3)
  • (modified) llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsISelLowering.cpp (+5)
  • (modified) llvm/lib/Target/Mips/MipsISelLowering.h (+1)
  • (added) llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/cttz-mips16.ll (+61)
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index ada4ffd3bcc89..27954affe7b61 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -727,6 +727,9 @@ class LLVM_ABI TargetLoweringBase {
     return false;
   }
 
+  /// Return true if constant pool can be used for CTTZ expansion.
+  virtual bool canUseConstantPoolForCTTZ() const { return true; }
+
   /// Return true if ctlz instruction is fast.
   virtual bool isCtlzFast() const {
     return false;
diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
index a10b93063dc27..2c02e761b1b03 100644
--- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
@@ -9691,8 +9691,9 @@ SDValue TargetLowering::expandCTTZ(SDNode *Node, SelectionDAG &DAG) const {
 
   // Emit Table Lookup if ISD::CTPOP used in the fallback path below is going
   // to be expanded or converted to a libcall.
+  const TargetLowering &TLI = DAG.getTargetLoweringInfo();
   if (!VT.isVector() && !isOperationLegalOrCustomOrPromote(ISD::CTPOP, VT) &&
-      !isOperationLegal(ISD::CTLZ, VT))
+      !isOperationLegal(ISD::CTLZ, VT) && TLI.canUseConstantPoolForCTTZ())
     if (SDValue V = CTTZTableLookup(Node, DAG, dl, VT, Op, NumBitsPerElt))
       return V;
 
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp
index f7e2825edcc5d..d92b70633fb94 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp
@@ -1190,6 +1190,11 @@ bool MipsTargetLowering::isCheapToSpeculateCtlz(Type *Ty) const {
   return Subtarget.hasMips32();
 }
 
+bool MipsTargetLowering::canUseConstantPoolForCTTZ() const {
+  // MIPS16 cannot use constant pools for CTTZ expansion.
+  return !Subtarget.inMips16Mode();
+}
+
 bool MipsTargetLowering::hasBitTest(SDValue X, SDValue Y) const {
   // We can use ANDI+SLTIU as a bit test. Y contains the bit position.
   // For MIPSR2 or later, we may be able to use the `ext` instruction or its
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.h b/llvm/lib/Target/Mips/MipsISelLowering.h
index 806fb59f5ff3e..17fd4db0ba152 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.h
+++ b/llvm/lib/Target/Mips/MipsISelLowering.h
@@ -81,6 +81,7 @@ class TargetRegisterClass;
 
     bool isCheapToSpeculateCttz(Type *Ty) const override;
     bool isCheapToSpeculateCtlz(Type *Ty) const override;
+    bool canUseConstantPoolForCTTZ() const override;
     bool hasBitTest(SDValue X, SDValue Y) const override;
     bool shouldFoldConstantShiftPairToMask(const SDNode *N) const override;
 
diff --git a/llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/cttz-mips16.ll b/llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/cttz-mips16.ll
new file mode 100644
index 0000000000000..7dbb4fc567a8b
--- /dev/null
+++ b/llvm/test/CodeGen/Mips/GlobalISel/llvm-ir/cttz-mips16.ll
@@ -0,0 +1,61 @@
+; RUN: llc -mtriple=mipsel-linux-gnu -mcpu=mips32 -mattr=+mips16 -verify-machineinstrs < %s | FileCheck %s -check-prefixes=MIPS16
+
+define i32 @cttz_i32(i32 %a) {
+; MIPS16-LABEL: cttz_i32:
+; MIPS16:       # %bb.0: # %entry
+; MIPS16-NEXT:    not $2, $4
+; MIPS16-NEXT:	  addiu $4, -1	# 16 bit inst
+; MIPS16-NEXT:	  and $4, $2
+; MIPS16-NEXT:	  srl $2, $4, 1
+; MIPS16-NEXT:	  lw $3, $CPI0_0	# 16 bit inst
+; MIPS16-NEXT:	  and $3, $2
+; MIPS16-NEXT:	  subu $2, $4, $3
+; MIPS16-NEXT:	  lw $3, $CPI0_1	# 16 bit inst
+; MIPS16-NEXT:	  srl $4, $2, 2
+; MIPS16-NEXT:	  and $2, $3
+; MIPS16-NEXT:	  and $4, $3
+; MIPS16-NEXT:	  addu $2, $2, $4
+; MIPS16-NEXT:	  srl $3, $2, 4
+; MIPS16-NEXT:	  addu $2, $2, $3
+; MIPS16-NEXT:	  lw $3, $CPI0_2	# 16 bit inst
+; MIPS16-NEXT:	  and $3, $2
+; MIPS16-NEXT:	  lw $2, $CPI0_3	# 16 bit inst
+; MIPS16-NEXT:	  mult $3, $2
+; MIPS16-NEXT:	  mflo $2
+; MIPS16-NEXT:	  srl $2, $2, 24
+; MIPS16-NEXT:	  jrc $ra
+
+entry:
+  %0 = call i32 @llvm.cttz.i32(i32 %a, i1 false)
+  ret i32 %0
+}
+declare i32 @llvm.cttz.i32(i32, i1 immarg)
+
+define i64 @cttz_i64(i64  %a) {
+; MIPS16-LABEL: cttz_i64:
+; MIPS16:       # %bb.1: # %entry
+; MIPS16-NEXT:    not $4, $5
+; MIPS16-NEXT:	  addiu	$5, -1	# 16 bit inst
+; MIPS16-NEXT:	  and $5, $4
+; MIPS16-NEXT:	  srl $4, $5, 1
+; MIPS16-NEXT:	  and $4, $7
+; MIPS16-NEXT:	  subu $4, $5, $4
+; MIPS16-NEXT:	  srl $5, $4, 2
+; MIPS16-NEXT:	  and $4, $6
+; MIPS16-NEXT:	  and $5, $6
+; MIPS16-NEXT:	  addu $4, $4, $5
+; MIPS16-NEXT:	  srl $5, $4, 4
+; MIPS16-NEXT:	  addu $4, $4, $5
+; MIPS16-NEXT:	  and $4, $3
+; MIPS16-NEXT:	  mult $4, $2
+; MIPS16-NEXT:	  mflo $2
+; MIPS16-NEXT:	  srl $2, $2, 24
+; MIPS16-NEXT:	  addiu	$2, 32	# 16 bit inst
+; MIPS16-NEXT:	  li $3, 0
+; MIPS16-NEXT:	  jrc $ra
+
+entry:
+  %0 = call i64 @llvm.cttz.i64(i64 %a, i1 false)
+  ret i64 %0
+}
+declare i64 @llvm.cttz.i64(i64, i1 immarg)

Copy link
Collaborator

@RKSimon RKSimon left a comment

Choose a reason for hiding this comment

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

Are we not just missing a legality check inside CTTZTableLookup? I'd really like to avoid yet another TLI hack.

Copy link
Contributor

@arsenm arsenm left a comment

Choose a reason for hiding this comment

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

No new TLI hook

Comment on lines +730 to +732
/// Return true if constant pool can be used for CTTZ expansion.
virtual bool canUseConstantPoolForCTTZ() const { return true; }

Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't make any sense and is too specific

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.

cttz.i32 fails to lower on mips16

4 participants