[Mlir-commits] [mlir] [mlir][SPIR-V] Add CopyMemorySized support (PR #232190)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Wed Oct 14 23:14:04 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-spirv
Author: Alexander Shaposhnikov (alexander-shaposhnikov)
<details>
<summary>Changes</summary>
Add spirv.CopyMemorySized with assembly syntax, verification, serialization, and deserialization. Allow different source and target pointee types and a scalar integer byte count. Reject zero and negative signed constant sizes, validate memory access attributes, and require SPIR-V 1.4 for the two-mask form during version, capability, and extension deduction.
---
Patch is 41.78 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/232190.diff
12 Files Affected:
- (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td (+3-1)
- (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td (+74)
- (modified) mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp (+90)
- (modified) mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp (+71-64)
- (modified) mlir/lib/Target/SPIRV/Deserialization/Deserializer.h (+5)
- (modified) mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp (+28-16)
- (modified) mlir/lib/Target/SPIRV/Serialization/Serializer.h (+4)
- (modified) mlir/test/Dialect/SPIRV/IR/memory-ops.mlir (+141)
- (added) mlir/test/Dialect/SPIRV/Transforms/copy-memory-sized-vce.mlir (+35)
- (added) mlir/test/Target/SPIRV/copy-memory-unsupported.mlir (+19)
- (modified) mlir/test/Target/SPIRV/memory-ops.mlir (+79)
- (modified) mlir/unittests/Dialect/SPIRV/DeserializationTest.cpp (+115)
``````````diff
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
index 7e799fc0191076..32feae3c19e3a9 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
@@ -4535,6 +4535,7 @@ def SPIRV_OC_OpVariable : I32EnumAttrCase<"OpVariable", 59
def SPIRV_OC_OpLoad : I32EnumAttrCase<"OpLoad", 61>;
def SPIRV_OC_OpStore : I32EnumAttrCase<"OpStore", 62>;
def SPIRV_OC_OpCopyMemory : I32EnumAttrCase<"OpCopyMemory", 63>;
+def SPIRV_OC_OpCopyMemorySized : I32EnumAttrCase<"OpCopyMemorySized", 64>;
def SPIRV_OC_OpAccessChain : I32EnumAttrCase<"OpAccessChain", 65>;
def SPIRV_OC_OpInBoundsAccessChain : I32EnumAttrCase<"OpInBoundsAccessChain", 66>;
def SPIRV_OC_OpPtrAccessChain : I32EnumAttrCase<"OpPtrAccessChain", 67>;
@@ -4790,7 +4791,8 @@ def SPIRV_OpcodeAttr :
SPIRV_OC_OpSpecConstantOp, SPIRV_OC_OpFunction, SPIRV_OC_OpFunctionParameter,
SPIRV_OC_OpFunctionEnd, SPIRV_OC_OpFunctionCall, SPIRV_OC_OpVariable,
SPIRV_OC_OpLoad, SPIRV_OC_OpStore, SPIRV_OC_OpCopyMemory,
- SPIRV_OC_OpAccessChain, SPIRV_OC_OpInBoundsAccessChain, SPIRV_OC_OpPtrAccessChain,
+ SPIRV_OC_OpCopyMemorySized, SPIRV_OC_OpAccessChain,
+ SPIRV_OC_OpInBoundsAccessChain, SPIRV_OC_OpPtrAccessChain,
SPIRV_OC_OpInBoundsPtrAccessChain, SPIRV_OC_OpDecorate,
SPIRV_OC_OpMemberDecorate, SPIRV_OC_OpVectorExtractDynamic,
SPIRV_OC_OpVectorInsertDynamic, SPIRV_OC_OpVectorShuffle,
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td
index 5ae6c8af830032..c6f2411fc71381 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVMemoryOps.td
@@ -169,6 +169,80 @@ def SPIRV_CopyMemoryOp : SPIRV_Op<"CopyMemory", [DeclareOpInterfaceMethods<Align
// -----
+def SPIRV_CopyMemorySizedOp : SPIRV_Op<"CopyMemorySized", [DeclareOpInterfaceMethods<AlignmentAttrOpInterface>]> {
+ let summary = [{
+ Copy from the memory pointed to by Source to the memory pointed to by
+ Target.
+ }];
+
+ let description = [{
+ Target and Source must have pointer types. Their pointee types may differ.
+
+ Size is the number of bytes to copy. It must have a scalar integer type.
+ If it is a constant instruction, the constant value must not be 0. It is
+ invalid for both the constant's type to have Signedness of 1 and to have
+ the sign bit set. Otherwise, as a run-time value, Size is treated as
+ unsigned, and if its value is 0, no memory access is made.
+
+ If present, any Memory Operands must begin with a memory operand
+ literal. If not present, it is the same as specifying the memory operand
+ None. Before version 1.4, at most one memory operands mask can be
+ provided. Starting with version 1.4 two masks can be provided, as
+ described in Memory Operands. If no masks or only one mask is present,
+ it applies to both Source and Target. If two masks are present, the
+ first applies to Target and must not include MakePointerVisible, and the
+ second applies to Source and must not include MakePointerAvailable.
+
+ <!-- End of AutoGen section -->
+
+ Serialization supports the Volatile, Aligned, Nontemporal, and
+ NonPrivatePointer memory operands. Masks requiring scope or alias-list
+ operands are not supported.
+
+ ```
+ copy-memory-sized-op ::= `spirv.CopyMemorySized ` ssa-use `, ` ssa-use
+ `, ` ssa-use
+ (`[` memory-access `]` (`, [` memory-access `]`)?)?
+ ` : ` spirv-pointer-type `, ` spirv-pointer-type
+ `, ` integer-type
+ ```
+
+ #### Example:
+
+ ```mlir
+ %0 = spirv.Variable : !spirv.ptr<f32, Function>
+ %1 = spirv.Variable : !spirv.ptr<i32, Function>
+ %2 = spirv.Constant 4 : i32
+ spirv.CopyMemorySized %0, %1, %2 : !spirv.ptr<f32, Function>, !spirv.ptr<i32, Function>, i32
+ spirv.CopyMemorySized %0, %1, %2 ["Aligned", 4] : !spirv.ptr<f32, Function>, !spirv.ptr<i32, Function>, i32
+ ```
+ }];
+
+ let availability = [
+ MinVersion<SPIRV_V_1_0>,
+ MaxVersion<SPIRV_V_1_6>,
+ Extension<[]>,
+ Capability<[SPIRV_C_Addresses]>
+ ];
+
+ let arguments = (ins
+ SPIRV_AnyPtr:$target,
+ SPIRV_AnyPtr:$source,
+ SPIRV_Integer:$size,
+ OptionalAttr<SPIRV_MemoryAccessAttr>:$memory_access,
+ OptionalAttr<IntValidAlignment<I32Attr>>:$alignment,
+ OptionalAttr<SPIRV_MemoryAccessAttr>:$source_memory_access,
+ OptionalAttr<IntValidAlignment<I32Attr>>:$source_alignment
+ );
+
+ let results = (outs);
+
+ let autogenSerialization = 0;
+ let autogenAvailability = 0;
+}
+
+// -----
+
def SPIRV_InBoundsPtrAccessChainOp : SPIRV_Op<"InBoundsPtrAccessChain", [Pure]> {
let summary = [{
Has the same semantics as OpPtrAccessChain, with the addition that the
diff --git a/mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp b/mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp
index c99b6e04ef1ac9..89bb80a7b82cdc 100644
--- a/mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/MemoryOps.cpp
@@ -115,6 +115,9 @@ static void printSourceMemoryAccessAttribute(
: memoryOp.getSourceMemoryAccess())) {
elidedAttrs.push_back(memoryOp.getSourceMemoryAccessAttrName());
+ // Keep the source mask in the second position even without a target mask.
+ if (!memoryOp.getMemoryAccess())
+ printer << " [\"None\"]";
printer << ", [\"" << stringifyMemoryAccess(*memAccess) << "\"";
if (spirv::bitEnumContainsAll(*memAccess, spirv::MemoryAccess::Aligned)) {
@@ -548,6 +551,93 @@ LogicalResult CopyMemoryOp::verify() {
MemoryAccessKind::Read);
}
+//===----------------------------------------------------------------------===//
+// spirv.CopyMemorySized
+//===----------------------------------------------------------------------===//
+
+void CopyMemorySizedOp::print(OpAsmPrinter &printer) {
+ printer << ' ' << getTarget() << ", " << getSource() << ", " << getSize();
+
+ SmallVector<StringRef, 4> elidedAttrs;
+ printMemoryAccessAttribute(*this, printer, elidedAttrs);
+ printSourceMemoryAccessAttribute(*this, printer, elidedAttrs,
+ getSourceMemoryAccess(),
+ getSourceAlignment());
+
+ printer.printOptionalAttrDict(
+ (*this)->getDiscardableAttrDictionary().getValue(), elidedAttrs);
+
+ printer << " : " << getTarget().getType() << ", " << getSource().getType()
+ << ", " << getSize().getType();
+}
+
+ParseResult CopyMemorySizedOp::parse(OpAsmParser &parser,
+ OperationState &result) {
+ SmallVector<OpAsmParser::UnresolvedOperand, 3> operandInfo;
+ SMLoc loc = parser.getCurrentLocation();
+ if (parser.parseOperandList(operandInfo, 3) ||
+ parseMemoryAccessAttributes<CopyMemorySizedOp>(parser, result))
+ return failure();
+
+ if (!parser.parseOptionalComma()) {
+ // Parse 2nd memory access attributes.
+ if (parseSourceMemoryAccessAttributes<CopyMemorySizedOp>(parser, result))
+ return failure();
+ }
+
+ SmallVector<Type, 3> operandTypes;
+ if (parser.parseOptionalAttrDict(result.attributes) || parser.parseColon() ||
+ parser.parseTypeList(operandTypes))
+ return failure();
+
+ return parser.resolveOperands(operandInfo, operandTypes, loc,
+ result.operands);
+}
+
+LogicalResult CopyMemorySizedOp::verify() {
+ if (auto constOp = getSize().getDefiningOp<spirv::ConstantOp>()) {
+ if (auto sizeAttr = dyn_cast<IntegerAttr>(constOp.getValue())) {
+ APInt size = sizeAttr.getValue();
+ if (size.isZero())
+ return emitOpError("size must not be a constant zero");
+ if (sizeAttr.getType().isSignedInteger() && size.isNegative())
+ return emitOpError("size must not be a negative signed constant");
+ }
+ }
+
+ // A lone mask applies to both operands. Only the first of two masks is
+ // Target-only.
+ MemoryAccessKind targetKind = getSourceMemoryAccess()
+ ? MemoryAccessKind::Write
+ : MemoryAccessKind::ReadWrite;
+ if (failed(verifyMemoryAccessAttribute(*this, targetKind)))
+ return failure();
+
+ return verifyMemoryAccessAttribute(
+ getOperation(), getSourceMemoryAccessAttr(), getSourceAlignmentAttr(),
+ MemoryAccessKind::Read);
+}
+
+std::optional<spirv::Version> CopyMemorySizedOp::getMinVersion() {
+ return getSourceMemoryAccess() ? spirv::Version::V_1_4
+ : spirv::Version::V_1_0;
+}
+
+std::optional<spirv::Version> CopyMemorySizedOp::getMaxVersion() {
+ return spirv::Version::V_1_6;
+}
+
+SmallVector<ArrayRef<spirv::Extension>, 1> CopyMemorySizedOp::getExtensions() {
+ return {};
+}
+
+SmallVector<ArrayRef<spirv::Capability>, 1>
+CopyMemorySizedOp::getCapabilities() {
+ static constexpr spirv::Capability capabilities[] = {
+ spirv::Capability::Addresses};
+ return {ArrayRef<spirv::Capability>(capabilities)};
+}
+
//===----------------------------------------------------------------------===//
// spirv.InBoundsPtrAccessChainOp
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp b/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp
index acb43b6e04bd67..c5b74280859710 100644
--- a/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp
+++ b/mlir/lib/Target/SPIRV/Deserialization/DeserializeOps.cpp
@@ -679,89 +679,96 @@ Deserializer::processOp<spirv::FunctionCallOp>(ArrayRef<uint32_t> operands) {
return success();
}
-template <>
-LogicalResult
-Deserializer::processOp<spirv::CopyMemoryOp>(ArrayRef<uint32_t> words) {
- SmallVector<Type, 1> resultTypes;
- size_t wordIndex = 0;
- SmallVector<Value, 4> operands;
- SmallVector<NamedAttribute, 4> attributes;
-
- if (wordIndex < words.size()) {
- auto arg = getValue(words[wordIndex]);
-
- if (!arg) {
- return emitError(unknownLoc, "unknown result <id> : ")
- << words[wordIndex];
- }
-
- operands.push_back(arg);
- wordIndex++;
+template <typename OpTy>
+LogicalResult Deserializer::processCopyMemoryOp(ArrayRef<uint32_t> words,
+ unsigned numValueOperands) {
+ if (words.size() < numValueOperands) {
+ return emitError(unknownLoc, "expected at least ")
+ << numValueOperands << " operands for " << OpTy::getOperationName()
+ << ", but got " << words.size();
}
- if (wordIndex < words.size()) {
- auto arg = getValue(words[wordIndex]);
-
- if (!arg) {
- return emitError(unknownLoc, "unknown result <id> : ")
- << words[wordIndex];
- }
-
- operands.push_back(arg);
- wordIndex++;
+ SmallVector<Value, 3> operands;
+ for (uint32_t operandID : words.take_front(numValueOperands)) {
+ Value operand = getValue(operandID);
+ if (!operand)
+ return emitError(unknownLoc, "unknown result <id> : ") << operandID;
+ operands.push_back(operand);
}
- bool isAlignedAttr = false;
-
- if (wordIndex < words.size()) {
- auto attrValue = words[wordIndex++];
- auto attr = opBuilder.getAttr<spirv::MemoryAccessAttr>(
- static_cast<spirv::MemoryAccess>(attrValue));
- attributes.push_back(
- opBuilder.getNamedAttr(attributeName<MemoryAccess>(), attr));
- isAlignedAttr = (attrValue == 2);
- }
-
- if (isAlignedAttr && wordIndex < words.size()) {
- attributes.push_back(opBuilder.getNamedAttr(
- "alignment", opBuilder.getI32IntegerAttr(words[wordIndex++])));
- }
-
- if (wordIndex < words.size()) {
- auto attrValue = words[wordIndex++];
- auto attr = opBuilder.getAttr<spirv::MemoryAccessAttr>(
- static_cast<spirv::MemoryAccess>(attrValue));
- attributes.push_back(opBuilder.getNamedAttr("source_memory_access", attr));
- }
-
- if (wordIndex < words.size()) {
+ SmallVector<NamedAttribute, 4> attributes;
+ size_t wordIndex = numValueOperands;
+ auto parseMemoryAccess = [&](StringRef memoryAccessName,
+ StringRef alignmentName) -> LogicalResult {
+ if (wordIndex == words.size())
+ return success();
+ auto memoryAccess = static_cast<spirv::MemoryAccess>(words[wordIndex++]);
+ // Other masks require scope or alias-list operands that these ops cannot
+ // represent. Do not mistake those operands for a second memory access mask.
+ constexpr auto supported = spirv::MemoryAccess::Volatile |
+ spirv::MemoryAccess::Aligned |
+ spirv::MemoryAccess::Nontemporal |
+ spirv::MemoryAccess::NonPrivatePointer;
+ if (static_cast<uint32_t>(memoryAccess) &
+ ~static_cast<uint32_t>(supported)) {
+ auto mask =
+ spirv::symbolizeMemoryAccess(static_cast<uint32_t>(memoryAccess));
+ return emitError(unknownLoc, "unsupported memory access mask: ")
+ << (mask ? spirv::stringifyMemoryAccess(*mask) : "unknown") << " ("
+ << static_cast<uint32_t>(memoryAccess) << ")";
+ }
attributes.push_back(opBuilder.getNamedAttr(
- "source_alignment", opBuilder.getI32IntegerAttr(words[wordIndex++])));
- }
+ memoryAccessName,
+ opBuilder.getAttr<spirv::MemoryAccessAttr>(memoryAccess)));
+ if (spirv::bitEnumContainsAll(memoryAccess, spirv::MemoryAccess::Aligned)) {
+ if (wordIndex == words.size())
+ return emitError(unknownLoc, "missing alignment literal for ")
+ << memoryAccessName;
+ attributes.push_back(opBuilder.getNamedAttr(
+ alignmentName, opBuilder.getI32IntegerAttr(words[wordIndex++])));
+ }
+ return success();
+ };
+ if (failed(parseMemoryAccess(attributeName<MemoryAccess>(), "alignment")) ||
+ failed(parseMemoryAccess("source_memory_access", "source_alignment")))
+ return failure();
if (wordIndex != words.size()) {
return emitError(unknownLoc,
- "found more operands than expected when deserializing "
- "spirv::CopyMemoryOp, only ")
- << wordIndex << " of " << words.size() << " processed";
+ "found more operands than expected when deserializing ")
+ << OpTy::getOperationName() << ", only " << wordIndex << " of "
+ << words.size() << " processed";
}
Location loc = createFileLineColLoc(opBuilder);
- spirv::CopyMemoryOp::Properties properties{};
- spirv::CopyMemoryOp::populateDefaultProperties(
- OperationName(spirv::CopyMemoryOp::getOperationName(),
- opBuilder.getContext()),
+ typename OpTy::Properties properties{};
+ OpTy::populateDefaultProperties(
+ OperationName(OpTy::getOperationName(), opBuilder.getContext()),
properties);
- if (failed(spirv::CopyMemoryOp::setPropertiesFromAttr(
+ if (failed(OpTy::setPropertiesFromAttr(
properties, opBuilder.getDictionaryAttr(attributes),
[&]() { return emitError(loc); })))
return failure();
- spirv::CopyMemoryOp::create(opBuilder, loc, resultTypes, operands, properties,
- /*discardableAttributes=*/{});
+ OpTy::create(opBuilder, loc, /*resultTypes=*/TypeRange(), operands,
+ properties, /*discardableAttributes=*/{});
return success();
}
+template <>
+LogicalResult
+Deserializer::processOp<spirv::CopyMemoryOp>(ArrayRef<uint32_t> words) {
+ return processCopyMemoryOp<spirv::CopyMemoryOp>(words,
+ /*numValueOperands=*/2);
+}
+
+template <>
+LogicalResult
+Deserializer::processOp<spirv::CopyMemorySizedOp>(ArrayRef<uint32_t> words) {
+ return processCopyMemoryOp<spirv::CopyMemorySizedOp>(words,
+ /*numValueOperands=*/3);
+}
+
template <>
LogicalResult Deserializer::processOp<spirv::GenericCastToPtrExplicitOp>(
ArrayRef<uint32_t> words) {
diff --git a/mlir/lib/Target/SPIRV/Deserialization/Deserializer.h b/mlir/lib/Target/SPIRV/Deserialization/Deserializer.h
index 543db3c99bddb0..3c3d1de90f1f94 100644
--- a/mlir/lib/Target/SPIRV/Deserialization/Deserializer.h
+++ b/mlir/lib/Target/SPIRV/Deserialization/Deserializer.h
@@ -585,6 +585,11 @@ class Deserializer {
<< OpTy::getOperationName() << " op";
}
+ /// Deserializes a memory copy with `numValueOperands` leading IDs.
+ template <typename OpTy>
+ LogicalResult processCopyMemoryOp(ArrayRef<uint32_t> words,
+ unsigned numValueOperands);
+
private:
/// The SPIR-V binary module.
ArrayRef<uint32_t> binary;
diff --git a/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp b/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
index da311c386a3242..027b8e041eef99 100644
--- a/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
+++ b/mlir/lib/Target/SPIRV/Serialization/SerializeOps.cpp
@@ -1117,11 +1117,19 @@ Serializer::processOp<spirv::FunctionCallOp>(spirv::FunctionCallOp op) {
return success();
}
-template <>
-LogicalResult
-Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) {
+template <typename OpTy>
+LogicalResult Serializer::processCopyMemoryOp(OpTy op, spirv::Opcode opcode) {
+ constexpr auto supported =
+ spirv::MemoryAccess::Volatile | spirv::MemoryAccess::Aligned |
+ spirv::MemoryAccess::Nontemporal | spirv::MemoryAccess::NonPrivatePointer;
+ for (auto mask : {op.getMemoryAccess(), op.getSourceMemoryAccess()}) {
+ if (mask &&
+ (static_cast<uint32_t>(*mask) & ~static_cast<uint32_t>(supported)))
+ return op.emitError("unsupported memory access mask: ")
+ << spirv::stringifyMemoryAccess(*mask);
+ }
+
SmallVector<uint32_t, 4> operands;
- SmallVector<StringRef, 2> elidedAttrs;
for (Value operand : op->getOperands()) {
auto id = getValueID(operand);
@@ -1129,35 +1137,39 @@ Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) {
operands.push_back(id);
}
- StringAttr memoryAccess = op.getMemoryAccessAttrName();
if (std::optional<spirv::MemoryAccess> value = op.getMemoryAccess())
operands.push_back(static_cast<uint32_t>(*value));
+ else if (op.getSourceMemoryAccess())
+ operands.push_back(static_cast<uint32_t>(spirv::MemoryAccess::None));
- elidedAttrs.push_back(memoryAccess.strref());
-
- StringAttr alignment = op.getAlignmentAttrName();
if (std::optional<uint32_t> value = op.getAlignment())
operands.push_back(*value);
- elidedAttrs.push_back(alignment.strref());
-
- StringAttr sourceMemoryAccess = op.getSourceMemoryAccessAttrName();
if (std::optional<spirv::MemoryAccess> value = op.getSourceMemoryAccess())
operands.push_back(static_cast<uint32_t>(*value));
- elidedAttrs.push_back(sourceMemoryAccess.strref());
-
- StringAttr sourceAlignment = op.getSourceAlignmentAttrName();
if (std::optional<uint32_t> value = op.getSourceAlignment())
operands.push_back(*value);
- elidedAttrs.push_back(sourceAlignment.strref());
if (failed(emitDebugLine(functionBody, op.getLoc())))
return failure();
- encodeInstructionInto(functionBody, spirv::Opcode::OpCopyMemory, operands);
+ encodeInstructionInto(functionBody, opcode, operands);
return success();
}
+
+template <>
+LogicalResult
+Serializer::processOp<spirv::CopyMemoryOp>(spirv::CopyMemoryOp op) {
+ return processCopyMemoryOp(op, spirv::Opcode::OpCopyMemory);
+}
+
+template <>
+LogicalResult
+Serializer::processOp<spirv::CopyMemorySizedOp>(spirv::CopyMemorySizedOp op) {
+ return processCopyMemoryOp(op, spirv::Opcode::OpCopyMemorySized);
+}
+
template <>
LogicalResult Serializer::processOp<spirv::GenericCastToPtrExplicitOp>(
spirv::GenericCastToPtrExplicitOp op) {
diff --git a/mlir/lib/Target/SPIRV/Serialization/Serializer.h b/mlir/lib/Target/SPIRV/Serialization/Serializer.h
index 235c050e7dc7e0..620887a6918e9e 100644
--- a/mlir/lib/Target/SPIRV/Serialization/Serializer.h
+++ b/mlir/lib/Target/SPIRV/Serialization/Serializer.h
@@ -376,6 +376,10 @@ class Serializer {
return op.emitError(...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/232190
More information about the Mlir-commits
mailing list