//===--- PreciseVisitor.cpp ------- Precise Visitor --------------*- C++ -*-==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "PreciseVisitor.h" #include "clang/SPIRV/AstTypeProbe.h" #include "clang/SPIRV/SpirvFunction.h" #include "clang/SPIRV/SpirvType.h" #include namespace { /// \brief Returns true if the given OpAccessChain instruction is accessing a /// precise variable, or accessing a precise member of a structure. Returns /// false otherwise. bool isAccessingPrecise(clang::spirv::SpirvAccessChain *inst) { using namespace clang::spirv; // If the access chain base is another access chain and so on, first flatten // them (from the bottom to the top). For example: // %x = OpAccessChain %obj %int_1 %int_2 // %y = OpAccessChain %x %int_3 %int_4 // %z = OpAccessChain %y %int_5 %int_6 // Should be flattened to: // %z = OpAccessChain %obj %int_1 %int_2 %int_3 %int_4 %int_5 %int_6 std::stack indexes; SpirvInstruction *base = inst; while (auto *accessChain = llvm::dyn_cast(base)) { for (auto iter = accessChain->getIndexes().rbegin(); iter != accessChain->getIndexes().rend(); ++iter) { indexes.push(*iter); } base = accessChain->getBase(); // If we reach a 'precise' base at any level, return true. if (base->isPrecise()) return true; } // Start from the lowest level base (%obj in the above example), and step // forward using the 'indexes'. If a 'precise' structure field is discovered // at any point, return true. const SpirvType *baseType = base->getResultType(); while (baseType && !indexes.empty()) { if (auto *vecType = llvm::dyn_cast(baseType)) { indexes.pop(); baseType = vecType->getElementType(); } else if (auto *matType = llvm::dyn_cast(baseType)) { indexes.pop(); baseType = matType->getVecType(); } else if (auto *arrType = llvm::dyn_cast(baseType)) { indexes.pop(); baseType = arrType->getElementType(); } else if (auto *raType = llvm::dyn_cast(baseType)) { indexes.pop(); baseType = raType->getElementType(); } else if (auto *structType = llvm::dyn_cast(baseType)) { SpirvInstruction *index = indexes.top(); if (auto *constInt = llvm::dyn_cast(index)) { uint32_t indexValue = static_cast(constInt->getValue().getZExtValue()); auto fields = structType->getFields(); assert(indexValue < fields.size()); auto &fieldInfo = fields[indexValue]; if (fieldInfo.isPrecise) { return true; } else { baseType = fieldInfo.type; indexes.pop(); } } else { // Trying to index into a structure using a variable? This shouldn't be // happening. assert(false && "indexing into a struct with variable value"); return false; } } else if (auto *ptrType = llvm::dyn_cast(baseType)) { // Note: no need to pop the stack here. baseType = ptrType->getPointeeType(); } else { return false; } } return false; } } // anonymous namespace namespace clang { namespace spirv { bool PreciseVisitor::visit(SpirvFunction *fn, Phase phase) { // Before going through the function instructions if (phase == Visitor::Phase::Init) { curFnRetValPrecise = fn->isPrecise(); } return true; } bool PreciseVisitor::visit(SpirvReturn *inst) { if (inst->hasReturnValue()) { inst->getReturnValue()->setPrecise(curFnRetValPrecise); } return true; } bool PreciseVisitor::visit(SpirvVariable *var) { if (var->hasInitializer()) var->getInitializer()->setPrecise(var->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvSelect *inst) { inst->getTrueObject()->setPrecise(inst->isPrecise()); inst->getFalseObject()->setPrecise(inst->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvVectorShuffle *inst) { // If the result of a vector shuffle is 'precise', the vectors from which the // elements are chosen should also be 'precise'. if (inst->isPrecise()) { auto *vec1 = inst->getVec1(); auto *vec2 = inst->getVec2(); const auto vec1Type = vec1->getAstResultType(); const auto vec2Type = vec2->getAstResultType(); 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; } if (vec1ElemUsed) vec1->setPrecise(); if (vec2ElemUsed) vec2->setPrecise(); } return true; } bool PreciseVisitor::visit(SpirvBitFieldExtract *inst) { inst->getBase()->setPrecise(inst->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvBitFieldInsert *inst) { inst->getBase()->setPrecise(inst->isPrecise()); inst->getInsert()->setPrecise(inst->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvAtomic *inst) { if (inst->isPrecise() && inst->hasValue()) inst->getValue()->setPrecise(); return true; } bool PreciseVisitor::visit(SpirvCompositeConstruct *inst) { if (inst->isPrecise()) for (auto *consituent : inst->getConstituents()) consituent->setPrecise(); return true; } bool PreciseVisitor::visit(SpirvCompositeExtract *inst) { inst->getComposite()->setPrecise(inst->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvCompositeInsert *inst) { inst->getComposite()->setPrecise(inst->isPrecise()); inst->getObject()->setPrecise(inst->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvLoad *inst) { // If the instruction result is precise, the pointer we're loading from should // also be marked as precise. if (inst->isPrecise()) inst->getPointer()->setPrecise(); return true; } bool PreciseVisitor::visit(SpirvStore *inst) { // If the 'pointer' to which we are storing is marked as 'precise', the object // we are storing should also be marked as 'precise'. // Note that the 'pointer' may either be an 'OpVariable' or it might be the // result of one or more access chains (in which case we should figure out if // the 'base' of the access chain is 'precise'). auto *ptr = inst->getPointer(); auto *obj = inst->getObject(); // The simple case (target is a precise variable). if (ptr->isPrecise()) { obj->setPrecise(); return true; } if (auto *accessChain = llvm::dyn_cast(ptr)) { if (isAccessingPrecise(accessChain)) { obj->setPrecise(); return true; } } return true; } bool PreciseVisitor::visit(SpirvBinaryOp *inst) { bool isPrecise = inst->isPrecise(); inst->getOperand1()->setPrecise(isPrecise); inst->getOperand2()->setPrecise(isPrecise); return true; } bool PreciseVisitor::visit(SpirvUnaryOp *inst) { inst->getOperand()->setPrecise(inst->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvNonUniformBinaryOp *inst) { inst->getArg1()->setPrecise(inst->isPrecise()); inst->getArg2()->setPrecise(inst->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvNonUniformUnaryOp *inst) { inst->getArg()->setPrecise(inst->isPrecise()); return true; } bool PreciseVisitor::visit(SpirvExtInst *inst) { if (inst->isPrecise()) for (auto *operand : inst->getOperands()) operand->setPrecise(); return true; } } // end namespace spirv } // end namespace clang