|
@@ -6574,6 +6574,9 @@ SpirvEvalInfo SPIRVEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) {
|
|
|
case hlsl::IntrinsicOp::IOP_lit:
|
|
|
retVal = processIntrinsicLit(callExpr);
|
|
|
break;
|
|
|
+ case hlsl::IntrinsicOp::IOP_mad:
|
|
|
+ retVal = processIntrinsicMad(callExpr);
|
|
|
+ break;
|
|
|
case hlsl::IntrinsicOp::IOP_modf:
|
|
|
retVal = processIntrinsicModf(callExpr);
|
|
|
break;
|
|
@@ -6747,7 +6750,6 @@ SpirvEvalInfo SPIRVEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) {
|
|
|
INTRINSIC_OP_CASE(lerp, FMix, true);
|
|
|
INTRINSIC_OP_CASE(log, Log, true);
|
|
|
INTRINSIC_OP_CASE(log2, Log2, true);
|
|
|
- INTRINSIC_OP_CASE(mad, Fma, true);
|
|
|
INTRINSIC_OP_CASE_SINT_UINT_FLOAT(max, SMax, UMax, FMax, true);
|
|
|
INTRINSIC_OP_CASE(umax, UMax, true);
|
|
|
INTRINSIC_OP_CASE_SINT_UINT_FLOAT(min, SMin, UMin, FMin, true);
|
|
@@ -7425,6 +7427,105 @@ uint32_t SPIRVEmitter::processIntrinsicModf(const CallExpr *callExpr) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+uint32_t SPIRVEmitter::processIntrinsicMad(const CallExpr *callExpr) {
|
|
|
+ // Signature is: ret mad(a,b,c)
|
|
|
+ // All of the above must be a scalar, vector, or matrix with the same
|
|
|
+ // component types. Component types can be float or int.
|
|
|
+ // The return value is equal to "a * b + c"
|
|
|
+
|
|
|
+ // In the case of float arguments, we can use the GLSL extended instruction
|
|
|
+ // set's Fma instruction with NoContraction decoration. In the case of integer
|
|
|
+ // arguments, we'll have to manually perform an OpIMul followed by an OpIAdd
|
|
|
+ // (We should also apply NoContraction decoration to these two instructions to
|
|
|
+ // get precise arithmetic).
|
|
|
+
|
|
|
+ // TODO: We currently don't propagate the NoContraction decoration.
|
|
|
+
|
|
|
+ const Expr *arg0 = callExpr->getArg(0);
|
|
|
+ const Expr *arg1 = callExpr->getArg(1);
|
|
|
+ const Expr *arg2 = callExpr->getArg(2);
|
|
|
+ // All arguments and the return type are the same.
|
|
|
+ const auto argType = arg0->getType();
|
|
|
+ const auto argTypeId = typeTranslator.translateType(argType);
|
|
|
+ const uint32_t arg0Id = doExpr(arg0);
|
|
|
+ const uint32_t arg1Id = doExpr(arg1);
|
|
|
+ const uint32_t arg2Id = doExpr(arg2);
|
|
|
+
|
|
|
+ // For floating point arguments, we can use the extended instruction set's Fma
|
|
|
+ // instruction. Sadly we can't simply call processIntrinsicUsingGLSLInst
|
|
|
+ // because we need to specifically decorate the Fma instruction with
|
|
|
+ // NoContraction decoration.
|
|
|
+ if (isFloatOrVecMatOfFloatType(argType)) {
|
|
|
+ const auto opcode = GLSLstd450::GLSLstd450Fma;
|
|
|
+ const uint32_t glslInstSetId = theBuilder.getGLSLExtInstSet();
|
|
|
+ // For matrix cases, operate on each row of the matrix.
|
|
|
+ if (isMxNMatrix(arg0->getType())) {
|
|
|
+ const auto actOnEachVec = [this, glslInstSetId, opcode, arg1Id,
|
|
|
+ arg2Id](uint32_t index, uint32_t vecType,
|
|
|
+ uint32_t arg0RowId) {
|
|
|
+ const uint32_t arg1RowId =
|
|
|
+ theBuilder.createCompositeExtract(vecType, arg1Id, {index});
|
|
|
+ const uint32_t arg2RowId =
|
|
|
+ theBuilder.createCompositeExtract(vecType, arg2Id, {index});
|
|
|
+ const uint32_t fma = theBuilder.createExtInst(
|
|
|
+ vecType, glslInstSetId, opcode, {arg0RowId, arg1RowId, arg2RowId});
|
|
|
+ theBuilder.decorateNoContraction(fma);
|
|
|
+ return fma;
|
|
|
+ };
|
|
|
+ return processEachVectorInMatrix(arg0, arg0Id, actOnEachVec);
|
|
|
+ }
|
|
|
+ // Non-matrix cases
|
|
|
+ const uint32_t fma = theBuilder.createExtInst(
|
|
|
+ argTypeId, glslInstSetId, opcode, {arg0Id, arg1Id, arg2Id});
|
|
|
+ theBuilder.decorateNoContraction(fma);
|
|
|
+ return fma;
|
|
|
+ }
|
|
|
+
|
|
|
+ // For scalar and vector argument types.
|
|
|
+ {
|
|
|
+ if (isScalarType(argType) || isVectorType(argType)) {
|
|
|
+ const auto mul =
|
|
|
+ theBuilder.createBinaryOp(spv::Op::OpIMul, argTypeId, arg0Id, arg1Id);
|
|
|
+ const auto add =
|
|
|
+ theBuilder.createBinaryOp(spv::Op::OpIAdd, argTypeId, mul, arg2Id);
|
|
|
+ theBuilder.decorateNoContraction(mul);
|
|
|
+ theBuilder.decorateNoContraction(add);
|
|
|
+ return add;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // For matrix argument types.
|
|
|
+ {
|
|
|
+ uint32_t rowCount = 0, colCount = 0;
|
|
|
+ QualType elemType = {};
|
|
|
+ if (isMxNMatrix(argType, &elemType, &rowCount, &colCount)) {
|
|
|
+ const auto elemTypeId = typeTranslator.translateType(elemType);
|
|
|
+ const auto colTypeId = theBuilder.getVecType(elemTypeId, colCount);
|
|
|
+ llvm::SmallVector<uint32_t, 4> resultRows;
|
|
|
+ for (uint32_t i = 0; i < rowCount; ++i) {
|
|
|
+ const auto rowArg0 =
|
|
|
+ theBuilder.createCompositeExtract(colTypeId, arg0Id, {i});
|
|
|
+ const auto rowArg1 =
|
|
|
+ theBuilder.createCompositeExtract(colTypeId, arg1Id, {i});
|
|
|
+ const auto rowArg2 =
|
|
|
+ theBuilder.createCompositeExtract(colTypeId, arg2Id, {i});
|
|
|
+ const auto mul = theBuilder.createBinaryOp(spv::Op::OpIMul, colTypeId,
|
|
|
+ rowArg0, rowArg1);
|
|
|
+ const auto add =
|
|
|
+ theBuilder.createBinaryOp(spv::Op::OpIAdd, colTypeId, mul, rowArg2);
|
|
|
+ theBuilder.decorateNoContraction(mul);
|
|
|
+ theBuilder.decorateNoContraction(add);
|
|
|
+ resultRows.push_back(add);
|
|
|
+ }
|
|
|
+ return theBuilder.createCompositeConstruct(argTypeId, resultRows);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ emitError("invalid argument type passed to mad intrinsic function",
|
|
|
+ callExpr->getExprLoc());
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
uint32_t SPIRVEmitter::processIntrinsicLit(const CallExpr *callExpr) {
|
|
|
// Signature is: float4 lit(float n_dot_l, float n_dot_h, float m)
|
|
|
//
|