CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
327 lines
13 KiB
C++
327 lines
13 KiB
C++
//===- CastOps.cpp - MLIR SPIR-V Cast Ops --------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines the cast and conversion operations in the SPIR-V dialect.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/Dialect/SPIRV/IR/SPIRVOps.h"
|
|
|
|
#include "SPIRVOpUtils.h"
|
|
#include "SPIRVParsingUtils.h"
|
|
|
|
#include "llvm/ADT/TypeSwitch.h"
|
|
|
|
using namespace mlir::spirv::AttrNames;
|
|
|
|
namespace mlir::spirv {
|
|
|
|
static LogicalResult verifyCastOp(Operation *op,
|
|
bool requireSameBitWidth = true,
|
|
bool skipBitWidthCheck = false) {
|
|
// Some CastOps have no limit on bit widths for result and operand type.
|
|
if (skipBitWidthCheck)
|
|
return success();
|
|
|
|
Type operandType = op->getOperand(0).getType();
|
|
Type resultType = op->getResult(0).getType();
|
|
|
|
// ODS checks that result type and operand type have the same shape. Check
|
|
// that composite types match and extract the element types, if any.
|
|
using TypePair = std::pair<Type, Type>;
|
|
auto [operandElemTy, resultElemTy] =
|
|
TypeSwitch<Type, TypePair>(operandType)
|
|
.Case<VectorType, spirv::CooperativeMatrixType>(
|
|
[resultType](auto concreteOperandTy) -> TypePair {
|
|
if (auto concreteResultTy =
|
|
dyn_cast<decltype(concreteOperandTy)>(resultType)) {
|
|
return {concreteOperandTy.getElementType(),
|
|
concreteResultTy.getElementType()};
|
|
}
|
|
return {};
|
|
})
|
|
.Default([resultType](Type operandType) -> TypePair {
|
|
return {operandType, resultType};
|
|
});
|
|
|
|
if (!operandElemTy || !resultElemTy)
|
|
return op->emitOpError("incompatible operand and result types");
|
|
|
|
unsigned operandTypeBitWidth = operandElemTy.getIntOrFloatBitWidth();
|
|
unsigned resultTypeBitWidth = resultElemTy.getIntOrFloatBitWidth();
|
|
bool isSameBitWidth = operandTypeBitWidth == resultTypeBitWidth;
|
|
|
|
if (requireSameBitWidth) {
|
|
if (!isSameBitWidth) {
|
|
return op->emitOpError(
|
|
"expected the same bit widths for operand type and result "
|
|
"type, but provided ")
|
|
<< operandElemTy << " and " << resultElemTy;
|
|
}
|
|
return success();
|
|
}
|
|
|
|
if (isSameBitWidth) {
|
|
return op->emitOpError(
|
|
"expected the different bit widths for operand type and result "
|
|
"type, but provided ")
|
|
<< operandElemTy << " and " << resultElemTy;
|
|
}
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.BitcastOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult BitcastOp::verify() {
|
|
// TODO: The SPIR-V spec validation rules are different for different
|
|
// versions.
|
|
auto operandType = getOperand().getType();
|
|
auto resultType = getResult().getType();
|
|
if (operandType == resultType) {
|
|
return emitError("result type must be different from operand type");
|
|
}
|
|
|
|
auto operandCoopMatrixType =
|
|
dyn_cast<spirv::CooperativeMatrixType>(operandType);
|
|
auto resultCoopMatrixType =
|
|
dyn_cast<spirv::CooperativeMatrixType>(resultType);
|
|
if (operandCoopMatrixType || resultCoopMatrixType) {
|
|
if (!operandCoopMatrixType || !resultCoopMatrixType)
|
|
return emitError("unhandled bit cast conversion from cooperative matrix "
|
|
"type to non-cooperative matrix type");
|
|
|
|
if (operandCoopMatrixType.getRows() != resultCoopMatrixType.getRows() ||
|
|
operandCoopMatrixType.getColumns() != resultCoopMatrixType.getColumns())
|
|
return emitError("cooperative matrix dimensions must match");
|
|
|
|
if (operandCoopMatrixType.getScope() != resultCoopMatrixType.getScope())
|
|
return emitError("cooperative matrix scope must match");
|
|
|
|
if (operandCoopMatrixType.getUse() != resultCoopMatrixType.getUse())
|
|
return emitError("cooperative matrix use must match");
|
|
|
|
unsigned operandBitWidth =
|
|
getBitWidth(operandCoopMatrixType.getElementType());
|
|
unsigned resultBitWidth =
|
|
getBitWidth(resultCoopMatrixType.getElementType());
|
|
if (operandBitWidth != resultBitWidth)
|
|
return emitOpError("mismatch in result and operand type bitwidth");
|
|
|
|
return success();
|
|
}
|
|
|
|
if (isa<spirv::PointerType>(operandType) &&
|
|
!isa<spirv::PointerType>(resultType)) {
|
|
return emitError(
|
|
"unhandled bit cast conversion from pointer type to non-pointer type");
|
|
}
|
|
if (!isa<spirv::PointerType>(operandType) &&
|
|
isa<spirv::PointerType>(resultType)) {
|
|
return emitError(
|
|
"unhandled bit cast conversion from non-pointer type to pointer type");
|
|
}
|
|
auto operandBitWidth = getBitWidth(operandType);
|
|
auto resultBitWidth = getBitWidth(resultType);
|
|
if (operandBitWidth != resultBitWidth) {
|
|
return emitOpError("mismatch in result type bitwidth ")
|
|
<< resultBitWidth << " and operand type bitwidth "
|
|
<< operandBitWidth;
|
|
}
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.ConvertPtrToUOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult ConvertPtrToUOp::verify() {
|
|
auto operandType = cast<spirv::PointerType>(getPointer().getType());
|
|
auto resultType = cast<spirv::ScalarType>(getResult().getType());
|
|
if (!resultType || !resultType.isSignlessInteger())
|
|
return emitError("result must be a scalar type of unsigned integer");
|
|
auto spirvModule = (*this)->getParentOfType<spirv::ModuleOp>();
|
|
if (!spirvModule)
|
|
return success();
|
|
auto addressingModel = spirvModule.getAddressingModel();
|
|
if ((addressingModel == spirv::AddressingModel::Logical) ||
|
|
(addressingModel == spirv::AddressingModel::PhysicalStorageBuffer64 &&
|
|
operandType.getStorageClass() !=
|
|
spirv::StorageClass::PhysicalStorageBuffer))
|
|
return emitError("operand must be a physical pointer");
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.ConvertUToPtrOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult ConvertUToPtrOp::verify() {
|
|
auto operandType = cast<spirv::ScalarType>(getOperand().getType());
|
|
auto resultType = cast<spirv::PointerType>(getResult().getType());
|
|
if (!operandType || !operandType.isSignlessInteger())
|
|
return emitError("result must be a scalar type of unsigned integer");
|
|
auto spirvModule = (*this)->getParentOfType<spirv::ModuleOp>();
|
|
if (!spirvModule)
|
|
return success();
|
|
auto addressingModel = spirvModule.getAddressingModel();
|
|
if ((addressingModel == spirv::AddressingModel::Logical) ||
|
|
(addressingModel == spirv::AddressingModel::PhysicalStorageBuffer64 &&
|
|
resultType.getStorageClass() !=
|
|
spirv::StorageClass::PhysicalStorageBuffer))
|
|
return emitError("result must be a physical pointer");
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.PtrCastToGenericOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult PtrCastToGenericOp::verify() {
|
|
auto operandType = cast<spirv::PointerType>(getPointer().getType());
|
|
auto resultType = cast<spirv::PointerType>(getResult().getType());
|
|
|
|
spirv::StorageClass operandStorage = operandType.getStorageClass();
|
|
if (operandStorage != spirv::StorageClass::Workgroup &&
|
|
operandStorage != spirv::StorageClass::CrossWorkgroup &&
|
|
operandStorage != spirv::StorageClass::Function)
|
|
return emitError("pointer must point to the Workgroup, CrossWorkgroup"
|
|
", or Function Storage Class");
|
|
|
|
spirv::StorageClass resultStorage = resultType.getStorageClass();
|
|
if (resultStorage != spirv::StorageClass::Generic)
|
|
return emitError("result type must be of storage class Generic");
|
|
|
|
Type operandPointeeType = operandType.getPointeeType();
|
|
Type resultPointeeType = resultType.getPointeeType();
|
|
if (operandPointeeType != resultPointeeType)
|
|
return emitOpError("pointer operand's pointee type must have the same "
|
|
"as the op result type, but found ")
|
|
<< operandPointeeType << " vs " << resultPointeeType;
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.GenericCastToPtrOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult GenericCastToPtrOp::verify() {
|
|
auto operandType = cast<spirv::PointerType>(getPointer().getType());
|
|
auto resultType = cast<spirv::PointerType>(getResult().getType());
|
|
|
|
spirv::StorageClass operandStorage = operandType.getStorageClass();
|
|
if (operandStorage != spirv::StorageClass::Generic)
|
|
return emitError("pointer type must be of storage class Generic");
|
|
|
|
spirv::StorageClass resultStorage = resultType.getStorageClass();
|
|
if (resultStorage != spirv::StorageClass::Workgroup &&
|
|
resultStorage != spirv::StorageClass::CrossWorkgroup &&
|
|
resultStorage != spirv::StorageClass::Function)
|
|
return emitError("result must point to the Workgroup, CrossWorkgroup, "
|
|
"or Function Storage Class");
|
|
|
|
Type operandPointeeType = operandType.getPointeeType();
|
|
Type resultPointeeType = resultType.getPointeeType();
|
|
if (operandPointeeType != resultPointeeType)
|
|
return emitOpError("pointer operand's pointee type must have the same "
|
|
"as the op result type, but found ")
|
|
<< operandPointeeType << " vs " << resultPointeeType;
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.GenericCastToPtrExplicitOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult GenericCastToPtrExplicitOp::verify() {
|
|
auto operandType = cast<spirv::PointerType>(getPointer().getType());
|
|
auto resultType = cast<spirv::PointerType>(getResult().getType());
|
|
|
|
spirv::StorageClass operandStorage = operandType.getStorageClass();
|
|
if (operandStorage != spirv::StorageClass::Generic)
|
|
return emitError("pointer type must be of storage class Generic");
|
|
|
|
spirv::StorageClass resultStorage = resultType.getStorageClass();
|
|
if (resultStorage != spirv::StorageClass::Workgroup &&
|
|
resultStorage != spirv::StorageClass::CrossWorkgroup &&
|
|
resultStorage != spirv::StorageClass::Function)
|
|
return emitError("result must point to the Workgroup, CrossWorkgroup, "
|
|
"or Function Storage Class");
|
|
|
|
Type operandPointeeType = operandType.getPointeeType();
|
|
Type resultPointeeType = resultType.getPointeeType();
|
|
if (operandPointeeType != resultPointeeType)
|
|
return emitOpError("pointer operand's pointee type must have the same "
|
|
"as the op result type, but found ")
|
|
<< operandPointeeType << " vs " << resultPointeeType;
|
|
return success();
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.ConvertFToSOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult ConvertFToSOp::verify() {
|
|
return verifyCastOp(*this, /*requireSameBitWidth=*/false,
|
|
/*skipBitWidthCheck=*/true);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.ConvertFToUOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult ConvertFToUOp::verify() {
|
|
return verifyCastOp(*this, /*requireSameBitWidth=*/false,
|
|
/*skipBitWidthCheck=*/true);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.ConvertSToFOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult ConvertSToFOp::verify() {
|
|
return verifyCastOp(*this, /*requireSameBitWidth=*/false,
|
|
/*skipBitWidthCheck=*/true);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.ConvertUToFOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult ConvertUToFOp::verify() {
|
|
return verifyCastOp(*this, /*requireSameBitWidth=*/false,
|
|
/*skipBitWidthCheck=*/true);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.FConvertOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult spirv::FConvertOp::verify() {
|
|
return verifyCastOp(*this, /*requireSameBitWidth=*/false);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.SConvertOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult spirv::SConvertOp::verify() {
|
|
return verifyCastOp(*this, /*requireSameBitWidth=*/false);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// spirv.UConvertOp
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
LogicalResult spirv::UConvertOp::verify() {
|
|
return verifyCastOp(*this, /*requireSameBitWidth=*/false);
|
|
}
|
|
|
|
} // namespace mlir::spirv
|