[Mlir-commits] [mlir] [MLIR][SPIRV] Add conversion for spirv.SpecConstant and spirv.mlir.referenceof to LLVM (PR #188746)
Mehdi Amini
llvmlistbot at llvm.org
Thu Mar 26 06:40:51 PDT 2026
https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/188746
`-convert-spirv-to-llvm` failed with "failed to legalize operation 'spirv.SpecConstant'" because no conversion pattern existed for `spirv.SpecConstant` or `spirv.mlir.referenceof`.
Add two new conversion patterns:
- `SpecConstantPattern`: converts `spirv.SpecConstant` to an `llvm.mlir.global` constant (private linkage) using the spec constant's default value as the initializer. Signed/unsigned integer types are converted to signless integers, consistent with `ConstantScalarAndVectorPattern`.
- `ReferenceOfPattern`: converts `spirv.mlir.referenceof` to an `llvm.mlir.addressof` + `llvm.load` pair, loading the value from the corresponding global constant.
Since LLVM IR has no notion of specialization constants, the default value is used unconditionally when lowering.
Fixes #159485
Assisted-by: Claude Code
>From 5085ceab7003ce5e92a4fb89500659aa00f9d5ea Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Thu, 26 Mar 2026 06:11:44 -0700
Subject: [PATCH] [MLIR][SPIRV] Add conversion for spirv.SpecConstant and
spirv.mlir.referenceof to LLVM
`-convert-spirv-to-llvm` failed with "failed to legalize operation
'spirv.SpecConstant'" because no conversion pattern existed for
`spirv.SpecConstant` or `spirv.mlir.referenceof`.
Add two new conversion patterns:
- `SpecConstantPattern`: converts `spirv.SpecConstant` to an
`llvm.mlir.global` constant (private linkage) using the spec constant's
default value as the initializer. Signed/unsigned integer types are
converted to signless integers, consistent with `ConstantScalarAndVectorPattern`.
- `ReferenceOfPattern`: converts `spirv.mlir.referenceof` to an
`llvm.mlir.addressof` + `llvm.load` pair, loading the value from the
corresponding global constant.
Since LLVM IR has no notion of specialization constants, the default value is
used unconditionally when lowering.
Fixes #159485
Assisted-by: Claude Code
---
.../Conversion/SPIRVToLLVM/SPIRVToLLVM.cpp | 61 ++++++++++++++++++-
.../SPIRVToLLVM/constant-op-to-llvm.mlir | 28 +++++++++
2 files changed, 87 insertions(+), 2 deletions(-)
diff --git a/mlir/lib/Conversion/SPIRVToLLVM/SPIRVToLLVM.cpp b/mlir/lib/Conversion/SPIRVToLLVM/SPIRVToLLVM.cpp
index d9144d0c5e228..5908db3c04817 100644
--- a/mlir/lib/Conversion/SPIRVToLLVM/SPIRVToLLVM.cpp
+++ b/mlir/lib/Conversion/SPIRVToLLVM/SPIRVToLLVM.cpp
@@ -441,6 +441,63 @@ class ConstantScalarAndVectorPattern
}
};
+/// Converts `spirv.SpecConstant` to an `llvm.mlir.global` constant with a
+/// private linkage, using the spec constant's default value as the initializer.
+/// When lowering to LLVM there is no notion of specialization, so the default
+/// value is used unconditionally.
+class SpecConstantPattern
+ : public SPIRVToLLVMConversion<spirv::SpecConstantOp> {
+public:
+ using SPIRVToLLVMConversion<spirv::SpecConstantOp>::SPIRVToLLVMConversion;
+
+ LogicalResult
+ matchAndRewrite(spirv::SpecConstantOp op, OpAdaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ auto defaultValue = cast<TypedAttr>(op.getDefaultValue());
+ auto srcType = defaultValue.getType();
+ auto dstType = getTypeConverter()->convertType(srcType);
+ if (!dstType)
+ return rewriter.notifyMatchFailure(op, "type conversion failed");
+
+ // Handle signed/unsigned integers: strip the sign by converting to a
+ // signless integer type (analogous to ConstantScalarAndVectorPattern).
+ Attribute initializer = defaultValue;
+ if (isSignedIntegerOrVector(srcType) ||
+ isUnsignedIntegerOrVector(srcType)) {
+ auto signlessType = rewriter.getIntegerType(getBitWidth(srcType));
+ dstType = getTypeConverter()->convertType(signlessType);
+ initializer = rewriter.getIntegerAttr(
+ signlessType, cast<IntegerAttr>(defaultValue).getValue());
+ }
+
+ rewriter.replaceOpWithNewOp<LLVM::GlobalOp>(
+ op, dstType, /*isConstant=*/true, LLVM::Linkage::Private,
+ op.getSymName(), initializer);
+ return success();
+ }
+};
+
+/// Converts `spirv.mlir.referenceof` (referencing a `spirv.SpecConstant`) to a
+/// load from the corresponding `llvm.mlir.global`.
+class ReferenceOfPattern : public SPIRVToLLVMConversion<spirv::ReferenceOfOp> {
+public:
+ using SPIRVToLLVMConversion<spirv::ReferenceOfOp>::SPIRVToLLVMConversion;
+
+ LogicalResult
+ matchAndRewrite(spirv::ReferenceOfOp op, OpAdaptor adaptor,
+ ConversionPatternRewriter &rewriter) const override {
+ auto dstType = getTypeConverter()->convertType(op.getType());
+ if (!dstType)
+ return rewriter.notifyMatchFailure(op, "type conversion failed");
+
+ auto ptrType = LLVM::LLVMPointerType::get(op.getContext());
+ Value addr = LLVM::AddressOfOp::create(rewriter, op.getLoc(), ptrType,
+ op.getSpecConst());
+ rewriter.replaceOpWithNewOp<LLVM::LoadOp>(op, dstType, addr);
+ return success();
+ }
+};
+
class BitFieldSExtractPattern
: public SPIRVToLLVMConversion<spirv::BitFieldSExtractOp> {
public:
@@ -1850,8 +1907,8 @@ void mlir::populateSPIRVToLLVMConversionPatterns(
IComparePattern<spirv::ULessThanEqualOp, LLVM::ICmpPredicate::ule>,
IComparePattern<spirv::ULessThanOp, LLVM::ICmpPredicate::ult>,
- // Constant op
- ConstantScalarAndVectorPattern,
+ // Constant ops
+ ConstantScalarAndVectorPattern, SpecConstantPattern, ReferenceOfPattern,
// Control Flow ops
BranchConversionPattern, BranchConditionalConversionPattern,
diff --git a/mlir/test/Conversion/SPIRVToLLVM/constant-op-to-llvm.mlir b/mlir/test/Conversion/SPIRVToLLVM/constant-op-to-llvm.mlir
index 2d74022b34406..7066b9fe1f388 100644
--- a/mlir/test/Conversion/SPIRVToLLVM/constant-op-to-llvm.mlir
+++ b/mlir/test/Conversion/SPIRVToLLVM/constant-op-to-llvm.mlir
@@ -59,3 +59,31 @@ spirv.func @float_constant_vector() "None" {
%0 = spirv.Constant dense<[2.000000e+00, 3.000000e+00]> : vector<2xf32>
spirv.Return
}
+
+//===----------------------------------------------------------------------===//
+// spirv.SpecConstant and spirv.mlir.referenceof
+//===----------------------------------------------------------------------===//
+
+// CHECK: llvm.mlir.global private constant @sc_int(-5 : i32) {{.*}} : i32
+// CHECK: llvm.mlir.global private constant @sc_signed(-5 : i32) {{.*}} : i32
+// CHECK: llvm.mlir.global private constant @sc_unsigned(10 : i16) {{.*}} : i16
+// CHECK: llvm.mlir.global private constant @sc_float(3.140000e+00 : f32) {{.*}} : f32
+// CHECK: llvm.mlir.global private constant @sc_bool(true) {{.*}} : i1
+spirv.module Logical GLSL450 {
+ spirv.SpecConstant @sc_int = -5 : i32
+ spirv.SpecConstant @sc_signed = -5 : si32
+ spirv.SpecConstant @sc_unsigned = 10 : ui16
+ spirv.SpecConstant @sc_float = 3.14 : f32
+ spirv.SpecConstant @sc_bool = true
+}
+
+// CHECK-LABEL: @use_spec_consts
+spirv.module Logical GLSL450 {
+ spirv.SpecConstant @sc = 42 : i32
+ spirv.func @use_spec_consts() -> i32 "None" {
+ // CHECK: %[[ADDR:.*]] = llvm.mlir.addressof @sc : !llvm.ptr
+ // CHECK: llvm.load %[[ADDR]] : !llvm.ptr -> i32
+ %0 = spirv.mlir.referenceof @sc : i32
+ spirv.ReturnValue %0 : i32
+ }
+}
More information about the Mlir-commits
mailing list