mirror of
https://gitee.com/openharmony/third_party_spirv-tools
synced 2024-11-23 07:20:28 +00:00
Add --strip-atomic-counter-memory (#2413)
Adds an optimization pass to remove usages of AtomicCounterMemory bit. This bit is ignored in Vulkan environments and outright forbidden in WebGPU ones. Fixes #2242
This commit is contained in:
parent
bdcb155163
commit
e545522146
@ -154,6 +154,7 @@ SPVTOOLS_OPT_SRC_FILES := \
|
||||
source/opt/simplification_pass.cpp \
|
||||
source/opt/ssa_rewrite_pass.cpp \
|
||||
source/opt/strength_reduction_pass.cpp \
|
||||
source/opt/strip_atomic_counter_memory_pass.cpp \
|
||||
source/opt/strip_debug_info_pass.cpp \
|
||||
source/opt/strip_reflect_info_pass.cpp \
|
||||
source/opt/struct_cfg_analysis.cpp \
|
||||
|
2
BUILD.gn
2
BUILD.gn
@ -612,6 +612,8 @@ static_library("spvtools_opt") {
|
||||
"source/opt/ssa_rewrite_pass.h",
|
||||
"source/opt/strength_reduction_pass.cpp",
|
||||
"source/opt/strength_reduction_pass.h",
|
||||
"source/opt/strip_atomic_counter_memory_pass.cpp",
|
||||
"source/opt/strip_atomic_counter_memory_pass.h",
|
||||
"source/opt/strip_debug_info_pass.cpp",
|
||||
"source/opt/strip_debug_info_pass.h",
|
||||
"source/opt/strip_reflect_info_pass.cpp",
|
||||
|
@ -208,6 +208,13 @@ class Optimizer {
|
||||
// A null pass does nothing to the SPIR-V module to be optimized.
|
||||
Optimizer::PassToken CreateNullPass();
|
||||
|
||||
// Creates a strip-atomic-counter-memory pass.
|
||||
// A strip-atomic-counter-memory pass removes all usages of the
|
||||
// AtomicCounterMemory bit in Memory Semantics bitmasks. This bit is a no-op in
|
||||
// Vulkan, so isn't needed in that env. And the related capability is not
|
||||
// allowed in WebGPU, so it is not allowed in that env.
|
||||
Optimizer::PassToken CreateStripAtomicCounterMemoryPass();
|
||||
|
||||
// Creates a strip-debug-info pass.
|
||||
// A strip-debug-info pass removes all debug instructions (as documented in
|
||||
// Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized.
|
||||
|
@ -605,3 +605,35 @@ bool spvOpcodeIsDebug(SpvOp opcode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
|
||||
switch (opcode) {
|
||||
case SpvOpMemoryBarrier:
|
||||
return {1};
|
||||
case SpvOpAtomicStore:
|
||||
case SpvOpControlBarrier:
|
||||
case SpvOpAtomicFlagClear:
|
||||
case SpvOpMemoryNamedBarrier:
|
||||
return {2};
|
||||
case SpvOpAtomicLoad:
|
||||
case SpvOpAtomicExchange:
|
||||
case SpvOpAtomicIIncrement:
|
||||
case SpvOpAtomicIDecrement:
|
||||
case SpvOpAtomicIAdd:
|
||||
case SpvOpAtomicISub:
|
||||
case SpvOpAtomicSMin:
|
||||
case SpvOpAtomicUMin:
|
||||
case SpvOpAtomicSMax:
|
||||
case SpvOpAtomicUMax:
|
||||
case SpvOpAtomicAnd:
|
||||
case SpvOpAtomicOr:
|
||||
case SpvOpAtomicXor:
|
||||
case SpvOpAtomicFlagTestAndSet:
|
||||
return {4};
|
||||
case SpvOpAtomicCompareExchange:
|
||||
case SpvOpAtomicCompareExchangeWeak:
|
||||
return {4, 5};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
@ -133,4 +133,8 @@ bool spvOpcodeIsScalarizable(SpvOp opcode);
|
||||
// Returns true if the given opcode is a debug instruction.
|
||||
bool spvOpcodeIsDebug(SpvOp opcode);
|
||||
|
||||
// Returns a vector containing the indices of the memory semantics <id>
|
||||
// operands for |opcode|.
|
||||
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
|
||||
|
||||
#endif // SOURCE_OPCODE_H_
|
||||
|
@ -96,6 +96,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
simplification_pass.h
|
||||
ssa_rewrite_pass.h
|
||||
strength_reduction_pass.h
|
||||
strip_atomic_counter_memory_pass.h
|
||||
strip_debug_info_pass.h
|
||||
strip_reflect_info_pass.h
|
||||
struct_cfg_analysis.h
|
||||
@ -189,6 +190,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
||||
simplification_pass.cpp
|
||||
ssa_rewrite_pass.cpp
|
||||
strength_reduction_pass.cpp
|
||||
strip_atomic_counter_memory_pass.cpp
|
||||
strip_debug_info_pass.cpp
|
||||
strip_reflect_info_pass.cpp
|
||||
struct_cfg_analysis.cpp
|
||||
|
@ -219,6 +219,8 @@ Optimizer& Optimizer::RegisterSizePasses() {
|
||||
|
||||
Optimizer& Optimizer::RegisterWebGPUPasses() {
|
||||
return RegisterPass(CreateStripDebugInfoPass())
|
||||
.RegisterPass(CreateStripAtomicCounterMemoryPass())
|
||||
.RegisterPass(CreateEliminateDeadConstantPass())
|
||||
.RegisterPass(CreateFlattenDecorationPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateDeadBranchElimPass());
|
||||
@ -266,7 +268,9 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
||||
//
|
||||
// Both Pass::name() and Pass::desc() should be static class members so they
|
||||
// can be invoked without creating a pass instance.
|
||||
if (pass_name == "strip-debug") {
|
||||
if (pass_name == "strip-atomic-counter-memory") {
|
||||
RegisterPass(CreateStripAtomicCounterMemoryPass());
|
||||
} else if (pass_name == "strip-debug") {
|
||||
RegisterPass(CreateStripDebugInfoPass());
|
||||
} else if (pass_name == "strip-reflect") {
|
||||
RegisterPass(CreateStripReflectInfoPass());
|
||||
@ -529,6 +533,11 @@ Optimizer::PassToken CreateNullPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateStripAtomicCounterMemoryPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::StripAtomicCounterMemoryPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateStripDebugInfoPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::StripDebugInfoPass>());
|
||||
|
@ -63,6 +63,7 @@
|
||||
#include "source/opt/simplification_pass.h"
|
||||
#include "source/opt/ssa_rewrite_pass.h"
|
||||
#include "source/opt/strength_reduction_pass.h"
|
||||
#include "source/opt/strip_atomic_counter_memory_pass.h"
|
||||
#include "source/opt/strip_debug_info_pass.h"
|
||||
#include "source/opt/strip_reflect_info_pass.h"
|
||||
#include "source/opt/unify_const_pass.h"
|
||||
|
57
source/opt/strip_atomic_counter_memory_pass.cpp
Normal file
57
source/opt/strip_atomic_counter_memory_pass.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2019 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/strip_atomic_counter_memory_pass.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status StripAtomicCounterMemoryPass::Process() {
|
||||
bool changed = false;
|
||||
context()->module()->ForEachInst([this, &changed](Instruction* inst) {
|
||||
auto indices = spvOpcodeMemorySemanticsOperandIndices(inst->opcode());
|
||||
if (indices.empty()) return;
|
||||
|
||||
for (auto idx : indices) {
|
||||
auto mem_sem_id = inst->GetSingleWordOperand(idx);
|
||||
const auto& mem_sem_inst =
|
||||
context()->get_def_use_mgr()->GetDef(mem_sem_id);
|
||||
// The spec explicitly says that this id must be an OpConstant
|
||||
auto mem_sem_val = mem_sem_inst->GetSingleWordOperand(2);
|
||||
if (!(mem_sem_val & SpvMemorySemanticsAtomicCounterMemoryMask)) {
|
||||
continue;
|
||||
}
|
||||
mem_sem_val &= ~SpvMemorySemanticsAtomicCounterMemoryMask;
|
||||
|
||||
analysis::Integer int_type(32, false);
|
||||
const analysis::Type* uint32_type =
|
||||
context()->get_type_mgr()->GetRegisteredType(&int_type);
|
||||
auto* new_const = context()->get_constant_mgr()->GetConstant(
|
||||
uint32_type, {mem_sem_val});
|
||||
auto* new_const_inst =
|
||||
context()->get_constant_mgr()->GetDefiningInstruction(new_const);
|
||||
auto new_const_id = new_const_inst->result_id();
|
||||
|
||||
inst->SetOperand(idx, {new_const_id});
|
||||
context()->UpdateDefUse(inst);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
51
source/opt/strip_atomic_counter_memory_pass.h
Normal file
51
source/opt/strip_atomic_counter_memory_pass.h
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2019 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_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_
|
||||
#define SOURCE_OPT_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "source/opt/pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// Removes the AtomicCounterMemory bit from the value being passed into memory
|
||||
// semantics. This bit being set is ignored in Vulkan environments and
|
||||
// forbidden WebGPU ones.
|
||||
class StripAtomicCounterMemoryPass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "strip-atomic-counter-memory"; }
|
||||
Status Process() override;
|
||||
|
||||
IRContext::Analysis GetPreservedAnalyses() override {
|
||||
return IRContext::kAnalysisInstrToBlockMapping |
|
||||
IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
|
||||
IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis |
|
||||
IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap |
|
||||
IRContext::kAnalysisScalarEvolution |
|
||||
IRContext::kAnalysisRegisterPressure |
|
||||
IRContext::kAnalysisValueNumberTable |
|
||||
IRContext::kAnalysisStructuredCFG |
|
||||
IRContext::kAnalysisBuiltinVarId |
|
||||
IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes |
|
||||
IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_
|
@ -77,6 +77,7 @@ add_spvtools_unittest(TARGET opt
|
||||
set_spec_const_default_value_test.cpp
|
||||
simplification_test.cpp
|
||||
strength_reduction_test.cpp
|
||||
strip_atomic_counter_memory_test.cpp
|
||||
strip_debug_info_test.cpp
|
||||
strip_reflect_info_test.cpp
|
||||
struct_cfg_analysis_test.cpp
|
||||
|
@ -231,9 +231,12 @@ TEST(Optimizer, WebGPUModeSetsCorrectPasses) {
|
||||
for (auto name = pass_names.begin(); name != pass_names.end(); ++name)
|
||||
registered_passes.push_back(*name);
|
||||
|
||||
std::vector<std::string> expected_passes = {
|
||||
"eliminate-dead-branches", "eliminate-dead-code-aggressive",
|
||||
"flatten-decorations", "strip-debug"};
|
||||
std::vector<std::string> expected_passes = {"eliminate-dead-branches",
|
||||
"eliminate-dead-code-aggressive",
|
||||
"eliminate-dead-const",
|
||||
"flatten-decorations",
|
||||
"strip-debug",
|
||||
"strip-atomic-counter-memory"};
|
||||
std::sort(registered_passes.begin(), registered_passes.end());
|
||||
std::sort(expected_passes.begin(), expected_passes.end());
|
||||
|
||||
@ -265,7 +268,6 @@ TEST_P(WebGPUPassTest, Ran) {
|
||||
class ValidatorOptions validator_options;
|
||||
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
|
||||
validator_options, true));
|
||||
|
||||
std::string disassembly;
|
||||
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
|
||||
|
||||
@ -274,7 +276,7 @@ TEST_P(WebGPUPassTest, Ran) {
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
WebGPU, WebGPUPassTest,
|
||||
Optimizer, WebGPUPassTest,
|
||||
::testing::ValuesIn(std::vector<WebGPUPassCase>{
|
||||
// FlattenDecorations
|
||||
{// input
|
||||
@ -346,7 +348,95 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"strip-debug"}}));
|
||||
"strip-debug"},
|
||||
// Eliminate Dead Constants
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\n"
|
||||
"OpEntryPoint Vertex %func \"shader\"\n"
|
||||
"%u32 = OpTypeInt 32 0\n"
|
||||
"%u32_ptr = OpTypePointer Workgroup %u32\n"
|
||||
"%u32_var = OpVariable %u32_ptr Workgroup\n"
|
||||
"%u32_1 = OpConstant %u32 1\n"
|
||||
"%cross_device = OpConstant %u32 0\n"
|
||||
"%relaxed = OpConstant %u32 0\n"
|
||||
"%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%void_f = OpTypeFunction %void\n"
|
||||
"%func = OpFunction %void None %void_f\n"
|
||||
"%label = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n"
|
||||
"%4 = OpVariable %_ptr_Workgroup_uint Workgroup\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%10 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %10\n"
|
||||
"%11 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
"eliminate-dead-const"},
|
||||
// Strip Atomic Counter Memory
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\n"
|
||||
"OpEntryPoint Vertex %func \"shader\"\n"
|
||||
"%u32 = OpTypeInt 32 0\n"
|
||||
"%u32_ptr = OpTypePointer Workgroup %u32\n"
|
||||
"%u32_var = OpVariable %u32_ptr Workgroup\n"
|
||||
"%u32_0 = OpConstant %u32 0\n"
|
||||
"%u32_1 = OpConstant %u32 1\n"
|
||||
"%cross_device = OpConstant %u32 0\n"
|
||||
"%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%void_f = OpTypeFunction %void\n"
|
||||
"%func = OpFunction %void None %void_f\n"
|
||||
"%label = OpLabel\n"
|
||||
"%val0 = OpAtomicStore %u32_var %cross_device "
|
||||
"%acquire_release_atomic_counter_workgroup %u32_1\n"
|
||||
"%val1 = OpAtomicIIncrement %u32 %u32_var %cross_device "
|
||||
"%acquire_release_atomic_counter_workgroup\n"
|
||||
"%val2 = OpAtomicCompareExchange %u32 %u32_var %cross_device "
|
||||
"%acquire_release_atomic_counter_workgroup "
|
||||
"%acquire_release_atomic_counter_workgroup %u32_0 %u32_0\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n"
|
||||
"%4 = OpVariable %_ptr_Workgroup_uint Workgroup\n"
|
||||
"%uint_0 = OpConstant %uint 0\n"
|
||||
"%uint_1 = OpConstant %uint 1\n"
|
||||
"%uint_0_0 = OpConstant %uint 0\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%10 = OpTypeFunction %void\n"
|
||||
"%uint_264 = OpConstant %uint 264\n"
|
||||
"%1 = OpFunction %void None %10\n"
|
||||
"%11 = OpLabel\n"
|
||||
"OpAtomicStore %4 %uint_0_0 %uint_264 %uint_1\n"
|
||||
"%12 = OpAtomicIIncrement %uint %4 %uint_0_0 %uint_264\n"
|
||||
"%13 = OpAtomicCompareExchange %uint %4 %uint_0_0 %uint_264 %uint_264 "
|
||||
"%uint_0 %uint_0\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"strip-atomic-counter-memory"}}));
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
|
406
test/opt/strip_atomic_counter_memory_test.cpp
Normal file
406
test/opt/strip_atomic_counter_memory_test.cpp
Normal file
@ -0,0 +1,406 @@
|
||||
// Copyright (c) 2019 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 <vector>
|
||||
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
typedef std::tuple<std::string, std::string> StripAtomicCounterMemoryParam;
|
||||
|
||||
using MemorySemanticsModified =
|
||||
PassTest<::testing::TestWithParam<StripAtomicCounterMemoryParam>>;
|
||||
using NonMemorySemanticsUnmodifiedTest = PassTest<::testing::Test>;
|
||||
|
||||
void operator+=(std::vector<const char*>& lhs, const char* rhs) {
|
||||
lhs.push_back(rhs);
|
||||
}
|
||||
|
||||
std::string GetConstDecl(std::string val) {
|
||||
std::string decl;
|
||||
decl += "%uint_" + val + " = OpConstant %uint " + val;
|
||||
return decl;
|
||||
}
|
||||
|
||||
std::string GetUnchangedString(std::string(generate_inst)(std::string),
|
||||
std::string val) {
|
||||
std::string decl = GetConstDecl(val);
|
||||
std::string inst = generate_inst(val);
|
||||
|
||||
std::vector<const char*> result = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModelKHR",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical VulkanKHR",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint",
|
||||
"%4 = OpVariable %_ptr_Workgroup_uint Workgroup",
|
||||
"%uint_0 = OpConstant %uint 0",
|
||||
"%uint_1 = OpConstant %uint 1",
|
||||
"%void = OpTypeVoid",
|
||||
"%8 = OpTypeFunction %void",
|
||||
decl.c_str(),
|
||||
"%1 = OpFunction %void None %8",
|
||||
"%10 = OpLabel",
|
||||
inst.c_str(),
|
||||
"OpReturn",
|
||||
"OpFunctionEnd"
|
||||
// clang-format on
|
||||
};
|
||||
return JoinAllInsts(result);
|
||||
}
|
||||
|
||||
std::string GetChangedString(std::string(generate_inst)(std::string),
|
||||
std::string orig, std::string changed) {
|
||||
std::string orig_decl = GetConstDecl(orig);
|
||||
std::string changed_decl = GetConstDecl(changed);
|
||||
std::string inst = generate_inst(changed);
|
||||
|
||||
std::vector<const char*> result = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModelKHR",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical VulkanKHR",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint",
|
||||
"%4 = OpVariable %_ptr_Workgroup_uint Workgroup",
|
||||
"%uint_0 = OpConstant %uint 0",
|
||||
"%uint_1 = OpConstant %uint 1",
|
||||
"%void = OpTypeVoid",
|
||||
"%8 = OpTypeFunction %void",
|
||||
orig_decl.c_str() };
|
||||
// clang-format on
|
||||
if (changed != "0") result += changed_decl.c_str();
|
||||
result += "%1 = OpFunction %void None %8";
|
||||
result += "%10 = OpLabel";
|
||||
result += inst.c_str();
|
||||
result += "OpReturn";
|
||||
result += "OpFunctionEnd";
|
||||
return JoinAllInsts(result);
|
||||
}
|
||||
|
||||
std::tuple<std::string, std::string> GetInputAndExpected(
|
||||
std::string(generate_inst)(std::string),
|
||||
StripAtomicCounterMemoryParam param) {
|
||||
std::string orig = std::get<0>(param);
|
||||
std::string changed = std::get<1>(param);
|
||||
std::string input = GetUnchangedString(generate_inst, orig);
|
||||
std::string expected = orig == changed
|
||||
? GetUnchangedString(generate_inst, changed)
|
||||
: GetChangedString(generate_inst, orig, changed);
|
||||
return std::make_tuple(input, expected);
|
||||
}
|
||||
|
||||
std::string GetOpControlBarrierInst(std::string val) {
|
||||
return "OpControlBarrier %uint_1 %uint_1 %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpControlBarrier) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpControlBarrierInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpMemoryBarrierInst(std::string val) {
|
||||
return "OpMemoryBarrier %uint_1 %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpMemoryBarrier) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpMemoryBarrierInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicLoadInst(std::string val) {
|
||||
return "%11 = OpAtomicLoad %uint %4 %uint_1 %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicLoad) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicLoadInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicStoreInst(std::string val) {
|
||||
return "OpAtomicStore %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicStore) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicStoreInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicExchangeInst(std::string val) {
|
||||
return "%11 = OpAtomicExchange %uint %4 %uint_1 %uint_" + val + " %uint_0";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicExchange) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicExchangeInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicCompareExchangeInst(std::string val) {
|
||||
return "%11 = OpAtomicCompareExchange %uint %4 %uint_1 %uint_" + val +
|
||||
" %uint_" + val + " %uint_0 %uint_0";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicCompareExchange) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicCompareExchangeInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicCompareExchangeWeakInst(std::string val) {
|
||||
return "%11 = OpAtomicCompareExchangeWeak %uint %4 %uint_1 %uint_" + val +
|
||||
" %uint_" + val + " %uint_0 %uint_0";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicCompareExchangeWeak) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicCompareExchangeWeakInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicIIncrementInst(std::string val) {
|
||||
return "%11 = OpAtomicIIncrement %uint %4 %uint_1 %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicIIncrement) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicIIncrementInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicIDecrementInst(std::string val) {
|
||||
return "%11 = OpAtomicIDecrement %uint %4 %uint_1 %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicIDecrement) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicIDecrementInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicIAddInst(std::string val) {
|
||||
return "%11 = OpAtomicIAdd %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicIAdd) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicIAddInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicISubInst(std::string val) {
|
||||
return "%11 = OpAtomicISub %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicISub) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicISubInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicSMinInst(std::string val) {
|
||||
return "%11 = OpAtomicSMin %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicSMin) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicSMinInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicUMinInst(std::string val) {
|
||||
return "%11 = OpAtomicUMin %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicUMin) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicUMinInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicSMaxInst(std::string val) {
|
||||
return "%11 = OpAtomicSMax %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicSMax) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicSMaxInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicUMaxInst(std::string val) {
|
||||
return "%11 = OpAtomicUMax %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicUMax) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicUMaxInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicAndInst(std::string val) {
|
||||
return "%11 = OpAtomicAnd %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicAnd) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicAndInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicOrInst(std::string val) {
|
||||
return "%11 = OpAtomicOr %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicOr) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicOrInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicXorInst(std::string val) {
|
||||
return "%11 = OpAtomicXor %uint %4 %uint_1 %uint_" + val + " %uint_1";
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicXor) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicXorInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicFlagTestAndSetInst(std::string val) {
|
||||
return "%11 = OpAtomicFlagTestAndSet %uint %4 %uint_1 %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicFlagTestAndSet) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicFlagTestAndSetInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpAtomicFlagClearInst(std::string val) {
|
||||
return "OpAtomicFlagClear %4 %uint_1 %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpAtomicFlagClear) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpAtomicFlagClearInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetOpMemoryNamedBarrierInst(std::string val) {
|
||||
return "OpMemoryNamedBarrier %4 %uint_1 %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_P(MemorySemanticsModified, OpMemoryNamedBarrier) {
|
||||
std::string input, expected;
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetOpMemoryNamedBarrierInst, GetParam());
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
StripAtomicCounterMemoryTest, MemorySemanticsModified,
|
||||
::testing::ValuesIn(std::vector<StripAtomicCounterMemoryParam>({
|
||||
std::make_tuple("1024", "0"),
|
||||
std::make_tuple("5", "5"),
|
||||
std::make_tuple("1288", "264"),
|
||||
std::make_tuple("264", "264")
|
||||
})));
|
||||
// clang-format on
|
||||
|
||||
std::string GetNoMemorySemanticsPresentInst(std::string val) {
|
||||
return "%11 = OpVariable %_ptr_Workgroup_uint Workgroup %uint_" + val;
|
||||
}
|
||||
|
||||
TEST_F(NonMemorySemanticsUnmodifiedTest, NoMemorySemanticsPresent) {
|
||||
std::string input, expected;
|
||||
StripAtomicCounterMemoryParam param = std::make_tuple("1288", "1288");
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetNoMemorySemanticsPresentInst, param);
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetMemorySemanticsPresentInst(std::string val) {
|
||||
return "%11 = OpAtomicIAdd %uint %4 %uint_1 %uint_" + val + " %uint_1288";
|
||||
}
|
||||
|
||||
TEST_F(NonMemorySemanticsUnmodifiedTest, MemorySemanticsPresent) {
|
||||
std::string input, expected;
|
||||
StripAtomicCounterMemoryParam param = std::make_tuple("1288", "264");
|
||||
std::tie(input, expected) =
|
||||
GetInputAndExpected(GetMemorySemanticsPresentInst, param);
|
||||
SinglePassRunAndCheck<StripAtomicCounterMemoryPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
@ -340,6 +340,8 @@ Options (in lexicographical order):
|
||||
This options should be used rarely, and with caution.
|
||||
--strength-reduction
|
||||
Replaces instructions with equivalent and less expensive ones.
|
||||
--strip-atomic-counter-memory
|
||||
Removes AtomicCountMemory bit from memory semantics values.
|
||||
--strip-debug
|
||||
Remove all debug instructions.
|
||||
--strip-reflect
|
||||
|
Loading…
Reference in New Issue
Block a user