mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 03:30:06 +00:00
spirv-opt: add pass to Spread Volatile semantics (#4667)
Add a pass to spread Volatile semantics to variables with SMIDNV, WarpIDNV, SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask BuiltIn decorations or OpLoad for them when the shader model is the ray generation, closest hit, miss, intersection, or callable shaders. This pass can be used for VUID-StandaloneSpirv-VulkanMemoryModel-04678 and VUID-StandaloneSpirv-VulkanMemoryModel-04679 (See "Standalone SPIR-V Validation" section of Vulkan spec "Appendix A: Vulkan Environment for SPIR-V"). Handle variables used by multiple entry points: 1. Update error check to make it working regardless of the order of entry points. 2. For a variable, if it is used by two entry points E1 and E2 and it needs the Volatile semantics for E1 while it does not for E2 - If VulkanMemoryModel capability is enabled, which means we have to set memory operation of load instructions for the variable, we update load instructions in E1, but do not update the ones in E2. - If VulkanMemoryModel capability is disabled, which means we have to add Volatile decoration for the variable, we report an error because E1 needs to add Volatile decoration for the variable while E2 does not. For the simplicity of the implementation, we assume that all functions other than entry point functions are inlined.
This commit is contained in:
parent
6938af7f82
commit
fb9a10cd48
@ -165,6 +165,7 @@ SPVTOOLS_OPT_SRC_FILES := \
|
|||||||
source/opt/scalar_replacement_pass.cpp \
|
source/opt/scalar_replacement_pass.cpp \
|
||||||
source/opt/set_spec_constant_default_value_pass.cpp \
|
source/opt/set_spec_constant_default_value_pass.cpp \
|
||||||
source/opt/simplification_pass.cpp \
|
source/opt/simplification_pass.cpp \
|
||||||
|
source/opt/spread_volatile_semantics.cpp \
|
||||||
source/opt/ssa_rewrite_pass.cpp \
|
source/opt/ssa_rewrite_pass.cpp \
|
||||||
source/opt/strength_reduction_pass.cpp \
|
source/opt/strength_reduction_pass.cpp \
|
||||||
source/opt/strip_debug_info_pass.cpp \
|
source/opt/strip_debug_info_pass.cpp \
|
||||||
|
2
BUILD.gn
2
BUILD.gn
@ -732,6 +732,8 @@ static_library("spvtools_opt") {
|
|||||||
"source/opt/set_spec_constant_default_value_pass.h",
|
"source/opt/set_spec_constant_default_value_pass.h",
|
||||||
"source/opt/simplification_pass.cpp",
|
"source/opt/simplification_pass.cpp",
|
||||||
"source/opt/simplification_pass.h",
|
"source/opt/simplification_pass.h",
|
||||||
|
"source/opt/spread_volatile_semantics.cpp",
|
||||||
|
"source/opt/spread_volatile_semantics.h",
|
||||||
"source/opt/ssa_rewrite_pass.cpp",
|
"source/opt/ssa_rewrite_pass.cpp",
|
||||||
"source/opt/ssa_rewrite_pass.h",
|
"source/opt/ssa_rewrite_pass.h",
|
||||||
"source/opt/strength_reduction_pass.cpp",
|
"source/opt/strength_reduction_pass.cpp",
|
||||||
|
@ -835,6 +835,19 @@ Optimizer::PassToken CreateFixStorageClassPass();
|
|||||||
// inclusive.
|
// inclusive.
|
||||||
Optimizer::PassToken CreateGraphicsRobustAccessPass();
|
Optimizer::PassToken CreateGraphicsRobustAccessPass();
|
||||||
|
|
||||||
|
// Create a pass to spread Volatile semantics to variables with SMIDNV,
|
||||||
|
// WarpIDNV, SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask,
|
||||||
|
// SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask BuiltIn
|
||||||
|
// decorations or OpLoad for them when the shader model is the ray generation,
|
||||||
|
// closest hit, miss, intersection, or callable. This pass can be used for
|
||||||
|
// VUID-StandaloneSpirv-VulkanMemoryModel-04678 and
|
||||||
|
// VUID-StandaloneSpirv-VulkanMemoryModel-04679 (See "Standalone SPIR-V
|
||||||
|
// Validation" section of Vulkan spec "Appendix A: Vulkan Environment for
|
||||||
|
// SPIR-V"). When the SPIR-V version is 1.6 or above, the pass also spreads
|
||||||
|
// the Volatile semantics to a variable with HelperInvocation BuiltIn decoration
|
||||||
|
// in the fragement shader.
|
||||||
|
Optimizer::PassToken CreateSpreadVolatileSemanticsPass();
|
||||||
|
|
||||||
// Create a pass to replace a descriptor access using variable index.
|
// Create a pass to replace a descriptor access using variable index.
|
||||||
// This pass replaces every access using a variable index to array variable
|
// This pass replaces every access using a variable index to array variable
|
||||||
// |desc| that has a DescriptorSet and Binding decorations with a constant
|
// |desc| that has a DescriptorSet and Binding decorations with a constant
|
||||||
|
@ -108,6 +108,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||||||
scalar_replacement_pass.h
|
scalar_replacement_pass.h
|
||||||
set_spec_constant_default_value_pass.h
|
set_spec_constant_default_value_pass.h
|
||||||
simplification_pass.h
|
simplification_pass.h
|
||||||
|
spread_volatile_semantics.h
|
||||||
ssa_rewrite_pass.h
|
ssa_rewrite_pass.h
|
||||||
strength_reduction_pass.h
|
strength_reduction_pass.h
|
||||||
strip_debug_info_pass.h
|
strip_debug_info_pass.h
|
||||||
@ -215,6 +216,7 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||||||
scalar_replacement_pass.cpp
|
scalar_replacement_pass.cpp
|
||||||
set_spec_constant_default_value_pass.cpp
|
set_spec_constant_default_value_pass.cpp
|
||||||
simplification_pass.cpp
|
simplification_pass.cpp
|
||||||
|
spread_volatile_semantics.cpp
|
||||||
ssa_rewrite_pass.cpp
|
ssa_rewrite_pass.cpp
|
||||||
strength_reduction_pass.cpp
|
strength_reduction_pass.cpp
|
||||||
strip_debug_info_pass.cpp
|
strip_debug_info_pass.cpp
|
||||||
|
@ -324,6 +324,8 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
|||||||
RegisterPass(CreateLocalAccessChainConvertPass());
|
RegisterPass(CreateLocalAccessChainConvertPass());
|
||||||
} else if (pass_name == "replace-desc-array-access-using-var-index") {
|
} else if (pass_name == "replace-desc-array-access-using-var-index") {
|
||||||
RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
|
RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
|
||||||
|
} else if (pass_name == "spread-volatile-semantics") {
|
||||||
|
RegisterPass(CreateSpreadVolatileSemanticsPass());
|
||||||
} else if (pass_name == "descriptor-scalar-replacement") {
|
} else if (pass_name == "descriptor-scalar-replacement") {
|
||||||
RegisterPass(CreateDescriptorScalarReplacementPass());
|
RegisterPass(CreateDescriptorScalarReplacementPass());
|
||||||
} else if (pass_name == "eliminate-dead-code-aggressive") {
|
} else if (pass_name == "eliminate-dead-code-aggressive") {
|
||||||
@ -976,6 +978,11 @@ Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
|
|||||||
MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
|
MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
|
||||||
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||||
|
MakeUnique<opt::SpreadVolatileSemantics>());
|
||||||
|
}
|
||||||
|
|
||||||
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
|
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
|
||||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||||
MakeUnique<opt::DescriptorScalarReplacement>());
|
MakeUnique<opt::DescriptorScalarReplacement>());
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
#include "source/opt/scalar_replacement_pass.h"
|
#include "source/opt/scalar_replacement_pass.h"
|
||||||
#include "source/opt/set_spec_constant_default_value_pass.h"
|
#include "source/opt/set_spec_constant_default_value_pass.h"
|
||||||
#include "source/opt/simplification_pass.h"
|
#include "source/opt/simplification_pass.h"
|
||||||
|
#include "source/opt/spread_volatile_semantics.h"
|
||||||
#include "source/opt/ssa_rewrite_pass.h"
|
#include "source/opt/ssa_rewrite_pass.h"
|
||||||
#include "source/opt/strength_reduction_pass.h"
|
#include "source/opt/strength_reduction_pass.h"
|
||||||
#include "source/opt/strip_debug_info_pass.h"
|
#include "source/opt/strip_debug_info_pass.h"
|
||||||
|
314
source/opt/spread_volatile_semantics.cpp
Normal file
314
source/opt/spread_volatile_semantics.cpp
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
// Copyright (c) 2022 Google LLC
|
||||||
|
//
|
||||||
|
// 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/spread_volatile_semantics.h"
|
||||||
|
|
||||||
|
#include "source/opt/decoration_manager.h"
|
||||||
|
#include "source/opt/ir_builder.h"
|
||||||
|
#include "source/spirv_constant.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace opt {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const uint32_t kOpDecorateInOperandBuiltinDecoration = 2u;
|
||||||
|
const uint32_t kOpLoadInOperandMemoryOperands = 1u;
|
||||||
|
const uint32_t kOpEntryPointInOperandEntryPoint = 1u;
|
||||||
|
const uint32_t kOpEntryPointInOperandInterface = 3u;
|
||||||
|
|
||||||
|
bool HasBuiltinDecoration(analysis::DecorationManager* decoration_manager,
|
||||||
|
uint32_t var_id, uint32_t built_in) {
|
||||||
|
return decoration_manager->FindDecoration(
|
||||||
|
var_id, SpvDecorationBuiltIn, [built_in](const Instruction& inst) {
|
||||||
|
return built_in == inst.GetSingleWordInOperand(
|
||||||
|
kOpDecorateInOperandBuiltinDecoration);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBuiltInForRayTracingVolatileSemantics(uint32_t built_in) {
|
||||||
|
switch (built_in) {
|
||||||
|
case SpvBuiltInSMIDNV:
|
||||||
|
case SpvBuiltInWarpIDNV:
|
||||||
|
case SpvBuiltInSubgroupSize:
|
||||||
|
case SpvBuiltInSubgroupLocalInvocationId:
|
||||||
|
case SpvBuiltInSubgroupEqMask:
|
||||||
|
case SpvBuiltInSubgroupGeMask:
|
||||||
|
case SpvBuiltInSubgroupGtMask:
|
||||||
|
case SpvBuiltInSubgroupLeMask:
|
||||||
|
case SpvBuiltInSubgroupLtMask:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasBuiltinForRayTracingVolatileSemantics(
|
||||||
|
analysis::DecorationManager* decoration_manager, uint32_t var_id) {
|
||||||
|
return decoration_manager->FindDecoration(
|
||||||
|
var_id, SpvDecorationBuiltIn, [](const Instruction& inst) {
|
||||||
|
uint32_t built_in =
|
||||||
|
inst.GetSingleWordInOperand(kOpDecorateInOperandBuiltinDecoration);
|
||||||
|
return IsBuiltInForRayTracingVolatileSemantics(built_in);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasVolatileDecoration(analysis::DecorationManager* decoration_manager,
|
||||||
|
uint32_t var_id) {
|
||||||
|
return decoration_manager->HasDecoration(var_id, SpvDecorationVolatile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasOnlyEntryPointsAsFunctions(IRContext* context, Module* module) {
|
||||||
|
std::unordered_set<uint32_t> entry_function_ids;
|
||||||
|
for (Instruction& entry_point : module->entry_points()) {
|
||||||
|
entry_function_ids.insert(
|
||||||
|
entry_point.GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint));
|
||||||
|
}
|
||||||
|
for (auto& function : *module) {
|
||||||
|
if (entry_function_ids.find(function.result_id()) ==
|
||||||
|
entry_function_ids.end()) {
|
||||||
|
std::string message(
|
||||||
|
"Functions of SPIR-V for spread-volatile-semantics pass input must "
|
||||||
|
"be inlined except entry points");
|
||||||
|
message += "\n " + function.DefInst().PrettyPrint(
|
||||||
|
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
|
||||||
|
context->consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Pass::Status SpreadVolatileSemantics::Process() {
|
||||||
|
if (!HasOnlyEntryPointsAsFunctions(context(), get_module())) {
|
||||||
|
return Status::Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool is_vk_memory_model_enabled =
|
||||||
|
context()->get_feature_mgr()->HasCapability(
|
||||||
|
SpvCapabilityVulkanMemoryModel);
|
||||||
|
CollectTargetsForVolatileSemantics(is_vk_memory_model_enabled);
|
||||||
|
|
||||||
|
// If VulkanMemoryModel capability is not enabled, we have to set Volatile
|
||||||
|
// decoration for interface variables instead of setting Volatile for load
|
||||||
|
// instructions. If an interface (or pointers to it) is used by two load
|
||||||
|
// instructions in two entry points and one must be volatile while another
|
||||||
|
// is not, we have to report an error for the conflict.
|
||||||
|
if (!is_vk_memory_model_enabled &&
|
||||||
|
HasInterfaceInConflictOfVolatileSemantics()) {
|
||||||
|
return Status::Failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SpreadVolatileSemanticsToVariables(is_vk_memory_model_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pass::Status SpreadVolatileSemantics::SpreadVolatileSemanticsToVariables(
|
||||||
|
const bool is_vk_memory_model_enabled) {
|
||||||
|
Status status = Status::SuccessWithoutChange;
|
||||||
|
for (Instruction& var : context()->types_values()) {
|
||||||
|
auto entry_function_ids =
|
||||||
|
EntryFunctionsToSpreadVolatileSemanticsForVar(var.result_id());
|
||||||
|
if (entry_function_ids.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_vk_memory_model_enabled) {
|
||||||
|
SetVolatileForLoadsInEntries(&var, entry_function_ids);
|
||||||
|
} else {
|
||||||
|
DecorateVarWithVolatile(&var);
|
||||||
|
}
|
||||||
|
status = Status::SuccessWithChange;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpreadVolatileSemantics::IsTargetUsedByNonVolatileLoadInEntryPoint(
|
||||||
|
uint32_t var_id, Instruction* entry_point) {
|
||||||
|
uint32_t entry_function_id =
|
||||||
|
entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
|
||||||
|
return !VisitLoadsOfPointersToVariableInEntries(
|
||||||
|
var_id,
|
||||||
|
[](Instruction* load) {
|
||||||
|
// If it has a load without volatile memory operand, finish traversal
|
||||||
|
// and return false.
|
||||||
|
if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t memory_operands =
|
||||||
|
load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
|
||||||
|
return (memory_operands & SpvMemoryAccessVolatileMask) != 0;
|
||||||
|
},
|
||||||
|
{entry_function_id});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpreadVolatileSemantics::HasInterfaceInConflictOfVolatileSemantics() {
|
||||||
|
for (Instruction& entry_point : get_module()->entry_points()) {
|
||||||
|
SpvExecutionModel execution_model =
|
||||||
|
static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
|
||||||
|
for (uint32_t operand_index = kOpEntryPointInOperandInterface;
|
||||||
|
operand_index < entry_point.NumInOperands(); ++operand_index) {
|
||||||
|
uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
|
||||||
|
if (!EntryFunctionsToSpreadVolatileSemanticsForVar(var_id).empty() &&
|
||||||
|
!IsTargetForVolatileSemantics(var_id, execution_model) &&
|
||||||
|
IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) {
|
||||||
|
Instruction* inst = context()->get_def_use_mgr()->GetDef(var_id);
|
||||||
|
context()->EmitErrorMessage(
|
||||||
|
"Variable is a target for Volatile semantics for an entry point, "
|
||||||
|
"but it is not for another entry point",
|
||||||
|
inst);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpreadVolatileSemantics::MarkVolatileSemanticsForVariable(
|
||||||
|
uint32_t var_id, Instruction* entry_point) {
|
||||||
|
uint32_t entry_function_id =
|
||||||
|
entry_point->GetSingleWordInOperand(kOpEntryPointInOperandEntryPoint);
|
||||||
|
auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id);
|
||||||
|
if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) {
|
||||||
|
var_ids_to_entry_fn_for_volatile_semantics_[var_id] = {entry_function_id};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
itr->second.insert(entry_function_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpreadVolatileSemantics::CollectTargetsForVolatileSemantics(
|
||||||
|
const bool is_vk_memory_model_enabled) {
|
||||||
|
for (Instruction& entry_point : get_module()->entry_points()) {
|
||||||
|
SpvExecutionModel execution_model =
|
||||||
|
static_cast<SpvExecutionModel>(entry_point.GetSingleWordInOperand(0));
|
||||||
|
for (uint32_t operand_index = kOpEntryPointInOperandInterface;
|
||||||
|
operand_index < entry_point.NumInOperands(); ++operand_index) {
|
||||||
|
uint32_t var_id = entry_point.GetSingleWordInOperand(operand_index);
|
||||||
|
if (!IsTargetForVolatileSemantics(var_id, execution_model)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is_vk_memory_model_enabled ||
|
||||||
|
IsTargetUsedByNonVolatileLoadInEntryPoint(var_id, &entry_point)) {
|
||||||
|
MarkVolatileSemanticsForVariable(var_id, &entry_point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpreadVolatileSemantics::DecorateVarWithVolatile(Instruction* var) {
|
||||||
|
analysis::DecorationManager* decoration_manager =
|
||||||
|
context()->get_decoration_mgr();
|
||||||
|
uint32_t var_id = var->result_id();
|
||||||
|
if (HasVolatileDecoration(decoration_manager, var_id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
get_decoration_mgr()->AddDecoration(
|
||||||
|
SpvOpDecorate, {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {var_id}},
|
||||||
|
{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER,
|
||||||
|
{SpvDecorationVolatile}}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpreadVolatileSemantics::VisitLoadsOfPointersToVariableInEntries(
|
||||||
|
uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
|
||||||
|
const std::unordered_set<uint32_t>& entry_function_ids) {
|
||||||
|
std::vector<uint32_t> worklist({var_id});
|
||||||
|
auto* def_use_mgr = context()->get_def_use_mgr();
|
||||||
|
while (!worklist.empty()) {
|
||||||
|
uint32_t ptr_id = worklist.back();
|
||||||
|
worklist.pop_back();
|
||||||
|
bool finish_traversal = !def_use_mgr->WhileEachUser(
|
||||||
|
ptr_id, [this, &worklist, &ptr_id, handle_load,
|
||||||
|
&entry_function_ids](Instruction* user) {
|
||||||
|
BasicBlock* block = context()->get_instr_block(user);
|
||||||
|
if (block == nullptr ||
|
||||||
|
entry_function_ids.find(block->GetParent()->result_id()) ==
|
||||||
|
entry_function_ids.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user->opcode() == SpvOpAccessChain ||
|
||||||
|
user->opcode() == SpvOpInBoundsAccessChain ||
|
||||||
|
user->opcode() == SpvOpPtrAccessChain ||
|
||||||
|
user->opcode() == SpvOpInBoundsPtrAccessChain ||
|
||||||
|
user->opcode() == SpvOpCopyObject) {
|
||||||
|
if (ptr_id == user->GetSingleWordInOperand(0))
|
||||||
|
worklist.push_back(user->result_id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user->opcode() != SpvOpLoad) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle_load(user);
|
||||||
|
});
|
||||||
|
if (finish_traversal) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpreadVolatileSemantics::SetVolatileForLoadsInEntries(
|
||||||
|
Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids) {
|
||||||
|
// Set Volatile memory operand for all load instructions if they do not have
|
||||||
|
// it.
|
||||||
|
VisitLoadsOfPointersToVariableInEntries(
|
||||||
|
var->result_id(),
|
||||||
|
[](Instruction* load) {
|
||||||
|
if (load->NumInOperands() <= kOpLoadInOperandMemoryOperands) {
|
||||||
|
load->AddOperand(
|
||||||
|
{SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessVolatileMask}});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint32_t memory_operands =
|
||||||
|
load->GetSingleWordInOperand(kOpLoadInOperandMemoryOperands);
|
||||||
|
memory_operands |= SpvMemoryAccessVolatileMask;
|
||||||
|
load->SetInOperand(kOpLoadInOperandMemoryOperands, {memory_operands});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
entry_function_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpreadVolatileSemantics::IsTargetForVolatileSemantics(
|
||||||
|
uint32_t var_id, SpvExecutionModel execution_model) {
|
||||||
|
analysis::DecorationManager* decoration_manager =
|
||||||
|
context()->get_decoration_mgr();
|
||||||
|
if (execution_model == SpvExecutionModelFragment) {
|
||||||
|
return get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 6) &&
|
||||||
|
HasBuiltinDecoration(decoration_manager, var_id,
|
||||||
|
SpvBuiltInHelperInvocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (execution_model == SpvExecutionModelIntersectionKHR ||
|
||||||
|
execution_model == SpvExecutionModelIntersectionNV) {
|
||||||
|
if (HasBuiltinDecoration(decoration_manager, var_id,
|
||||||
|
SpvBuiltInRayTmaxKHR)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (execution_model) {
|
||||||
|
case SpvExecutionModelRayGenerationKHR:
|
||||||
|
case SpvExecutionModelClosestHitKHR:
|
||||||
|
case SpvExecutionModelMissKHR:
|
||||||
|
case SpvExecutionModelCallableKHR:
|
||||||
|
case SpvExecutionModelIntersectionKHR:
|
||||||
|
return HasBuiltinForRayTracingVolatileSemantics(decoration_manager,
|
||||||
|
var_id);
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace opt
|
||||||
|
} // namespace spvtools
|
110
source/opt/spread_volatile_semantics.h
Normal file
110
source/opt/spread_volatile_semantics.h
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright (c) 2022 Google LLC
|
||||||
|
//
|
||||||
|
// 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_SPREAD_VOLATILE_SEMANTICS_H_
|
||||||
|
#define SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_
|
||||||
|
|
||||||
|
#include "source/opt/pass.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace opt {
|
||||||
|
|
||||||
|
// See optimizer.hpp for documentation.
|
||||||
|
class SpreadVolatileSemantics : public Pass {
|
||||||
|
public:
|
||||||
|
SpreadVolatileSemantics() {}
|
||||||
|
|
||||||
|
const char* name() const override { return "spread-volatile-semantics"; }
|
||||||
|
|
||||||
|
Status Process() override;
|
||||||
|
|
||||||
|
IRContext::Analysis GetPreservedAnalyses() override {
|
||||||
|
return IRContext::kAnalysisDefUse | IRContext::kAnalysisDecorations |
|
||||||
|
IRContext::kAnalysisInstrToBlockMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Iterates interface variables and spreads the Volatile semantics if it has
|
||||||
|
// load instructions for the Volatile semantics.
|
||||||
|
Pass::Status SpreadVolatileSemanticsToVariables(
|
||||||
|
const bool is_vk_memory_model_enabled);
|
||||||
|
|
||||||
|
// Returns whether |var_id| is the result id of a target builtin variable for
|
||||||
|
// the volatile semantics for |execution_model| based on the Vulkan spec
|
||||||
|
// VUID-StandaloneSpirv-VulkanMemoryModel-04678 or
|
||||||
|
// VUID-StandaloneSpirv-VulkanMemoryModel-04679.
|
||||||
|
bool IsTargetForVolatileSemantics(uint32_t var_id,
|
||||||
|
SpvExecutionModel execution_model);
|
||||||
|
|
||||||
|
// Collects interface variables that need the volatile semantics.
|
||||||
|
// |is_vk_memory_model_enabled| is true if VulkanMemoryModel capability is
|
||||||
|
// enabled.
|
||||||
|
void CollectTargetsForVolatileSemantics(
|
||||||
|
const bool is_vk_memory_model_enabled);
|
||||||
|
|
||||||
|
// Reports an error if an interface variable is used by two entry points and
|
||||||
|
// it needs the Volatile decoration for one but not for another. Returns true
|
||||||
|
// if the error must be reported.
|
||||||
|
bool HasInterfaceInConflictOfVolatileSemantics();
|
||||||
|
|
||||||
|
// Returns whether the variable whose result is |var_id| is used by a
|
||||||
|
// non-volatile load or a pointer to it is used by a non-volatile load in
|
||||||
|
// |entry_point| or not.
|
||||||
|
bool IsTargetUsedByNonVolatileLoadInEntryPoint(uint32_t var_id,
|
||||||
|
Instruction* entry_point);
|
||||||
|
|
||||||
|
// Visits load instructions of pointers to variable whose result id is
|
||||||
|
// |var_id| if the load instructions are in entry points whose
|
||||||
|
// function id is one of |entry_function_ids|. |handle_load| is a function to
|
||||||
|
// do some actions for the load instructions. Finishes the traversal and
|
||||||
|
// returns false if |handle_load| returns false for a load instruction.
|
||||||
|
// Otherwise, returns true after running |handle_load| for all the load
|
||||||
|
// instructions.
|
||||||
|
bool VisitLoadsOfPointersToVariableInEntries(
|
||||||
|
uint32_t var_id, const std::function<bool(Instruction*)>& handle_load,
|
||||||
|
const std::unordered_set<uint32_t>& entry_function_ids);
|
||||||
|
|
||||||
|
// Sets Memory Operands of OpLoad instructions that load |var| or pointers
|
||||||
|
// of |var| as Volatile if the function id of the OpLoad instruction is
|
||||||
|
// included in |entry_function_ids|.
|
||||||
|
void SetVolatileForLoadsInEntries(
|
||||||
|
Instruction* var, const std::unordered_set<uint32_t>& entry_function_ids);
|
||||||
|
|
||||||
|
// Adds OpDecorate Volatile for |var| if it does not exist.
|
||||||
|
void DecorateVarWithVolatile(Instruction* var);
|
||||||
|
|
||||||
|
// Returns a set of entry function ids to spread the volatile semantics for
|
||||||
|
// the variable with the result id |var_id|.
|
||||||
|
std::unordered_set<uint32_t> EntryFunctionsToSpreadVolatileSemanticsForVar(
|
||||||
|
uint32_t var_id) {
|
||||||
|
auto itr = var_ids_to_entry_fn_for_volatile_semantics_.find(var_id);
|
||||||
|
if (itr == var_ids_to_entry_fn_for_volatile_semantics_.end()) return {};
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specifies that we have to spread the volatile semantics for the
|
||||||
|
// variable with the result id |var_id| for the entry point |entry_point|.
|
||||||
|
void MarkVolatileSemanticsForVariable(uint32_t var_id,
|
||||||
|
Instruction* entry_point);
|
||||||
|
|
||||||
|
// Result ids of variables to entry function ids for the volatile semantics
|
||||||
|
// spread.
|
||||||
|
std::unordered_map<uint32_t, std::unordered_set<uint32_t>>
|
||||||
|
var_ids_to_entry_fn_for_volatile_semantics_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace opt
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_OPT_SPREAD_VOLATILE_SEMANTICS_H_
|
@ -91,6 +91,7 @@ add_spvtools_unittest(TARGET opt
|
|||||||
scalar_replacement_test.cpp
|
scalar_replacement_test.cpp
|
||||||
set_spec_const_default_value_test.cpp
|
set_spec_const_default_value_test.cpp
|
||||||
simplification_test.cpp
|
simplification_test.cpp
|
||||||
|
spread_volatile_semantics_test.cpp
|
||||||
strength_reduction_test.cpp
|
strength_reduction_test.cpp
|
||||||
strip_debug_info_test.cpp
|
strip_debug_info_test.cpp
|
||||||
strip_nonsemantic_info_test.cpp
|
strip_nonsemantic_info_test.cpp
|
||||||
|
1118
test/opt/spread_volatile_semantics_test.cpp
Normal file
1118
test/opt/spread_volatile_semantics_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -168,6 +168,16 @@ Options (in lexicographical order):)",
|
|||||||
with a switch that has a case for every possible value of the
|
with a switch that has a case for every possible value of the
|
||||||
index.)");
|
index.)");
|
||||||
printf(R"(
|
printf(R"(
|
||||||
|
--spread-volatile-semantics
|
||||||
|
Spread Volatile semantics to variables with SMIDNV, WarpIDNV,
|
||||||
|
SubgroupSize, SubgroupLocalInvocationId, SubgroupEqMask,
|
||||||
|
SubgroupGeMask, SubgroupGtMask, SubgroupLeMask, or SubgroupLtMask
|
||||||
|
BuiltIn decorations or OpLoad for them when the shader model is
|
||||||
|
ray generation, closest hit, miss, intersection, or callable.
|
||||||
|
For the SPIR-V version is 1.6 or above, it also spreads Volatile
|
||||||
|
semantics to a variable with HelperInvocation BuiltIn decoration
|
||||||
|
in the fragement shader.)");
|
||||||
|
printf(R"(
|
||||||
--descriptor-scalar-replacement
|
--descriptor-scalar-replacement
|
||||||
Replaces every array variable |desc| that has a DescriptorSet
|
Replaces every array variable |desc| that has a DescriptorSet
|
||||||
and Binding decorations with a new variable for each element of
|
and Binding decorations with a new variable for each element of
|
||||||
|
@ -43,7 +43,7 @@ AUTHORS = ['The Khronos Group Inc.',
|
|||||||
'ZHOU He']
|
'ZHOU He']
|
||||||
CURRENT_YEAR='2021'
|
CURRENT_YEAR='2021'
|
||||||
|
|
||||||
YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)'
|
YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021|2022)'
|
||||||
COPYRIGHT_RE = re.compile(
|
COPYRIGHT_RE = re.compile(
|
||||||
'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
|
'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS)))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user