From f04c51b3bdf6510d79b29b28046fec6aa485d197 Mon Sep 17 00:00:00 2001 From: John Kessenich Date: Fri, 3 Aug 2018 15:56:12 -0600 Subject: [PATCH] Non-Functional: Add postprocess as more robust way to add capabilities When capabilities are needed for specific SPIR-V instructions, it is fragile to do so based on GLSL/AST usage; it should be based on actual instructions they got translated to. --- SPIRV/CMakeLists.txt | 1 + SPIRV/GlslangToSpv.cpp | 16 +--- SPIRV/SpvBuilder.cpp | 39 ---------- SPIRV/SpvBuilder.h | 12 ++- SPIRV/SpvPostProcess.cpp | 164 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+), 54 deletions(-) create mode 100755 SPIRV/SpvPostProcess.cpp diff --git a/SPIRV/CMakeLists.txt b/SPIRV/CMakeLists.txt index be58d021bc..48ca779532 100755 --- a/SPIRV/CMakeLists.txt +++ b/SPIRV/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES InReadableOrder.cpp Logger.cpp SpvBuilder.cpp + SpvPostProcess.cpp doc.cpp disassemble.cpp) diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index c3e794011e..86c4d49117 100755 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -1191,6 +1191,7 @@ TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion, const gl // Finish creating SPV, after the traversal is complete. void TGlslangToSpvTraverser::finishSpv() { + // Finish the entry point function if (! entryPointTerminated) { builder.setBuildPoint(shaderEntry->getLastBlock()); builder.leaveFunction(); @@ -1200,7 +1201,9 @@ void TGlslangToSpvTraverser::finishSpv() for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it) entryPoint->addIdOperand(*it); - builder.eliminateDeadDecorations(); + // Add capabilities, extensions, remove unneeded decorations, etc., + // based on the resulting SPIR-V. + builder.postProcess(); } // Write the SPV into 'out'. @@ -4627,27 +4630,21 @@ spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, OpDe unaryOp = spv::OpFwidth; break; case glslang::EOpDPdxFine: - builder.addCapability(spv::CapabilityDerivativeControl); unaryOp = spv::OpDPdxFine; break; case glslang::EOpDPdyFine: - builder.addCapability(spv::CapabilityDerivativeControl); unaryOp = spv::OpDPdyFine; break; case glslang::EOpFwidthFine: - builder.addCapability(spv::CapabilityDerivativeControl); unaryOp = spv::OpFwidthFine; break; case glslang::EOpDPdxCoarse: - builder.addCapability(spv::CapabilityDerivativeControl); unaryOp = spv::OpDPdxCoarse; break; case glslang::EOpDPdyCoarse: - builder.addCapability(spv::CapabilityDerivativeControl); unaryOp = spv::OpDPdyCoarse; break; case glslang::EOpFwidthCoarse: - builder.addCapability(spv::CapabilityDerivativeControl); unaryOp = spv::OpFwidthCoarse; break; case glslang::EOpInterpolateAtCentroid: @@ -4655,7 +4652,6 @@ spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, OpDe if (typeProxy == glslang::EbtFloat16) builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); #endif - builder.addCapability(spv::CapabilityInterpolationFunction); libCall = spv::GLSLstd450InterpolateAtCentroid; break; case glslang::EOpAny: @@ -4791,8 +4787,6 @@ spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, OpDe #endif #ifdef NV_EXTENSIONS case glslang::EOpSubgroupPartition: - builder.addExtension(spv::E_SPV_NV_shader_subgroup_partitioned); - builder.addCapability(spv::CapabilityGroupNonUniformPartitionedNV); unaryOp = spv::OpGroupNonUniformPartitionNV; break; #endif @@ -6087,7 +6081,6 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: if (typeProxy == glslang::EbtFloat16) builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); #endif - builder.addCapability(spv::CapabilityInterpolationFunction); libCall = spv::GLSLstd450InterpolateAtSample; break; case glslang::EOpInterpolateAtOffset: @@ -6095,7 +6088,6 @@ spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv:: if (typeProxy == glslang::EbtFloat16) builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); #endif - builder.addCapability(spv::CapabilityInterpolationFunction); libCall = spv::GLSLstd450InterpolateAtOffset; break; case glslang::EOpAddCarry: diff --git a/SPIRV/SpvBuilder.cpp b/SPIRV/SpvBuilder.cpp index 6fd0ee8547..852a3e3ff8 100755 --- a/SPIRV/SpvBuilder.cpp +++ b/SPIRV/SpvBuilder.cpp @@ -1774,9 +1774,6 @@ Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, // Comments in header Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) { - // All these need a capability - addCapability(CapabilityImageQuery); - // Figure out the result type Id resultType = 0; switch (opCode) { @@ -2483,42 +2480,6 @@ Id Builder::accessChainGetInferredType() return type; } -// comment in header -void Builder::eliminateDeadDecorations() { - std::unordered_set reachable_blocks; - std::unordered_set unreachable_definitions; - // Collect IDs defined in unreachable blocks. For each function, label the - // reachable blocks first. Then for each unreachable block, collect the - // result IDs of the instructions in it. - for (std::vector::const_iterator fi = module.getFunctions().cbegin(); - fi != module.getFunctions().cend(); fi++) { - Function* f = *fi; - Block* entry = f->getEntryBlock(); - inReadableOrder(entry, [&reachable_blocks](const Block* b) { - reachable_blocks.insert(b); - }); - for (std::vector::const_iterator bi = f->getBlocks().cbegin(); - bi != f->getBlocks().cend(); bi++) { - Block* b = *bi; - if (!reachable_blocks.count(b)) { - for (std::vector >::const_iterator - ii = b->getInstructions().cbegin(); - ii != b->getInstructions().cend(); ii++) { - Instruction* i = ii->get(); - unreachable_definitions.insert(i->getResultId()); - } - } - } - } - decorations.erase(std::remove_if(decorations.begin(), decorations.end(), - [&unreachable_definitions](std::unique_ptr& I) -> bool { - Instruction* inst = I.get(); - Id decoration_id = inst->getIdOperand(0); - return unreachable_definitions.count(decoration_id) != 0; - }), - decorations.end()); -} - void Builder::dump(std::vector& out) const { // Header, before first instructions: diff --git a/SPIRV/SpvBuilder.h b/SPIRV/SpvBuilder.h index 099b1d957f..8894f01174 100755 --- a/SPIRV/SpvBuilder.h +++ b/SPIRV/SpvBuilder.h @@ -563,9 +563,15 @@ class Builder { // based on the type of the base and the chain of dereferences. Id accessChainGetInferredType(); - // Remove OpDecorate instructions whose operands are defined in unreachable - // blocks. - void eliminateDeadDecorations(); + // Add capabilities, extensions, remove unneeded decorations, etc., + // based on the resulting SPIR-V. + void postProcess(); + + // Hook to visit each instruction in a block in a function + void postProcess(Instruction& inst); + // Hook to visit each instruction in a reachable block in a function. + void postProcessReachable(Instruction& inst); + void dump(std::vector&) const; void createBranch(Block* block); diff --git a/SPIRV/SpvPostProcess.cpp b/SPIRV/SpvPostProcess.cpp new file mode 100755 index 0000000000..256079997f --- /dev/null +++ b/SPIRV/SpvPostProcess.cpp @@ -0,0 +1,164 @@ +// +// Copyright (C) 2016-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Post-processing for SPIR-V IR, in internal form, not standard binary form. +// + +#include +#include + +#include +#include + +#include "SpvBuilder.h" + +#include "spirv.hpp" +#include "GlslangToSpv.h" +#include "SpvBuilder.h" +namespace spv { + #include "GLSL.std.450.h" + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" +#ifdef AMD_EXTENSIONS + #include "GLSL.ext.AMD.h" +#endif +#ifdef NV_EXTENSIONS + #include "GLSL.ext.NV.h" +#endif +} + +namespace spv { + +// Called for each instruction in a block. +void Builder::postProcess(Instruction& inst) +{ + // Add capabilities based simply on the opcode. + switch (inst.getOpCode()) { + case OpExtInst: + switch (inst.getIdOperand(1)) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + addCapability(CapabilityInterpolationFunction); + break; + default: + break; + } + break; + case OpDPdxFine: + case OpDPdyFine: + case OpFwidthFine: + case OpDPdxCoarse: + case OpDPdyCoarse: + case OpFwidthCoarse: + addCapability(CapabilityDerivativeControl); + break; + + case OpImageQueryLod: + case OpImageQuerySize: + case OpImageQuerySizeLod: + case OpImageQuerySamples: + case OpImageQueryLevels: + addCapability(CapabilityImageQuery); + break; + +#ifdef NV_EXTENSIONS + case OpGroupNonUniformPartitionNV: + addExtension(E_SPV_NV_shader_subgroup_partitioned); + addCapability(CapabilityGroupNonUniformPartitionedNV); + break; +#endif + + default: + break; + } +} + +// Called for each instruction in a reachable block. +void Builder::postProcessReachable(Instruction& inst) +{ + // did have code here, but questionable to do so without deleting the instructions +} + +// comment in header +void Builder::postProcess() +{ + std::unordered_set reachableBlocks; + std::unordered_set unreachableDefinitions; + // Collect IDs defined in unreachable blocks. For each function, label the + // reachable blocks first. Then for each unreachable block, collect the + // result IDs of the instructions in it. + for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + Block* entry = f->getEntryBlock(); + inReadableOrder(entry, [&reachableBlocks](const Block* b) { reachableBlocks.insert(b); }); + for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + if (reachableBlocks.count(b) == 0) { + for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++) + unreachableDefinitions.insert(ii->get()->getResultId()); + } + } + } + + // Remove unneeded decorations, for unreachable instructions + decorations.erase(std::remove_if(decorations.begin(), decorations.end(), + [&unreachableDefinitions](std::unique_ptr& I) -> bool { + Id decoration_id = I.get()->getIdOperand(0); + return unreachableDefinitions.count(decoration_id) != 0; + }), + decorations.end()); + + // Add per-instruction capabilities, extensions, etc., + + // process all reachable instructions... + for (auto bi = reachableBlocks.cbegin(); bi != reachableBlocks.cend(); ++bi) { + const Block* block = *bi; + const auto function = [this](const std::unique_ptr& inst) { postProcessReachable(*inst.get()); }; + std::for_each(block->getInstructions().begin(), block->getInstructions().end(), function); + } + + // process all block-contained instructions + for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++) + postProcess(*ii->get()); + } + } +} + +}; // end spv namespace