[llvm] [RISCV][GlobalISel] Add G_CLMULH support (PR #221686)
Jian Li via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 7 05:44:15 PDT 2026
https://github.com/janearthur updated https://github.com/llvm/llvm-project/pull/221686
>From 289df580e1aba10802fde8494c26189ac987e09a Mon Sep 17 00:00:00 2001
From: Jian Li <hellothedreamer at 126.com>
Date: Mon, 7 Sep 2026 16:56:07 +0800
Subject: [PATCH 1/2] [RISCV][GlobalISel] Add G_CLMULH support
Add G_CLMULH to represent the high half of a carry-less
multiplication in generic Machine IR.
Translate llvm.riscv.clmulh to G_CLMULH and mark it legal for
native XLEN scalar types when Zbkc is available. Reuse the existing
SelectionDAG pattern to select the RISC-V CLMULH instruction.
---
llvm/docs/GlobalISel/GenericOpcode.md | 10 ++++++++++
llvm/include/llvm/Support/TargetOpcodes.def | 3 +++
llvm/include/llvm/Target/GenericOpcodes.td | 8 ++++++++
.../llvm/Target/GlobalISel/SelectionDAGCompat.td | 1 +
.../lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 9 +++++++++
.../GlobalISel/legalizer-info-validation.mir | 3 +++
llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv32.ll | 15 +++++++++++++++
llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv64.ll | 15 +++++++++++++++
.../GlobalISel/legalizer-info-validation.mir | 3 +++
llvm/test/TableGen/get-named-operand-idx.td | 4 ++--
10 files changed, 69 insertions(+), 2 deletions(-)
diff --git a/llvm/docs/GlobalISel/GenericOpcode.md b/llvm/docs/GlobalISel/GenericOpcode.md
index 8e2a5be0022b5..39b11b1d76355 100644
--- a/llvm/docs/GlobalISel/GenericOpcode.md
+++ b/llvm/docs/GlobalISel/GenericOpcode.md
@@ -311,6 +311,16 @@ Perform integer carry-less multiplication.
%dst:_(s32) = G_CLMUL %src_0:_(s32), %src1:_(32)
```
+### G_CLMULH
+
+Perform carry-less multiplication and return the high half of the result.
+For N-bit operands, compute the 2N-bit polynomial product over GF(2) and
+return bits [2N-1:N]. The destination and both sources have the same type.
+
+```none
+%dst:_(s32) = G_CLMULH %src0:_(s32), %src1:_(s32)
+```
+
### G_SDIVREM, G_UDIVREM
Perform integer division and remainder thereby producing two results.
diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def
index 549a93b7bec3a..314c2b277a5f8 100644
--- a/llvm/include/llvm/Support/TargetOpcodes.def
+++ b/llvm/include/llvm/Support/TargetOpcodes.def
@@ -862,6 +862,9 @@ HANDLE_TARGET_OPCODE(G_BITREVERSE)
// Generic carry-less multiply instruction.
HANDLE_TARGET_OPCODE(G_CLMUL)
+// Generic carry-less multiply high instruction.
+HANDLE_TARGET_OPCODE(G_CLMULH)
+
/// Floating point ceil.
HANDLE_TARGET_OPCODE(G_FCEIL)
diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index b1a7a01f4bbe3..6e1a2b7f50954 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -592,6 +592,14 @@ def G_CLMUL : GenericInstruction {
let isCommutable = true;
}
+// Generic carry-less multiplication returning the high half of the result.
+def G_CLMULH : GenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins type0:$src1, type0:$src2);
+ let hasSideEffects = false;
+ let isCommutable = true;
+}
+
//------------------------------------------------------------------------------
// Overflow ops
diff --git a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
index a91ef6058b7e4..a3423d5019e68 100644
--- a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
+++ b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
@@ -73,6 +73,7 @@ def : GINodeEquiv<G_ADD, add>;
def : GINodeEquiv<G_SUB, sub>;
def : GINodeEquiv<G_MUL, mul>;
def : GINodeEquiv<G_CLMUL, clmul>;
+def : GINodeEquiv<G_CLMULH, clmulh>;
def : GINodeEquiv<G_UMULH, mulhu>;
def : GINodeEquiv<G_SMULH, mulhs>;
def : GINodeEquiv<G_SDIV, sdiv>;
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index d0ee9bb39cc8b..587e34576f231 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -237,6 +237,10 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
.legalFor(ST.hasStdExtZbkc(), {sXLen})
.unsupported();
+ getActionDefinitionsBuilder(G_CLMULH)
+ .legalFor(ST.hasStdExtZbkc(), {sXLen})
+ .unsupported();
+
auto &CountZerosActions = getActionDefinitionsBuilder({G_CTLZ, G_CTTZ});
auto &CountZerosPoisonActions =
getActionDefinitionsBuilder({G_CTLZ_ZERO_POISON, G_CTTZ_ZERO_POISON});
@@ -845,6 +849,11 @@ bool RISCVLegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
switch (IntrinsicID) {
default:
return false;
+ case Intrinsic::riscv_clmulh:
+ Helper.MIRBuilder.buildInstr(TargetOpcode::G_CLMULH, {MI.getOperand(0)},
+ {MI.getOperand(2), MI.getOperand(3)});
+ MI.eraseFromParent();
+ return true;
case Intrinsic::vacopy: {
// vacopy arguments must be legal because of the intrinsic signature.
// No need to check here.
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
index b99c06cba9208..2fd1291bd2f88 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
@@ -778,6 +778,9 @@
# DEBUG-NEXT: G_CLMUL (opcode [[CLMUL_OPC:[0-9]+]]): 1 type index, 0 imm indices
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
+# DEBUG-NEXT: G_CLMULH (opcode {{[0-9]+}}): 1 type index, 0 imm indices
+# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: G_FCEIL (opcode {{[0-9]+}}): 1 type index, 0 imm indices
# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv32.ll b/llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv32.ll
index 316069fb06e1b..95a1bf586d879 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv32.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv32.ll
@@ -7,6 +7,7 @@
; RUN: | FileCheck %s --check-prefix=RV32ZBKC
declare i32 @llvm.clmul.i32(i32, i32)
+declare i32 @llvm.riscv.clmulh.i32(i32, i32)
define i32 @clmul_i32(i32 %a, i32 %b) {
; RV32ZBC-LABEL: clmul_i32:
@@ -21,3 +22,17 @@ define i32 @clmul_i32(i32 %a, i32 %b) {
%r = call i32 @llvm.clmul.i32(i32 %a, i32 %b)
ret i32 %r
}
+
+define i32 @clmulh_i32(i32 %a, i32 %b) {
+; RV32ZBC-LABEL: clmulh_i32:
+; RV32ZBC: # %bb.0:
+; RV32ZBC-NEXT: clmulh a0, a0, a1
+; RV32ZBC-NEXT: ret
+;
+; RV32ZBKC-LABEL: clmulh_i32:
+; RV32ZBKC: # %bb.0:
+; RV32ZBKC-NEXT: clmulh a0, a0, a1
+; RV32ZBKC-NEXT: ret
+ %r = call i32 @llvm.riscv.clmulh.i32(i32 %a, i32 %b)
+ ret i32 %r
+}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv64.ll b/llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv64.ll
index 6549e63ab9af2..4e97a02c3af6e 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv64.ll
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/clmul-rv64.ll
@@ -7,6 +7,7 @@
; RUN: | FileCheck %s --check-prefix=RV64ZBKC
declare i64 @llvm.clmul.i64(i64, i64)
+declare i64 @llvm.riscv.clmulh.i64(i64, i64)
define i64 @clmul_i64(i64 %a, i64 %b) {
; RV64ZBC-LABEL: clmul_i64:
@@ -21,3 +22,17 @@ define i64 @clmul_i64(i64 %a, i64 %b) {
%r = call i64 @llvm.clmul.i64(i64 %a, i64 %b)
ret i64 %r
}
+
+define i64 @clmulh_i64(i64 %a, i64 %b) {
+; RV64ZBC-LABEL: clmulh_i64:
+; RV64ZBC: # %bb.0:
+; RV64ZBC-NEXT: clmulh a0, a0, a1
+; RV64ZBC-NEXT: ret
+;
+; RV64ZBKC-LABEL: clmulh_i64:
+; RV64ZBKC: # %bb.0:
+; RV64ZBKC-NEXT: clmulh a0, a0, a1
+; RV64ZBKC-NEXT: ret
+ %r = call i64 @llvm.riscv.clmulh.i64(i64 %a, i64 %b)
+ ret i64 %r
+}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
index a9a045daaaa9f..944cadc863378 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
@@ -772,6 +772,9 @@
# DEBUG-NEXT: G_CLMUL (opcode {{[0-9]+}}): 1 type index, 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_CLMULH (opcode {{[0-9]+}}): 1 type index, 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_FCEIL (opcode {{[0-9]+}}): 1 type index, 0 imm indices
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
diff --git a/llvm/test/TableGen/get-named-operand-idx.td b/llvm/test/TableGen/get-named-operand-idx.td
index 31de6faec0da4..fab74e2b6447e 100644
--- a/llvm/test/TableGen/get-named-operand-idx.td
+++ b/llvm/test/TableGen/get-named-operand-idx.td
@@ -98,8 +98,8 @@ defm : RemapAllTargetPseudoPointerOperands<RegClass>;
// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2,
-// CHECK-NEXT: 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
+// CHECK-NEXT: 2, 0,
// CHECK-NEXT: };
// CHECK-NEXT: return InstructionIndex[Opcode];
// CHECK-NEXT: }
>From cb0281b69dbca583a015ae3eab5894b3854313c6 Mon Sep 17 00:00:00 2001
From: Jian Li <hellothedreamer at 126.com>
Date: Mon, 7 Sep 2026 20:40:10 +0800
Subject: [PATCH 2/2] [RISCV][GlobalISel] Fix G_CLMULH formatting
---
llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
index 587e34576f231..3e28aa5184421 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -851,7 +851,7 @@ bool RISCVLegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
return false;
case Intrinsic::riscv_clmulh:
Helper.MIRBuilder.buildInstr(TargetOpcode::G_CLMULH, {MI.getOperand(0)},
- {MI.getOperand(2), MI.getOperand(3)});
+ {MI.getOperand(2), MI.getOperand(3)});
MI.eraseFromParent();
return true;
case Intrinsic::vacopy: {
More information about the llvm-commits
mailing list