[llvm] 5e14ef5 - [RISCV][GlobalISel] Add G_CLMULR support (#224525)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 09:52:09 PDT 2026
Author: Jian Li
Date: 2026-09-21T09:51:34-07:00
New Revision: 5e14ef5d10879eafe6b412278d6d851719e62c88
URL: https://github.com/llvm/llvm-project/commit/5e14ef5d10879eafe6b412278d6d851719e62c88
DIFF: https://github.com/llvm/llvm-project/commit/5e14ef5d10879eafe6b412278d6d851719e62c88.diff
LOG: [RISCV][GlobalISel] Add G_CLMULR support (#224525)
Add G_CLMULR to represent reversed carry-less multiplication in generic
Machine IR.
Translate llvm.riscv.clmulr to G_CLMULR and mark it legal for native
XLEN scalar types when Zbc is available.
Added:
llvm/test/CodeGen/RISCV/GlobalISel/clmulr-rv32.ll
llvm/test/CodeGen/RISCV/GlobalISel/clmulr-rv64.ll
Modified:
llvm/docs/GlobalISel/GenericOpcode.md
llvm/include/llvm/Support/TargetOpcodes.def
llvm/include/llvm/Target/GenericOpcodes.td
llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
llvm/test/TableGen/get-named-operand-idx.td
Removed:
################################################################################
diff --git a/llvm/docs/GlobalISel/GenericOpcode.md b/llvm/docs/GlobalISel/GenericOpcode.md
index 39b11b1d76355..de316fcfb134d 100644
--- a/llvm/docs/GlobalISel/GenericOpcode.md
+++ b/llvm/docs/GlobalISel/GenericOpcode.md
@@ -321,6 +321,18 @@ return bits [2N-1:N]. The destination and both sources have the same type.
%dst:_(s32) = G_CLMULH %src0:_(s32), %src1:_(s32)
```
+### G_CLMULR
+
+Perform reversed carry-less multiplication.
+For N-bit operands, compute the 2N-bit polynomial product over GF(2) and
+return bits [2N-2:N-1]. Equivalent to
+`bitreverse(clmul(bitreverse(x), bitreverse(y)))`.
+The destination and both sources have the same type.
+
+```none
+%dst:_(s32) = G_CLMULR %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 314c2b277a5f8..9e21793627c54 100644
--- a/llvm/include/llvm/Support/TargetOpcodes.def
+++ b/llvm/include/llvm/Support/TargetOpcodes.def
@@ -865,6 +865,9 @@ HANDLE_TARGET_OPCODE(G_CLMUL)
// Generic carry-less multiply high instruction.
HANDLE_TARGET_OPCODE(G_CLMULH)
+// Generic reversed carry-less multiply instruction.
+HANDLE_TARGET_OPCODE(G_CLMULR)
+
/// 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 6e1a2b7f50954..183abcff9a9ba 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -600,6 +600,16 @@ def G_CLMULH : GenericInstruction {
let isCommutable = true;
}
+// Generic reversed carry-less multiplication.
+// For N-bit operands, compute the 2N-bit polynomial product over GF(2) and
+// return bits [2N-2:N-1].
+def G_CLMULR : 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 a3423d5019e68..8db92b0702584 100644
--- a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
+++ b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
@@ -74,6 +74,7 @@ def : GINodeEquiv<G_SUB, sub>;
def : GINodeEquiv<G_MUL, mul>;
def : GINodeEquiv<G_CLMUL, clmul>;
def : GINodeEquiv<G_CLMULH, clmulh>;
+def : GINodeEquiv<G_CLMULR, clmulr>;
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 1840d0b7c8ebf..451d21df4d95c 100644
--- a/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
+++ b/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp
@@ -242,6 +242,12 @@ RISCVLegalizerInfo::RISCVLegalizerInfo(const RISCVSubtarget &ST)
.customFor(ST.is64Bit() && ST.hasStdExtZbkc(), {s32})
.unsupported();
+ // CLMULR is Zbc-only; Zbkc is a subset that has CLMUL/CLMULH but not CLMULR.
+ getActionDefinitionsBuilder(G_CLMULR)
+ .legalFor(ST.hasStdExtZbc(), {sXLen})
+ .customFor(ST.is64Bit() && ST.hasStdExtZbc(), {s32})
+ .unsupported();
+
auto &CountZerosActions = getActionDefinitionsBuilder({G_CTLZ, G_CTTZ});
auto &CountZerosPoisonActions =
getActionDefinitionsBuilder({G_CTLZ_ZERO_POISON, G_CTTZ_ZERO_POISON});
@@ -868,6 +874,11 @@ bool RISCVLegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
{MI.getOperand(2), MI.getOperand(3)});
MI.eraseFromParent();
return true;
+ case Intrinsic::riscv_clmulr:
+ Helper.MIRBuilder.buildInstr(TargetOpcode::G_CLMULR, {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.
@@ -1626,18 +1637,20 @@ bool RISCVLegalizerInfo::legalizeCustom(
return false;
case TargetOpcode::G_ABS:
return Helper.lowerAbsToMaxNeg(MI);
- case TargetOpcode::G_CLMULH: {
+ case TargetOpcode::G_CLMULH:
+ case TargetOpcode::G_CLMULR: {
assert(STI.is64Bit() &&
MRI.getType(MI.getOperand(0).getReg()) == LLT::scalar(32) &&
"Unexpected custom legalization");
// Shift both inputs by 32 so the full product has 64 trailing zeros.
- // CLMULH then returns the original 64-bit product. Extract its high half.
+ // Perform CLMULH or CLMULR on the shifted inputs, then extract the upper
+ // 32 bits of the result.
auto Shift = MIRBuilder.buildConstant(sXLen, 32);
auto LHS = MIRBuilder.buildAnyExt(sXLen, MI.getOperand(1));
auto RHS = MIRBuilder.buildAnyExt(sXLen, MI.getOperand(2));
auto ShiftedLHS = MIRBuilder.buildShl(sXLen, LHS, Shift);
auto ShiftedRHS = MIRBuilder.buildShl(sXLen, RHS, Shift);
- auto Product = MIRBuilder.buildInstr(TargetOpcode::G_CLMULH, {sXLen},
+ auto Product = MIRBuilder.buildInstr(MI.getOpcode(), {sXLen},
{ShiftedLHS, ShiftedRHS});
auto High = MIRBuilder.buildLShr(sXLen, Product, Shift);
MIRBuilder.buildTrunc(MI.getOperand(0), High);
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
index a02e9ed4dff85..61b38ac3fdc93 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
@@ -781,6 +781,9 @@
# 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_CLMULR (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/clmulr-rv32.ll b/llvm/test/CodeGen/RISCV/GlobalISel/clmulr-rv32.ll
new file mode 100644
index 0000000000000..301140fee41b8
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/clmulr-rv32.ll
@@ -0,0 +1,15 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -mattr=+zbc -global-isel \
+; RUN: -global-isel-abort=1 -verify-machineinstrs < %s \
+; RUN: | FileCheck %s --check-prefix=RV32ZBC
+
+declare i32 @llvm.riscv.clmulr.i32(i32, i32)
+
+define i32 @clmulr_i32(i32 %a, i32 %b) {
+; RV32ZBC-LABEL: clmulr_i32:
+; RV32ZBC: # %bb.0:
+; RV32ZBC-NEXT: clmulr a0, a0, a1
+; RV32ZBC-NEXT: ret
+ %r = call i32 @llvm.riscv.clmulr.i32(i32 %a, i32 %b)
+ ret i32 %r
+}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/clmulr-rv64.ll b/llvm/test/CodeGen/RISCV/GlobalISel/clmulr-rv64.ll
new file mode 100644
index 0000000000000..9dee309f8575b
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/clmulr-rv64.ll
@@ -0,0 +1,42 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 -mattr=+zbc -global-isel \
+; RUN: -global-isel-abort=1 -verify-machineinstrs < %s \
+; RUN: | FileCheck %s --check-prefix=RV64ZBC
+
+declare i64 @llvm.riscv.clmulr.i64(i64, i64)
+declare i32 @llvm.riscv.clmulr.i32(i32, i32)
+
+define i64 @clmulr_i64(i64 %a, i64 %b) {
+; RV64ZBC-LABEL: clmulr_i64:
+; RV64ZBC: # %bb.0:
+; RV64ZBC-NEXT: clmulr a0, a0, a1
+; RV64ZBC-NEXT: ret
+ %r = call i64 @llvm.riscv.clmulr.i64(i64 %a, i64 %b)
+ ret i64 %r
+}
+
+define signext i32 @clmulr_i32(i32 signext %a, i32 signext %b) {
+; RV64ZBC-LABEL: clmulr_i32:
+; RV64ZBC: # %bb.0:
+; RV64ZBC-NEXT: slli a0, a0, 32
+; RV64ZBC-NEXT: slli a1, a1, 32
+; RV64ZBC-NEXT: clmulr a0, a0, a1
+; RV64ZBC-NEXT: srli a0, a0, 32
+; RV64ZBC-NEXT: sext.w a0, a0
+; RV64ZBC-NEXT: ret
+ %r = call i32 @llvm.riscv.clmulr.i32(i32 %a, i32 %b)
+ ret i32 %r
+}
+
+define signext i32 @clmulr_i32_zext(i32 zeroext %a, i32 zeroext %b) {
+; RV64ZBC-LABEL: clmulr_i32_zext:
+; RV64ZBC: # %bb.0:
+; RV64ZBC-NEXT: slli a0, a0, 32
+; RV64ZBC-NEXT: slli a1, a1, 32
+; RV64ZBC-NEXT: clmulr a0, a0, a1
+; RV64ZBC-NEXT: srli a0, a0, 32
+; RV64ZBC-NEXT: sext.w a0, a0
+; RV64ZBC-NEXT: ret
+ %r = call i32 @llvm.riscv.clmulr.i32(i32 %a, i32 %b)
+ ret i32 %r
+}
diff --git a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
index 5b75b68557d1e..cc4d7977b3921 100644
--- a/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/RISCV/GlobalISel/legalizer-info-validation.mir
@@ -776,6 +776,9 @@
# 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_CLMULR (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 fab74e2b6447e..165d3f705c04c 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, 0, 1, 2,
-// CHECK-NEXT: 2, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+// CHECK-NEXT: 2, 2, 0,
// CHECK-NEXT: };
// CHECK-NEXT: return InstructionIndex[Opcode];
// CHECK-NEXT: }
More information about the llvm-commits
mailing list