|
@@ -0,0 +1,250 @@
|
|
|
+//===--- RelaxedPrecisionVisitor.cpp - RelaxedPrecision Visitor --*- C++ -*-==//
|
|
|
+//
|
|
|
+// The LLVM Compiler Infrastructure
|
|
|
+//
|
|
|
+// This file is distributed under the University of Illinois Open Source
|
|
|
+// License. See LICENSE.TXT for details.
|
|
|
+//
|
|
|
+//===----------------------------------------------------------------------===//
|
|
|
+
|
|
|
+#include "RelaxedPrecisionVisitor.h"
|
|
|
+#include "clang/SPIRV/AstTypeProbe.h"
|
|
|
+#include "clang/SPIRV/SpirvBuilder.h"
|
|
|
+
|
|
|
+namespace clang {
|
|
|
+namespace spirv {
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvFunction *fn, Phase phase) {
|
|
|
+ assert(fn);
|
|
|
+ if (phase == Visitor::Phase::Init)
|
|
|
+ if (isRelaxedPrecisionType(fn->getAstReturnType(), spvOptions))
|
|
|
+ fn->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvVectorShuffle *inst) {
|
|
|
+ // The result of vector shuffle must have RelaxedPrecision if the chosen
|
|
|
+ // elements come from a vector that is RelaxedPrecision.
|
|
|
+ auto *vec1 = inst->getVec1();
|
|
|
+ auto *vec2 = inst->getVec2();
|
|
|
+ const auto vec1Type = vec1->getAstResultType();
|
|
|
+ const auto vec2Type = vec2->getAstResultType();
|
|
|
+ const bool isVec1Relaxed = isRelaxedPrecisionType(vec1Type, spvOptions);
|
|
|
+ const bool isVec2Relaxed = isRelaxedPrecisionType(vec2Type, spvOptions);
|
|
|
+ uint32_t vec1Size;
|
|
|
+ uint32_t vec2Size;
|
|
|
+ (void)isVectorType(vec1Type, nullptr, &vec1Size);
|
|
|
+ (void)isVectorType(vec2Type, nullptr, &vec2Size);
|
|
|
+ bool vec1ElemUsed = false;
|
|
|
+ bool vec2ElemUsed = false;
|
|
|
+ for (auto component : inst->getComponents()) {
|
|
|
+ if (component < vec1Size)
|
|
|
+ vec1ElemUsed = true;
|
|
|
+ else
|
|
|
+ vec2ElemUsed = true;
|
|
|
+ }
|
|
|
+ const bool onlyVec1Used = vec1ElemUsed && !vec2ElemUsed;
|
|
|
+ const bool onlyVec2Used = vec2ElemUsed && !vec1ElemUsed;
|
|
|
+ if ((onlyVec1Used && isVec1Relaxed) || (onlyVec2Used && isVec2Relaxed) ||
|
|
|
+ (vec1ElemUsed && vec2ElemUsed && isVec1Relaxed && isVec2Relaxed))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvUnaryOp *inst) {
|
|
|
+ // For conversion operations, check the result QualType. For example: if we
|
|
|
+ // are converting from min12int to int, the result should no longet get
|
|
|
+ // RelaxedPrecision.
|
|
|
+ switch (inst->getopcode()) {
|
|
|
+ case spv::Op::OpBitcast:
|
|
|
+ case spv::Op::OpFConvert:
|
|
|
+ case spv::Op::OpSConvert:
|
|
|
+ case spv::Op::OpUConvert: {
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions)) {
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If the argument of the unary operation is RelaxedPrecision, the result is
|
|
|
+ // also RelaxedPrecision.
|
|
|
+ if (inst->getOperand()->isRelaxedPrecision())
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvBinaryOp *inst) {
|
|
|
+ // If either argument of the binary operation is RelaxedPrecision, the result
|
|
|
+ // is also RelaxedPrecision.
|
|
|
+ if (inst->getOperand1()->isRelaxedPrecision() &&
|
|
|
+ inst->getOperand2()->isRelaxedPrecision())
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvSpecConstantUnaryOp *inst) {
|
|
|
+ // If the argument of the unary operation is RelaxedPrecision, the result is
|
|
|
+ // also RelaxedPrecision.
|
|
|
+ if (inst->getOperand()->isRelaxedPrecision())
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvSpecConstantBinaryOp *inst) {
|
|
|
+ // If either argument of the binary operation is RelaxedPrecision, the result
|
|
|
+ // is also RelaxedPrecision.
|
|
|
+ if (inst->getOperand1()->isRelaxedPrecision() &&
|
|
|
+ inst->getOperand2()->isRelaxedPrecision())
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvLoad *inst) {
|
|
|
+ // If loading from a RelaxedPrecision variable, the result is also decorated
|
|
|
+ // with RelaxedPrecision.
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvStore *inst) { return true; }
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvSelect *inst) {
|
|
|
+ if (inst->getTrueObject()->isRelaxedPrecision() &&
|
|
|
+ inst->getFalseObject()->isRelaxedPrecision())
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvFunctionCall *inst) {
|
|
|
+ // If the return type of the function is RelaxedPrecision, we can decorate the
|
|
|
+ // result-id of the OpFunctionCall.
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvExtInst *inst) {
|
|
|
+ // If all operands to numeric instructions in GLSL extended instruction set is
|
|
|
+ // RelaxedPrecision, the result of the opration is also RelaxedPrecision.
|
|
|
+ if (inst->getInstructionSet()->getExtendedInstSetName() == "GLSL.std.450") {
|
|
|
+ const auto &operands = inst->getOperands();
|
|
|
+ if (std::all_of(operands.begin(), operands.end(), [](SpirvInstruction *op) {
|
|
|
+ return op->isRelaxedPrecision();
|
|
|
+ })) {
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvCompositeInsert *inst) {
|
|
|
+ // If inserting a RelaxedPrecision object into a composite, check the type of
|
|
|
+ // the resulting composite. For example: if you are inserting a
|
|
|
+ // RelaxedPrecision object as a member into a structure, the resulting
|
|
|
+ // structure type is not RelaxedPrecision. But, if you are inserting a
|
|
|
+ // RelaxedPrecision object into a vector of RelaxedPrecision integers, the
|
|
|
+ // resulting composite *is* RelaxedPrecision.
|
|
|
+ // In short, it simply depends on the composite type.
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvCompositeExtract *inst) {
|
|
|
+ // If extracting a RelaxedPrecision object from a composite, check the type of
|
|
|
+ // the extracted object. For example: if extracting different members of a
|
|
|
+ // structure, depending on the member, you may or may not want to apply the
|
|
|
+ // RelaxedPrecision decoration.
|
|
|
+ // In short, it simply depends on the type of what you have extracted.
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvCompositeConstruct *inst) {
|
|
|
+ // When constructing a composite, only look at the type of the resulting
|
|
|
+ // composite.
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvConstantBoolean *) {
|
|
|
+ // Booleans do not have precision!
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvConstantInteger *inst) {
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvConstantFloat *inst) {
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvConstantComposite *inst) {
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvBitFieldExtract *inst) {
|
|
|
+ if (inst->getBase()->isRelaxedPrecision())
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvBitFieldInsert *inst) {
|
|
|
+ if (inst->getBase()->isRelaxedPrecision())
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvAtomic *inst) {
|
|
|
+ // If the original pointer is RelaxedPrecision or operating on a value that is
|
|
|
+ // RelaxedPrecision, result is RelaxedPrecision.
|
|
|
+ if (inst->getPointer()->isRelaxedPrecision()) {
|
|
|
+ if (!inst->hasValue() ||
|
|
|
+ (inst->hasValue() && inst->getValue()->isRelaxedPrecision()))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvAccessChain *) {
|
|
|
+ // The access chain operation itself is irrelevant regarding precision.
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvFunctionParameter *inst) {
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvVariable *inst) {
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool RelaxedPrecisionVisitor::visit(SpirvImageOp *inst) {
|
|
|
+ // If the operation result type or the underlying image type is relaxed
|
|
|
+ // precision, the instruction can be considered relaxed precision.
|
|
|
+ if (isRelaxedPrecisionType(inst->getAstResultType(), spvOptions) ||
|
|
|
+ isRelaxedPrecisionType(inst->getImage()->getAstResultType(), spvOptions))
|
|
|
+ inst->setRelaxedPrecision();
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+} // end namespace spirv
|
|
|
+} // namespace clang
|