opt: add capability trimming pass (not default). (#5278)

This commit adds a new optimization which tries to remove unnecessary
capabilities from a SPIR-V module.

When compiling a SPIR-V module, you may have some dead-code using
features gated by a capability.
DCE will remove this code, but the capability will remain. This means
your module would still require some capability, even if it doesn't
require it. Calling this pass on your module would remove obsolete
capabilities.

This pass wouldn't be enabled by default, and would only be usable
from the API (at least for now).

NOTE: this commit only adds the basic skeleton/structure, and
doesn't mark as supported many capabilities it could support.
I'll add them as supported as I write tests.

Signed-off-by: Nathan Gauër <brioche@google.com>
This commit is contained in:
Nathan Gauër 2023-07-25 16:52:41 +02:00 committed by GitHub
parent ec90d2872a
commit 35d8b05de4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1254 additions and 0 deletions

View File

@ -981,6 +981,17 @@ Optimizer::PassToken CreateRemoveDontInlinePass();
// object, currently the pass would remove accesschain pointer argument passed
// to the function
Optimizer::PassToken CreateFixFuncCallArgumentsPass();
// Creates a trim-capabilities pass.
// This pass removes unused capabilities for a given module, and if possible,
// associated extensions.
// See `trim_capabilities.h` for the list of supported capabilities.
//
// If the module contains unsupported capabilities, this pass will ignore them.
// This should be fine in most cases, but could yield to incorrect results if
// the unknown capability interacts with one of the trimmed capabilities.
Optimizer::PassToken CreateTrimCapabilitiesPass();
} // namespace spvtools
#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_

View File

@ -122,6 +122,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
strip_nonsemantic_info_pass.h
struct_cfg_analysis.h
tree_iterator.h
trim_capabilities_pass.h
type_manager.h
types.h
unify_const_pass.h
@ -236,6 +237,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
strip_debug_info_pass.cpp
strip_nonsemantic_info_pass.cpp
struct_cfg_analysis.cpp
trim_capabilities_pass.cpp
type_manager.cpp
types.cpp
unify_const_pass.cpp

View File

@ -154,6 +154,12 @@ class IRContext {
inline IteratorRange<Module::inst_iterator> capabilities();
inline IteratorRange<Module::const_inst_iterator> capabilities() const;
// Iterators for extensions instructions contained in this module.
inline Module::inst_iterator extension_begin();
inline Module::inst_iterator extension_end();
inline IteratorRange<Module::inst_iterator> extensions();
inline IteratorRange<Module::const_inst_iterator> extensions() const;
// Iterators for types, constants and global variables instructions.
inline Module::inst_iterator types_values_begin();
inline Module::inst_iterator types_values_end();
@ -982,6 +988,22 @@ IteratorRange<Module::const_inst_iterator> IRContext::capabilities() const {
return ((const Module*)module())->capabilities();
}
Module::inst_iterator IRContext::extension_begin() {
return module()->extension_begin();
}
Module::inst_iterator IRContext::extension_end() {
return module()->extension_end();
}
IteratorRange<Module::inst_iterator> IRContext::extensions() {
return module()->extensions();
}
IteratorRange<Module::const_inst_iterator> IRContext::extensions() const {
return ((const Module*)module())->extensions();
}
Module::inst_iterator IRContext::types_values_begin() {
return module()->types_values_begin();
}

View File

@ -82,6 +82,7 @@
#include "source/opt/strength_reduction_pass.h"
#include "source/opt/strip_debug_info_pass.h"
#include "source/opt/strip_nonsemantic_info_pass.h"
#include "source/opt/trim_capabilities_pass.h"
#include "source/opt/unify_const_pass.h"
#include "source/opt/upgrade_memory_model.h"
#include "source/opt/vector_dce.h"

View File

@ -0,0 +1,320 @@
// Copyright (c) 2023 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "source/opt/trim_capabilities_pass.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <functional>
#include <optional>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "source/enum_set.h"
#include "source/enum_string_mapping.h"
#include "source/opt/ir_context.h"
#include "source/spirv_target_env.h"
#include "source/util/string_utils.h"
namespace spvtools {
namespace opt {
namespace {
constexpr uint32_t kVariableStorageClassIndex = 0;
constexpr uint32_t kTypeArrayTypeIndex = 0;
constexpr uint32_t kOpTypeScalarBitWidthIndex = 0;
constexpr uint32_t kTypePointerTypeIdInIdx = 1;
} // namespace
// ============== Begin opcode handler implementations. =======================
//
// Adding support for a new capability should only require adding a new handler,
// and updating the
// kSupportedCapabilities/kUntouchableCapabilities/kFordiddenCapabilities lists.
//
// Handler names follow the following convention:
// Handler_<Opcode>_<Capability>()
static std::optional<spv::Capability> Handler_OpVariable_StorageInputOutput16(
const Instruction* instruction) {
assert(instruction->opcode() == spv::Op::OpVariable &&
"This handler only support OpVariable opcodes.");
// This capability is only required if the variable as an Input/Output storage
// class.
spv::StorageClass storage_class = spv::StorageClass(
instruction->GetSingleWordInOperand(kVariableStorageClassIndex));
if (storage_class != spv::StorageClass::Input &&
storage_class != spv::StorageClass::Output) {
return std::nullopt;
}
// This capability is only required if the type involves a 16-bit component.
// Quick check: are 16-bit types allowed?
const CapabilitySet& capabilities =
instruction->context()->get_feature_mgr()->GetCapabilities();
if (!capabilities.contains(spv::Capability::Float16) &&
!capabilities.contains(spv::Capability::Int16)) {
return std::nullopt;
}
// We need to walk the type definition.
std::queue<uint32_t> instructions_to_visit;
instructions_to_visit.push(instruction->type_id());
const auto* def_use_mgr = instruction->context()->get_def_use_mgr();
while (!instructions_to_visit.empty()) {
const Instruction* item =
def_use_mgr->GetDef(instructions_to_visit.front());
instructions_to_visit.pop();
if (item->opcode() == spv::Op::OpTypePointer) {
instructions_to_visit.push(
item->GetSingleWordInOperand(kTypePointerTypeIdInIdx));
continue;
}
if (item->opcode() == spv::Op::OpTypeMatrix ||
item->opcode() == spv::Op::OpTypeVector ||
item->opcode() == spv::Op::OpTypeArray ||
item->opcode() == spv::Op::OpTypeRuntimeArray) {
instructions_to_visit.push(
item->GetSingleWordInOperand(kTypeArrayTypeIndex));
continue;
}
if (item->opcode() == spv::Op::OpTypeStruct) {
item->ForEachInOperand([&instructions_to_visit](const uint32_t* op_id) {
instructions_to_visit.push(*op_id);
});
continue;
}
if (item->opcode() != spv::Op::OpTypeInt &&
item->opcode() != spv::Op::OpTypeFloat) {
continue;
}
if (item->GetSingleWordInOperand(kOpTypeScalarBitWidthIndex) == 16) {
return spv::Capability::StorageInputOutput16;
}
}
return std::nullopt;
}
// Opcode of interest to determine capabilities requirements.
constexpr std::array<std::pair<spv::Op, OpcodeHandler>, 1> kOpcodeHandlers{{
{spv::Op::OpVariable, Handler_OpVariable_StorageInputOutput16},
}};
// ============== End opcode handler implementations. =======================
namespace {
ExtensionSet getExtensionsRelatedTo(const CapabilitySet& capabilities,
const AssemblyGrammar& grammar) {
ExtensionSet output;
const spv_operand_desc_t* desc = nullptr;
for (auto capability : capabilities) {
if (SPV_SUCCESS != grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY,
static_cast<uint32_t>(capability),
&desc)) {
continue;
}
for (uint32_t i = 0; i < desc->numExtensions; ++i) {
output.insert(desc->extensions[i]);
}
}
return output;
}
} // namespace
TrimCapabilitiesPass::TrimCapabilitiesPass()
: supportedCapabilities_(
TrimCapabilitiesPass::kSupportedCapabilities.cbegin(),
TrimCapabilitiesPass::kSupportedCapabilities.cend()),
forbiddenCapabilities_(
TrimCapabilitiesPass::kForbiddenCapabilities.cbegin(),
TrimCapabilitiesPass::kForbiddenCapabilities.cend()),
untouchableCapabilities_(
TrimCapabilitiesPass::kUntouchableCapabilities.cbegin(),
TrimCapabilitiesPass::kUntouchableCapabilities.cend()),
opcodeHandlers_(kOpcodeHandlers.cbegin(), kOpcodeHandlers.cend()) {}
void TrimCapabilitiesPass::addInstructionRequirements(
Instruction* instruction, CapabilitySet* capabilities,
ExtensionSet* extensions) const {
// Ignoring OpCapability instructions.
if (instruction->opcode() == spv::Op::OpCapability) {
return;
}
// First case: the opcode is itself gated by a capability.
{
const spv_opcode_desc_t* desc = {};
auto result =
context()->grammar().lookupOpcode(instruction->opcode(), &desc);
if (result == SPV_SUCCESS) {
addSupportedCapabilitiesToSet(desc->numCapabilities, desc->capabilities,
capabilities);
if (desc->minVersion <=
spvVersionForTargetEnv(context()->GetTargetEnv())) {
extensions->insert(desc->extensions,
desc->extensions + desc->numExtensions);
}
}
}
// Second case: one of the opcode operand is gated by a capability.
const uint32_t operandCount = instruction->NumOperands();
for (uint32_t i = 0; i < operandCount; i++) {
const auto& operand = instruction->GetOperand(i);
// No supported capability relies on a 2+-word operand.
if (operand.words.size() != 1) {
continue;
}
// No supported capability relies on a literal string operand.
if (operand.type == SPV_OPERAND_TYPE_LITERAL_STRING) {
continue;
}
const spv_operand_desc_t* desc = {};
auto result = context()->grammar().lookupOperand(operand.type,
operand.words[0], &desc);
if (result != SPV_SUCCESS) {
continue;
}
addSupportedCapabilitiesToSet(desc->numCapabilities, desc->capabilities,
capabilities);
if (desc->minVersion <= spvVersionForTargetEnv(context()->GetTargetEnv())) {
extensions->insert(desc->extensions,
desc->extensions + desc->numExtensions);
}
}
// Last case: some complex logic needs to be run to determine capabilities.
auto[begin, end] = opcodeHandlers_.equal_range(instruction->opcode());
for (auto it = begin; it != end; it++) {
const OpcodeHandler handler = it->second;
auto result = handler(instruction);
if (result.has_value()) {
capabilities->insert(*result);
}
}
}
std::pair<CapabilitySet, ExtensionSet>
TrimCapabilitiesPass::DetermineRequiredCapabilitiesAndExtensions() const {
CapabilitySet required_capabilities;
ExtensionSet required_extensions;
get_module()->ForEachInst([&](Instruction* instruction) {
addInstructionRequirements(instruction, &required_capabilities,
&required_extensions);
});
#if !defined(NDEBUG)
// Debug only. We check the outputted required capabilities against the
// supported capabilities list. The supported capabilities list is useful for
// API users to quickly determine if they can use the pass or not. But this
// list has to remain up-to-date with the pass code. If we can detect a
// capability as required, but it's not listed, it means the list is
// out-of-sync. This method is not ideal, but should cover most cases.
{
for (auto capability : required_capabilities) {
assert(supportedCapabilities_.contains(capability) &&
"Module is using a capability that is not listed as supported.");
}
}
#endif
return std::make_pair(std::move(required_capabilities),
std::move(required_extensions));
}
Pass::Status TrimCapabilitiesPass::TrimUnrequiredCapabilities(
const CapabilitySet& required_capabilities) const {
const FeatureManager* feature_manager = context()->get_feature_mgr();
CapabilitySet capabilities_to_trim;
for (auto capability : feature_manager->GetCapabilities()) {
// Forbidden capability completely prevents trimming. Early exit.
if (forbiddenCapabilities_.contains(capability)) {
return Pass::Status::SuccessWithoutChange;
}
// Some capabilities cannot be safely removed. Leaving them untouched.
if (untouchableCapabilities_.contains(capability)) {
continue;
}
// If the capability is unsupported, don't trim it.
if (!supportedCapabilities_.contains(capability)) {
continue;
}
if (required_capabilities.contains(capability)) {
continue;
}
capabilities_to_trim.insert(capability);
}
for (auto capability : capabilities_to_trim) {
context()->RemoveCapability(capability);
}
return capabilities_to_trim.size() == 0 ? Pass::Status::SuccessWithoutChange
: Pass::Status::SuccessWithChange;
}
Pass::Status TrimCapabilitiesPass::TrimUnrequiredExtensions(
const ExtensionSet& required_extensions) const {
const auto supported_extensions =
getExtensionsRelatedTo(supportedCapabilities_, context()->grammar());
bool modified_module = false;
for (auto extension : supported_extensions) {
if (!required_extensions.contains(extension)) {
modified_module = true;
context()->RemoveExtension(extension);
}
}
return modified_module ? Pass::Status::SuccessWithChange
: Pass::Status::SuccessWithoutChange;
}
Pass::Status TrimCapabilitiesPass::Process() {
auto[required_capabilities, required_extensions] =
DetermineRequiredCapabilitiesAndExtensions();
Pass::Status status = TrimUnrequiredCapabilities(required_capabilities);
// If no capabilities were removed, we have no extension to trim.
// Note: this is true because this pass only removes unused extensions caused
// by unused capabilities.
// This is not an extension trimming pass.
if (status == Pass::Status::SuccessWithoutChange) {
return status;
}
return TrimUnrequiredExtensions(required_extensions);
}
} // namespace opt
} // namespace spvtools

View File

@ -0,0 +1,151 @@
// Copyright (c) 2023 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SOURCE_OPT_TRIM_CAPABILITIES_PASS_H_
#define SOURCE_OPT_TRIM_CAPABILITIES_PASS_H_
#include <algorithm>
#include <array>
#include <functional>
#include <optional>
#include <unordered_map>
#include <unordered_set>
#include "source/enum_set.h"
#include "source/extensions.h"
#include "source/opt/ir_context.h"
#include "source/opt/module.h"
#include "source/opt/pass.h"
namespace spvtools {
namespace opt {
// This is required for NDK build. The unordered_set/unordered_map
// implementation don't work with class enums.
struct ClassEnumHash {
std::size_t operator()(spv::Capability value) const {
using StoringType = typename std::underlying_type_t<spv::Capability>;
return std::hash<StoringType>{}(static_cast<StoringType>(value));
}
std::size_t operator()(spv::Op value) const {
using StoringType = typename std::underlying_type_t<spv::Op>;
return std::hash<StoringType>{}(static_cast<StoringType>(value));
}
};
// An opcode handler is a function which, given an instruction, returns either
// the required capability, or nothing.
// Each handler checks one case for a capability requirement.
//
// Example:
// - `OpTypeImage` can have operand `A` operand which requires capability 1
// - `OpTypeImage` can also have operand `B` which requires capability 2.
// -> We have 2 handlers: `Handler_OpTypeImage_1` and
// `Handler_OpTypeImage_2`.
using OpcodeHandler =
std::optional<spv::Capability> (*)(const Instruction* instruction);
// This pass tried to remove superfluous capabilities declared in the module.
// - If all the capabilities listed by an extension are removed, the extension
// is also trimmed.
// - If the module countains any capability listed in `kForbiddenCapabilities`,
// the module is left untouched.
// - No capabilities listed in `kUntouchableCapabilities` are trimmed, even when
// not used.
// - Only capabilitied listed in `kSupportedCapabilities` are supported.
// - If the module contains unsupported capabilities, results might be
// incorrect.
class TrimCapabilitiesPass : public Pass {
private:
// All the capabilities supported by this optimization pass. If your module
// contains unsupported instruction, the pass could yield bad results.
static constexpr std::array kSupportedCapabilities{
// clang-format off
spv::Capability::Groups,
spv::Capability::Linkage,
spv::Capability::MinLod,
spv::Capability::Shader,
spv::Capability::ShaderClockKHR,
spv::Capability::StorageInputOutput16
// clang-format on
};
// Those capabilities disable all transformation of the module.
static constexpr std::array kForbiddenCapabilities{
spv::Capability::Linkage,
};
// Those capabilities are never removed from a module because we cannot
// guess from the SPIR-V only if they are required or not.
static constexpr std::array kUntouchableCapabilities{
spv::Capability::Shader,
};
public:
TrimCapabilitiesPass();
TrimCapabilitiesPass(const TrimCapabilitiesPass&) = delete;
TrimCapabilitiesPass(TrimCapabilitiesPass&&) = delete;
private:
// Inserts every capability in `capabilities[capabilityCount]` supported by
// this pass into `output`.
inline void addSupportedCapabilitiesToSet(
uint32_t capabilityCount, const spv::Capability* const capabilities,
CapabilitySet* output) const {
for (uint32_t i = 0; i < capabilityCount; ++i) {
if (supportedCapabilities_.contains(capabilities[i])) {
output->insert(capabilities[i]);
}
}
}
// Given an `instruction`, determines the capabilities and extension it
// requires, and output them in `capabilities` and `extensions`. The returned
// capabilities form a subset of kSupportedCapabilities.
void addInstructionRequirements(Instruction* instruction,
CapabilitySet* capabilities,
ExtensionSet* extensions) const;
// Returns the list of required capabilities and extensions for the module.
// The returned capabilities form a subset of kSupportedCapabilities.
std::pair<CapabilitySet, ExtensionSet>
DetermineRequiredCapabilitiesAndExtensions() const;
// Trims capabilities not listed in `required_capabilities` if possible.
// Returns whether or not the module was modified.
Pass::Status TrimUnrequiredCapabilities(
const CapabilitySet& required_capabilities) const;
// Trims extensions not listed in `required_extensions` if supported by this
// pass. An extensions is considered supported as soon as one capability this
// pass support requires it.
Pass::Status TrimUnrequiredExtensions(
const ExtensionSet& required_extensions) const;
public:
const char* name() const override { return "trim-capabilities"; }
Status Process() override;
private:
const CapabilitySet supportedCapabilities_;
const CapabilitySet forbiddenCapabilities_;
const CapabilitySet untouchableCapabilities_;
const std::unordered_multimap<spv::Op, OpcodeHandler, ClassEnumHash>
opcodeHandlers_;
};
} // namespace opt
} // namespace spvtools
#endif // SOURCE_OPT_TRIM_CAPABILITIES_H_

View File

@ -103,6 +103,7 @@ add_spvtools_unittest(TARGET opt
strip_debug_info_test.cpp
strip_nonsemantic_info_test.cpp
struct_cfg_analysis_test.cpp
trim_capabilities_pass_test.cpp
type_manager_test.cpp
types_test.cpp
unify_const_test.cpp

View File

@ -0,0 +1,746 @@
// Copyright (c) 2023 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "spirv-tools/optimizer.hpp"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using TrimCapabilitiesPassTest = PassTest<::testing::Test>;
TEST_F(TrimCapabilitiesPassTest, CheckKnownAliasTransformations) {
// Those are expected changes caused by the test process:
// - SPV is assembled. -> capability goes from text to number.
// - SPV is optimized.
// - SPV is disassembled -> capability goes from number to text.
// - CHECK rule compares both text versions.
// Because some capabilities share the same number (aliases), the text
// compared with the CHECK rules depends on which alias is the first on the
// SPIRV-Headers enum. This could change, and we want to easily distinguish
// real failure from alias order change. This test is only here to list known
// alias transformations. If this test breaks, it's not a bug in the
// optimization pass, but just the SPIRV-Headers enum order that has changed.
// If that happens, tests needs to be updated to the correct alias is used in
// the CHECK rule.
const std::string kTest = R"(
OpCapability Linkage
OpCapability StorageUniform16
OpCapability StorageUniformBufferBlock16
OpCapability ShaderViewportIndexLayerNV
OpCapability FragmentBarycentricNV
OpCapability ShadingRateNV
OpCapability ShaderNonUniformEXT
OpCapability RuntimeDescriptorArrayEXT
OpCapability InputAttachmentArrayDynamicIndexingEXT
OpCapability UniformTexelBufferArrayDynamicIndexingEXT
OpCapability StorageTexelBufferArrayDynamicIndexingEXT
OpCapability UniformBufferArrayNonUniformIndexingEXT
OpCapability SampledImageArrayNonUniformIndexingEXT
OpCapability StorageBufferArrayNonUniformIndexingEXT
OpCapability StorageImageArrayNonUniformIndexingEXT
OpCapability InputAttachmentArrayNonUniformIndexingEXT
OpCapability UniformTexelBufferArrayNonUniformIndexingEXT
OpCapability StorageTexelBufferArrayNonUniformIndexingEXT
OpCapability VulkanMemoryModelKHR
OpCapability VulkanMemoryModelDeviceScopeKHR
OpCapability PhysicalStorageBufferAddressesEXT
OpCapability DemoteToHelperInvocationEXT
OpCapability DotProductInputAllKHR
OpCapability DotProductInput4x8BitKHR
OpCapability DotProductInput4x8BitPackedKHR
OpCapability DotProductKHR
; CHECK: OpCapability Linkage
; CHECK-NOT: OpCapability StorageUniform16
; CHECK-NOT: OpCapability StorageUniformBufferBlock16
; CHECK-NOT: OpCapability ShaderViewportIndexLayerNV
; CHECK-NOT: OpCapability FragmentBarycentricNV
; CHECK-NOT: OpCapability ShadingRateNV
; CHECK-NOT: OpCapability ShaderNonUniformEXT
; CHECK-NOT: OpCapability RuntimeDescriptorArrayEXT
; CHECK-NOT: OpCapability InputAttachmentArrayDynamicIndexingEXT
; CHECK-NOT: OpCapability UniformTexelBufferArrayDynamicIndexingEXT
; CHECK-NOT: OpCapability StorageTexelBufferArrayDynamicIndexingEXT
; CHECK-NOT: OpCapability UniformBufferArrayNonUniformIndexingEXT
; CHECK-NOT: OpCapability SampledImageArrayNonUniformIndexingEXT
; CHECK-NOT: OpCapability StorageBufferArrayNonUniformIndexingEXT
; CHECK-NOT: OpCapability StorageImageArrayNonUniformIndexingEXT
; CHECK-NOT: OpCapability InputAttachmentArrayNonUniformIndexingEXT
; CHECK-NOT: OpCapability UniformTexelBufferArrayNonUniformIndexingEXT
; CHECK-NOT: OpCapability StorageTexelBufferArrayNonUniformIndexingEXT
; CHECK-NOT: OpCapability VulkanMemoryModelKHR
; CHECK-NOT: OpCapability VulkanMemoryModelDeviceScopeKHR
; CHECK-NOT: OpCapability PhysicalStorageBufferAddressesEXT
; CHECK-NOT: OpCapability DemoteToHelperInvocationEXT
; CHECK-NOT: OpCapability DotProductInputAllKHR
; CHECK-NOT: OpCapability DotProductInput4x8BitKHR
; CHECK-NOT: OpCapability DotProductInput4x8BitPackedKHR
; CHECK-NOT: OpCapability DotProductKHR
; CHECK: OpCapability UniformAndStorageBuffer16BitAccess
; CHECK: OpCapability StorageBuffer16BitAccess
; CHECK: OpCapability ShaderViewportIndexLayerEXT
; CHECK: OpCapability FragmentBarycentricKHR
; CHECK: OpCapability FragmentDensityEXT
; CHECK: OpCapability ShaderNonUniform
; CHECK: OpCapability RuntimeDescriptorArray
; CHECK: OpCapability InputAttachmentArrayDynamicIndexing
; CHECK: OpCapability UniformTexelBufferArrayDynamicIndexing
; CHECK: OpCapability StorageTexelBufferArrayDynamicIndexing
; CHECK: OpCapability UniformBufferArrayNonUniformIndexing
; CHECK: OpCapability SampledImageArrayNonUniformIndexing
; CHECK: OpCapability StorageBufferArrayNonUniformIndexing
; CHECK: OpCapability StorageImageArrayNonUniformIndexing
; CHECK: OpCapability InputAttachmentArrayNonUniformIndexing
; CHECK: OpCapability UniformTexelBufferArrayNonUniformIndexing
; CHECK: OpCapability StorageTexelBufferArrayNonUniformIndexing
; CHECK: OpCapability VulkanMemoryModel
; CHECK: OpCapability VulkanMemoryModelDeviceScope
; CHECK: OpCapability PhysicalStorageBufferAddresses
; CHECK: OpCapability DemoteToHelperInvocation
; CHECK: OpCapability DotProductInputAll
; CHECK: OpCapability DotProductInput4x8Bit
; CHECK: OpCapability DotProductInput4x8BitPacked
; CHECK: OpCapability DotProduct
OpMemoryModel Logical Vulkan
OpEntryPoint GLCompute %1 "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%1 = OpFunction %void None %3
%6 = OpLabel
OpReturn
OpFunctionEnd;
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
const auto result =
SinglePassRunAndMatch<EmptyPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest, LinkagePreventsChanges) {
const std::string kTest = R"(
OpCapability Linkage
OpCapability ClipDistance
OpCapability CullDistance
OpCapability DemoteToHelperInvocation
OpCapability DeviceGroup
OpCapability DrawParameters
OpCapability Float16
OpCapability Float64
OpCapability FragmentBarycentricKHR
OpCapability FragmentFullyCoveredEXT
OpCapability FragmentShadingRateKHR
OpCapability GroupNonUniform
OpCapability GroupNonUniformArithmetic
OpCapability GroupNonUniformBallot
OpCapability GroupNonUniformQuad
OpCapability GroupNonUniformShuffle
OpCapability Image1D
OpCapability ImageBuffer
OpCapability ImageGatherExtended
OpCapability ImageMSArray
OpCapability ImageQuery
OpCapability InputAttachment
OpCapability InputAttachmentArrayNonUniformIndexing
OpCapability Int16
OpCapability Int64
OpCapability Int64Atomics
OpCapability Int64ImageEXT
OpCapability MeshShadingNV
OpCapability MinLod
OpCapability MultiView
OpCapability MultiViewport
OpCapability PhysicalStorageBufferAddresses
OpCapability RayQueryKHR
OpCapability RayTracingKHR
OpCapability RayTracingNV
OpCapability RayTraversalPrimitiveCullingKHR
OpCapability RuntimeDescriptorArray
OpCapability SampleMaskPostDepthCoverage
OpCapability SampleRateShading
OpCapability Sampled1D
OpCapability SampledBuffer
OpCapability SampledImageArrayNonUniformIndexing
OpCapability Shader
OpCapability ShaderClockKHR
OpCapability ShaderLayer
OpCapability ShaderNonUniform
OpCapability ShaderViewportIndex
OpCapability ShaderViewportIndexLayerEXT
OpCapability SparseResidency
OpCapability StencilExportEXT
OpCapability StorageImageArrayNonUniformIndexingEXT
OpCapability StorageImageExtendedFormats
OpCapability StorageImageReadWithoutFormat
OpCapability StorageImageWriteWithoutFormat
OpCapability StorageInputOutput16
OpCapability StoragePushConstant16
OpCapability StorageTexelBufferArrayNonUniformIndexing
OpCapability StorageUniform16
OpCapability StorageUniformBufferBlock16
OpCapability Tessellation
OpCapability UniformTexelBufferArrayNonUniformIndexing
OpCapability VulkanMemoryModel
OpExtension "SPV_EXT_fragment_fully_covered"
OpExtension "SPV_EXT_shader_image_int64"
OpExtension "SPV_EXT_shader_stencil_export"
OpExtension "SPV_EXT_shader_viewport_index_layer"
OpExtension "SPV_KHR_fragment_shader_barycentric"
OpExtension "SPV_KHR_fragment_shading_rate"
OpExtension "SPV_KHR_post_depth_coverage"
OpExtension "SPV_KHR_ray_query"
OpExtension "SPV_KHR_ray_tracing"
OpExtension "SPV_KHR_shader_clock"
OpExtension "SPV_NV_mesh_shader"
OpExtension "SPV_NV_ray_tracing"
OpExtension "SPV_NV_viewport_array2"
; CHECK: OpCapability Linkage
; CHECK: OpCapability ClipDistance
; CHECK: OpCapability CullDistance
; CHECK: OpCapability DemoteToHelperInvocation
; CHECK: OpCapability DeviceGroup
; CHECK: OpCapability DrawParameters
; CHECK: OpCapability Float16
; CHECK: OpCapability Float64
; CHECK: OpCapability FragmentBarycentricKHR
; CHECK: OpCapability FragmentFullyCoveredEXT
; CHECK: OpCapability FragmentShadingRateKHR
; CHECK: OpCapability GroupNonUniform
; CHECK: OpCapability GroupNonUniformArithmetic
; CHECK: OpCapability GroupNonUniformBallot
; CHECK: OpCapability GroupNonUniformQuad
; CHECK: OpCapability GroupNonUniformShuffle
; CHECK: OpCapability Image1D
; CHECK: OpCapability ImageBuffer
; CHECK: OpCapability ImageGatherExtended
; CHECK: OpCapability ImageMSArray
; CHECK: OpCapability ImageQuery
; CHECK: OpCapability InputAttachment
; CHECK: OpCapability InputAttachmentArrayNonUniformIndexing
; CHECK: OpCapability Int16
; CHECK: OpCapability Int64
; CHECK: OpCapability Int64Atomics
; CHECK: OpCapability Int64ImageEXT
; CHECK: OpCapability MeshShadingNV
; CHECK: OpCapability MinLod
; CHECK: OpCapability MultiView
; CHECK: OpCapability MultiViewport
; CHECK: OpCapability PhysicalStorageBufferAddresses
; CHECK: OpCapability RayQueryKHR
; CHECK: OpCapability RayTracingKHR
; CHECK: OpCapability RayTracingNV
; CHECK: OpCapability RayTraversalPrimitiveCullingKHR
; CHECK: OpCapability RuntimeDescriptorArray
; CHECK: OpCapability SampleMaskPostDepthCoverage
; CHECK: OpCapability SampleRateShading
; CHECK: OpCapability Sampled1D
; CHECK: OpCapability SampledBuffer
; CHECK: OpCapability SampledImageArrayNonUniformIndexing
; CHECK: OpCapability Shader
; CHECK: OpCapability ShaderClockKHR
; CHECK: OpCapability ShaderLayer
; CHECK: OpCapability ShaderNonUniform
; CHECK: OpCapability ShaderViewportIndex
; CHECK: OpCapability ShaderViewportIndexLayerEXT
; CHECK: OpCapability SparseResidency
; CHECK: OpCapability StencilExportEXT
; CHECK: OpCapability StorageImageArrayNonUniformIndexing
; CHECK: OpCapability StorageImageExtendedFormats
; CHECK: OpCapability StorageImageReadWithoutFormat
; CHECK: OpCapability StorageImageWriteWithoutFormat
; CHECK: OpCapability StorageInputOutput16
; CHECK: OpCapability StoragePushConstant16
; CHECK: OpCapability StorageTexelBufferArrayNonUniformIndexing
; CHECK: OpCapability Tessellation
; CHECK: OpCapability UniformTexelBufferArrayNonUniformIndex
; CHECK: OpCapability VulkanMemoryModel
; CHECK: OpExtension "SPV_EXT_fragment_fully_covered"
; CHECK: OpExtension "SPV_EXT_shader_image_int64"
; CHECK: OpExtension "SPV_EXT_shader_stencil_export"
; CHECK: OpExtension "SPV_EXT_shader_viewport_index_layer"
; CHECK: OpExtension "SPV_KHR_fragment_shader_barycentric"
; CHECK: OpExtension "SPV_KHR_fragment_shading_rate"
; CHECK: OpExtension "SPV_KHR_post_depth_coverage"
; CHECK: OpExtension "SPV_KHR_ray_query"
; CHECK: OpExtension "SPV_KHR_ray_tracing"
; CHECK: OpExtension "SPV_KHR_shader_clock"
; CHECK: OpExtension "SPV_NV_mesh_shader"
; CHECK: OpExtension "SPV_NV_ray_tracing"
; CHECK: OpExtension "SPV_NV_viewport_array2"
OpMemoryModel Logical Vulkan
%void = OpTypeVoid
%3 = OpTypeFunction %void
%1 = OpFunction %void None %3
%6 = OpLabel
OpReturn
OpFunctionEnd;
)";
SetTargetEnv(SPV_ENV_VULKAN_1_3);
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest, KeepShader) {
const std::string kTest = R"(
OpCapability Shader
; CHECK: OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%1 = OpFunction %void None %3
%6 = OpLabel
OpReturn
OpFunctionEnd;
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest, KeepShaderClockWhenInUse) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Int64
OpCapability ShaderClockKHR
OpExtension "SPV_KHR_shader_clock"
; CHECK: OpCapability ShaderClockKHR
; CHECK: OpExtension "SPV_KHR_shader_clock"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%ulong = OpTypeInt 64 0
%scope = OpConstant %uint 1
%3 = OpTypeFunction %void
%1 = OpFunction %void None %3
%6 = OpLabel
%7 = OpReadClockKHR %ulong %scope
OpReturn
OpFunctionEnd;
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest, TrimShaderClockWhenUnused) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Int64
OpCapability ShaderClockKHR
OpExtension "SPV_KHR_shader_clock"
; CHECK-NOT: OpCapability ShaderClockKHR
; CHECK-NOT: OpExtension "SPV_KHR_shader_clock"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%1 = OpFunction %void None %3
%6 = OpLabel
OpReturn
OpFunctionEnd;
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
}
TEST_F(TrimCapabilitiesPassTest, AMDShaderBallotExtensionRemains) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Groups
OpExtension "SPV_AMD_shader_ballot"
; CHECK: OpCapability Groups
; CHECK: OpExtension "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
%void = OpTypeVoid
%uint = OpTypeInt 32 0
%1 = OpTypeFunction %void
%uint_0 = OpConstant %uint 0
%2 = OpFunction %void None %1
%3 = OpLabel
%4 = OpGroupIAddNonUniformAMD %uint %uint_0 ExclusiveScan %uint_0
OpReturn
OpFunctionEnd;
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest, AMDShaderBallotExtensionRemoved) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Groups
OpExtension "SPV_AMD_shader_ballot"
; CHECK-NOT: OpCapability Groups
; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
%void = OpTypeVoid
%1 = OpTypeFunction %void
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd;
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
}
TEST_F(TrimCapabilitiesPassTest, MinLodCapabilityRemoved) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Sampled1D
OpCapability MinLod
; CHECK-NOT: OpCapability MinLod
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
%void = OpTypeVoid
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%v4float = OpTypeVector %float 4
%type_image = OpTypeImage %float Cube 2 0 0 1 Rgba32f
%ptr_type_image = OpTypePointer UniformConstant %type_image
%type_sampler = OpTypeSampler
%ptr_type_sampler = OpTypePointer UniformConstant %type_sampler
%float_0 = OpConstant %float 0
%float_000 = OpConstantComposite %v3float %float_0 %float_0 %float_0
%image = OpVariable %ptr_type_image UniformConstant
%sampler = OpVariable %ptr_type_sampler UniformConstant
%1 = OpTypeFunction %void
%2 = OpFunction %void None %1
%3 = OpLabel
%21 = OpLoad %type_image %image
%22 = OpLoad %type_sampler %sampler
%24 = OpSampledImage %type_sampled_image %21 %22
%25 = OpImageSampleImplicitLod %v4float %24 %float_000
OpReturn
OpFunctionEnd;
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
}
TEST_F(TrimCapabilitiesPassTest, MinLodCapabilityRemains) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Sampled1D
OpCapability MinLod
; CHECK: OpCapability MinLod
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %1 "main"
%void = OpTypeVoid
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%v4float = OpTypeVector %float 4
%type_image = OpTypeImage %float Cube 2 0 0 1 Rgba32f
%ptr_type_image = OpTypePointer UniformConstant %type_image
%type_sampler = OpTypeSampler
%ptr_type_sampler = OpTypePointer UniformConstant %type_sampler
%float_0 = OpConstant %float 0
%float_000 = OpConstantComposite %v3float %float_0 %float_0 %float_0
%image = OpVariable %ptr_type_image UniformConstant
%sampler = OpVariable %ptr_type_sampler UniformConstant
%1 = OpTypeFunction %void
%2 = OpFunction %void None %1
%3 = OpLabel
%21 = OpLoad %type_image %image
%22 = OpLoad %type_sampler %sampler
%24 = OpSampledImage %type_sampled_image %21 %22
%25 = OpImageSampleImplicitLod %v4float %24 %float_000 MinLod %float_0
OpReturn
OpFunctionEnd;
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest, StorageInputOutput16RemainsWithInputVariable) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%half = OpTypeFloat 16
%ptr = OpTypePointer Input %half
%1 = OpTypeFunction %void
%input_var = OpVariable %ptr Input
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16RemainsWithInputVariableArray) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%half = OpTypeFloat 16
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%array = OpTypeArray %half %uint_1
%ptr = OpTypePointer Input %array
%1 = OpTypeFunction %void
%input_var = OpVariable %ptr Input
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16RemainsWithInputVariableStruct) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%half = OpTypeFloat 16
%struct = OpTypeStruct %half
%ptr = OpTypePointer Input %struct
%1 = OpTypeFunction %void
%input_var = OpVariable %ptr Input
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16RemainsWithInputVariableStructOfStruct) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%half = OpTypeFloat 16
%float = OpTypeFloat 32
%struct = OpTypeStruct %float %half
%parent = OpTypeStruct %float %struct
%ptr = OpTypePointer Input %parent
%1 = OpTypeFunction %void
%input_var = OpVariable %ptr Input
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16RemainsWithInputVariableArrayOfStruct) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%half = OpTypeFloat 16
%struct = OpTypeStruct %half
%uint = OpTypeInt 32 0
%uint_1 = OpConstant %uint 1
%array = OpTypeArray %struct %uint_1
%ptr = OpTypePointer Input %array
%1 = OpTypeFunction %void
%input_var = OpVariable %ptr Input
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16RemainsWithInputVariableVector) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%half = OpTypeFloat 16
%vector = OpTypeVector %half 4
%ptr = OpTypePointer Input %vector
%1 = OpTypeFunction %void
%input_var = OpVariable %ptr Input
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16RemainsWithInputVariableMatrix) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%half = OpTypeFloat 16
%vector = OpTypeVector %half 4
%matrix = OpTypeMatrix %vector 4
%ptr = OpTypePointer Input %matrix
%1 = OpTypeFunction %void
%input_var = OpVariable %ptr Input
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16IsRemovedWithoutInputVariable) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK-NOT: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%1 = OpTypeFunction %void
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16RemainsWithOutputVariable) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%half = OpTypeFloat 16
%ptr = OpTypePointer Output %half
%1 = OpTypeFunction %void
%input_var = OpVariable %ptr Output
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange);
}
TEST_F(TrimCapabilitiesPassTest,
StorageInputOutput16IsRemovedWithoutOutputVariable) {
const std::string kTest = R"(
OpCapability Shader
OpCapability Float16
OpCapability StorageInputOutput16
; CHECK-NOT: OpCapability StorageInputOutput16
OpExtension "SPV_KHR_16bit_storage"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %2 "main" %input_var
OpDecorate %input_var BuiltIn LocalInvocationIndex
%void = OpTypeVoid
%1 = OpTypeFunction %void
%2 = OpFunction %void None %1
%3 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto result =
SinglePassRunAndMatch<TrimCapabilitiesPass>(kTest, /* skip_nop= */ false);
EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange);
}
} // namespace
} // namespace opt
} // namespace spvtools