|
@@ -2022,7 +2022,7 @@ SpirvEvalInfo SPIRVEmitter::doCastExpr(const CastExpr *expr) {
|
|
|
theBuilder.createVectorShuffle(vec2Type, vec, vec, {2, 3});
|
|
|
|
|
|
const auto mat = theBuilder.createCompositeConstruct(
|
|
|
- theBuilder.getMatType(vec2Type, 2), {subVec1, subVec2});
|
|
|
+ theBuilder.getMatType(elemType, vec2Type, 2), {subVec1, subVec2});
|
|
|
|
|
|
return SpirvEvalInfo(mat).setRValue();
|
|
|
}
|
|
@@ -2250,11 +2250,6 @@ uint32_t SPIRVEmitter::processFlatConversion(const QualType type,
|
|
|
QualType elemType = {};
|
|
|
uint32_t rowCount = 0, colCount = 0;
|
|
|
if (TypeTranslator::isMxNMatrix(type, &elemType, &rowCount, &colCount)) {
|
|
|
- if (!elemType->isFloatingType()) {
|
|
|
- emitError("non-floating-point matrix type unimplemented", {});
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
// By default HLSL matrices are row major, while SPIR-V matrices are
|
|
|
// column major. We are mapping what HLSL semantically mean a row into a
|
|
|
// column here.
|
|
@@ -4293,7 +4288,7 @@ SpirvEvalInfo SPIRVEmitter::doUnaryOperator(const UnaryOperator *expr) {
|
|
|
? getMatElemValueOne(subType)
|
|
|
: getValueOne(subType);
|
|
|
uint32_t incValue = 0;
|
|
|
- if (TypeTranslator::isSpirvAcceptableMatrixType(subType)) {
|
|
|
+ if (TypeTranslator::isMxNMatrix(subType)) {
|
|
|
// For matrices, we can only increment/decrement each vector of it.
|
|
|
const auto actOnEachVec = [this, spvOp, one](uint32_t /*index*/,
|
|
|
uint32_t vecType,
|
|
@@ -4601,7 +4596,7 @@ SpirvEvalInfo SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
|
|
|
// onto each element vector iff the operands are not degenerated matrices
|
|
|
// and we don't have a matrix specific SPIR-V instruction for the operation.
|
|
|
if (!isSpirvMatrixOp(mandateGenOpcode) &&
|
|
|
- TypeTranslator::isSpirvAcceptableMatrixType(lhs->getType())) {
|
|
|
+ TypeTranslator::isMxNMatrix(lhs->getType())) {
|
|
|
return processMatrixBinaryOp(lhs, rhs, opcode, sourceRange);
|
|
|
}
|
|
|
|
|
@@ -5253,7 +5248,7 @@ SpirvEvalInfo SPIRVEmitter::processEachVectorInMatrix(
|
|
|
llvm::function_ref<uint32_t(uint32_t, uint32_t, uint32_t)>
|
|
|
actOnEachVector) {
|
|
|
const auto matType = matrix->getType();
|
|
|
- assert(TypeTranslator::isSpirvAcceptableMatrixType(matType));
|
|
|
+ assert(TypeTranslator::isMxNMatrix(matType));
|
|
|
const uint32_t vecType = typeTranslator.getComponentVectorType(matType);
|
|
|
|
|
|
uint32_t rowCount = 0, colCount = 0;
|
|
@@ -5344,7 +5339,7 @@ SPIRVEmitter::processMatrixBinaryOp(const Expr *lhs, const Expr *rhs,
|
|
|
SourceRange range) {
|
|
|
// TODO: some code are duplicated from processBinaryOp. Try to unify them.
|
|
|
const auto lhsType = lhs->getType();
|
|
|
- assert(TypeTranslator::isSpirvAcceptableMatrixType(lhsType));
|
|
|
+ assert(TypeTranslator::isMxNMatrix(lhsType));
|
|
|
const spv::Op spvOp = translateOp(opcode, lhsType);
|
|
|
|
|
|
uint32_t rhsVal, lhsPtr, lhsVal;
|
|
@@ -5515,11 +5510,32 @@ uint32_t SPIRVEmitter::castToBool(const uint32_t fromVal, QualType fromType,
|
|
|
if (TypeTranslator::isSameScalarOrVecType(fromType, toBoolType))
|
|
|
return fromVal;
|
|
|
|
|
|
+ const uint32_t boolType = typeTranslator.translateType(toBoolType);
|
|
|
+
|
|
|
+ { // Special case handling for converting to a matrix of booleans.
|
|
|
+ QualType elemType = {};
|
|
|
+ uint32_t rowCount = 0, colCount = 0;
|
|
|
+ if (TypeTranslator::isMxNMatrix(fromType, &elemType, &rowCount,
|
|
|
+ &colCount)) {
|
|
|
+ const auto fromRowQualType =
|
|
|
+ astContext.getExtVectorType(elemType, colCount);
|
|
|
+ const auto fromRowQualTypeId =
|
|
|
+ typeTranslator.translateType(fromRowQualType);
|
|
|
+ const auto toBoolRowQualType =
|
|
|
+ astContext.getExtVectorType(astContext.BoolTy, colCount);
|
|
|
+ llvm::SmallVector<uint32_t, 4> rows;
|
|
|
+ for (uint32_t i = 0; i < rowCount; ++i) {
|
|
|
+ const auto row =
|
|
|
+ theBuilder.createCompositeExtract(fromRowQualTypeId, fromVal, {i});
|
|
|
+ rows.push_back(castToBool(row, fromRowQualType, toBoolRowQualType));
|
|
|
+ }
|
|
|
+ return theBuilder.createCompositeConstruct(boolType, rows);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Converting to bool means comparing with value zero.
|
|
|
const spv::Op spvOp = translateOp(BO_NE, fromType);
|
|
|
- const uint32_t boolType = typeTranslator.translateType(toBoolType);
|
|
|
const uint32_t zeroVal = getValueZero(fromType);
|
|
|
-
|
|
|
return theBuilder.createBinaryOp(spvOp, boolType, fromVal, zeroVal);
|
|
|
}
|
|
|
|
|
@@ -5549,8 +5565,38 @@ uint32_t SPIRVEmitter::castToInt(const uint32_t fromVal, QualType fromType,
|
|
|
} else {
|
|
|
emitError("casting from floating point to integer unimplemented", srcLoc);
|
|
|
}
|
|
|
- } else {
|
|
|
- emitError("casting to integer unimplemented", srcLoc);
|
|
|
+ }
|
|
|
+
|
|
|
+ {
|
|
|
+ QualType elemType = {};
|
|
|
+ uint32_t numRows = 0, numCols = 0;
|
|
|
+ if (TypeTranslator::isMxNMatrix(fromType, &elemType, &numRows, &numCols)) {
|
|
|
+ // The source matrix and the target matrix must have the same dimensions.
|
|
|
+ QualType toElemType = {};
|
|
|
+ uint32_t toNumRows = 0, toNumCols = 0;
|
|
|
+ assert(TypeTranslator::isMxNMatrix(toIntType, &toElemType, &toNumRows,
|
|
|
+ &toNumCols) &&
|
|
|
+ numRows == toNumRows && numCols == toNumCols);
|
|
|
+ (void)toElemType;
|
|
|
+ (void)toNumRows;
|
|
|
+ (void)toNumCols;
|
|
|
+
|
|
|
+ // Casting to a matrix of integers: Cast each row and construct a
|
|
|
+ // composite.
|
|
|
+ llvm::SmallVector<uint32_t, 4> castedRows;
|
|
|
+ const uint32_t vecType = typeTranslator.getComponentVectorType(fromType);
|
|
|
+ const auto fromVecQualType =
|
|
|
+ astContext.getExtVectorType(elemType, numCols);
|
|
|
+ const auto toIntVecQualType =
|
|
|
+ astContext.getExtVectorType(toElemType, numCols);
|
|
|
+ for (uint32_t row = 0; row < numRows; ++row) {
|
|
|
+ const auto rowId =
|
|
|
+ theBuilder.createCompositeExtract(vecType, fromVal, {row});
|
|
|
+ castedRows.push_back(
|
|
|
+ castToInt(rowId, fromVecQualType, toIntVecQualType, srcLoc));
|
|
|
+ }
|
|
|
+ return theBuilder.createCompositeConstruct(intType, castedRows);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -5582,6 +5628,39 @@ uint32_t SPIRVEmitter::castToFloat(const uint32_t fromVal, QualType fromType,
|
|
|
return theBuilder.createUnaryOp(spv::Op::OpFConvert, floatType, fromVal);
|
|
|
}
|
|
|
|
|
|
+ // Casting matrix types
|
|
|
+ {
|
|
|
+ QualType elemType = {};
|
|
|
+ uint32_t numRows = 0, numCols = 0;
|
|
|
+ if (TypeTranslator::isMxNMatrix(fromType, &elemType, &numRows, &numCols)) {
|
|
|
+ // The source matrix and the target matrix must have the same dimensions.
|
|
|
+ QualType toElemType = {};
|
|
|
+ uint32_t toNumRows = 0, toNumCols = 0;
|
|
|
+ assert(TypeTranslator::isMxNMatrix(toFloatType, &toElemType, &toNumRows,
|
|
|
+ &toNumCols) &&
|
|
|
+ numRows == toNumRows && numCols == toNumCols);
|
|
|
+ (void)toElemType;
|
|
|
+ (void)toNumRows;
|
|
|
+ (void)toNumCols;
|
|
|
+
|
|
|
+ // Casting to a matrix of floats: Cast each row and construct a
|
|
|
+ // composite.
|
|
|
+ llvm::SmallVector<uint32_t, 4> castedRows;
|
|
|
+ const uint32_t vecType = typeTranslator.getComponentVectorType(fromType);
|
|
|
+ const auto fromVecQualType =
|
|
|
+ astContext.getExtVectorType(elemType, numCols);
|
|
|
+ const auto toIntVecQualType =
|
|
|
+ astContext.getExtVectorType(toElemType, numCols);
|
|
|
+ for (uint32_t row = 0; row < numRows; ++row) {
|
|
|
+ const auto rowId =
|
|
|
+ theBuilder.createCompositeExtract(vecType, fromVal, {row});
|
|
|
+ castedRows.push_back(
|
|
|
+ castToFloat(rowId, fromVecQualType, toIntVecQualType, srcLoc));
|
|
|
+ }
|
|
|
+ return theBuilder.createCompositeConstruct(floatType, castedRows);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
emitError("casting to floating point unimplemented", srcLoc);
|
|
|
return 0;
|
|
|
}
|
|
@@ -5726,7 +5805,9 @@ SpirvEvalInfo SPIRVEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) {
|
|
|
retVal =
|
|
|
theBuilder.createImageSparseTexelsResident(doExpr(callExpr->getArg(0)));
|
|
|
break;
|
|
|
+
|
|
|
case hlsl::IntrinsicOp::IOP_mul:
|
|
|
+ case hlsl::IntrinsicOp::IOP_umul:
|
|
|
retVal = processIntrinsicMul(callExpr);
|
|
|
break;
|
|
|
case hlsl::IntrinsicOp::IOP_all:
|
|
@@ -5806,7 +5887,17 @@ SpirvEvalInfo SPIRVEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) {
|
|
|
<< callee->getName();
|
|
|
return 0;
|
|
|
}
|
|
|
- INTRINSIC_SPIRV_OP_CASE(transpose, Transpose, false);
|
|
|
+ case hlsl::IntrinsicOp::IOP_transpose: {
|
|
|
+ const Expr *mat = callExpr->getArg(0);
|
|
|
+ const QualType matType = mat->getType();
|
|
|
+ if (hlsl::GetHLSLMatElementType(matType)->isFloatingType())
|
|
|
+ retVal =
|
|
|
+ processIntrinsicUsingSpirvInst(callExpr, spv::Op::OpTranspose, false);
|
|
|
+ else
|
|
|
+ retVal = processNonFpMatrixTranspose(matType, doExpr(mat));
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
INTRINSIC_SPIRV_OP_CASE(ddx, DPdx, true);
|
|
|
INTRINSIC_SPIRV_OP_WITH_CAP_CASE(ddx_coarse, DPdxCoarse, false,
|
|
|
spv::Capability::DerivativeControl);
|
|
@@ -6189,14 +6280,6 @@ uint32_t SPIRVEmitter::processIntrinsicModf(const CallExpr *callExpr) {
|
|
|
const uint32_t argId = doExpr(arg);
|
|
|
const uint32_t ipId = doExpr(ipArg);
|
|
|
|
|
|
- // TODO: We currently do not support non-float matrices.
|
|
|
- QualType ipElemType = {};
|
|
|
- if (TypeTranslator::isMxNMatrix(ipType, &ipElemType) &&
|
|
|
- !ipElemType->isFloatingType()) {
|
|
|
- emitError("non-floating-point matrix type unimplemented", {});
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
// For scalar and vector argument types.
|
|
|
{
|
|
|
if (TypeTranslator::isScalarType(argType) ||
|
|
@@ -6235,12 +6318,20 @@ uint32_t SPIRVEmitter::processIntrinsicModf(const CallExpr *callExpr) {
|
|
|
modfStructTypeId, glslInstSetId, GLSLstd450::GLSLstd450ModfStruct,
|
|
|
{curRow});
|
|
|
auto ip = theBuilder.createCompositeExtract(colTypeId, modf, {1});
|
|
|
+
|
|
|
ips.push_back(ip);
|
|
|
fracs.push_back(
|
|
|
theBuilder.createCompositeExtract(colTypeId, modf, {0}));
|
|
|
}
|
|
|
- theBuilder.createStore(
|
|
|
- ipId, theBuilder.createCompositeConstruct(returnTypeId, ips));
|
|
|
+
|
|
|
+ uint32_t ip = theBuilder.createCompositeConstruct(
|
|
|
+ typeTranslator.translateType(argType), ips);
|
|
|
+ // If the 'ip' is not a float type, the AST will not contain a CastExpr
|
|
|
+ // because this is internal to the intrinsic function. So, in such a
|
|
|
+ // case we need to cast manually.
|
|
|
+ if (!hlsl::GetHLSLMatElementType(ipType)->isFloatingType())
|
|
|
+ ip = castToInt(ip, argType, ipType, ipArg->getExprLoc());
|
|
|
+ theBuilder.createStore(ipId, ip);
|
|
|
return theBuilder.createCompositeConstruct(returnTypeId, fracs);
|
|
|
}
|
|
|
}
|
|
@@ -6532,7 +6623,7 @@ uint32_t SPIRVEmitter::processIntrinsicClamp(const CallExpr *callExpr) {
|
|
|
|
|
|
// FClamp, UClamp, and SClamp do not operate on matrices, so we should perform
|
|
|
// the operation on each vector of the matrix.
|
|
|
- if (TypeTranslator::isSpirvAcceptableMatrixType(argX->getType())) {
|
|
|
+ if (TypeTranslator::isMxNMatrix(argX->getType())) {
|
|
|
const auto actOnEachVec = [this, glslInstSetId, glslOpcode, argMinId,
|
|
|
argMaxId](uint32_t index, uint32_t vecType,
|
|
|
uint32_t curRowId) {
|
|
@@ -6617,6 +6708,209 @@ uint32_t SPIRVEmitter::processIntrinsicMemoryBarrier(const CallExpr *callExpr,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+uint32_t SPIRVEmitter::processNonFpMatrixTranspose(QualType matType,
|
|
|
+ uint32_t matId) {
|
|
|
+ // Simplest way is to flatten the matrix construct a new matrix from the
|
|
|
+ // flattened elements. (for a mat4x4).
|
|
|
+ QualType elemType = {};
|
|
|
+ uint32_t numRows = 0, numCols = 0;
|
|
|
+ const bool isMat =
|
|
|
+ TypeTranslator::isMxNMatrix(matType, &elemType, &numRows, &numCols);
|
|
|
+ assert(isMat && !elemType->isFloatingType());
|
|
|
+
|
|
|
+ const auto rowQualType = astContext.getExtVectorType(elemType, numCols);
|
|
|
+ const auto colQualType = astContext.getExtVectorType(elemType, numRows);
|
|
|
+ const uint32_t rowTypeId = typeTranslator.translateType(rowQualType);
|
|
|
+ const uint32_t colTypeId = typeTranslator.translateType(colQualType);
|
|
|
+ const uint32_t elemTypeId = typeTranslator.translateType(elemType);
|
|
|
+
|
|
|
+ // You cannot perform a composite construct of an array using a few vectors.
|
|
|
+ // The number of constutients passed to OpCompositeConstruct must be equal to
|
|
|
+ // the number of array elements.
|
|
|
+ llvm::SmallVector<uint32_t, 4> elems;
|
|
|
+ for (uint32_t i = 0; i < numRows; ++i)
|
|
|
+ for (uint32_t j = 0; j < numCols; ++j)
|
|
|
+ elems.push_back(
|
|
|
+ theBuilder.createCompositeExtract(elemTypeId, matId, {i, j}));
|
|
|
+
|
|
|
+ llvm::SmallVector<uint32_t, 4> cols;
|
|
|
+ for (uint32_t i = 0; i < numCols; ++i) {
|
|
|
+ // The elements in the ith vector of the "transposed" array are at offset i,
|
|
|
+ // i + <original-vector-size>, ...
|
|
|
+ llvm::SmallVector<uint32_t, 4> indexes;
|
|
|
+ for (uint32_t j = 0; j < numRows; ++j)
|
|
|
+ indexes.push_back(elems[i + (j * numCols)]);
|
|
|
+
|
|
|
+ cols.push_back(theBuilder.createCompositeConstruct(colTypeId, indexes));
|
|
|
+ }
|
|
|
+
|
|
|
+ const auto transposeTypeId =
|
|
|
+ theBuilder.getArrayType(colTypeId, theBuilder.getConstantUint32(numCols));
|
|
|
+ return theBuilder.createCompositeConstruct(transposeTypeId, cols);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t SPIRVEmitter::processNonFpDot(uint32_t vec1Id, uint32_t vec2Id,
|
|
|
+ uint32_t vecSize, QualType elemType) {
|
|
|
+ const auto elemTypeId = typeTranslator.translateType(elemType);
|
|
|
+ llvm::SmallVector<uint32_t, 4> muls;
|
|
|
+ for (uint32_t i = 0; i < vecSize; ++i) {
|
|
|
+ const auto elem1 =
|
|
|
+ theBuilder.createCompositeExtract(elemTypeId, vec1Id, {i});
|
|
|
+ const auto elem2 =
|
|
|
+ theBuilder.createCompositeExtract(elemTypeId, vec2Id, {i});
|
|
|
+ muls.push_back(theBuilder.createBinaryOp(translateOp(BO_Mul, elemType),
|
|
|
+ elemTypeId, elem1, elem2));
|
|
|
+ }
|
|
|
+ uint32_t sum = muls[0];
|
|
|
+ for (uint32_t i = 1; i < vecSize; ++i) {
|
|
|
+ sum = theBuilder.createBinaryOp(translateOp(BO_Add, elemType), elemTypeId,
|
|
|
+ sum, muls[i]);
|
|
|
+ }
|
|
|
+ return sum;
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t SPIRVEmitter::processNonFpScalarTimesMatrix(QualType scalarType,
|
|
|
+ uint32_t scalarId,
|
|
|
+ QualType matrixType,
|
|
|
+ uint32_t matrixId) {
|
|
|
+ assert(TypeTranslator::isScalarType(scalarType));
|
|
|
+ QualType elemType = {};
|
|
|
+ uint32_t numRows = 0, numCols = 0;
|
|
|
+ const bool isMat =
|
|
|
+ TypeTranslator::isMxNMatrix(matrixType, &elemType, &numRows, &numCols);
|
|
|
+ assert(isMat);
|
|
|
+ assert(typeTranslator.isSameType(scalarType, elemType));
|
|
|
+
|
|
|
+ // We need to multiply the scalar by each vector of the matrix.
|
|
|
+ // The front-end guarantees that the scalar and matrix element type are
|
|
|
+ // the same. For example, if the scalar is a float, the matrix is casted
|
|
|
+ // to a float matrix before being passed to mul(). It is also guaranteed
|
|
|
+ // that types such as bool are casted to float or int before being
|
|
|
+ // passed to mul().
|
|
|
+ const auto rowType = astContext.getExtVectorType(elemType, numCols);
|
|
|
+ const auto rowTypeId = typeTranslator.translateType(rowType);
|
|
|
+ llvm::SmallVector<uint32_t, 4> splat(size_t(numCols), scalarId);
|
|
|
+ const auto scalarSplat =
|
|
|
+ theBuilder.createCompositeConstruct(rowTypeId, splat);
|
|
|
+ llvm::SmallVector<uint32_t, 4> mulRows;
|
|
|
+ for (uint32_t row = 0; row < numRows; ++row) {
|
|
|
+ const auto rowId =
|
|
|
+ theBuilder.createCompositeExtract(rowTypeId, matrixId, {row});
|
|
|
+ mulRows.push_back(theBuilder.createBinaryOp(translateOp(BO_Mul, scalarType),
|
|
|
+ rowTypeId, rowId, scalarSplat));
|
|
|
+ }
|
|
|
+ return theBuilder.createCompositeConstruct(
|
|
|
+ typeTranslator.translateType(matrixType), mulRows);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t SPIRVEmitter::processNonFpVectorTimesMatrix(QualType vecType,
|
|
|
+ uint32_t vecId,
|
|
|
+ QualType matType,
|
|
|
+ uint32_t matId,
|
|
|
+ uint32_t matTransposeId) {
|
|
|
+ // This function assumes that the vector element type and matrix elemet type
|
|
|
+ // are the same.
|
|
|
+ QualType vecElemType = {}, matElemType = {};
|
|
|
+ uint32_t vecSize = 0, numRows = 0, numCols = 0;
|
|
|
+ const bool isVec =
|
|
|
+ TypeTranslator::isVectorType(vecType, &vecElemType, &vecSize);
|
|
|
+ const bool isMat =
|
|
|
+ TypeTranslator::isMxNMatrix(matType, &matElemType, &numRows, &numCols);
|
|
|
+ assert(typeTranslator.isSameType(vecElemType, matElemType));
|
|
|
+ assert(isVec);
|
|
|
+ assert(isMat);
|
|
|
+ assert(vecSize == numRows);
|
|
|
+
|
|
|
+ // When processing vector times matrix, the vector is a row vector, and it
|
|
|
+ // should be multiplied by the matrix *columns*. The most efficient way to
|
|
|
+ // handle this in SPIR-V would be to first transpose the matrix, and then use
|
|
|
+ // OpAccessChain.
|
|
|
+ if (!matTransposeId)
|
|
|
+ matTransposeId = processNonFpMatrixTranspose(matType, matId);
|
|
|
+
|
|
|
+ const auto vecTypeId = typeTranslator.translateType(vecType);
|
|
|
+ llvm::SmallVector<uint32_t, 4> resultElems;
|
|
|
+ for (uint32_t col = 0; col < numCols; ++col) {
|
|
|
+ const auto colId =
|
|
|
+ theBuilder.createCompositeExtract(vecTypeId, matTransposeId, {col});
|
|
|
+ resultElems.push_back(processNonFpDot(vecId, colId, vecSize, vecElemType));
|
|
|
+ }
|
|
|
+ return theBuilder.createCompositeConstruct(
|
|
|
+ typeTranslator.translateType(
|
|
|
+ astContext.getExtVectorType(vecElemType, numCols)),
|
|
|
+ resultElems);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t SPIRVEmitter::processNonFpMatrixTimesVector(QualType matType,
|
|
|
+ uint32_t matId,
|
|
|
+ QualType vecType,
|
|
|
+ uint32_t vecId) {
|
|
|
+ // This function assumes that the vector element type and matrix elemet type
|
|
|
+ // are the same.
|
|
|
+ QualType vecElemType = {}, matElemType = {};
|
|
|
+ uint32_t vecSize = 0, numRows = 0, numCols = 0;
|
|
|
+ const bool isVec =
|
|
|
+ TypeTranslator::isVectorType(vecType, &vecElemType, &vecSize);
|
|
|
+ const bool isMat =
|
|
|
+ TypeTranslator::isMxNMatrix(matType, &matElemType, &numRows, &numCols);
|
|
|
+ assert(typeTranslator.isSameType(vecElemType, matElemType));
|
|
|
+ assert(isVec);
|
|
|
+ assert(isMat);
|
|
|
+ assert(vecSize == numCols);
|
|
|
+
|
|
|
+ // When processing matrix times vector, the vector is a column vector. So we
|
|
|
+ // simply get each row of the matrix and perform a dot product with the
|
|
|
+ // vector.
|
|
|
+ const auto vecTypeId = typeTranslator.translateType(vecType);
|
|
|
+ llvm::SmallVector<uint32_t, 4> resultElems;
|
|
|
+ for (uint32_t row = 0; row < numRows; ++row) {
|
|
|
+ const auto rowId =
|
|
|
+ theBuilder.createCompositeExtract(vecTypeId, matId, {row});
|
|
|
+ resultElems.push_back(processNonFpDot(rowId, vecId, vecSize, vecElemType));
|
|
|
+ }
|
|
|
+ return theBuilder.createCompositeConstruct(
|
|
|
+ typeTranslator.translateType(
|
|
|
+ astContext.getExtVectorType(vecElemType, numRows)),
|
|
|
+ resultElems);
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t SPIRVEmitter::processNonFpMatrixTimesMatrix(QualType lhsType,
|
|
|
+ uint32_t lhsId,
|
|
|
+ QualType rhsType,
|
|
|
+ uint32_t rhsId) {
|
|
|
+ // This function assumes that the vector element type and matrix elemet type
|
|
|
+ // are the same.
|
|
|
+ QualType lhsElemType = {}, rhsElemType = {};
|
|
|
+ uint32_t lhsNumRows = 0, lhsNumCols = 0;
|
|
|
+ uint32_t rhsNumRows = 0, rhsNumCols = 0;
|
|
|
+ const bool lhsIsMat = TypeTranslator::isMxNMatrix(lhsType, &lhsElemType,
|
|
|
+ &lhsNumRows, &lhsNumCols);
|
|
|
+ const bool rhsIsMat = TypeTranslator::isMxNMatrix(rhsType, &rhsElemType,
|
|
|
+ &rhsNumRows, &rhsNumCols);
|
|
|
+ assert(typeTranslator.isSameType(lhsElemType, rhsElemType));
|
|
|
+ assert(lhsIsMat && rhsIsMat);
|
|
|
+ assert(lhsNumCols == rhsNumRows);
|
|
|
+
|
|
|
+ const uint32_t rhsTranspose = processNonFpMatrixTranspose(rhsType, rhsId);
|
|
|
+
|
|
|
+ const auto vecType = astContext.getExtVectorType(lhsElemType, lhsNumCols);
|
|
|
+ const auto vecTypeId = typeTranslator.translateType(vecType);
|
|
|
+ llvm::SmallVector<uint32_t, 4> resultRows;
|
|
|
+ for (uint32_t row = 0; row < lhsNumRows; ++row) {
|
|
|
+ const auto rowId =
|
|
|
+ theBuilder.createCompositeExtract(vecTypeId, lhsId, {row});
|
|
|
+ resultRows.push_back(processNonFpVectorTimesMatrix(vecType, rowId, rhsType,
|
|
|
+ rhsId, rhsTranspose));
|
|
|
+ }
|
|
|
+
|
|
|
+ // The resulting matrix will have 'lhsNumRows' rows and 'rhsNumCols' columns.
|
|
|
+ const auto elemTypeId = typeTranslator.translateType(lhsElemType);
|
|
|
+ const auto resultNumRows = theBuilder.getConstantUint32(lhsNumRows);
|
|
|
+ const auto resultColType = theBuilder.getVecType(elemTypeId, rhsNumCols);
|
|
|
+ const auto resultType = theBuilder.getArrayType(resultColType, resultNumRows);
|
|
|
+ return theBuilder.createCompositeConstruct(resultType, resultRows);
|
|
|
+}
|
|
|
+
|
|
|
uint32_t SPIRVEmitter::processIntrinsicMul(const CallExpr *callExpr) {
|
|
|
const QualType returnType = callExpr->getType();
|
|
|
const uint32_t returnTypeId =
|
|
@@ -6688,61 +6982,85 @@ uint32_t SPIRVEmitter::processIntrinsicMul(const CallExpr *callExpr) {
|
|
|
returnTypeId, arg0Id, arg1Id);
|
|
|
|
|
|
// mul(scalar, matrix)
|
|
|
- if (TypeTranslator::isScalarType(arg0Type) &&
|
|
|
- TypeTranslator::isMxNMatrix(arg1Type)) {
|
|
|
- // We currently only support float matrices. So we can use
|
|
|
- // OpMatrixTimesScalar
|
|
|
- if (arg0Type->isFloatingType())
|
|
|
- return theBuilder.createBinaryOp(spv::Op::OpMatrixTimesScalar,
|
|
|
- returnTypeId, arg1Id, arg0Id);
|
|
|
+ {
|
|
|
+ QualType elemType = {};
|
|
|
+ if (TypeTranslator::isScalarType(arg0Type) &&
|
|
|
+ TypeTranslator::isMxNMatrix(arg1Type, &elemType)) {
|
|
|
+ // OpMatrixTimesScalar can only be used if *both* the matrix element type
|
|
|
+ // and the scalar type are float.
|
|
|
+ if (arg0Type->isFloatingType() && elemType->isFloatingType())
|
|
|
+ return theBuilder.createBinaryOp(spv::Op::OpMatrixTimesScalar,
|
|
|
+ returnTypeId, arg1Id, arg0Id);
|
|
|
+ else
|
|
|
+ return processNonFpScalarTimesMatrix(arg0Type, arg0Id, arg1Type,
|
|
|
+ arg1Id);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// mul(matrix, scalar)
|
|
|
- if (TypeTranslator::isScalarType(arg1Type) &&
|
|
|
- TypeTranslator::isMxNMatrix(arg0Type)) {
|
|
|
- // We currently only support float matrices. So we can use
|
|
|
- // OpMatrixTimesScalar
|
|
|
- if (arg1Type->isFloatingType())
|
|
|
- return theBuilder.createBinaryOp(spv::Op::OpMatrixTimesScalar,
|
|
|
- returnTypeId, arg0Id, arg1Id);
|
|
|
+ {
|
|
|
+ QualType elemType = {};
|
|
|
+ if (TypeTranslator::isScalarType(arg1Type) &&
|
|
|
+ TypeTranslator::isMxNMatrix(arg0Type, &elemType)) {
|
|
|
+ // OpMatrixTimesScalar can only be used if *both* the matrix element type
|
|
|
+ // and the scalar type are float.
|
|
|
+ if (arg1Type->isFloatingType() && elemType->isFloatingType())
|
|
|
+ return theBuilder.createBinaryOp(spv::Op::OpMatrixTimesScalar,
|
|
|
+ returnTypeId, arg0Id, arg1Id);
|
|
|
+ else
|
|
|
+ return processNonFpScalarTimesMatrix(arg1Type, arg1Id, arg0Type,
|
|
|
+ arg0Id);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// mul(vector, matrix)
|
|
|
{
|
|
|
- QualType elemType = {};
|
|
|
+ QualType vecElemType = {}, matElemType = {};
|
|
|
uint32_t elemCount = 0, numRows = 0;
|
|
|
- if (TypeTranslator::isVectorType(arg0Type, &elemType, &elemCount) &&
|
|
|
- TypeTranslator::isMxNMatrix(arg1Type, nullptr, &numRows, nullptr) &&
|
|
|
- elemType->isFloatingType()) {
|
|
|
+ if (TypeTranslator::isVectorType(arg0Type, &vecElemType, &elemCount) &&
|
|
|
+ TypeTranslator::isMxNMatrix(arg1Type, &matElemType, &numRows)) {
|
|
|
assert(elemCount == numRows);
|
|
|
- return theBuilder.createBinaryOp(spv::Op::OpMatrixTimesVector,
|
|
|
- returnTypeId, arg1Id, arg0Id);
|
|
|
+
|
|
|
+ if (vecElemType->isFloatingType() && matElemType->isFloatingType())
|
|
|
+ return theBuilder.createBinaryOp(spv::Op::OpMatrixTimesVector,
|
|
|
+ returnTypeId, arg1Id, arg0Id);
|
|
|
+ else
|
|
|
+ return processNonFpVectorTimesMatrix(arg0Type, arg0Id, arg1Type,
|
|
|
+ arg1Id);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// mul(matrix, vector)
|
|
|
{
|
|
|
- QualType elemType = {};
|
|
|
+ QualType vecElemType = {}, matElemType = {};
|
|
|
uint32_t elemCount = 0, numCols = 0;
|
|
|
- if (TypeTranslator::isMxNMatrix(arg0Type, nullptr, nullptr, &numCols) &&
|
|
|
- TypeTranslator::isVectorType(arg1Type, &elemType, &elemCount) &&
|
|
|
- elemType->isFloatingType()) {
|
|
|
+ if (TypeTranslator::isMxNMatrix(arg0Type, &matElemType, nullptr,
|
|
|
+ &numCols) &&
|
|
|
+ TypeTranslator::isVectorType(arg1Type, &vecElemType, &elemCount)) {
|
|
|
assert(elemCount == numCols);
|
|
|
- return theBuilder.createBinaryOp(spv::Op::OpVectorTimesMatrix,
|
|
|
- returnTypeId, arg1Id, arg0Id);
|
|
|
+ if (vecElemType->isFloatingType() && matElemType->isFloatingType())
|
|
|
+ return theBuilder.createBinaryOp(spv::Op::OpVectorTimesMatrix,
|
|
|
+ returnTypeId, arg1Id, arg0Id);
|
|
|
+ else
|
|
|
+ return processNonFpMatrixTimesVector(arg0Type, arg0Id, arg1Type,
|
|
|
+ arg1Id);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// mul(matrix, matrix)
|
|
|
{
|
|
|
+ // The front-end ensures that the two matrix element types match.
|
|
|
QualType elemType = {};
|
|
|
- uint32_t arg0Cols = 0, arg1Rows = 0;
|
|
|
- if (TypeTranslator::isMxNMatrix(arg0Type, &elemType, nullptr, &arg0Cols) &&
|
|
|
- TypeTranslator::isMxNMatrix(arg1Type, nullptr, &arg1Rows, nullptr) &&
|
|
|
- elemType->isFloatingType()) {
|
|
|
- assert(arg0Cols == arg1Rows);
|
|
|
- return theBuilder.createBinaryOp(spv::Op::OpMatrixTimesMatrix,
|
|
|
- returnTypeId, arg1Id, arg0Id);
|
|
|
+ uint32_t lhsCols = 0, rhsRows = 0;
|
|
|
+ if (TypeTranslator::isMxNMatrix(arg0Type, &elemType, nullptr, &lhsCols) &&
|
|
|
+ TypeTranslator::isMxNMatrix(arg1Type, nullptr, &rhsRows, nullptr)) {
|
|
|
+ assert(lhsCols == rhsRows);
|
|
|
+ if (elemType->isFloatingType())
|
|
|
+ return theBuilder.createBinaryOp(spv::Op::OpMatrixTimesMatrix,
|
|
|
+ returnTypeId, arg1Id, arg0Id);
|
|
|
+ else
|
|
|
+ return processNonFpMatrixTimesMatrix(arg0Type, arg0Id, arg1Type,
|
|
|
+ arg1Id);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -6889,13 +7207,6 @@ uint32_t SPIRVEmitter::processIntrinsicAllOrAny(const CallExpr *callExpr,
|
|
|
uint32_t matRowCount = 0, matColCount = 0;
|
|
|
if (TypeTranslator::isMxNMatrix(argType, &elemType, &matRowCount,
|
|
|
&matColCount)) {
|
|
|
- if (!elemType->isFloatingType()) {
|
|
|
- emitError("non-floating-point matrix arguments in all/any intrinsic "
|
|
|
- "function unimplemented",
|
|
|
- callExpr->getExprLoc());
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
uint32_t matrixId = doExpr(arg);
|
|
|
const uint32_t vecType = typeTranslator.getComponentVectorType(argType);
|
|
|
llvm::SmallVector<uint32_t, 4> rowResults;
|
|
@@ -6967,24 +7278,36 @@ uint32_t SPIRVEmitter::processIntrinsicAsType(const CallExpr *callExpr) {
|
|
|
const QualType argType = arg0->getType();
|
|
|
|
|
|
// Method 3 return type may be the same as arg type, so it would be a no-op.
|
|
|
- if (returnType.getCanonicalType() == argType.getCanonicalType())
|
|
|
+ if (typeTranslator.isSameType(returnType, argType))
|
|
|
return doExpr(arg0);
|
|
|
|
|
|
- // SPIR-V does not support non-floating point matrices. For the above methods
|
|
|
- // that involve matrices, either the input or the output is a non-float
|
|
|
- // matrix. (except for 'asfloat' taking a float matrix and returning a float
|
|
|
- // matrix, which is a no-op and is handled by the condition above).
|
|
|
- if (TypeTranslator::isMxNMatrix(argType)) {
|
|
|
- emitError("non-floating-point matrix type unimplemented",
|
|
|
- callExpr->getExprLoc());
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
switch (numArgs) {
|
|
|
case 1: {
|
|
|
// Handling Method 1, 2, and 3.
|
|
|
- return theBuilder.createUnaryOp(spv::Op::OpBitcast, returnTypeId,
|
|
|
- doExpr(arg0));
|
|
|
+ const auto argId = doExpr(arg0);
|
|
|
+ QualType fromElemType = {};
|
|
|
+ uint32_t numRows = 0, numCols = 0;
|
|
|
+ // For non-matrix arguments (scalar or vector), just do an OpBitCast.
|
|
|
+ if (!TypeTranslator::isMxNMatrix(argType, &fromElemType, &numRows,
|
|
|
+ &numCols)) {
|
|
|
+ return theBuilder.createUnaryOp(spv::Op::OpBitcast, returnTypeId, argId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Input or output type is a matrix.
|
|
|
+ const QualType toElemType = hlsl::GetHLSLMatElementType(returnType);
|
|
|
+ llvm::SmallVector<uint32_t, 4> castedRows;
|
|
|
+ const auto fromVecQualType =
|
|
|
+ astContext.getExtVectorType(fromElemType, numCols);
|
|
|
+ const auto toVecQualType = astContext.getExtVectorType(toElemType, numCols);
|
|
|
+ const auto fromVecTypeId = typeTranslator.translateType(fromVecQualType);
|
|
|
+ const auto toVecTypeId = typeTranslator.translateType(toVecQualType);
|
|
|
+ for (uint32_t row = 0; row < numRows; ++row) {
|
|
|
+ const auto rowId =
|
|
|
+ theBuilder.createCompositeExtract(fromVecTypeId, argId, {row});
|
|
|
+ castedRows.push_back(
|
|
|
+ theBuilder.createUnaryOp(spv::Op::OpBitcast, toVecTypeId, rowId));
|
|
|
+ }
|
|
|
+ return theBuilder.createCompositeConstruct(returnTypeId, castedRows);
|
|
|
}
|
|
|
case 2: {
|
|
|
const uint32_t lowbits = doExpr(arg0);
|
|
@@ -7142,7 +7465,7 @@ uint32_t SPIRVEmitter::processIntrinsicFloatSign(const CallExpr *callExpr) {
|
|
|
uint32_t floatSignResultId = 0;
|
|
|
|
|
|
// For matrices, we can perform the instruction on each vector of the matrix.
|
|
|
- if (TypeTranslator::isSpirvAcceptableMatrixType(argType)) {
|
|
|
+ if (TypeTranslator::isMxNMatrix(argType)) {
|
|
|
const auto actOnEachVec = [this, glslInstSetId](uint32_t /*index*/,
|
|
|
uint32_t vecType,
|
|
|
uint32_t curRowId) {
|
|
@@ -7257,8 +7580,7 @@ uint32_t SPIRVEmitter::processIntrinsicUsingSpirvInst(
|
|
|
|
|
|
// If the instruction does not operate on matrices, we can perform the
|
|
|
// instruction on each vector of the matrix.
|
|
|
- if (actPerRowForMatrices &&
|
|
|
- TypeTranslator::isSpirvAcceptableMatrixType(arg->getType())) {
|
|
|
+ if (actPerRowForMatrices && TypeTranslator::isMxNMatrix(arg->getType())) {
|
|
|
const auto actOnEachVec = [this, opcode](uint32_t /*index*/,
|
|
|
uint32_t vecType,
|
|
|
uint32_t curRowId) {
|
|
@@ -7273,8 +7595,7 @@ uint32_t SPIRVEmitter::processIntrinsicUsingSpirvInst(
|
|
|
const uint32_t arg1Id = doExpr(callExpr->getArg(1));
|
|
|
// If the instruction does not operate on matrices, we can perform the
|
|
|
// instruction on each vector of the matrix.
|
|
|
- if (actPerRowForMatrices &&
|
|
|
- TypeTranslator::isSpirvAcceptableMatrixType(arg0->getType())) {
|
|
|
+ if (actPerRowForMatrices && TypeTranslator::isMxNMatrix(arg0->getType())) {
|
|
|
const auto actOnEachVec = [this, opcode, arg1Id](uint32_t index,
|
|
|
uint32_t vecType,
|
|
|
uint32_t arg0RowId) {
|
|
@@ -7303,8 +7624,7 @@ uint32_t SPIRVEmitter::processIntrinsicUsingGLSLInst(
|
|
|
|
|
|
// If the instruction does not operate on matrices, we can perform the
|
|
|
// instruction on each vector of the matrix.
|
|
|
- if (actPerRowForMatrices &&
|
|
|
- TypeTranslator::isSpirvAcceptableMatrixType(arg->getType())) {
|
|
|
+ if (actPerRowForMatrices && TypeTranslator::isMxNMatrix(arg->getType())) {
|
|
|
const auto actOnEachVec = [this, glslInstSetId,
|
|
|
opcode](uint32_t /*index*/, uint32_t vecType,
|
|
|
uint32_t curRowId) {
|
|
@@ -7320,8 +7640,7 @@ uint32_t SPIRVEmitter::processIntrinsicUsingGLSLInst(
|
|
|
const uint32_t arg1Id = doExpr(callExpr->getArg(1));
|
|
|
// If the instruction does not operate on matrices, we can perform the
|
|
|
// instruction on each vector of the matrix.
|
|
|
- if (actPerRowForMatrices &&
|
|
|
- TypeTranslator::isSpirvAcceptableMatrixType(arg0->getType())) {
|
|
|
+ if (actPerRowForMatrices && TypeTranslator::isMxNMatrix(arg0->getType())) {
|
|
|
const auto actOnEachVec = [this, glslInstSetId, opcode,
|
|
|
arg1Id](uint32_t index, uint32_t vecType,
|
|
|
uint32_t arg0RowId) {
|
|
@@ -7341,8 +7660,7 @@ uint32_t SPIRVEmitter::processIntrinsicUsingGLSLInst(
|
|
|
const uint32_t arg2Id = doExpr(callExpr->getArg(2));
|
|
|
// If the instruction does not operate on matrices, we can perform the
|
|
|
// instruction on each vector of the matrix.
|
|
|
- if (actPerRowForMatrices &&
|
|
|
- TypeTranslator::isSpirvAcceptableMatrixType(arg0->getType())) {
|
|
|
+ if (actPerRowForMatrices && TypeTranslator::isMxNMatrix(arg0->getType())) {
|
|
|
const auto actOnEachVec = [this, glslInstSetId, opcode, arg0Id, arg1Id,
|
|
|
arg2Id](uint32_t index, uint32_t vecType,
|
|
|
uint32_t arg0RowId) {
|
|
@@ -7407,7 +7725,16 @@ uint32_t SPIRVEmitter::getValueZero(QualType type) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // TODO: Handle getValueZero for MxN matrices.
|
|
|
+ {
|
|
|
+ QualType elemType = {};
|
|
|
+ uint32_t rowCount = 0, colCount = 0;
|
|
|
+ if (TypeTranslator::isMxNMatrix(type, &elemType, &rowCount, &colCount)) {
|
|
|
+ const auto row = getVecValueZero(elemType, colCount);
|
|
|
+ llvm::SmallVector<uint32_t, 4> rows((size_t)rowCount, row);
|
|
|
+ return theBuilder.createCompositeConstruct(
|
|
|
+ typeTranslator.translateType(type), rows);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
emitError("getting value 0 for type %0 unimplemented", {})
|
|
|
<< type.getAsString();
|