Remove WebGPU support (#4108)
Leaves SPV_ENV_WEBGPU_0 enum in place, but marked deprecated, so users of the library are not broken by an API enum being removed. Fixes #4101
This commit is contained in:
Родитель
b2cfc5d1ce
Коммит
9150cd441f
|
@ -93,7 +93,6 @@ SPVTOOLS_OPT_SRC_FILES := \
|
|||
source/opt/dead_branch_elim_pass.cpp \
|
||||
source/opt/dead_insert_elim_pass.cpp \
|
||||
source/opt/dead_variable_elimination.cpp \
|
||||
source/opt/decompose_initialized_variables_pass.cpp \
|
||||
source/opt/decoration_manager.cpp \
|
||||
source/opt/debug_info_manager.cpp \
|
||||
source/opt/def_use_manager.cpp \
|
||||
|
@ -112,7 +111,6 @@ SPVTOOLS_OPT_SRC_FILES := \
|
|||
source/opt/fold_spec_constant_op_and_composite_pass.cpp \
|
||||
source/opt/freeze_spec_constant_value_pass.cpp \
|
||||
source/opt/function.cpp \
|
||||
source/opt/generate_webgpu_initializers_pass.cpp \
|
||||
source/opt/graphics_robust_access_pass.cpp \
|
||||
source/opt/if_conversion.cpp \
|
||||
source/opt/inline_pass.cpp \
|
||||
|
@ -126,7 +124,6 @@ SPVTOOLS_OPT_SRC_FILES := \
|
|||
source/opt/instrument_pass.cpp \
|
||||
source/opt/ir_context.cpp \
|
||||
source/opt/ir_loader.cpp \
|
||||
source/opt/legalize_vector_shuffle_pass.cpp \
|
||||
source/opt/licm_pass.cpp \
|
||||
source/opt/local_access_chain_convert_pass.cpp \
|
||||
source/opt/local_redundancy_elimination.cpp \
|
||||
|
@ -161,10 +158,8 @@ SPVTOOLS_OPT_SRC_FILES := \
|
|||
source/opt/scalar_replacement_pass.cpp \
|
||||
source/opt/set_spec_constant_default_value_pass.cpp \
|
||||
source/opt/simplification_pass.cpp \
|
||||
source/opt/split_invalid_unreachable_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 \
|
||||
|
|
10
BUILD.gn
10
BUILD.gn
|
@ -539,8 +539,6 @@ static_library("spvtools_opt") {
|
|||
"source/opt/dead_insert_elim_pass.h",
|
||||
"source/opt/dead_variable_elimination.cpp",
|
||||
"source/opt/dead_variable_elimination.h",
|
||||
"source/opt/decompose_initialized_variables_pass.cpp",
|
||||
"source/opt/decompose_initialized_variables_pass.h",
|
||||
"source/opt/decoration_manager.cpp",
|
||||
"source/opt/decoration_manager.h",
|
||||
"source/opt/debug_info_manager.cpp",
|
||||
|
@ -578,8 +576,6 @@ static_library("spvtools_opt") {
|
|||
"source/opt/freeze_spec_constant_value_pass.h",
|
||||
"source/opt/function.cpp",
|
||||
"source/opt/function.h",
|
||||
"source/opt/generate_webgpu_initializers_pass.cpp",
|
||||
"source/opt/generate_webgpu_initializers_pass.h",
|
||||
"source/opt/graphics_robust_access_pass.cpp",
|
||||
"source/opt/graphics_robust_access_pass.h",
|
||||
"source/opt/if_conversion.cpp",
|
||||
|
@ -608,8 +604,6 @@ static_library("spvtools_opt") {
|
|||
"source/opt/ir_loader.cpp",
|
||||
"source/opt/ir_loader.h",
|
||||
"source/opt/iterator.h",
|
||||
"source/opt/legalize_vector_shuffle_pass.cpp",
|
||||
"source/opt/legalize_vector_shuffle_pass.h",
|
||||
"source/opt/licm_pass.cpp",
|
||||
"source/opt/licm_pass.h",
|
||||
"source/opt/local_access_chain_convert_pass.cpp",
|
||||
|
@ -680,14 +674,10 @@ static_library("spvtools_opt") {
|
|||
"source/opt/set_spec_constant_default_value_pass.h",
|
||||
"source/opt/simplification_pass.cpp",
|
||||
"source/opt/simplification_pass.h",
|
||||
"source/opt/split_invalid_unreachable_pass.cpp",
|
||||
"source/opt/split_invalid_unreachable_pass.h",
|
||||
"source/opt/ssa_rewrite_pass.cpp",
|
||||
"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",
|
||||
|
|
|
@ -136,7 +136,6 @@ As of this writing, there are 67 transforms including examples such as:
|
|||
* Loop-invariant code motion
|
||||
* Loop unroll
|
||||
* Other
|
||||
* Generate WebGPU initializers
|
||||
* Graphics robust access
|
||||
* Upgrade memory model to VulkanKHR
|
||||
|
||||
|
|
|
@ -482,7 +482,7 @@ typedef enum {
|
|||
SPV_ENV_OPENCL_EMBEDDED_2_2, // OpenCL Embedded Profile 2.2 latest revision.
|
||||
SPV_ENV_UNIVERSAL_1_3, // SPIR-V 1.3 latest revision, no other restrictions.
|
||||
SPV_ENV_VULKAN_1_1, // Vulkan 1.1 latest revision.
|
||||
SPV_ENV_WEBGPU_0, // Work in progress WebGPU 1.0.
|
||||
SPV_ENV_WEBGPU_0, // DEPRECATED, may be removed in the future.
|
||||
SPV_ENV_UNIVERSAL_1_4, // SPIR-V 1.4 latest revision, no other restrictions.
|
||||
|
||||
// Vulkan 1.1 with VK_KHR_spirv_1_4, i.e. SPIR-V 1.4 binary.
|
||||
|
|
|
@ -68,11 +68,6 @@ class Optimizer {
|
|||
// The instance will have an empty message consumer, which ignores all
|
||||
// messages from the library. Use SetMessageConsumer() to supply a consumer
|
||||
// if messages are of concern.
|
||||
//
|
||||
// For collections of passes that are meant to transform the input into
|
||||
// another execution environment, then the source environment should be
|
||||
// supplied. e.g. for VulkanToWebGPUPasses the environment should be
|
||||
// SPV_ENV_VULKAN_1_1 not SPV_ENV_WEBGPU_0.
|
||||
explicit Optimizer(spv_target_env env);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
|
@ -106,16 +101,6 @@ class Optimizer {
|
|||
// from time to time.
|
||||
Optimizer& RegisterSizePasses();
|
||||
|
||||
// Registers passes that have been prescribed for converting from Vulkan to
|
||||
// WebGPU. This sequence of passes is subject to constant review and will
|
||||
// change from time to time.
|
||||
Optimizer& RegisterVulkanToWebGPUPasses();
|
||||
|
||||
// Registers passes that have been prescribed for converting from WebGPU to
|
||||
// Vulkan. This sequence of passes is subject to constant review and will
|
||||
// change from time to time.
|
||||
Optimizer& RegisterWebGPUToVulkanPasses();
|
||||
|
||||
// Registers passes that attempt to legalize the generated code.
|
||||
//
|
||||
// Note: this recipe is specially designed for legalizing SPIR-V. It should be
|
||||
|
@ -238,13 +223,6 @@ 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.
|
||||
|
@ -802,30 +780,11 @@ Optimizer::PassToken CreateUpgradeMemoryModelPass();
|
|||
// where an instruction is moved into a more deeply nested construct.
|
||||
Optimizer::PassToken CreateCodeSinkingPass();
|
||||
|
||||
// Create a pass to adds initializers for OpVariable calls that require them
|
||||
// in WebGPU. Currently this pass naively initializes variables that are
|
||||
// missing an initializer with a null value. In the future it may initialize
|
||||
// variables to the first value stored in them, if that is a constant.
|
||||
Optimizer::PassToken CreateGenerateWebGPUInitializersPass();
|
||||
|
||||
// Create a pass to fix incorrect storage classes. In order to make code
|
||||
// generation simpler, DXC may generate code where the storage classes do not
|
||||
// match up correctly. This pass will fix the errors that it can.
|
||||
Optimizer::PassToken CreateFixStorageClassPass();
|
||||
|
||||
// Create a pass to legalize OpVectorShuffle operands going into WebGPU. WebGPU
|
||||
// forbids using 0xFFFFFFFF, which indicates an undefined result, so this pass
|
||||
// converts those literals to 0.
|
||||
Optimizer::PassToken CreateLegalizeVectorShufflePass();
|
||||
|
||||
// Create a pass to decompose initialized variables into a seperate variable
|
||||
// declaration and an initial store.
|
||||
Optimizer::PassToken CreateDecomposeInitializedVariablesPass();
|
||||
|
||||
// Create a pass to attempt to split up invalid unreachable merge-blocks and
|
||||
// continue-targets to legalize for WebGPU.
|
||||
Optimizer::PassToken CreateSplitInvalidUnreachablePass();
|
||||
|
||||
// Creates a graphics robust access pass.
|
||||
//
|
||||
// This pass injects code to clamp indexed accesses to buffers and internal
|
||||
|
|
|
@ -89,7 +89,6 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable,
|
|||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_UNIVERSAL_1_5:
|
||||
case SPV_ENV_VULKAN_1_2:
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
@ -60,7 +60,9 @@ struct SpirvTools::Impl {
|
|||
spv_context context; // C interface context object.
|
||||
};
|
||||
|
||||
SpirvTools::SpirvTools(spv_target_env env) : impl_(new Impl(env)) {}
|
||||
SpirvTools::SpirvTools(spv_target_env env) : impl_(new Impl(env)) {
|
||||
assert(env != SPV_ENV_WEBGPU_0);
|
||||
}
|
||||
|
||||
SpirvTools::~SpirvTools() {}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||
dead_branch_elim_pass.h
|
||||
dead_insert_elim_pass.h
|
||||
dead_variable_elimination.h
|
||||
decompose_initialized_variables_pass.h
|
||||
decoration_manager.h
|
||||
debug_info_manager.h
|
||||
def_use_manager.h
|
||||
|
@ -52,7 +51,6 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||
fold_spec_constant_op_and_composite_pass.h
|
||||
freeze_spec_constant_value_pass.h
|
||||
function.h
|
||||
generate_webgpu_initializers_pass.h
|
||||
graphics_robust_access_pass.h
|
||||
if_conversion.h
|
||||
inline_exhaustive_pass.h
|
||||
|
@ -103,10 +101,8 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||
scalar_replacement_pass.h
|
||||
set_spec_constant_default_value_pass.h
|
||||
simplification_pass.h
|
||||
split_invalid_unreachable_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
|
||||
|
@ -140,7 +136,6 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||
dead_branch_elim_pass.cpp
|
||||
dead_insert_elim_pass.cpp
|
||||
dead_variable_elimination.cpp
|
||||
decompose_initialized_variables_pass.cpp
|
||||
decoration_manager.cpp
|
||||
debug_info_manager.cpp
|
||||
def_use_manager.cpp
|
||||
|
@ -160,7 +155,6 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||
freeze_spec_constant_value_pass.cpp
|
||||
function.cpp
|
||||
graphics_robust_access_pass.cpp
|
||||
generate_webgpu_initializers_pass.cpp
|
||||
if_conversion.cpp
|
||||
inline_exhaustive_pass.cpp
|
||||
inline_opaque_pass.cpp
|
||||
|
@ -173,7 +167,6 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||
instrument_pass.cpp
|
||||
ir_context.cpp
|
||||
ir_loader.cpp
|
||||
legalize_vector_shuffle_pass.cpp
|
||||
licm_pass.cpp
|
||||
local_access_chain_convert_pass.cpp
|
||||
local_redundancy_elimination.cpp
|
||||
|
@ -208,10 +201,8 @@ set(SPIRV_TOOLS_OPT_SOURCES
|
|||
scalar_replacement_pass.cpp
|
||||
set_spec_constant_default_value_pass.cpp
|
||||
simplification_pass.cpp
|
||||
split_invalid_unreachable_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
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
// Copyright (c) 2019 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/decompose_initialized_variables_pass.h"
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
using inst_iterator = InstructionList::iterator;
|
||||
|
||||
namespace {
|
||||
|
||||
bool HasInitializer(Instruction* inst) {
|
||||
if (inst->opcode() != SpvOpVariable) return false;
|
||||
if (inst->NumOperands() < 4) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Pass::Status DecomposeInitializedVariablesPass::Process() {
|
||||
auto* module = context()->module();
|
||||
std::unordered_set<Instruction*> changed;
|
||||
|
||||
std::vector<std::tuple<uint32_t, uint32_t>> global_stores;
|
||||
for (auto iter = module->types_values_begin();
|
||||
iter != module->types_values_end(); ++iter) {
|
||||
Instruction* inst = &(*iter);
|
||||
if (!HasInitializer(inst)) continue;
|
||||
|
||||
auto var_id = inst->result_id();
|
||||
auto val_id = inst->GetOperand(3).words[0];
|
||||
global_stores.push_back(std::make_tuple(var_id, val_id));
|
||||
iter->RemoveOperand(3);
|
||||
changed.insert(&*iter);
|
||||
}
|
||||
|
||||
std::unordered_set<uint32_t> entry_ids;
|
||||
for (auto entry = module->entry_points().begin();
|
||||
entry != module->entry_points().end(); ++entry) {
|
||||
entry_ids.insert(entry->GetSingleWordInOperand(1));
|
||||
}
|
||||
|
||||
for (auto func = module->begin(); func != module->end(); ++func) {
|
||||
std::vector<Instruction*> function_stores;
|
||||
auto first_block = func->entry().get();
|
||||
inst_iterator insert_point = first_block->begin();
|
||||
for (auto iter = first_block->begin();
|
||||
iter != first_block->end() && iter->opcode() == SpvOpVariable;
|
||||
++iter) {
|
||||
// For valid SPIRV-V, there is guaranteed to be at least one instruction
|
||||
// after the OpVariable instructions.
|
||||
insert_point = (*iter).NextNode();
|
||||
Instruction* inst = &(*iter);
|
||||
if (!HasInitializer(inst)) continue;
|
||||
|
||||
auto var_id = inst->result_id();
|
||||
auto val_id = inst->GetOperand(3).words[0];
|
||||
Instruction* store_inst = new Instruction(
|
||||
context(), SpvOpStore, 0, 0,
|
||||
{{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}});
|
||||
function_stores.push_back(store_inst);
|
||||
iter->RemoveOperand(3);
|
||||
changed.insert(&*iter);
|
||||
}
|
||||
|
||||
if (entry_ids.find(func->result_id()) != entry_ids.end()) {
|
||||
for (auto store_ids : global_stores) {
|
||||
uint32_t var_id;
|
||||
uint32_t val_id;
|
||||
std::tie(var_id, val_id) = store_ids;
|
||||
auto* store_inst = new Instruction(
|
||||
context(), SpvOpStore, 0, 0,
|
||||
{{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}});
|
||||
context()->set_instr_block(store_inst, &*first_block);
|
||||
first_block->AddInstruction(std::unique_ptr<Instruction>(store_inst));
|
||||
store_inst->InsertBefore(&*insert_point);
|
||||
changed.insert(store_inst);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto store = function_stores.begin(); store != function_stores.end();
|
||||
++store) {
|
||||
context()->set_instr_block(*store, first_block);
|
||||
(*store)->InsertBefore(&*insert_point);
|
||||
changed.insert(*store);
|
||||
}
|
||||
}
|
||||
|
||||
auto* def_use_mgr = get_def_use_mgr();
|
||||
for (auto* inst : changed) def_use_mgr->UpdateDefUse(inst);
|
||||
|
||||
return !changed.empty() ? Pass::Status::SuccessWithChange
|
||||
: Pass::Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) 2019 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_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_
|
||||
#define SOURCE_OPT_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "source/opt/pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// Converts variable declartions with initializers into seperate declaration and
|
||||
// assignment statements. This is done due to known issues with some Vulkan
|
||||
// implementations' handling of initialized variables.
|
||||
//
|
||||
// Only decomposes variables with storage classes that are valid in Vulkan
|
||||
// execution environments; Output, Private, and Function.
|
||||
// Currently only Function is implemented.
|
||||
class DecomposeInitializedVariablesPass : public Pass {
|
||||
public:
|
||||
const char* name() const override {
|
||||
return "decompose-initialized-variables";
|
||||
}
|
||||
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_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_
|
|
@ -1,116 +0,0 @@
|
|||
// Copyright (c) 2019 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/generate_webgpu_initializers_pass.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
using inst_iterator = InstructionList::iterator;
|
||||
|
||||
namespace {
|
||||
|
||||
bool NeedsWebGPUInitializer(Instruction* inst) {
|
||||
if (inst->opcode() != SpvOpVariable) return false;
|
||||
|
||||
auto storage_class = inst->GetSingleWordOperand(2);
|
||||
if (storage_class != SpvStorageClassOutput &&
|
||||
storage_class != SpvStorageClassPrivate &&
|
||||
storage_class != SpvStorageClassFunction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inst->NumOperands() > 3) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Pass::Status GenerateWebGPUInitializersPass::Process() {
|
||||
auto* module = context()->module();
|
||||
bool changed = false;
|
||||
|
||||
// Handle global/module scoped variables
|
||||
for (auto iter = module->types_values_begin();
|
||||
iter != module->types_values_end(); ++iter) {
|
||||
Instruction* inst = &(*iter);
|
||||
|
||||
if (inst->opcode() == SpvOpConstantNull) {
|
||||
null_constant_type_map_[inst->type_id()] = inst;
|
||||
seen_null_constants_.insert(inst);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!NeedsWebGPUInitializer(inst)) continue;
|
||||
|
||||
changed = true;
|
||||
|
||||
auto* constant_inst = GetNullConstantForVariable(inst);
|
||||
if (!constant_inst) return Status::Failure;
|
||||
|
||||
if (seen_null_constants_.find(constant_inst) ==
|
||||
seen_null_constants_.end()) {
|
||||
constant_inst->InsertBefore(inst);
|
||||
null_constant_type_map_[inst->type_id()] = inst;
|
||||
seen_null_constants_.insert(inst);
|
||||
}
|
||||
AddNullInitializerToVariable(constant_inst, inst);
|
||||
}
|
||||
|
||||
// Handle local/function scoped variables
|
||||
for (auto func = module->begin(); func != module->end(); ++func) {
|
||||
auto block = func->entry().get();
|
||||
for (auto iter = block->begin();
|
||||
iter != block->end() && iter->opcode() == SpvOpVariable; ++iter) {
|
||||
Instruction* inst = &(*iter);
|
||||
if (!NeedsWebGPUInitializer(inst)) continue;
|
||||
|
||||
changed = true;
|
||||
auto* constant_inst = GetNullConstantForVariable(inst);
|
||||
if (!constant_inst) return Status::Failure;
|
||||
|
||||
AddNullInitializerToVariable(constant_inst, inst);
|
||||
}
|
||||
}
|
||||
|
||||
return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
Instruction* GenerateWebGPUInitializersPass::GetNullConstantForVariable(
|
||||
Instruction* variable_inst) {
|
||||
auto constant_mgr = context()->get_constant_mgr();
|
||||
auto* def_use_mgr = get_def_use_mgr();
|
||||
|
||||
auto* ptr_inst = def_use_mgr->GetDef(variable_inst->type_id());
|
||||
auto type_id = ptr_inst->GetInOperand(1).words[0];
|
||||
if (null_constant_type_map_.find(type_id) == null_constant_type_map_.end()) {
|
||||
auto* constant_type = context()->get_type_mgr()->GetType(type_id);
|
||||
auto* constant = constant_mgr->GetConstant(constant_type, {});
|
||||
return constant_mgr->GetDefiningInstruction(constant, type_id);
|
||||
} else {
|
||||
return null_constant_type_map_[type_id];
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateWebGPUInitializersPass::AddNullInitializerToVariable(
|
||||
Instruction* constant_inst, Instruction* variable_inst) {
|
||||
auto constant_id = constant_inst->result_id();
|
||||
variable_inst->AddOperand(Operand(SPV_OPERAND_TYPE_ID, {constant_id}));
|
||||
get_def_use_mgr()->AnalyzeInstUse(variable_inst);
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) 2019 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_GENERATE_WEBGPU_INITIALIZERS_PASS_H_
|
||||
#define SOURCE_OPT_GENERATE_WEBGPU_INITIALIZERS_PASS_H_
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "source/opt/pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// Adds initializers to variables with storage classes Output, Private, and
|
||||
// Function if they are missing. In the WebGPU environment these storage classes
|
||||
// require that the variables are initialized. Currently they are initialized to
|
||||
// NULL, though in the future some of them may be initialized to the first value
|
||||
// that is stored in them, if that was a constant.
|
||||
class GenerateWebGPUInitializersPass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "generate-webgpu-initializers"; }
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
using NullConstantTypeMap = std::unordered_map<uint32_t, Instruction*>;
|
||||
NullConstantTypeMap null_constant_type_map_;
|
||||
std::unordered_set<Instruction*> seen_null_constants_;
|
||||
|
||||
Instruction* GetNullConstantForVariable(Instruction* variable_inst);
|
||||
void AddNullInitializerToVariable(Instruction* constant_inst,
|
||||
Instruction* variable_inst);
|
||||
};
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_OPT_GENERATE_WEBGPU_INITIALIZERS_PASS_H_
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) 2019 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/legalize_vector_shuffle_pass.h"
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status LegalizeVectorShufflePass::Process() {
|
||||
bool changed = false;
|
||||
context()->module()->ForEachInst([&changed](Instruction* inst) {
|
||||
if (inst->opcode() != SpvOpVectorShuffle) return;
|
||||
|
||||
for (uint32_t idx = 2; idx < inst->NumInOperands(); ++idx) {
|
||||
auto literal = inst->GetSingleWordInOperand(idx);
|
||||
if (literal != 0xFFFFFFFF) continue;
|
||||
changed = true;
|
||||
inst->SetInOperand(idx, {0});
|
||||
}
|
||||
});
|
||||
|
||||
return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
|
@ -1,53 +0,0 @@
|
|||
// Copyright (c) 2019 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_LEGALIZE_VECTOR_SHUFFLE_PASS_H_
|
||||
#define SOURCE_OPT_LEGALIZE_VECTOR_SHUFFLE_PASS_H_
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "source/opt/pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// Converts any usages of 0xFFFFFFFF for the literals in OpVectorShuffle to a
|
||||
// literal 0. This is needed because using OxFFFFFFFF is forbidden by the WebGPU
|
||||
// spec. 0xFFFFFFFF in the main spec indicates that the result for this
|
||||
// component has no source, thus is undefined. Since this is undefined
|
||||
// behaviour we are free to use 0.
|
||||
class LegalizeVectorShufflePass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "legalize-vector-shuffle"; }
|
||||
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_LEGALIZE_VECTOR_SHUFFLE_PASS_H_
|
|
@ -62,7 +62,9 @@ struct Optimizer::Impl {
|
|||
opt::PassManager pass_manager; // Internal implementation pass manager.
|
||||
};
|
||||
|
||||
Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {}
|
||||
Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {
|
||||
assert(env != SPV_ENV_WEBGPU_0);
|
||||
}
|
||||
|
||||
Optimizer::~Optimizer() {}
|
||||
|
||||
|
@ -239,23 +241,6 @@ Optimizer& Optimizer::RegisterSizePasses() {
|
|||
.RegisterPass(CreateCFGCleanupPass());
|
||||
}
|
||||
|
||||
Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
|
||||
return RegisterPass(CreateStripAtomicCounterMemoryPass())
|
||||
.RegisterPass(CreateGenerateWebGPUInitializersPass())
|
||||
.RegisterPass(CreateLegalizeVectorShufflePass())
|
||||
.RegisterPass(CreateSplitInvalidUnreachablePass())
|
||||
.RegisterPass(CreateEliminateDeadConstantPass())
|
||||
.RegisterPass(CreateFlattenDecorationPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateDeadBranchElimPass())
|
||||
.RegisterPass(CreateCompactIdsPass());
|
||||
}
|
||||
|
||||
Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() {
|
||||
return RegisterPass(CreateDecomposeInitializedVariablesPass())
|
||||
.RegisterPass(CreateCompactIdsPass());
|
||||
}
|
||||
|
||||
bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
|
||||
for (const auto& flag : flags) {
|
||||
if (!RegisterPassFromFlag(flag)) {
|
||||
|
@ -298,9 +283,7 @@ 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-atomic-counter-memory") {
|
||||
RegisterPass(CreateStripAtomicCounterMemoryPass());
|
||||
} else if (pass_name == "strip-debug") {
|
||||
if (pass_name == "strip-debug") {
|
||||
RegisterPass(CreateStripDebugInfoPass());
|
||||
} else if (pass_name == "strip-reflect") {
|
||||
RegisterPass(CreateStripReflectInfoPass());
|
||||
|
@ -505,14 +488,6 @@ bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
|
|||
RegisterSizePasses();
|
||||
} else if (pass_name == "legalize-hlsl") {
|
||||
RegisterLegalizationPasses();
|
||||
} else if (pass_name == "generate-webgpu-initializers") {
|
||||
RegisterPass(CreateGenerateWebGPUInitializersPass());
|
||||
} else if (pass_name == "legalize-vector-shuffle") {
|
||||
RegisterPass(CreateLegalizeVectorShufflePass());
|
||||
} else if (pass_name == "split-invalid-unreachable") {
|
||||
RegisterPass(CreateSplitInvalidUnreachablePass());
|
||||
} else if (pass_name == "decompose-initialized-variables") {
|
||||
RegisterPass(CreateDecomposeInitializedVariablesPass());
|
||||
} else if (pass_name == "graphics-robust-access") {
|
||||
RegisterPass(CreateGraphicsRobustAccessPass());
|
||||
} else if (pass_name == "wrap-opkill") {
|
||||
|
@ -629,11 +604,6 @@ 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>());
|
||||
|
@ -931,31 +901,11 @@ Optimizer::PassToken CreateCodeSinkingPass() {
|
|||
MakeUnique<opt::CodeSinkingPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateGenerateWebGPUInitializersPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::GenerateWebGPUInitializersPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateFixStorageClassPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::FixStorageClass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateLegalizeVectorShufflePass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::LegalizeVectorShufflePass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateDecomposeInitializedVariablesPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::DecomposeInitializedVariablesPass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateSplitInvalidUnreachablePass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::SplitInvalidUnreachablePass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateGraphicsRobustAccessPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::GraphicsRobustAccessPass>());
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
#include "source/opt/dead_branch_elim_pass.h"
|
||||
#include "source/opt/dead_insert_elim_pass.h"
|
||||
#include "source/opt/dead_variable_elimination.h"
|
||||
#include "source/opt/decompose_initialized_variables_pass.h"
|
||||
#include "source/opt/desc_sroa.h"
|
||||
#include "source/opt/eliminate_dead_constant_pass.h"
|
||||
#include "source/opt/eliminate_dead_functions_pass.h"
|
||||
|
@ -40,7 +39,6 @@
|
|||
#include "source/opt/flatten_decoration_pass.h"
|
||||
#include "source/opt/fold_spec_constant_op_and_composite_pass.h"
|
||||
#include "source/opt/freeze_spec_constant_value_pass.h"
|
||||
#include "source/opt/generate_webgpu_initializers_pass.h"
|
||||
#include "source/opt/graphics_robust_access_pass.h"
|
||||
#include "source/opt/if_conversion.h"
|
||||
#include "source/opt/inline_exhaustive_pass.h"
|
||||
|
@ -48,7 +46,6 @@
|
|||
#include "source/opt/inst_bindless_check_pass.h"
|
||||
#include "source/opt/inst_buff_addr_check_pass.h"
|
||||
#include "source/opt/inst_debug_printf_pass.h"
|
||||
#include "source/opt/legalize_vector_shuffle_pass.h"
|
||||
#include "source/opt/licm_pass.h"
|
||||
#include "source/opt/local_access_chain_convert_pass.h"
|
||||
#include "source/opt/local_redundancy_elimination.h"
|
||||
|
@ -70,10 +67,8 @@
|
|||
#include "source/opt/scalar_replacement_pass.h"
|
||||
#include "source/opt/set_spec_constant_default_value_pass.h"
|
||||
#include "source/opt/simplification_pass.h"
|
||||
#include "source/opt/split_invalid_unreachable_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"
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
// Copyright (c) 2019 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/split_invalid_unreachable_pass.h"
|
||||
|
||||
#include "source/opt/ir_builder.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
Pass::Status SplitInvalidUnreachablePass::Process() {
|
||||
bool changed = false;
|
||||
std::unordered_set<uint32_t> entry_points;
|
||||
for (auto entry_point : context()->module()->entry_points()) {
|
||||
entry_points.insert(entry_point.GetSingleWordOperand(1));
|
||||
}
|
||||
|
||||
for (auto func = context()->module()->begin();
|
||||
func != context()->module()->end(); ++func) {
|
||||
if (entry_points.find(func->result_id()) == entry_points.end()) continue;
|
||||
std::unordered_set<uint32_t> continue_targets;
|
||||
std::unordered_set<uint32_t> merge_blocks;
|
||||
std::unordered_set<BasicBlock*> unreachable_blocks;
|
||||
for (auto block = func->begin(); block != func->end(); ++block) {
|
||||
unreachable_blocks.insert(&*block);
|
||||
uint32_t continue_target = block->ContinueBlockIdIfAny();
|
||||
if (continue_target != 0) continue_targets.insert(continue_target);
|
||||
uint32_t merge_block = block->MergeBlockIdIfAny();
|
||||
if (merge_block != 0) merge_blocks.insert(merge_block);
|
||||
}
|
||||
|
||||
cfg()->ForEachBlockInPostOrder(
|
||||
func->entry().get(), [&unreachable_blocks](BasicBlock* inner_block) {
|
||||
unreachable_blocks.erase(inner_block);
|
||||
});
|
||||
|
||||
for (auto unreachable : unreachable_blocks) {
|
||||
uint32_t block_id = unreachable->id();
|
||||
if (continue_targets.find(block_id) == continue_targets.end() ||
|
||||
merge_blocks.find(block_id) == merge_blocks.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::tuple<Instruction*, uint32_t>> usages;
|
||||
context()->get_def_use_mgr()->ForEachUse(
|
||||
unreachable->GetLabelInst(),
|
||||
[&usages](Instruction* use, uint32_t idx) {
|
||||
if ((use->opcode() == SpvOpLoopMerge && idx == 0) ||
|
||||
use->opcode() == SpvOpSelectionMerge) {
|
||||
usages.push_back(std::make_pair(use, idx));
|
||||
}
|
||||
});
|
||||
|
||||
for (auto usage : usages) {
|
||||
Instruction* use;
|
||||
uint32_t idx;
|
||||
std::tie(use, idx) = usage;
|
||||
uint32_t new_id = context()->TakeNextId();
|
||||
std::unique_ptr<Instruction> new_label(
|
||||
new Instruction(context(), SpvOpLabel, 0, new_id, {}));
|
||||
get_def_use_mgr()->AnalyzeInstDefUse(new_label.get());
|
||||
std::unique_ptr<BasicBlock> new_block(
|
||||
new BasicBlock(std::move(new_label)));
|
||||
auto* block_ptr = new_block.get();
|
||||
InstructionBuilder builder(context(), new_block.get(),
|
||||
IRContext::kAnalysisDefUse |
|
||||
IRContext::kAnalysisInstrToBlockMapping);
|
||||
builder.AddUnreachable();
|
||||
cfg()->RegisterBlock(block_ptr);
|
||||
(&*func)->InsertBasicBlockBefore(std::move(new_block), unreachable);
|
||||
use->SetInOperand(0, {new_id});
|
||||
get_def_use_mgr()->UpdateDefUse(use);
|
||||
cfg()->AddEdges(block_ptr);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright (c) 2019 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_SPLIT_INVALID_UNREACHABLE_PASS_H_
|
||||
#define SOURCE_OPT_SPLIT_INVALID_UNREACHABLE_PASS_H_
|
||||
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "source/opt/pass.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
// Attempts to legalize for WebGPU by splitting up invalid unreachable blocks.
|
||||
// Specifically, looking for cases of unreachable merge-blocks and
|
||||
// continue-targets that are used more then once, which is illegal in WebGPU.
|
||||
class SplitInvalidUnreachablePass : public Pass {
|
||||
public:
|
||||
const char* name() const override { return "split-invalid-unreachable"; }
|
||||
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_SPLIT_INVALID_UNREACHABLE_PASS_H_
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright (c) 2019 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/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
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright (c) 2019 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_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_
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "source/spirv_target_env.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
|
@ -61,7 +62,8 @@ const char* spvTargetEnvDescription(spv_target_env env) {
|
|||
case SPV_ENV_VULKAN_1_1:
|
||||
return "SPIR-V 1.3 (under Vulkan 1.1 semantics)";
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return "SPIR-V 1.3 (under WIP WebGPU semantics)";
|
||||
assert(false);
|
||||
break;
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
return "SPIR-V 1.4";
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
|
@ -98,8 +100,10 @@ uint32_t spvVersionForTargetEnv(spv_target_env env) {
|
|||
return SPV_SPIRV_VERSION_WORD(1, 2);
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return SPV_SPIRV_VERSION_WORD(1, 3);
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
break;
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
return SPV_SPIRV_VERSION_WORD(1, 4);
|
||||
|
@ -134,7 +138,6 @@ static const std::pair<const char*, spv_target_env> spvTargetEnvNameMap[] = {
|
|||
{"opengl4.2", SPV_ENV_OPENGL_4_2},
|
||||
{"opengl4.3", SPV_ENV_OPENGL_4_3},
|
||||
{"opengl4.5", SPV_ENV_OPENGL_4_5},
|
||||
{"webgpu0", SPV_ENV_WEBGPU_0},
|
||||
};
|
||||
|
||||
bool spvParseTargetEnv(const char* s, spv_target_env* env) {
|
||||
|
@ -200,7 +203,6 @@ bool spvIsVulkanEnv(spv_target_env env) {
|
|||
case SPV_ENV_OPENCL_2_2:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_UNIVERSAL_1_5:
|
||||
return false;
|
||||
|
@ -209,6 +211,9 @@ bool spvIsVulkanEnv(spv_target_env env) {
|
|||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_VULKAN_1_2:
|
||||
return true;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -226,7 +231,6 @@ bool spvIsOpenCLEnv(spv_target_env env) {
|
|||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_UNIVERSAL_1_5:
|
||||
|
@ -241,38 +245,9 @@ bool spvIsOpenCLEnv(spv_target_env env) {
|
|||
case SPV_ENV_OPENCL_2_1:
|
||||
case SPV_ENV_OPENCL_2_2:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool spvIsWebGPUEnv(spv_target_env env) {
|
||||
switch (env) {
|
||||
case SPV_ENV_UNIVERSAL_1_0:
|
||||
case SPV_ENV_VULKAN_1_0:
|
||||
case SPV_ENV_UNIVERSAL_1_1:
|
||||
case SPV_ENV_OPENGL_4_0:
|
||||
case SPV_ENV_OPENGL_4_1:
|
||||
case SPV_ENV_OPENGL_4_2:
|
||||
case SPV_ENV_OPENGL_4_3:
|
||||
case SPV_ENV_OPENGL_4_5:
|
||||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_OPENCL_1_2:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_1_2:
|
||||
case SPV_ENV_OPENCL_2_0:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_0:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_1:
|
||||
case SPV_ENV_OPENCL_EMBEDDED_2_2:
|
||||
case SPV_ENV_OPENCL_2_1:
|
||||
case SPV_ENV_OPENCL_2_2:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_UNIVERSAL_1_5:
|
||||
case SPV_ENV_VULKAN_1_2:
|
||||
return false;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return true;
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -293,7 +268,6 @@ bool spvIsOpenGLEnv(spv_target_env env) {
|
|||
case SPV_ENV_OPENCL_EMBEDDED_2_2:
|
||||
case SPV_ENV_OPENCL_2_1:
|
||||
case SPV_ENV_OPENCL_2_2:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_UNIVERSAL_1_5:
|
||||
|
@ -305,14 +279,13 @@ bool spvIsOpenGLEnv(spv_target_env env) {
|
|||
case SPV_ENV_OPENGL_4_3:
|
||||
case SPV_ENV_OPENGL_4_5:
|
||||
return true;
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool spvIsVulkanOrWebGPUEnv(spv_target_env env) {
|
||||
return spvIsVulkanEnv(env) || spvIsWebGPUEnv(env);
|
||||
}
|
||||
|
||||
std::string spvLogStringForEnv(spv_target_env env) {
|
||||
switch (env) {
|
||||
case SPV_ENV_OPENCL_1_2:
|
||||
|
@ -338,9 +311,6 @@ std::string spvLogStringForEnv(spv_target_env env) {
|
|||
case SPV_ENV_VULKAN_1_2:
|
||||
return "Vulkan";
|
||||
}
|
||||
case SPV_ENV_WEBGPU_0: {
|
||||
return "WebGPU";
|
||||
}
|
||||
case SPV_ENV_UNIVERSAL_1_0:
|
||||
case SPV_ENV_UNIVERSAL_1_1:
|
||||
case SPV_ENV_UNIVERSAL_1_2:
|
||||
|
@ -349,6 +319,9 @@ std::string spvLogStringForEnv(spv_target_env env) {
|
|||
case SPV_ENV_UNIVERSAL_1_5: {
|
||||
return "Universal";
|
||||
}
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
|
|
@ -25,20 +25,14 @@ bool spvIsVulkanEnv(spv_target_env env);
|
|||
// Returns true if |env| is an OPENCL environment, false otherwise.
|
||||
bool spvIsOpenCLEnv(spv_target_env env);
|
||||
|
||||
// Returns true if |env| is an WEBGPU environment, false otherwise.
|
||||
bool spvIsWebGPUEnv(spv_target_env env);
|
||||
|
||||
// Returns true if |env| is an OPENGL environment, false otherwise.
|
||||
bool spvIsOpenGLEnv(spv_target_env env);
|
||||
|
||||
// Returns true if |env| is a VULKAN or WEBGPU environment, false otherwise.
|
||||
bool spvIsVulkanOrWebGPUEnv(spv_target_env env);
|
||||
|
||||
// Returns the version number for the given SPIR-V target environment.
|
||||
uint32_t spvVersionForTargetEnv(spv_target_env env);
|
||||
|
||||
// Returns a string to use in logging messages that indicates the class of
|
||||
// environment, i.e. "Vulkan", "WebGPU", "OpenCL", etc.
|
||||
// environment, i.e. "Vulkan", "OpenCL", etc.
|
||||
std::string spvLogStringForEnv(spv_target_env env);
|
||||
|
||||
// Returns a formatted list of all SPIR-V target environment names that
|
||||
|
|
|
@ -38,7 +38,6 @@ spv_context spvContextCreate(spv_target_env env) {
|
|||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_UNIVERSAL_1_5:
|
||||
case SPV_ENV_VULKAN_1_2:
|
||||
|
|
|
@ -111,57 +111,6 @@ spv_result_t ValidateForwardDecls(ValidationState_t& _) {
|
|||
<< id_str.substr(0, id_str.size() - 1);
|
||||
}
|
||||
|
||||
std::vector<std::string> CalculateNamesForEntryPoint(ValidationState_t& _,
|
||||
const uint32_t id) {
|
||||
auto id_descriptions = _.entry_point_descriptions(id);
|
||||
auto id_names = std::vector<std::string>();
|
||||
id_names.reserve((id_descriptions.size()));
|
||||
|
||||
for (auto description : id_descriptions) id_names.push_back(description.name);
|
||||
|
||||
return id_names;
|
||||
}
|
||||
|
||||
spv_result_t ValidateEntryPointNameUnique(ValidationState_t& _,
|
||||
const uint32_t id) {
|
||||
auto id_names = CalculateNamesForEntryPoint(_, id);
|
||||
const auto names =
|
||||
std::unordered_set<std::string>(id_names.begin(), id_names.end());
|
||||
|
||||
if (id_names.size() != names.size()) {
|
||||
std::sort(id_names.begin(), id_names.end());
|
||||
for (size_t i = 0; i < id_names.size() - 1; i++) {
|
||||
if (id_names[i] == id_names[i + 1]) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id))
|
||||
<< "Entry point name \"" << id_names[i]
|
||||
<< "\" is not unique, which is not allow in WebGPU env.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto other_id : _.entry_points()) {
|
||||
if (other_id == id) continue;
|
||||
const auto other_id_names = CalculateNamesForEntryPoint(_, other_id);
|
||||
for (const auto& other_id_name : other_id_names) {
|
||||
if (names.find(other_id_name) != names.end()) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id))
|
||||
<< "Entry point name \"" << other_id_name
|
||||
<< "\" is not unique, which is not allow in WebGPU env.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateEntryPointNamesUnique(ValidationState_t& _) {
|
||||
for (const auto id : _.entry_points()) {
|
||||
auto result = ValidateEntryPointNameUnique(_, id);
|
||||
if (result != SPV_SUCCESS) return result;
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Entry point validation. Based on 2.16.1 (Universal Validation Rules) of the
|
||||
// SPIRV spec:
|
||||
// * There is at least one OpEntryPoint instruction, unless the Linkage
|
||||
|
@ -169,8 +118,7 @@ spv_result_t ValidateEntryPointNamesUnique(ValidationState_t& _) {
|
|||
// * No function can be targeted by both an OpEntryPoint instruction and an
|
||||
// OpFunctionCall instruction.
|
||||
//
|
||||
// Additionally enforces that entry points for Vulkan and WebGPU should not have
|
||||
// recursion. And that entry names should be unique for WebGPU.
|
||||
// Additionally enforces that entry points for Vulkan should not have recursion.
|
||||
spv_result_t ValidateEntryPoints(ValidationState_t& _) {
|
||||
_.ComputeFunctionToEntryPointMapping();
|
||||
_.ComputeRecursiveEntryPoints();
|
||||
|
@ -189,21 +137,15 @@ spv_result_t ValidateEntryPoints(ValidationState_t& _) {
|
|||
"an OpFunctionCall instruction.";
|
||||
}
|
||||
|
||||
// For Vulkan and WebGPU, the static function-call graph for an entry point
|
||||
// For Vulkan, the static function-call graph for an entry point
|
||||
// must not contain cycles.
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (_.recursive_entry_points().find(entry_point) !=
|
||||
_.recursive_entry_points().end()) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(entry_point))
|
||||
<< "Entry points may not have a call graph with cycles.";
|
||||
}
|
||||
}
|
||||
|
||||
// For WebGPU all entry point names must be unique.
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
const auto result = ValidateEntryPointNamesUnique(_);
|
||||
if (result != SPV_SUCCESS) return result;
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
|
@ -223,12 +165,6 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
|||
<< "Invalid SPIR-V magic number.";
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(context.target_env) && endian != SPV_ENDIANNESS_LITTLE) {
|
||||
return DiagnosticStream(position, context.consumer, "",
|
||||
SPV_ERROR_INVALID_BINARY)
|
||||
<< "WebGPU requires SPIR-V to be little endian.";
|
||||
}
|
||||
|
||||
spv_header_t header;
|
||||
if (spvBinaryHeaderGet(binary.get(), endian, &header)) {
|
||||
return DiagnosticStream(position, context.consumer, "",
|
||||
|
@ -321,13 +257,6 @@ spv_result_t ValidateBinaryUsingContextAndValidationState(
|
|||
}
|
||||
|
||||
const auto called_id = inst->GetOperandAs<uint32_t>(2);
|
||||
if (spvIsWebGPUEnv(context.target_env) &&
|
||||
!vstate->IsFunctionCallDefined(called_id)) {
|
||||
return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction)
|
||||
<< "For WebGPU, functions need to be defined before being "
|
||||
"called.";
|
||||
}
|
||||
|
||||
vstate->AddFunctionCallTarget(called_id);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,36 +22,6 @@ namespace spvtools {
|
|||
namespace val {
|
||||
namespace {
|
||||
|
||||
bool IsValidWebGPUDecoration(uint32_t decoration) {
|
||||
switch (decoration) {
|
||||
case SpvDecorationSpecId:
|
||||
case SpvDecorationBlock:
|
||||
case SpvDecorationRowMajor:
|
||||
case SpvDecorationColMajor:
|
||||
case SpvDecorationArrayStride:
|
||||
case SpvDecorationMatrixStride:
|
||||
case SpvDecorationBuiltIn:
|
||||
case SpvDecorationNoPerspective:
|
||||
case SpvDecorationFlat:
|
||||
case SpvDecorationCentroid:
|
||||
case SpvDecorationRestrict:
|
||||
case SpvDecorationAliased:
|
||||
case SpvDecorationNonWritable:
|
||||
case SpvDecorationNonReadable:
|
||||
case SpvDecorationUniform:
|
||||
case SpvDecorationLocation:
|
||||
case SpvDecorationComponent:
|
||||
case SpvDecorationIndex:
|
||||
case SpvDecorationBinding:
|
||||
case SpvDecorationDescriptorSet:
|
||||
case SpvDecorationOffset:
|
||||
case SpvDecorationNoContraction:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string LogStringForDecoration(uint32_t decoration) {
|
||||
switch (decoration) {
|
||||
case SpvDecorationRelaxedPrecision:
|
||||
|
@ -212,13 +182,6 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
|
|||
}
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(_.context()->target_env) &&
|
||||
!IsValidWebGPUDecoration(decoration)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpDecorate decoration '" << LogStringForDecoration(decoration)
|
||||
<< "' is not valid for the WebGPU execution environment.";
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if ((decoration == SpvDecorationGLSLShared) ||
|
||||
(decoration == SpvDecorationGLSLPacked)) {
|
||||
|
@ -271,25 +234,11 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _,
|
|||
<< " members. Largest valid index is " << member_count - 1 << ".";
|
||||
}
|
||||
|
||||
const auto decoration = inst->GetOperandAs<uint32_t>(2);
|
||||
if (spvIsWebGPUEnv(_.context()->target_env) &&
|
||||
!IsValidWebGPUDecoration(decoration)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpMemberDecorate decoration '" << _.getIdName(decoration)
|
||||
<< "' is not valid for the WebGPU execution environment.";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateDecorationGroup(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, inst)
|
||||
<< "OpDecorationGroup is not allowed in the WebGPU execution "
|
||||
<< "environment.";
|
||||
}
|
||||
|
||||
const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
|
||||
const auto decoration_group = _.FindDef(decoration_group_id);
|
||||
for (auto pair : decoration_group->uses()) {
|
||||
|
@ -309,12 +258,6 @@ spv_result_t ValidateDecorationGroup(ValidationState_t& _,
|
|||
|
||||
spv_result_t ValidateGroupDecorate(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, inst)
|
||||
<< "OpGroupDecorate is not allowed in the WebGPU execution "
|
||||
<< "environment.";
|
||||
}
|
||||
|
||||
const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
|
||||
auto decoration_group = _.FindDef(decoration_group_id);
|
||||
if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) {
|
||||
|
@ -337,12 +280,6 @@ spv_result_t ValidateGroupDecorate(ValidationState_t& _,
|
|||
|
||||
spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, inst)
|
||||
<< "OpGroupMemberDecorate is not allowed in the WebGPU execution "
|
||||
<< "environment.";
|
||||
}
|
||||
|
||||
const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
|
||||
const auto decoration_group = _.FindDef(decoration_group_id);
|
||||
if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) {
|
||||
|
|
|
@ -113,28 +113,6 @@ SpvStorageClass GetStorageClass(const Instruction& inst) {
|
|||
return SpvStorageClassMax;
|
||||
}
|
||||
|
||||
bool IsBuiltInValidForWebGPU(SpvBuiltIn label) {
|
||||
switch (label) {
|
||||
case SpvBuiltInPosition:
|
||||
case SpvBuiltInVertexIndex:
|
||||
case SpvBuiltInInstanceIndex:
|
||||
case SpvBuiltInFrontFacing:
|
||||
case SpvBuiltInFragCoord:
|
||||
case SpvBuiltInFragDepth:
|
||||
case SpvBuiltInNumWorkgroups:
|
||||
case SpvBuiltInWorkgroupSize:
|
||||
case SpvBuiltInLocalInvocationId:
|
||||
case SpvBuiltInGlobalInvocationId:
|
||||
case SpvBuiltInLocalInvocationIndex: {
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef enum VUIDError_ {
|
||||
VUIDErrorExecutionModel = 0,
|
||||
VUIDErrorStorageClass = 1,
|
||||
|
@ -1260,7 +1238,7 @@ spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference(
|
|||
|
||||
spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition(
|
||||
const Decoration& decoration, const Instruction& inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (spv_result_t error = ValidateF32Vec(
|
||||
decoration, inst, 4,
|
||||
[this, &inst](const std::string& message) -> spv_result_t {
|
||||
|
@ -1284,7 +1262,7 @@ spv_result_t BuiltInsValidator::ValidateFragCoordAtReference(
|
|||
const Decoration& decoration, const Instruction& built_in_inst,
|
||||
const Instruction& referenced_inst,
|
||||
const Instruction& referenced_from_inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
|
||||
if (storage_class != SpvStorageClassMax &&
|
||||
storage_class != SpvStorageClassInput) {
|
||||
|
@ -1322,7 +1300,7 @@ spv_result_t BuiltInsValidator::ValidateFragCoordAtReference(
|
|||
|
||||
spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition(
|
||||
const Decoration& decoration, const Instruction& inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (spv_result_t error = ValidateF32(
|
||||
decoration, inst,
|
||||
[this, &inst](const std::string& message) -> spv_result_t {
|
||||
|
@ -1345,7 +1323,7 @@ spv_result_t BuiltInsValidator::ValidateFragDepthAtReference(
|
|||
const Decoration& decoration, const Instruction& built_in_inst,
|
||||
const Instruction& referenced_inst,
|
||||
const Instruction& referenced_from_inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
|
||||
if (storage_class != SpvStorageClassMax &&
|
||||
storage_class != SpvStorageClassOutput) {
|
||||
|
@ -1398,7 +1376,7 @@ spv_result_t BuiltInsValidator::ValidateFragDepthAtReference(
|
|||
|
||||
spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition(
|
||||
const Decoration& decoration, const Instruction& inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (spv_result_t error = ValidateBool(
|
||||
decoration, inst,
|
||||
[this, &inst](const std::string& message) -> spv_result_t {
|
||||
|
@ -1421,7 +1399,7 @@ spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference(
|
|||
const Decoration& decoration, const Instruction& built_in_inst,
|
||||
const Instruction& referenced_inst,
|
||||
const Instruction& referenced_from_inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
|
||||
if (storage_class != SpvStorageClassMax &&
|
||||
storage_class != SpvStorageClassInput) {
|
||||
|
@ -1579,7 +1557,7 @@ spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference(
|
|||
|
||||
spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition(
|
||||
const Decoration& decoration, const Instruction& inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (spv_result_t error = ValidateI32(
|
||||
decoration, inst,
|
||||
[this, &inst](const std::string& message) -> spv_result_t {
|
||||
|
@ -1602,7 +1580,7 @@ spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference(
|
|||
const Decoration& decoration, const Instruction& built_in_inst,
|
||||
const Instruction& referenced_inst,
|
||||
const Instruction& referenced_from_inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
|
||||
if (storage_class != SpvStorageClassMax &&
|
||||
storage_class != SpvStorageClassInput) {
|
||||
|
@ -1992,46 +1970,6 @@ spv_result_t BuiltInsValidator::ValidatePositionAtReference(
|
|||
}
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
|
||||
if (storage_class != SpvStorageClassMax &&
|
||||
storage_class != SpvStorageClassOutput) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
<< "WebGPU spec allows BuiltIn Position to be only used for "
|
||||
"variables with Output storage class. "
|
||||
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
|
||||
referenced_from_inst)
|
||||
<< " " << GetStorageClassDesc(referenced_from_inst);
|
||||
}
|
||||
|
||||
for (const SpvExecutionModel execution_model : execution_models_) {
|
||||
switch (execution_model) {
|
||||
case SpvExecutionModelVertex: {
|
||||
if (spv_result_t error = ValidateF32Vec(
|
||||
decoration, built_in_inst, 4,
|
||||
[this, &referenced_from_inst](
|
||||
const std::string& message) -> spv_result_t {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
<< "According to the WebGPU spec BuiltIn Position "
|
||||
"variable needs to be a 4-component 32-bit float "
|
||||
"vector. "
|
||||
<< message;
|
||||
})) {
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
<< "WebGPU spec allows BuiltIn Position to be used only "
|
||||
"with the Vertex execution model. "
|
||||
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
|
||||
referenced_from_inst, execution_model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (function_id_ == 0) {
|
||||
// Propagate this rule to all dependant ids in the global scope.
|
||||
id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind(
|
||||
|
@ -2547,7 +2485,7 @@ spv_result_t BuiltInsValidator::ValidateTessLevelAtReference(
|
|||
|
||||
spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition(
|
||||
const Decoration& decoration, const Instruction& inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (spv_result_t error = ValidateI32(
|
||||
decoration, inst,
|
||||
[this, &inst](const std::string& message) -> spv_result_t {
|
||||
|
@ -2580,51 +2518,14 @@ spv_result_t BuiltInsValidator::ValidateVertexIdAtDefinition(
|
|||
|
||||
spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition(
|
||||
const Decoration& decoration, const Instruction& inst) {
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
if (spv_result_t error = ValidateI32(
|
||||
decoration, inst,
|
||||
[this, &inst](const std::string& message) -> spv_result_t {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
|
||||
<< "According to the WebGPU spec BuiltIn "
|
||||
"LocalInvocationIndex variable needs to be a 32-bit "
|
||||
"int."
|
||||
<< message;
|
||||
})) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
// Seed at reference checks with this built-in.
|
||||
return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst);
|
||||
}
|
||||
|
||||
spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference(
|
||||
const Decoration& decoration, const Instruction& built_in_inst,
|
||||
const Instruction& referenced_inst,
|
||||
const Instruction&,
|
||||
const Instruction& referenced_from_inst) {
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
|
||||
if (storage_class != SpvStorageClassMax &&
|
||||
storage_class != SpvStorageClassInput) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
<< "WebGPU spec allows BuiltIn LocalInvocationIndex to be only "
|
||||
"used for variables with Input storage class. "
|
||||
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
|
||||
referenced_from_inst)
|
||||
<< " " << GetStorageClassDesc(referenced_from_inst);
|
||||
}
|
||||
|
||||
for (const SpvExecutionModel execution_model : execution_models_) {
|
||||
if (execution_model != SpvExecutionModelGLCompute) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
<< "WebGPU spec allows BuiltIn VertexIndex to be used only "
|
||||
"with GLCompute execution model. "
|
||||
<< GetReferenceDesc(decoration, built_in_inst, referenced_inst,
|
||||
referenced_from_inst, execution_model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (function_id_ == 0) {
|
||||
// Propagate this rule to all dependant ids in the global scope.
|
||||
id_to_at_reference_checks_[referenced_from_inst.id()].push_back(
|
||||
|
@ -2640,7 +2541,7 @@ spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference(
|
|||
const Decoration& decoration, const Instruction& built_in_inst,
|
||||
const Instruction& referenced_inst,
|
||||
const Instruction& referenced_from_inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
|
||||
if (storage_class != SpvStorageClassMax &&
|
||||
storage_class != SpvStorageClassInput) {
|
||||
|
@ -2838,7 +2739,7 @@ spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference(
|
|||
|
||||
spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition(
|
||||
const Decoration& decoration, const Instruction& inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
|
||||
if (spv_result_t error = ValidateI32Vec(
|
||||
decoration, inst, 3,
|
||||
|
@ -2867,7 +2768,7 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference(
|
|||
const Decoration& decoration, const Instruction& built_in_inst,
|
||||
const Instruction& referenced_inst,
|
||||
const Instruction& referenced_from_inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const SpvBuiltIn builtin = SpvBuiltIn(decoration.params()[0]);
|
||||
const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst);
|
||||
if (storage_class != SpvStorageClassMax &&
|
||||
|
@ -2887,9 +2788,7 @@ spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference(
|
|||
bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute ||
|
||||
execution_model == SpvExecutionModelTaskNV ||
|
||||
execution_model == SpvExecutionModelMeshNV;
|
||||
bool has_webgpu_model = execution_model == SpvExecutionModelGLCompute;
|
||||
if ((spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) ||
|
||||
(spvIsWebGPUEnv(_.context()->target_env) && !has_webgpu_model)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) {
|
||||
uint32_t vuid = GetVUIDForBuiltin(builtin, VUIDErrorExecutionModel);
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
<< _.VkErrorID(vuid)
|
||||
|
@ -3086,7 +2985,7 @@ spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition(
|
|||
|
||||
spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition(
|
||||
const Decoration& decoration, const Instruction& inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
!spvOpcodeIsConstant(inst.opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
|
||||
|
@ -3118,7 +3017,7 @@ spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference(
|
|||
const Decoration& decoration, const Instruction& built_in_inst,
|
||||
const Instruction& referenced_inst,
|
||||
const Instruction& referenced_from_inst) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
for (const SpvExecutionModel execution_model : execution_models_) {
|
||||
if (execution_model != SpvExecutionModelGLCompute) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst)
|
||||
|
@ -3768,26 +3667,17 @@ spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition(
|
|||
<< "BuiltIns can only target variables, structs or constants";
|
||||
}
|
||||
|
||||
if (!spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
// Early return. All currently implemented rules are based on Vulkan or
|
||||
// WebGPU spec.
|
||||
if (!spvIsVulkanEnv(_.context()->target_env)) {
|
||||
// Early return. All currently implemented rules are based on Vulkan spec.
|
||||
//
|
||||
// TODO: If you are adding validation rules for environments other than
|
||||
// Vulkan or WebGPU (or general rules which are not environment
|
||||
// independent), then you need to modify or remove this condition. Consider
|
||||
// also adding early returns into BuiltIn-specific rules, so that the system
|
||||
// doesn't spawn new rules which don't do anything.
|
||||
// Vulkan (or general rules which are not environment independent), then
|
||||
// you need to modify or remove this condition. Consider also adding early
|
||||
// returns into BuiltIn-specific rules, so that the system doesn't spawn new
|
||||
// rules which don't do anything.
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(_.context()->target_env) &&
|
||||
!IsBuiltInValidForWebGPU(label)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
|
||||
<< "WebGPU does not allow BuiltIn "
|
||||
<< _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN,
|
||||
decoration.params()[0]);
|
||||
}
|
||||
|
||||
// If you are adding a new BuiltIn enum, please register it here.
|
||||
// If the newly added enum has validation rules associated with it
|
||||
// consider leaving a TODO and/or creating an issue.
|
||||
|
|
|
@ -260,19 +260,6 @@ bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool IsSupportGuaranteedWebGPU(uint32_t capability) {
|
||||
switch (capability) {
|
||||
case SpvCapabilityMatrix:
|
||||
case SpvCapabilityShader:
|
||||
case SpvCapabilitySampled1D:
|
||||
case SpvCapabilityImage1D:
|
||||
case SpvCapabilityDerivativeControl:
|
||||
case SpvCapabilityImageQuery:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Validates that capability declarations use operands allowed in the current
|
||||
|
@ -365,14 +352,6 @@ spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) {
|
|||
<< " Profile specification"
|
||||
<< " (or requires extension or capability)";
|
||||
}
|
||||
} else if (env == SPV_ENV_WEBGPU_0) {
|
||||
if (!IsSupportGuaranteedWebGPU(capability) &&
|
||||
!IsEnabledByExtension(_, capability)) {
|
||||
return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
|
||||
<< "Capability " << capability_str()
|
||||
<< " is not allowed by WebGPU specification"
|
||||
<< " (or requires extension)";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
|
|
|
@ -820,120 +820,6 @@ spv_result_t StructuredControlFlowChecks(
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t PerformWebGPUCfgChecks(ValidationState_t& _, Function* function) {
|
||||
for (auto& block : function->ordered_blocks()) {
|
||||
if (block->reachable()) continue;
|
||||
if (block->is_type(kBlockTypeMerge)) {
|
||||
// 1. Find the referencing merge and confirm that it is reachable.
|
||||
BasicBlock* merge_header = function->GetMergeHeader(block);
|
||||
assert(merge_header != nullptr);
|
||||
if (!merge_header->reachable()) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable merge-blocks must be referenced by "
|
||||
"a reachable merge instruction.";
|
||||
}
|
||||
|
||||
// 2. Check that the only instructions are OpLabel and OpUnreachable.
|
||||
auto* label_inst = block->label();
|
||||
auto* terminator_inst = block->terminator();
|
||||
assert(label_inst != nullptr);
|
||||
assert(terminator_inst != nullptr);
|
||||
|
||||
if (terminator_inst->opcode() != SpvOpUnreachable) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable merge-blocks must terminate with "
|
||||
"OpUnreachable.";
|
||||
}
|
||||
|
||||
auto label_idx = label_inst - &_.ordered_instructions()[0];
|
||||
auto terminator_idx = terminator_inst - &_.ordered_instructions()[0];
|
||||
if (label_idx + 1 != terminator_idx) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable merge-blocks must only contain an "
|
||||
"OpLabel and OpUnreachable instruction.";
|
||||
}
|
||||
|
||||
// 3. Use label instruction to confirm there is no uses by branches.
|
||||
for (auto use : label_inst->uses()) {
|
||||
const auto* use_inst = use.first;
|
||||
if (spvOpcodeIsBranch(use_inst->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable merge-blocks cannot be the target "
|
||||
"of a branch.";
|
||||
}
|
||||
}
|
||||
} else if (block->is_type(kBlockTypeContinue)) {
|
||||
// 1. Find referencing loop and confirm that it is reachable.
|
||||
std::vector<BasicBlock*> continue_headers =
|
||||
function->GetContinueHeaders(block);
|
||||
if (continue_headers.empty()) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable continue-target must be referenced "
|
||||
"by a loop instruction.";
|
||||
}
|
||||
|
||||
std::vector<BasicBlock*> reachable_headers(continue_headers.size());
|
||||
auto iter =
|
||||
std::copy_if(continue_headers.begin(), continue_headers.end(),
|
||||
reachable_headers.begin(),
|
||||
[](BasicBlock* header) { return header->reachable(); });
|
||||
reachable_headers.resize(std::distance(reachable_headers.begin(), iter));
|
||||
|
||||
if (reachable_headers.empty()) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable continue-target must be referenced "
|
||||
"by a reachable loop instruction.";
|
||||
}
|
||||
|
||||
// 2. Check that the only instructions are OpLabel and OpBranch.
|
||||
auto* label_inst = block->label();
|
||||
auto* terminator_inst = block->terminator();
|
||||
assert(label_inst != nullptr);
|
||||
assert(terminator_inst != nullptr);
|
||||
|
||||
if (terminator_inst->opcode() != SpvOpBranch) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable continue-target must terminate with "
|
||||
"OpBranch.";
|
||||
}
|
||||
|
||||
auto label_idx = label_inst - &_.ordered_instructions()[0];
|
||||
auto terminator_idx = terminator_inst - &_.ordered_instructions()[0];
|
||||
if (label_idx + 1 != terminator_idx) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable continue-target must only contain "
|
||||
"an OpLabel and an OpBranch instruction.";
|
||||
}
|
||||
|
||||
// 3. Use label instruction to confirm there is no uses by branches.
|
||||
for (auto use : label_inst->uses()) {
|
||||
const auto* use_inst = use.first;
|
||||
if (spvOpcodeIsBranch(use_inst->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable continue-target cannot be the "
|
||||
"target of a branch.";
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Confirm that continue-target has a back edge to a reachable loop
|
||||
// header block.
|
||||
auto branch_target = terminator_inst->GetOperandAs<uint32_t>(0);
|
||||
for (auto* continue_header : reachable_headers) {
|
||||
if (branch_target != continue_header->id()) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, unreachable continue-target must only have a "
|
||||
"back edge to a single reachable loop instruction.";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "For WebGPU, all blocks must be reachable, unless they are "
|
||||
<< "degenerate cases of merge-block or continue-target.";
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t PerformCfgChecks(ValidationState_t& _) {
|
||||
for (auto& function : _.functions()) {
|
||||
// Check all referenced blocks are defined within a function
|
||||
|
@ -1014,13 +900,6 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
|
|||
<< _.getIdName(idom->id());
|
||||
}
|
||||
}
|
||||
|
||||
// For WebGPU check that all unreachable blocks are degenerate cases for
|
||||
// merge-block or continue-target.
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
spv_result_t result = PerformWebGPUCfgChecks(_, &function);
|
||||
if (result != SPV_SUCCESS) return result;
|
||||
}
|
||||
}
|
||||
// If we have structed control flow, check that no block has a control
|
||||
// flow nesting depth larger than the limit.
|
||||
|
|
|
@ -535,12 +535,10 @@ spv_result_t ValidateVectorShuffle(ValidationState_t& _,
|
|||
}
|
||||
|
||||
// All Component literals must either be FFFFFFFF or in [0, N - 1].
|
||||
// For WebGPU specifically, Component literals cannot be FFFFFFFF.
|
||||
auto vector1ComponentCount = vector1Type->GetOperandAs<uint32_t>(2);
|
||||
auto vector2ComponentCount = vector2Type->GetOperandAs<uint32_t>(2);
|
||||
auto N = vector1ComponentCount + vector2ComponentCount;
|
||||
auto firstLiteralIndex = 4;
|
||||
const auto is_webgpu_env = spvIsWebGPUEnv(_.context()->target_env);
|
||||
for (size_t i = firstLiteralIndex; i < inst->operands().size(); ++i) {
|
||||
auto literal = inst->GetOperandAs<uint32_t>(i);
|
||||
if (literal != 0xFFFFFFFF && literal >= N) {
|
||||
|
@ -548,12 +546,6 @@ spv_result_t ValidateVectorShuffle(ValidationState_t& _,
|
|||
<< "Component index " << literal << " is out of bounds for "
|
||||
<< "combined (Vector1 + Vector2) size of " << N << ".";
|
||||
}
|
||||
|
||||
if (is_webgpu_env && literal == 0xFFFFFFFF) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Component literal at operand " << i - firstLiteralIndex
|
||||
<< " cannot be 0xFFFFFFFF in WebGPU execution environment.";
|
||||
}
|
||||
}
|
||||
|
||||
if (_.HasCapability(SpvCapabilityShader) &&
|
||||
|
|
|
@ -685,34 +685,9 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _,
|
|||
|
||||
} // anonymous namespace
|
||||
|
||||
spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
std::string extension = GetExtensionString(&(inst->c_inst()));
|
||||
|
||||
if (extension != ExtensionToString(kSPV_KHR_vulkan_memory_model)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For WebGPU, the only valid parameter to OpExtension is "
|
||||
<< "\"" << ExtensionToString(kSPV_KHR_vulkan_memory_model)
|
||||
<< "\".";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateExtInstImport(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto name_id = 1;
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
const std::string name(reinterpret_cast<const char*>(
|
||||
inst->words().data() + inst->operands()[name_id].offset));
|
||||
if (name != "GLSL.std.450") {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For WebGPU, the only valid parameter to OpExtInstImport is "
|
||||
"\"GLSL.std.450\".";
|
||||
}
|
||||
}
|
||||
|
||||
if (!_.HasExtension(kSPV_KHR_non_semantic_info)) {
|
||||
const std::string name(reinterpret_cast<const char*>(
|
||||
inst->words().data() + inst->operands()[name_id].offset));
|
||||
|
@ -3149,7 +3124,6 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
|||
|
||||
spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst) {
|
||||
const SpvOp opcode = inst->opcode();
|
||||
if (opcode == SpvOpExtension) return ValidateExtension(_, inst);
|
||||
if (opcode == SpvOpExtInstImport) return ValidateExtInstImport(_, inst);
|
||||
if (opcode == SpvOpExtInst) return ValidateExtInst(_, inst);
|
||||
|
||||
|
|
|
@ -821,12 +821,11 @@ spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) {
|
|||
<< "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)";
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(target_env) || spvIsWebGPUEnv(target_env)) {
|
||||
if (spvIsVulkanEnv(target_env)) {
|
||||
if (info.sampled == 0) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(4657) << "Sampled must be 1 or 2 in the "
|
||||
<< (spvIsVulkanEnv(target_env) ? "Vulkan" : "WebGPU")
|
||||
<< " environment.";
|
||||
<< _.VkErrorID(4657)
|
||||
<< "Sampled must be 1 or 2 in the Vulkan environment.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1469,9 +1468,9 @@ spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) {
|
|||
}
|
||||
|
||||
const auto target_env = _.context()->target_env;
|
||||
// Vulkan and WebGPU require the result to be a 4-element int or float
|
||||
// Vulkan requires the result to be a 4-element int or float
|
||||
// vector.
|
||||
if (spvIsVulkanEnv(target_env) || spvIsWebGPUEnv(target_env)) {
|
||||
if (spvIsVulkanEnv(target_env)) {
|
||||
if (_.GetDimension(actual_result_type) != 4) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected " << GetActualResultTypeStr(opcode)
|
||||
|
|
|
@ -578,12 +578,12 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
|||
}
|
||||
}
|
||||
|
||||
// WebGPU & Vulkan Appendix A: Check that if contains initializer, then
|
||||
// Vulkan Appendix A: Check that if contains initializer, then
|
||||
// storage class is Output, Private, or Function.
|
||||
if (inst->operands().size() > 3 && storage_class != SpvStorageClassOutput &&
|
||||
storage_class != SpvStorageClassPrivate &&
|
||||
storage_class != SpvStorageClassFunction) {
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< _.VkErrorID(4651) << "OpVariable, <id> '"
|
||||
<< _.getIdName(inst->id())
|
||||
|
@ -597,20 +597,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
|||
}
|
||||
}
|
||||
|
||||
// WebGPU: All variables with storage class Output, Private, or Function MUST
|
||||
// have an initializer.
|
||||
if (spvIsWebGPUEnv(_.context()->target_env) && inst->operands().size() <= 3 &&
|
||||
(storage_class == SpvStorageClassOutput ||
|
||||
storage_class == SpvStorageClassPrivate ||
|
||||
storage_class == SpvStorageClassFunction)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable, <id> '" << _.getIdName(inst->id())
|
||||
<< "', must have an initializer.\n"
|
||||
<< "From WebGPU execution environment spec:\n"
|
||||
<< "All variables in the following storage classes must have an "
|
||||
<< "initializer: Output, Private, or Function";
|
||||
}
|
||||
|
||||
if (storage_class == SpvStorageClassPhysicalStorageBufferEXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "PhysicalStorageBufferEXT must not be used with OpVariable.";
|
||||
|
@ -703,41 +689,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
|||
}
|
||||
}
|
||||
|
||||
// WebGPU specific validation rules for OpTypeRuntimeArray
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
// OpTypeRuntimeArray should only ever be in an OpTypeStruct,
|
||||
// so should never appear as a bare variable.
|
||||
if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable, <id> '" << _.getIdName(inst->id())
|
||||
<< "', is attempting to create memory for an illegal type, "
|
||||
<< "OpTypeRuntimeArray.\nFor WebGPU OpTypeRuntimeArray can only "
|
||||
<< "appear as the final member of an OpTypeStruct, thus cannot "
|
||||
<< "be instantiated via OpVariable";
|
||||
}
|
||||
|
||||
// If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it
|
||||
// must have the storage class StorageBuffer and be decorated
|
||||
// with Block.
|
||||
if (value_type && value_type->opcode() == SpvOpTypeStruct) {
|
||||
if (DoesStructContainRTA(_, value_type)) {
|
||||
if (storage_class == SpvStorageClassStorageBuffer) {
|
||||
if (!_.HasDecoration(value_id, SpvDecorationBlock)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "For WebGPU, an OpTypeStruct variable containing an "
|
||||
<< "OpTypeRuntimeArray must be decorated with Block if it "
|
||||
<< "has storage class StorageBuffer.";
|
||||
}
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "For WebGPU, OpTypeStruct variables containing "
|
||||
<< "OpTypeRuntimeArray must have storage class of "
|
||||
<< "StorageBuffer";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cooperative matrix types can only be allocated in Function or Private
|
||||
if ((storage_class != SpvStorageClassFunction &&
|
||||
storage_class != SpvStorageClassPrivate) &&
|
||||
|
|
|
@ -56,55 +56,6 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
|
|||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
uint32_t valid_bits;
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpControlBarrier:
|
||||
if (!(value & SpvMemorySemanticsAcquireReleaseMask)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For WebGPU, AcquireRelease must be set for Memory "
|
||||
"Semantics of OpControlBarrier.";
|
||||
}
|
||||
|
||||
if (!(value & SpvMemorySemanticsWorkgroupMemoryMask)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For WebGPU, WorkgroupMemory must be set for Memory "
|
||||
"Semantics of OpControlBarrier.";
|
||||
}
|
||||
|
||||
valid_bits = SpvMemorySemanticsAcquireReleaseMask |
|
||||
SpvMemorySemanticsWorkgroupMemoryMask;
|
||||
if (value & ~valid_bits) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For WebGPU only WorkgroupMemory and AcquireRelease may be "
|
||||
"set for Memory Semantics of OpControlBarrier.";
|
||||
}
|
||||
break;
|
||||
case SpvOpMemoryBarrier:
|
||||
if (!(value & SpvMemorySemanticsImageMemoryMask)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For WebGPU, ImageMemory must be set for Memory Semantics "
|
||||
"of OpMemoryBarrier.";
|
||||
}
|
||||
valid_bits = SpvMemorySemanticsImageMemoryMask;
|
||||
if (value & ~valid_bits) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For WebGPU only ImageMemory may be set for Memory "
|
||||
"Semantics of OpMemoryBarrier.";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (spvOpcodeIsAtomicOp(inst->opcode())) {
|
||||
if (value != 0) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "For WebGPU Memory no bits may be set for Memory "
|
||||
"Semantics of OpAtomic* instructions.";
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits(
|
||||
value & (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask |
|
||||
SpvMemorySemanticsAcquireReleaseMask |
|
||||
|
|
|
@ -37,10 +37,6 @@ spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) {
|
|||
<< "Cannot create undefined values with 8- or 16-bit types";
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, inst) << "OpUndef is disallowed";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -471,21 +471,6 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
|
|||
}
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
if (mode != SpvExecutionModeOriginUpperLeft &&
|
||||
mode != SpvExecutionModeDepthReplacing &&
|
||||
mode != SpvExecutionModeDepthGreater &&
|
||||
mode != SpvExecutionModeDepthLess &&
|
||||
mode != SpvExecutionModeDepthUnchanged &&
|
||||
mode != SpvExecutionModeLocalSize &&
|
||||
mode != SpvExecutionModeLocalSizeHint) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Execution mode must be one of OriginUpperLeft, "
|
||||
"DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
|
||||
"LocalSize, or LocalSizeHint for WebGPU environment.";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -500,13 +485,6 @@ spv_result_t ValidateMemoryModel(ValidationState_t& _,
|
|||
"the VulkanKHR memory model is used.";
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
if (_.addressing_model() != SpvAddressingModelLogical) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Addressing model must be Logical for WebGPU environment.";
|
||||
}
|
||||
}
|
||||
|
||||
if (spvIsOpenCLEnv(_.context()->target_env)) {
|
||||
if ((_.addressing_model() != SpvAddressingModelPhysical32) &&
|
||||
(_.addressing_model() != SpvAddressingModelPhysical64)) {
|
||||
|
|
|
@ -137,30 +137,6 @@ spv_result_t ValidateExecutionScope(ValidationState_t& _,
|
|||
}
|
||||
}
|
||||
|
||||
// WebGPU Specific rules
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
if (value != SpvScopeWorkgroup) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< ": in WebGPU environment Execution Scope is limited to "
|
||||
<< "Workgroup";
|
||||
} else {
|
||||
_.function(inst->function()->id())
|
||||
->RegisterExecutionModelLimitation(
|
||||
[](SpvExecutionModel model, std::string* message) {
|
||||
if (model != SpvExecutionModelGLCompute) {
|
||||
if (message) {
|
||||
*message =
|
||||
": in WebGPU environment, Workgroup Execution Scope is "
|
||||
"limited to GLCompute execution model";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
|
||||
|
||||
// General SPIRV rules
|
||||
|
@ -260,62 +236,6 @@ spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst,
|
|||
}
|
||||
}
|
||||
|
||||
// WebGPU specific rules
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpControlBarrier:
|
||||
if (value != SpvScopeWorkgroup) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< ": in WebGPU environment Memory Scope is limited to "
|
||||
<< "Workgroup for OpControlBarrier";
|
||||
}
|
||||
break;
|
||||
case SpvOpMemoryBarrier:
|
||||
if (value != SpvScopeWorkgroup) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< ": in WebGPU environment Memory Scope is limited to "
|
||||
<< "Workgroup for OpMemoryBarrier";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (spvOpcodeIsAtomicOp(inst->opcode())) {
|
||||
if (value != SpvScopeQueueFamilyKHR) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< ": in WebGPU environment Memory Scope is limited to "
|
||||
<< "QueueFamilyKHR for OpAtomic* operations";
|
||||
}
|
||||
}
|
||||
|
||||
if (value != SpvScopeWorkgroup && value != SpvScopeInvocation &&
|
||||
value != SpvScopeQueueFamilyKHR) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< ": in WebGPU environment Memory Scope is limited to "
|
||||
<< "Workgroup, Invocation, and QueueFamilyKHR";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (value == SpvScopeWorkgroup) {
|
||||
_.function(inst->function()->id())
|
||||
->RegisterExecutionModelLimitation(
|
||||
[](SpvExecutionModel model, std::string* message) {
|
||||
if (model != SpvExecutionModelGLCompute) {
|
||||
if (message) {
|
||||
*message =
|
||||
": in WebGPU environment, Workgroup Memory Scope is "
|
||||
"limited to GLCompute execution model";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments.
|
||||
|
||||
return SPV_SUCCESS;
|
||||
|
|
|
@ -40,21 +40,6 @@ int64_t ConstantLiteralAsInt64(uint32_t width,
|
|||
return static_cast<int64_t>(uint64_t(lo_word) | uint64_t(hi_word) << 32);
|
||||
}
|
||||
|
||||
// Returns, as an uint64_t, the literal value from an OpConstant or the
|
||||
// default value of an OpSpecConstant, assuming it is an integral type.
|
||||
// For signed integers, relies the rule that literal value is sign extended
|
||||
// to fill out to word granularity. Assumes that the constant value
|
||||
// has
|
||||
int64_t ConstantLiteralAsUint64(uint32_t width,
|
||||
const std::vector<uint32_t>& const_words) {
|
||||
const uint32_t lo_word = const_words[3];
|
||||
if (width <= 32) return lo_word;
|
||||
assert(width <= 64);
|
||||
assert(const_words.size() > 4);
|
||||
const uint32_t hi_word = const_words[4]; // Must exist, per spec.
|
||||
return (uint64_t(lo_word) | uint64_t(hi_word) << 32);
|
||||
}
|
||||
|
||||
// Validates that type declarations are unique, unless multiple declarations
|
||||
// of the same data type are allowed by the specification.
|
||||
// (see section 2.8 Types and Variables)
|
||||
|
@ -240,7 +225,7 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) {
|
|||
<< "' is a void type.";
|
||||
}
|
||||
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) &&
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
element_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id)
|
||||
|
@ -279,18 +264,6 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) {
|
|||
<< "OpTypeArray Length <id> '" << _.getIdName(length_id)
|
||||
<< "' default value must be at least 1: found " << ivalue;
|
||||
}
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
// WebGPU has maximum integer width of 32 bits, and max array size
|
||||
// is one more than the max signed integer representation.
|
||||
const uint64_t max_permitted = (uint64_t(1) << 31);
|
||||
const uint64_t uvalue = ConstantLiteralAsUint64(width, length->words());
|
||||
if (uvalue > max_permitted) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeArray Length <id> '" << _.getIdName(length_id)
|
||||
<< "' size exceeds max value " << max_permitted
|
||||
<< " permitted by WebGPU: got " << uvalue;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case SpvOpConstantNull:
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
|
@ -322,7 +295,7 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _,
|
|||
<< _.getIdName(element_id) << "' is a void type.";
|
||||
}
|
||||
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) &&
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
element_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeRuntimeArray Element Type <id> '"
|
||||
|
@ -394,7 +367,7 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
|
|||
<< ".";
|
||||
}
|
||||
|
||||
if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) &&
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
member_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
const bool is_last_member =
|
||||
member_type_index == inst->operands().size() - 1;
|
||||
|
|
|
@ -1240,23 +1240,6 @@ bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const {
|
|||
|
||||
bool ValidationState_t::IsValidStorageClass(
|
||||
SpvStorageClass storage_class) const {
|
||||
if (spvIsWebGPUEnv(context()->target_env)) {
|
||||
switch (storage_class) {
|
||||
case SpvStorageClassUniformConstant:
|
||||
case SpvStorageClassUniform:
|
||||
case SpvStorageClassStorageBuffer:
|
||||
case SpvStorageClassInput:
|
||||
case SpvStorageClassOutput:
|
||||
case SpvStorageClassImage:
|
||||
case SpvStorageClassWorkgroup:
|
||||
case SpvStorageClassPrivate:
|
||||
case SpvStorageClassFunction:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(context()->target_env)) {
|
||||
switch (storage_class) {
|
||||
case SpvStorageClassUniformConstant:
|
||||
|
|
|
@ -39,10 +39,7 @@ if (!build_with_chromium || use_fuzzing_engine) {
|
|||
":spvtools_opt_legalization_fuzzer",
|
||||
":spvtools_opt_performance_fuzzer",
|
||||
":spvtools_opt_size_fuzzer",
|
||||
":spvtools_opt_webgputovulkan_fuzzer",
|
||||
":spvtools_opt_vulkantowebgpu_fuzzer",
|
||||
":spvtools_val_fuzzer",
|
||||
":spvtools_val_webgpu_fuzzer",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -104,31 +101,12 @@ spvtools_fuzzer("spvtools_opt_size_fuzzer_src") {
|
|||
]
|
||||
}
|
||||
|
||||
|
||||
spvtools_fuzzer("spvtools_opt_webgputovulkan_fuzzer_src") {
|
||||
sources = [
|
||||
"spvtools_opt_webgputovulkan_fuzzer.cpp",
|
||||
]
|
||||
}
|
||||
|
||||
spvtools_fuzzer("spvtools_opt_vulkantowebgpu_fuzzer_src") {
|
||||
sources = [
|
||||
"spvtools_opt_vulkantowebgpu_fuzzer.cpp",
|
||||
]
|
||||
}
|
||||
|
||||
spvtools_fuzzer("spvtools_val_fuzzer_src") {
|
||||
sources = [
|
||||
"spvtools_val_fuzzer.cpp",
|
||||
]
|
||||
}
|
||||
|
||||
spvtools_fuzzer("spvtools_val_webgpu_fuzzer_src") {
|
||||
sources = [
|
||||
"spvtools_val_webgpu_fuzzer.cpp",
|
||||
]
|
||||
}
|
||||
|
||||
if (!build_with_chromium || use_fuzzing_engine) {
|
||||
fuzzer_test("spvtools_as_fuzzer") {
|
||||
sources = []
|
||||
|
@ -181,22 +159,6 @@ if (!build_with_chromium || use_fuzzing_engine) {
|
|||
seed_corpus = "corpora/spv"
|
||||
}
|
||||
|
||||
fuzzer_test("spvtools_opt_webgputovulkan_fuzzer") {
|
||||
sources = []
|
||||
deps = [
|
||||
":spvtools_opt_webgputovulkan_fuzzer_src",
|
||||
]
|
||||
seed_corpus = "corpora/spv"
|
||||
}
|
||||
|
||||
fuzzer_test("spvtools_opt_vulkantowebgpu_fuzzer") {
|
||||
sources = []
|
||||
deps = [
|
||||
":spvtools_opt_vulkantowebgpu_fuzzer_src",
|
||||
]
|
||||
seed_corpus = "corpora/spv"
|
||||
}
|
||||
|
||||
fuzzer_test("spvtools_val_fuzzer") {
|
||||
sources = []
|
||||
deps = [
|
||||
|
@ -204,12 +166,4 @@ if (!build_with_chromium || use_fuzzing_engine) {
|
|||
]
|
||||
seed_corpus = "corpora/spv"
|
||||
}
|
||||
|
||||
fuzzer_test("spvtools_val_webgpu_fuzzer") {
|
||||
sources = []
|
||||
deps = [
|
||||
":spvtools_val_webgpu_fuzzer_src",
|
||||
]
|
||||
seed_corpus = "corpora/spv"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
// 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 <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv-tools/optimizer.hpp"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
|
||||
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
|
||||
const spv_position_t&, const char*) {});
|
||||
|
||||
std::vector<uint32_t> input;
|
||||
input.resize(size >> 2);
|
||||
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; (i + 3) < size; i += 4) {
|
||||
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
|
||||
(data[i + 3]) << 24;
|
||||
}
|
||||
|
||||
optimizer.RegisterVulkanToWebGPUPasses();
|
||||
optimizer.Run(input.data(), input.size(), &input);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
// 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 <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv-tools/optimizer.hpp"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
|
||||
optimizer.SetMessageConsumer([](spv_message_level_t, const char*,
|
||||
const spv_position_t&, const char*) {});
|
||||
|
||||
std::vector<uint32_t> input;
|
||||
input.resize(size >> 2);
|
||||
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; (i + 3) < size; i += 4) {
|
||||
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
|
||||
(data[i + 3]) << 24;
|
||||
}
|
||||
|
||||
optimizer.RegisterWebGPUToVulkanPasses();
|
||||
optimizer.Run(input.data(), input.size(), &input);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
// 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 <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "spirv-tools/libspirv.hpp"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
spvtools::SpirvTools tools(SPV_ENV_WEBGPU_0);
|
||||
tools.SetMessageConsumer([](spv_message_level_t, const char*,
|
||||
const spv_position_t&, const char*) {});
|
||||
|
||||
std::vector<uint32_t> input;
|
||||
input.resize(size >> 2);
|
||||
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; (i + 3) < size; i += 4) {
|
||||
input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) |
|
||||
(data[i + 3]) << 24;
|
||||
}
|
||||
|
||||
tools.Validate(input);
|
||||
return 0;
|
||||
}
|
|
@ -34,7 +34,6 @@ add_spvtools_unittest(TARGET opt
|
|||
dead_insert_elim_test.cpp
|
||||
dead_variable_elim_test.cpp
|
||||
debug_info_manager_test.cpp
|
||||
decompose_initialized_variables_test.cpp
|
||||
decoration_manager_test.cpp
|
||||
def_use_test.cpp
|
||||
desc_sroa_test.cpp
|
||||
|
@ -48,7 +47,6 @@ add_spvtools_unittest(TARGET opt
|
|||
fold_test.cpp
|
||||
freeze_spec_const_test.cpp
|
||||
function_test.cpp
|
||||
generate_webgpu_initializers_test.cpp
|
||||
graphics_robust_access_test.cpp
|
||||
if_conversion_test.cpp
|
||||
inline_opaque_test.cpp
|
||||
|
@ -63,7 +61,6 @@ add_spvtools_unittest(TARGET opt
|
|||
ir_context_test.cpp
|
||||
ir_loader_test.cpp
|
||||
iterator_test.cpp
|
||||
legalize_vector_shuffle_test.cpp
|
||||
line_debug_info_test.cpp
|
||||
local_access_chain_convert_test.cpp
|
||||
local_redundancy_elimination_test.cpp
|
||||
|
@ -88,9 +85,7 @@ add_spvtools_unittest(TARGET opt
|
|||
scalar_replacement_test.cpp
|
||||
set_spec_const_default_value_test.cpp
|
||||
simplification_test.cpp
|
||||
split_invalid_unreachable_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
|
||||
|
|
|
@ -1,252 +0,0 @@
|
|||
// Copyright (c) 2019 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 <vector>
|
||||
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
using DecomposeInitializedVariablesTest = PassTest<::testing::Test>;
|
||||
|
||||
std::string single_entry_header = R"(OpCapability Shader
|
||||
OpCapability VulkanMemoryModel
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical Vulkan
|
||||
OpEntryPoint Vertex %1 "shader"
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%4 = OpConstantNull %uint
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
)";
|
||||
|
||||
std::string GetFunctionTest(std::string body) {
|
||||
auto result = single_entry_header;
|
||||
result += "%_ptr_Function_uint = OpTypePointer Function %uint\n";
|
||||
result += "%1 = OpFunction %void None %6\n";
|
||||
result += "%8 = OpLabel\n";
|
||||
result += body + "\n";
|
||||
result += "OpReturn\n";
|
||||
result += "OpFunctionEnd\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionChanged) {
|
||||
std::string input = "%9 = OpVariable %_ptr_Function_uint Function %uint_1";
|
||||
std::string expected = R"(%9 = OpVariable %_ptr_Function_uint Function
|
||||
OpStore %9 %uint_1)";
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
GetFunctionTest(input), GetFunctionTest(expected),
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionUnchanged) {
|
||||
std::string input = "%9 = OpVariable %_ptr_Function_uint Function";
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
GetFunctionTest(input), GetFunctionTest(input), /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionMultipleVariables) {
|
||||
std::string input = R"(%9 = OpVariable %_ptr_Function_uint Function %uint_1
|
||||
%10 = OpVariable %_ptr_Function_uint Function %4)";
|
||||
std::string expected = R"(%9 = OpVariable %_ptr_Function_uint Function
|
||||
%10 = OpVariable %_ptr_Function_uint Function
|
||||
OpStore %9 %uint_1
|
||||
OpStore %10 %4)";
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
GetFunctionTest(input), GetFunctionTest(expected),
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetGlobalTest(std::string storage_class, bool initialized,
|
||||
bool decomposed) {
|
||||
auto result = single_entry_header;
|
||||
|
||||
result += "%_ptr_" + storage_class + "_uint = OpTypePointer " +
|
||||
storage_class + " %uint\n";
|
||||
if (initialized) {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + " %4\n";
|
||||
} else {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + "\n";
|
||||
}
|
||||
result += R"(%1 = OpFunction %void None %9
|
||||
%9 = OpLabel
|
||||
)";
|
||||
if (decomposed) result += "OpStore %8 %4\n";
|
||||
result += R"(OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateChanged) {
|
||||
std::string input = GetGlobalTest("Private", true, false);
|
||||
std::string expected = GetGlobalTest("Private", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateUnchanged) {
|
||||
std::string input = GetGlobalTest("Private", false, false);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputChanged) {
|
||||
std::string input = GetGlobalTest("Output", true, false);
|
||||
std::string expected = GetGlobalTest("Output", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputUnchanged) {
|
||||
std::string input = GetGlobalTest("Output", false, false);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string multiple_entry_header = R"(OpCapability Shader
|
||||
OpCapability VulkanMemoryModel
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical Vulkan
|
||||
OpEntryPoint Vertex %1 "vertex"
|
||||
OpEntryPoint Fragment %2 "fragment"
|
||||
%uint = OpTypeInt 32 0
|
||||
%4 = OpConstantNull %uint
|
||||
%void = OpTypeVoid
|
||||
%6 = OpTypeFunction %void
|
||||
)";
|
||||
|
||||
std::string GetGlobalMultipleEntryTest(std::string storage_class,
|
||||
bool initialized, bool decomposed) {
|
||||
auto result = multiple_entry_header;
|
||||
result += "%_ptr_" + storage_class + "_uint = OpTypePointer " +
|
||||
storage_class + " %uint\n";
|
||||
if (initialized) {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + " %4\n";
|
||||
} else {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + "\n";
|
||||
}
|
||||
result += R"(%1 = OpFunction %void None %9
|
||||
%9 = OpLabel
|
||||
)";
|
||||
if (decomposed) result += "OpStore %8 %4\n";
|
||||
result += R"(OpReturn
|
||||
OpFunctionEnd
|
||||
%2 = OpFunction %void None %10
|
||||
%10 = OpLabel
|
||||
)";
|
||||
if (decomposed) result += "OpStore %8 %4\n";
|
||||
result += R"(OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateMultipleEntryChanged) {
|
||||
std::string input = GetGlobalMultipleEntryTest("Private", true, false);
|
||||
std::string expected = GetGlobalMultipleEntryTest("Private", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateMultipleEntryUnchanged) {
|
||||
std::string input = GetGlobalMultipleEntryTest("Private", false, false);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputMultipleEntryChanged) {
|
||||
std::string input = GetGlobalMultipleEntryTest("Output", true, false);
|
||||
std::string expected = GetGlobalMultipleEntryTest("Output", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputMultipleEntryUnchanged) {
|
||||
std::string input = GetGlobalMultipleEntryTest("Output", false, false);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
std::string GetGlobalWithNonEntryPointTest(std::string storage_class,
|
||||
bool initialized, bool decomposed) {
|
||||
auto result = single_entry_header;
|
||||
result += "%_ptr_" + storage_class + "_uint = OpTypePointer " +
|
||||
storage_class + " %uint\n";
|
||||
if (initialized) {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + " %4\n";
|
||||
} else {
|
||||
result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " +
|
||||
storage_class + "\n";
|
||||
}
|
||||
result += R"(%1 = OpFunction %void None %9
|
||||
%9 = OpLabel
|
||||
)";
|
||||
if (decomposed) result += "OpStore %8 %4\n";
|
||||
result += R"(OpReturn
|
||||
OpFunctionEnd
|
||||
%10 = OpFunction %void None %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateWithNonEntryPointChanged) {
|
||||
std::string input = GetGlobalWithNonEntryPointTest("Private", true, false);
|
||||
std::string expected = GetGlobalWithNonEntryPointTest("Private", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, PrivateWithNonEntryPointUnchanged) {
|
||||
std::string input = GetGlobalWithNonEntryPointTest("Private", false, false);
|
||||
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputWithNonEntryPointChanged) {
|
||||
std::string input = GetGlobalWithNonEntryPointTest("Output", true, false);
|
||||
std::string expected = GetGlobalWithNonEntryPointTest("Output", false, true);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, OutputWithNonEntryPointUnchanged) {
|
||||
std::string input = GetGlobalWithNonEntryPointTest("Output", false, false);
|
||||
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
|
@ -1,347 +0,0 @@
|
|||
// Copyright (c) 2019 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 <vector>
|
||||
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
typedef std::tuple<std::string, bool> GenerateWebGPUInitializersParam;
|
||||
|
||||
using GlobalVariableTest =
|
||||
PassTest<::testing::TestWithParam<GenerateWebGPUInitializersParam>>;
|
||||
using LocalVariableTest =
|
||||
PassTest<::testing::TestWithParam<GenerateWebGPUInitializersParam>>;
|
||||
|
||||
using GenerateWebGPUInitializersTest = PassTest<::testing::Test>;
|
||||
|
||||
void operator+=(std::vector<const char*>& lhs, const char* rhs) {
|
||||
lhs.push_back(rhs);
|
||||
}
|
||||
|
||||
void operator+=(std::vector<const char*>& lhs,
|
||||
const std::vector<const char*>& rhs) {
|
||||
lhs.reserve(lhs.size() + rhs.size());
|
||||
for (auto* c : rhs) lhs.push_back(c);
|
||||
}
|
||||
|
||||
std::string GetGlobalVariableTestString(std::string ptr_str,
|
||||
std::string var_str,
|
||||
std::string const_str = "") {
|
||||
std::vector<const char*> result = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
ptr_str.c_str()};
|
||||
// clang-format on
|
||||
|
||||
if (!const_str.empty()) result += const_str.c_str();
|
||||
|
||||
result += {
|
||||
// clang-format off
|
||||
var_str.c_str(),
|
||||
"%uint_0 = OpConstant %uint 0",
|
||||
"%void = OpTypeVoid",
|
||||
"%7 = OpTypeFunction %void",
|
||||
"%1 = OpFunction %void None %7",
|
||||
"%8 = OpLabel",
|
||||
"OpStore %4 %uint_0",
|
||||
"OpReturn",
|
||||
"OpFunctionEnd"
|
||||
// clang-format on
|
||||
};
|
||||
return JoinAllInsts(result);
|
||||
}
|
||||
|
||||
std::string GetPointerString(std::string storage_type) {
|
||||
std::string result = "%_ptr_";
|
||||
result += storage_type + "_uint = OpTypePointer ";
|
||||
result += storage_type + " %uint";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetGlobalVariableString(std::string storage_type,
|
||||
bool initialized) {
|
||||
std::string result = "%4 = OpVariable %_ptr_";
|
||||
result += storage_type + "_uint ";
|
||||
result += storage_type;
|
||||
if (initialized) result += " %9";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetUninitializedGlobalVariableTestString(std::string storage_type) {
|
||||
return GetGlobalVariableTestString(
|
||||
GetPointerString(storage_type),
|
||||
GetGlobalVariableString(storage_type, false));
|
||||
}
|
||||
|
||||
std::string GetNullConstantString() { return "%9 = OpConstantNull %uint"; }
|
||||
|
||||
std::string GetInitializedGlobalVariableTestString(std::string storage_type) {
|
||||
return GetGlobalVariableTestString(
|
||||
GetPointerString(storage_type),
|
||||
GetGlobalVariableString(storage_type, true), GetNullConstantString());
|
||||
}
|
||||
|
||||
TEST_P(GlobalVariableTest, Check) {
|
||||
std::string storage_class = std::get<0>(GetParam());
|
||||
bool changed = std::get<1>(GetParam());
|
||||
std::string input = GetUninitializedGlobalVariableTestString(storage_class);
|
||||
std::string expected =
|
||||
changed ? GetInitializedGlobalVariableTestString(storage_class) : input;
|
||||
|
||||
SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
GenerateWebGPUInitializers, GlobalVariableTest,
|
||||
::testing::ValuesIn(std::vector<GenerateWebGPUInitializersParam>({
|
||||
std::make_tuple("Private", true),
|
||||
std::make_tuple("Output", true),
|
||||
std::make_tuple("Function", true),
|
||||
std::make_tuple("UniformConstant", false),
|
||||
std::make_tuple("Input", false),
|
||||
std::make_tuple("Uniform", false),
|
||||
std::make_tuple("Workgroup", false)
|
||||
})));
|
||||
// clang-format on
|
||||
|
||||
std::string GetLocalVariableTestString(std::string ptr_str, std::string var_str,
|
||||
std::string const_str = "") {
|
||||
std::vector<const char*> result = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
ptr_str.c_str(),
|
||||
"%uint_0 = OpConstant %uint 0",
|
||||
"%void = OpTypeVoid",
|
||||
"%6 = OpTypeFunction %void"};
|
||||
// clang-format on
|
||||
|
||||
if (!const_str.empty()) result += const_str.c_str();
|
||||
|
||||
result += {
|
||||
// clang-format off
|
||||
"%1 = OpFunction %void None %6",
|
||||
"%7 = OpLabel",
|
||||
var_str.c_str(),
|
||||
"OpStore %8 %uint_0"
|
||||
// clang-format on
|
||||
};
|
||||
return JoinAllInsts(result);
|
||||
}
|
||||
|
||||
std::string GetLocalVariableString(std::string storage_type, bool initialized) {
|
||||
std::string result = "%8 = OpVariable %_ptr_";
|
||||
result += storage_type + "_uint ";
|
||||
result += storage_type;
|
||||
if (initialized) result += " %9";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetUninitializedLocalVariableTestString(std::string storage_type) {
|
||||
return GetLocalVariableTestString(
|
||||
GetPointerString(storage_type),
|
||||
GetLocalVariableString(storage_type, false));
|
||||
}
|
||||
|
||||
std::string GetInitializedLocalVariableTestString(std::string storage_type) {
|
||||
return GetLocalVariableTestString(GetPointerString(storage_type),
|
||||
GetLocalVariableString(storage_type, true),
|
||||
GetNullConstantString());
|
||||
}
|
||||
|
||||
TEST_P(LocalVariableTest, Check) {
|
||||
std::string storage_class = std::get<0>(GetParam());
|
||||
bool changed = std::get<1>(GetParam());
|
||||
|
||||
std::string input = GetUninitializedLocalVariableTestString(storage_class);
|
||||
std::string expected =
|
||||
changed ? GetInitializedLocalVariableTestString(storage_class) : input;
|
||||
|
||||
SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
GenerateWebGPUInitializers, LocalVariableTest,
|
||||
::testing::ValuesIn(std::vector<GenerateWebGPUInitializersParam>({
|
||||
std::make_tuple("Private", true),
|
||||
std::make_tuple("Output", true),
|
||||
std::make_tuple("Function", true),
|
||||
std::make_tuple("UniformConstant", false),
|
||||
std::make_tuple("Input", false),
|
||||
std::make_tuple("Uniform", false),
|
||||
std::make_tuple("Workgroup", false)
|
||||
})));
|
||||
// clang-format on
|
||||
|
||||
TEST_F(GenerateWebGPUInitializersTest, AlreadyInitializedUnchanged) {
|
||||
std::vector<const char*> spirv = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%_ptr_Private_uint = OpTypePointer Private %uint",
|
||||
"%uint_0 = OpConstant %uint 0",
|
||||
"%5 = OpVariable %_ptr_Private_uint Private %uint_0",
|
||||
"%void = OpTypeVoid",
|
||||
"%7 = OpTypeFunction %void",
|
||||
"%1 = OpFunction %void None %7",
|
||||
"%8 = OpLabel",
|
||||
"OpReturn",
|
||||
"OpFunctionEnd"
|
||||
// clang-format on
|
||||
};
|
||||
std::string str = JoinAllInsts(spirv);
|
||||
|
||||
SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(str, str,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(GenerateWebGPUInitializersTest, AmbigiousArrays) {
|
||||
std::vector<const char*> input_spirv = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%uint_2 = OpConstant %uint 2",
|
||||
"%_arr_uint_uint_2 = OpTypeArray %uint %uint_2",
|
||||
"%_arr_uint_uint_2_0 = OpTypeArray %uint %uint_2",
|
||||
"%_ptr_Private__arr_uint_uint_2 = OpTypePointer Private %_arr_uint_uint_2",
|
||||
"%_ptr_Private__arr_uint_uint_2_0 = OpTypePointer Private %_arr_uint_uint_2_0",
|
||||
"%8 = OpConstantNull %_arr_uint_uint_2_0",
|
||||
"%9 = OpVariable %_ptr_Private__arr_uint_uint_2 Private",
|
||||
"%10 = OpVariable %_ptr_Private__arr_uint_uint_2_0 Private %8",
|
||||
"%void = OpTypeVoid",
|
||||
"%12 = OpTypeFunction %void",
|
||||
"%1 = OpFunction %void None %12",
|
||||
"%13 = OpLabel",
|
||||
"OpReturn",
|
||||
"OpFunctionEnd"
|
||||
// clang-format on
|
||||
};
|
||||
std::string input_str = JoinAllInsts(input_spirv);
|
||||
|
||||
std::vector<const char*> expected_spirv = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%uint_2 = OpConstant %uint 2",
|
||||
"%_arr_uint_uint_2 = OpTypeArray %uint %uint_2",
|
||||
"%_arr_uint_uint_2_0 = OpTypeArray %uint %uint_2",
|
||||
"%_ptr_Private__arr_uint_uint_2 = OpTypePointer Private %_arr_uint_uint_2",
|
||||
"%_ptr_Private__arr_uint_uint_2_0 = OpTypePointer Private %_arr_uint_uint_2_0",
|
||||
"%8 = OpConstantNull %_arr_uint_uint_2_0",
|
||||
"%14 = OpConstantNull %_arr_uint_uint_2",
|
||||
"%9 = OpVariable %_ptr_Private__arr_uint_uint_2 Private %14",
|
||||
"%10 = OpVariable %_ptr_Private__arr_uint_uint_2_0 Private %8",
|
||||
"%void = OpTypeVoid",
|
||||
"%12 = OpTypeFunction %void",
|
||||
"%1 = OpFunction %void None %12",
|
||||
"%13 = OpLabel",
|
||||
"OpReturn",
|
||||
"OpFunctionEnd"
|
||||
// clang-format on
|
||||
};
|
||||
std::string expected_str = JoinAllInsts(expected_spirv);
|
||||
|
||||
SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(input_str, expected_str,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(GenerateWebGPUInitializersTest, AmbigiousStructs) {
|
||||
std::vector<const char*> input_spirv = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%_struct_3 = OpTypeStruct %uint",
|
||||
"%_struct_4 = OpTypeStruct %uint",
|
||||
"%_ptr_Private__struct_3 = OpTypePointer Private %_struct_3",
|
||||
"%_ptr_Private__struct_4 = OpTypePointer Private %_struct_4",
|
||||
"%7 = OpConstantNull %_struct_3",
|
||||
"%8 = OpVariable %_ptr_Private__struct_3 Private %7",
|
||||
"%9 = OpVariable %_ptr_Private__struct_4 Private",
|
||||
"%void = OpTypeVoid",
|
||||
"%11 = OpTypeFunction %void",
|
||||
"%1 = OpFunction %void None %11",
|
||||
"%12 = OpLabel",
|
||||
"OpReturn",
|
||||
"OpFunctionEnd"
|
||||
// clang-format on
|
||||
};
|
||||
std::string input_str = JoinAllInsts(input_spirv);
|
||||
|
||||
std::vector<const char*> expected_spirv = {
|
||||
// clang-format off
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%_struct_3 = OpTypeStruct %uint",
|
||||
"%_struct_4 = OpTypeStruct %uint",
|
||||
"%_ptr_Private__struct_3 = OpTypePointer Private %_struct_3",
|
||||
"%_ptr_Private__struct_4 = OpTypePointer Private %_struct_4",
|
||||
"%7 = OpConstantNull %_struct_3",
|
||||
"%8 = OpVariable %_ptr_Private__struct_3 Private %7",
|
||||
"%13 = OpConstantNull %_struct_4",
|
||||
"%9 = OpVariable %_ptr_Private__struct_4 Private %13",
|
||||
"%void = OpTypeVoid",
|
||||
"%11 = OpTypeFunction %void",
|
||||
"%1 = OpFunction %void None %11",
|
||||
"%12 = OpLabel",
|
||||
"OpReturn",
|
||||
"OpFunctionEnd"
|
||||
// clang-format on
|
||||
};
|
||||
std::string expected_str = JoinAllInsts(expected_spirv);
|
||||
|
||||
SinglePassRunAndCheck<GenerateWebGPUInitializersPass>(input_str, expected_str,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
|
@ -1,81 +0,0 @@
|
|||
// Copyright (c) 2019 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 <vector>
|
||||
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
using LegalizeVectorShuffleTest = PassTest<::testing::Test>;
|
||||
|
||||
void operator+=(std::vector<const char*>& lhs, const char* rhs) {
|
||||
lhs.push_back(rhs);
|
||||
}
|
||||
|
||||
void operator+=(std::vector<const char*>& lhs,
|
||||
const std::vector<const char*> rhs) {
|
||||
for (auto elem : rhs) lhs.push_back(elem);
|
||||
}
|
||||
|
||||
std::vector<const char*> header = {
|
||||
"OpCapability Shader",
|
||||
"OpCapability VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"OpEntryPoint Vertex %1 \"shader\"",
|
||||
"%uint = OpTypeInt 32 0",
|
||||
"%v3uint = OpTypeVector %uint 3"};
|
||||
|
||||
std::string GetTestString(const char* shuffle) {
|
||||
std::vector<const char*> result = header;
|
||||
result += {"%_ptr_Function_v3uint = OpTypePointer Function %v3uint",
|
||||
"%void = OpTypeVoid",
|
||||
"%6 = OpTypeFunction %void",
|
||||
"%1 = OpFunction %void None %6",
|
||||
"%7 = OpLabel",
|
||||
"%8 = OpVariable %_ptr_Function_v3uint Function",
|
||||
"%9 = OpLoad %v3uint %8",
|
||||
"%10 = OpLoad %v3uint %8"};
|
||||
result += shuffle;
|
||||
result += {"OpReturn", "OpFunctionEnd"};
|
||||
return JoinAllInsts(result);
|
||||
}
|
||||
|
||||
TEST_F(LegalizeVectorShuffleTest, Changed) {
|
||||
std::string input =
|
||||
GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0xFFFFFFFF");
|
||||
std::string expected =
|
||||
GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0");
|
||||
|
||||
SinglePassRunAndCheck<LegalizeVectorShufflePass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(LegalizeVectorShuffleTest, FunctionUnchanged) {
|
||||
std::string input =
|
||||
GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0");
|
||||
std::string expected =
|
||||
GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0");
|
||||
|
||||
SinglePassRunAndCheck<LegalizeVectorShufflePass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
|
@ -221,498 +221,6 @@ TEST(Optimizer, CanRegisterPassesFromFlags) {
|
|||
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
|
||||
}
|
||||
|
||||
TEST(Optimizer, VulkanToWebGPUSetsCorrectPasses) {
|
||||
Optimizer opt(SPV_ENV_VULKAN_1_1);
|
||||
opt.RegisterVulkanToWebGPUPasses();
|
||||
std::vector<const char*> pass_names = opt.GetPassNames();
|
||||
|
||||
std::vector<std::string> registered_passes;
|
||||
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",
|
||||
"eliminate-dead-const",
|
||||
"flatten-decorations",
|
||||
"strip-atomic-counter-memory",
|
||||
"generate-webgpu-initializers",
|
||||
"legalize-vector-shuffle",
|
||||
"split-invalid-unreachable",
|
||||
"compact-ids"};
|
||||
std::sort(registered_passes.begin(), registered_passes.end());
|
||||
std::sort(expected_passes.begin(), expected_passes.end());
|
||||
|
||||
ASSERT_EQ(registered_passes.size(), expected_passes.size());
|
||||
for (size_t i = 0; i < registered_passes.size(); i++)
|
||||
EXPECT_EQ(registered_passes[i], expected_passes[i]);
|
||||
}
|
||||
|
||||
struct VulkanToWebGPUPassCase {
|
||||
// Input SPIR-V
|
||||
std::string input;
|
||||
// Expected result SPIR-V
|
||||
std::string expected;
|
||||
// Specific pass under test, used for logging messages.
|
||||
std::string pass;
|
||||
};
|
||||
|
||||
using VulkanToWebGPUPassTest =
|
||||
PassTest<::testing::TestWithParam<VulkanToWebGPUPassCase>>;
|
||||
|
||||
TEST_P(VulkanToWebGPUPassTest, Ran) {
|
||||
std::vector<uint32_t> binary;
|
||||
{
|
||||
SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
||||
tools.Assemble(GetParam().input, &binary);
|
||||
}
|
||||
|
||||
Optimizer opt(SPV_ENV_VULKAN_1_1);
|
||||
opt.RegisterVulkanToWebGPUPasses();
|
||||
|
||||
std::vector<uint32_t> optimized;
|
||||
class ValidatorOptions validator_options;
|
||||
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
|
||||
validator_options, true))
|
||||
<< GetParam().input << "\n";
|
||||
std::string disassembly;
|
||||
{
|
||||
SpirvTools tools(SPV_ENV_WEBGPU_0);
|
||||
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
|
||||
}
|
||||
|
||||
EXPECT_EQ(GetParam().expected, disassembly)
|
||||
<< "Was expecting pass '" << GetParam().pass << "' to have been run.\n";
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
Optimizer, VulkanToWebGPUPassTest,
|
||||
::testing::ValuesIn(std::vector<VulkanToWebGPUPassCase>{
|
||||
// FlattenDecorations
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Fragment %main \"main\" %hue %saturation %value\n"
|
||||
"OpExecutionMode %main OriginUpperLeft\n"
|
||||
"OpDecorate %group Flat\n"
|
||||
"OpDecorate %group NoPerspective\n"
|
||||
"%group = OpDecorationGroup\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%void_fn = OpTypeFunction %void\n"
|
||||
"%float = OpTypeFloat 32\n"
|
||||
"%_ptr_Input_float = OpTypePointer Input %float\n"
|
||||
"%hue = OpVariable %_ptr_Input_float Input\n"
|
||||
"%saturation = OpVariable %_ptr_Input_float Input\n"
|
||||
"%value = OpVariable %_ptr_Input_float Input\n"
|
||||
"%main = OpFunction %void None %void_fn\n"
|
||||
"%entry = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Fragment %1 \"main\" %2 %3 %4\n"
|
||||
"OpExecutionMode %1 OriginUpperLeft\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%6 = OpTypeFunction %void\n"
|
||||
"%float = OpTypeFloat 32\n"
|
||||
"%_ptr_Input_float = OpTypePointer Input %float\n"
|
||||
"%2 = OpVariable %_ptr_Input_float Input\n"
|
||||
"%3 = OpVariable %_ptr_Input_float Input\n"
|
||||
"%4 = OpVariable %_ptr_Input_float Input\n"
|
||||
"%1 = OpFunction %void None %6\n"
|
||||
"%9 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"flatten-decorations"},
|
||||
// Eliminate Dead Constants
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\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 VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\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"
|
||||
"%6 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %6\n"
|
||||
"%7 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
"eliminate-dead-const"},
|
||||
// Strip Atomic Counter Memory
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\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"
|
||||
" 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 VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\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"
|
||||
"%9 = OpTypeFunction %void\n"
|
||||
"%uint_264 = OpConstant %uint 264\n"
|
||||
"%1 = OpFunction %void None %9\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"},
|
||||
// Generate WebGPU Initializers
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %func \"shader\"\n"
|
||||
"%u32 = OpTypeInt 32 0\n"
|
||||
"%u32_ptr = OpTypePointer Private %u32\n"
|
||||
"%u32_var = OpVariable %u32_ptr Private\n"
|
||||
"%u32_0 = OpConstant %u32 0\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%void_f = OpTypeFunction %void\n"
|
||||
"%func = OpFunction %void None %void_f\n"
|
||||
"%label = OpLabel\n"
|
||||
"OpStore %u32_var %u32_0\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%_ptr_Private_uint = OpTypePointer Private %uint\n"
|
||||
"%4 = OpConstantNull %uint\n"
|
||||
"%5 = OpVariable %_ptr_Private_uint Private %4\n"
|
||||
"%uint_0 = OpConstant %uint 0\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%8 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %8\n"
|
||||
"%9 = OpLabel\n"
|
||||
"OpStore %5 %uint_0\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"generate-webgpu-initializers"},
|
||||
// Legalize Vector Shuffle
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%v3uint = OpTypeVector %uint 3\n"
|
||||
"%_ptr_Function_v3uint = OpTypePointer Function %v3uint\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%6 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %6\n"
|
||||
"%7 = OpLabel\n"
|
||||
"%8 = OpVariable %_ptr_Function_v3uint Function\n"
|
||||
"%9 = OpLoad %v3uint %8\n"
|
||||
"%10 = OpLoad %v3uint %8\n"
|
||||
"%11 = OpVectorShuffle %v3uint %9 %10 2 1 0xFFFFFFFF\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%v3uint = OpTypeVector %uint 3\n"
|
||||
"%_ptr_Function_v3uint = OpTypePointer Function %v3uint\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%6 = OpTypeFunction %void\n"
|
||||
"%7 = OpConstantNull %v3uint\n"
|
||||
"%1 = OpFunction %void None %6\n"
|
||||
"%8 = OpLabel\n"
|
||||
"%9 = OpVariable %_ptr_Function_v3uint Function %7\n"
|
||||
"%10 = OpLoad %v3uint %9\n"
|
||||
"%11 = OpLoad %v3uint %9\n"
|
||||
"%12 = OpVectorShuffle %v3uint %10 %11 2 1 0\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"legalize-vector-shuffle"},
|
||||
// Split Invalid Unreachable
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%uint_1 = OpConstant %uint 1\n"
|
||||
"%uint_2 = OpConstant %uint 2\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%bool = OpTypeBool\n"
|
||||
"%7 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %7\n"
|
||||
"%8 = OpLabel\n"
|
||||
"OpBranch %9\n"
|
||||
"%9 = OpLabel\n"
|
||||
"OpLoopMerge %10 %11 None\n"
|
||||
"OpBranch %12\n"
|
||||
"%12 = OpLabel\n"
|
||||
"%13 = OpSLessThan %bool %uint_1 %uint_2\n"
|
||||
"OpSelectionMerge %11 None\n"
|
||||
"OpBranchConditional %13 %14 %15\n"
|
||||
"%14 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"%15 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"%10 = OpLabel\n"
|
||||
"OpUnreachable\n"
|
||||
"%11 = OpLabel\n"
|
||||
"OpBranch %9\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%uint_1 = OpConstant %uint 1\n"
|
||||
"%uint_2 = OpConstant %uint 2\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%bool = OpTypeBool\n"
|
||||
"%7 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %7\n"
|
||||
"%8 = OpLabel\n"
|
||||
"OpBranch %9\n"
|
||||
"%9 = OpLabel\n"
|
||||
"OpLoopMerge %10 %11 None\n"
|
||||
"OpBranch %12\n"
|
||||
"%12 = OpLabel\n"
|
||||
"%13 = OpSLessThan %bool %uint_1 %uint_2\n"
|
||||
"OpSelectionMerge %14 None\n"
|
||||
"OpBranchConditional %13 %15 %16\n"
|
||||
"%15 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"%16 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"%10 = OpLabel\n"
|
||||
"OpUnreachable\n"
|
||||
"%14 = OpLabel\n"
|
||||
"OpUnreachable\n"
|
||||
"%11 = OpLabel\n"
|
||||
"OpBranch %9\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"split-invalid-unreachable"},
|
||||
// Compact IDs
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1000 \"shader\"\n"
|
||||
"%10 = OpTypeVoid\n"
|
||||
"%100 = OpTypeFunction %10\n"
|
||||
"%1000 = OpFunction %10 None %100\n"
|
||||
"%10000 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%3 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %3\n"
|
||||
"%4 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"compact-ids"}}));
|
||||
|
||||
TEST(Optimizer, WebGPUToVulkanSetsCorrectPasses) {
|
||||
Optimizer opt(SPV_ENV_WEBGPU_0);
|
||||
opt.RegisterWebGPUToVulkanPasses();
|
||||
std::vector<const char*> pass_names = opt.GetPassNames();
|
||||
|
||||
std::vector<std::string> registered_passes;
|
||||
for (auto name = pass_names.begin(); name != pass_names.end(); ++name)
|
||||
registered_passes.push_back(*name);
|
||||
|
||||
std::vector<std::string> expected_passes = {"decompose-initialized-variables",
|
||||
"compact-ids"};
|
||||
std::sort(registered_passes.begin(), registered_passes.end());
|
||||
std::sort(expected_passes.begin(), expected_passes.end());
|
||||
|
||||
ASSERT_EQ(registered_passes.size(), expected_passes.size());
|
||||
for (size_t i = 0; i < registered_passes.size(); i++)
|
||||
EXPECT_EQ(registered_passes[i], expected_passes[i]);
|
||||
}
|
||||
|
||||
struct WebGPUToVulkanPassCase {
|
||||
// Input SPIR-V
|
||||
std::string input;
|
||||
// Expected result SPIR-V
|
||||
std::string expected;
|
||||
// Specific pass under test, used for logging messages.
|
||||
std::string pass;
|
||||
};
|
||||
|
||||
using WebGPUToVulkanPassTest =
|
||||
PassTest<::testing::TestWithParam<WebGPUToVulkanPassCase>>;
|
||||
|
||||
TEST_P(WebGPUToVulkanPassTest, Ran) {
|
||||
std::vector<uint32_t> binary;
|
||||
{
|
||||
SpirvTools tools(SPV_ENV_WEBGPU_0);
|
||||
tools.Assemble(GetParam().input, &binary);
|
||||
}
|
||||
|
||||
Optimizer opt(SPV_ENV_WEBGPU_0);
|
||||
opt.RegisterWebGPUToVulkanPasses();
|
||||
|
||||
std::vector<uint32_t> optimized;
|
||||
class ValidatorOptions validator_options;
|
||||
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
|
||||
validator_options, true));
|
||||
std::string disassembly;
|
||||
{
|
||||
SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
||||
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
|
||||
}
|
||||
|
||||
EXPECT_EQ(GetParam().expected, disassembly)
|
||||
<< "Was expecting pass '" << GetParam().pass << "' to have been run.\n";
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
Optimizer, WebGPUToVulkanPassTest,
|
||||
::testing::ValuesIn(std::vector<WebGPUToVulkanPassCase>{
|
||||
// Decompose Initialized Variables
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%_ptr_Function_uint = OpTypePointer Function %uint\n"
|
||||
"%4 = OpConstantNull %uint\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%6 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %6\n"
|
||||
"%7 = OpLabel\n"
|
||||
"%8 = OpVariable %_ptr_Function_uint Function %4\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%uint = OpTypeInt 32 0\n"
|
||||
"%_ptr_Function_uint = OpTypePointer Function %uint\n"
|
||||
"%4 = OpConstantNull %uint\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%6 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %6\n"
|
||||
"%7 = OpLabel\n"
|
||||
"%8 = OpVariable %_ptr_Function_uint Function\n"
|
||||
"OpStore %8 %4\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"decompose-initialized-variables"},
|
||||
// Compact IDs
|
||||
{// input
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1000 \"shader\"\n"
|
||||
"%10 = OpTypeVoid\n"
|
||||
"%100 = OpTypeFunction %10\n"
|
||||
"%1000 = OpFunction %10 None %100\n"
|
||||
"%10000 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// expected
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModel\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical Vulkan\n"
|
||||
"OpEntryPoint Vertex %1 \"shader\"\n"
|
||||
"%void = OpTypeVoid\n"
|
||||
"%3 = OpTypeFunction %void\n"
|
||||
"%1 = OpFunction %void None %3\n"
|
||||
"%4 = OpLabel\n"
|
||||
"OpReturn\n"
|
||||
"OpFunctionEnd\n",
|
||||
// pass
|
||||
"compact-ids"}}));
|
||||
|
||||
TEST(Optimizer, RemoveNop) {
|
||||
// Test that OpNops are removed even if no optimizations are run.
|
||||
|
@ -754,7 +262,7 @@ OpFunctionEnd
|
|||
<< before << "\n";
|
||||
std::string disassembly;
|
||||
{
|
||||
SpirvTools tools(SPV_ENV_WEBGPU_0);
|
||||
SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
||||
tools.Disassemble(optimized.data(), optimized.size(), &disassembly);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
// Copyright (c) 2019 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 <vector>
|
||||
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
namespace {
|
||||
|
||||
using SplitInvalidUnreachableTest = PassTest<::testing::Test>;
|
||||
|
||||
std::string spirv_header = R"(OpCapability Shader
|
||||
OpCapability VulkanMemoryModel
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical Vulkan
|
||||
OpEntryPoint Vertex %1 "shader"
|
||||
%uint = OpTypeInt 32 0
|
||||
%uint_1 = OpConstant %uint 1
|
||||
%uint_2 = OpConstant %uint 2
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%7 = OpTypeFunction %void
|
||||
)";
|
||||
|
||||
std::string function_head = R"(%1 = OpFunction %void None %7
|
||||
%8 = OpLabel
|
||||
OpBranch %9
|
||||
)";
|
||||
|
||||
std::string function_tail = "OpFunctionEnd\n";
|
||||
|
||||
std::string GetLoopMergeBlock(std::string block_id, std::string merge_id,
|
||||
std::string continue_id, std::string body_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += "OpLoopMerge " + merge_id + " " + continue_id + " None\n";
|
||||
result += "OpBranch " + body_id + "\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetSelectionMergeBlock(std::string block_id,
|
||||
std::string condition_id,
|
||||
std::string merge_id, std::string true_id,
|
||||
std::string false_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += condition_id + " = OpSLessThan %bool %uint_1 %uint_2\n";
|
||||
result += "OpSelectionMerge " + merge_id + " None\n";
|
||||
result += "OpBranchConditional " + condition_id + " " + true_id + " " +
|
||||
false_id + "\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetReturnBlock(std::string block_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += "OpReturn\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetUnreachableBlock(std::string block_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += "OpUnreachable\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GetBranchBlock(std::string block_id, std::string target_id) {
|
||||
std::string result;
|
||||
result += block_id + " = OpLabel\n";
|
||||
result += "OpBranch " + target_id + "\n";
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_F(SplitInvalidUnreachableTest, NoInvalidBlocks) {
|
||||
std::string input = spirv_header + function_head;
|
||||
input += GetLoopMergeBlock("%9", "%10", "%11", "%12");
|
||||
input += GetSelectionMergeBlock("%12", "%13", "%14", "%15", "%16");
|
||||
input += GetReturnBlock("%15");
|
||||
input += GetReturnBlock("%16");
|
||||
input += GetUnreachableBlock("%10");
|
||||
input += GetBranchBlock("%11", "%9");
|
||||
input += GetUnreachableBlock("%14");
|
||||
input += function_tail;
|
||||
|
||||
SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, input,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(SplitInvalidUnreachableTest, SelectionInLoop) {
|
||||
std::string input = spirv_header + function_head;
|
||||
input += GetLoopMergeBlock("%9", "%10", "%11", "%12");
|
||||
input += GetSelectionMergeBlock("%12", "%13", "%11", "%15", "%16");
|
||||
input += GetReturnBlock("%15");
|
||||
input += GetReturnBlock("%16");
|
||||
input += GetUnreachableBlock("%10");
|
||||
input += GetBranchBlock("%11", "%9");
|
||||
input += function_tail;
|
||||
|
||||
std::string expected = spirv_header + function_head;
|
||||
expected += GetLoopMergeBlock("%9", "%10", "%11", "%12");
|
||||
expected += GetSelectionMergeBlock("%12", "%13", "%16", "%14", "%15");
|
||||
expected += GetReturnBlock("%14");
|
||||
expected += GetReturnBlock("%15");
|
||||
expected += GetUnreachableBlock("%10");
|
||||
expected += GetUnreachableBlock("%16");
|
||||
expected += GetBranchBlock("%11", "%9");
|
||||
expected += function_tail;
|
||||
|
||||
SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(SplitInvalidUnreachableTest, LoopInSelection) {
|
||||
std::string input = spirv_header + function_head;
|
||||
input += GetSelectionMergeBlock("%9", "%10", "%11", "%12", "%13");
|
||||
input += GetLoopMergeBlock("%12", "%14", "%11", "%15");
|
||||
input += GetReturnBlock("%13");
|
||||
input += GetUnreachableBlock("%14");
|
||||
input += GetBranchBlock("%11", "%12");
|
||||
input += GetReturnBlock("%15");
|
||||
input += function_tail;
|
||||
|
||||
std::string expected = spirv_header + function_head;
|
||||
expected += GetSelectionMergeBlock("%9", "%10", "%16", "%12", "%13");
|
||||
expected += GetLoopMergeBlock("%12", "%14", "%11", "%15");
|
||||
expected += GetReturnBlock("%13");
|
||||
expected += GetUnreachableBlock("%14");
|
||||
expected += GetUnreachableBlock("%16");
|
||||
expected += GetBranchBlock("%11", "%12");
|
||||
expected += GetReturnBlock("%15");
|
||||
expected += function_tail;
|
||||
|
||||
SinglePassRunAndCheck<SplitInvalidUnreachablePass>(input, expected,
|
||||
/* skip_nop = */ false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
|
@ -1,406 +0,0 @@
|
|||
// Copyright (c) 2019 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 <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 VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"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 VulkanMemoryModel",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical Vulkan",
|
||||
"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
|
|
@ -95,7 +95,6 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
{"opencl2.0embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_0},
|
||||
{"opencl2.1embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_1},
|
||||
{"opencl2.2embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_2},
|
||||
{"webgpu0", true, SPV_ENV_WEBGPU_0},
|
||||
{"opencl2.3", false, SPV_ENV_UNIVERSAL_1_0},
|
||||
{"opencl3.0", false, SPV_ENV_UNIVERSAL_1_0},
|
||||
{"vulkan1.9", false, SPV_ENV_UNIVERSAL_1_0},
|
||||
|
|
|
@ -73,10 +73,7 @@ class TestValidPassFlags(expect.ValidObjectFile1_5,
|
|||
'--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite',
|
||||
'--scalar-replacement', '--scalar-replacement=42', '--strength-reduction',
|
||||
'--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209',
|
||||
'--unify-const', '--legalize-vector-shuffle',
|
||||
'--split-invalid-unreachable', '--generate-webgpu-initializers',
|
||||
'--decompose-initialized-variables', '--graphics-robust-access',
|
||||
'--wrap-opkill', '--amd-ext-to-khr'
|
||||
'--unify-const', '--graphics-robust-access', '--wrap-opkill', '--amd-ext-to-khr'
|
||||
]
|
||||
expected_passes = [
|
||||
'wrap-opkill',
|
||||
|
@ -124,10 +121,6 @@ class TestValidPassFlags(expect.ValidObjectFile1_5,
|
|||
'vector-dce',
|
||||
'workaround-1209',
|
||||
'unify-const',
|
||||
'legalize-vector-shuffle',
|
||||
'split-invalid-unreachable',
|
||||
'generate-webgpu-initializers',
|
||||
'decompose-initialized-variables',
|
||||
'graphics-robust-access',
|
||||
'wrap-opkill',
|
||||
'amd-ext-to-khr'
|
||||
|
@ -362,45 +355,3 @@ class TestLoopPeelingThresholdArgsInvalidNumber(expect.ErrorMessageSubstr):
|
|||
|
||||
spirv_args = ['--loop-peeling-threshold=a10f']
|
||||
expected_error_substr = 'must have a positive integer argument'
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestWebGPUToVulkanThenVulkanToWebGPUIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
|
||||
"""Tests Vulkan->WebGPU flag cannot be used after WebGPU->Vulkan flag."""
|
||||
|
||||
spirv_args = ['--webgpu-to-vulkan', '--vulkan-to-webgpu']
|
||||
expected_error_substr = 'Cannot use both'
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestVulkanToWebGPUThenWebGPUToVulkanIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
|
||||
"""Tests WebGPU->Vulkan flag cannot be used after Vulkan->WebGPU flag."""
|
||||
|
||||
spirv_args = ['--vulkan-to-webgpu', '--webgpu-to-vulkan']
|
||||
expected_error_substr = 'Cannot use both'
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestTargetEnvThenVulkanToWebGPUIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
|
||||
"""Tests Vulkan->WebGPU flag cannot be used after target env flag."""
|
||||
|
||||
spirv_args = ['--target-env=opengl4.0', '--vulkan-to-webgpu']
|
||||
expected_error_substr = 'defines the target environment'
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestVulkanToWebGPUThenTargetEnvIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
|
||||
"""Tests target env flag cannot be used after Vulkan->WebGPU flag."""
|
||||
|
||||
spirv_args = ['--vulkan-to-webgpu', '--target-env=opengl4.0']
|
||||
expected_error_substr = 'defines the target environment'
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestTargetEnvThenWebGPUToVulkanIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
|
||||
"""Tests WebGPU->Vulkan flag cannot be used after target env flag."""
|
||||
|
||||
spirv_args = ['--target-env=opengl4.0', '--webgpu-to-vulkan']
|
||||
expected_error_substr = 'defines the target environment'
|
||||
|
||||
@inside_spirv_testsuite('SpirvOptFlags')
|
||||
class TestWebGPUToVulkanThenTargetEnvIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr):
|
||||
"""Tests target env flag cannot be used after WebGPU->Vulkan flag."""
|
||||
|
||||
spirv_args = ['--webgpu-to-vulkan', '--target-env=opengl4.0']
|
||||
expected_error_substr = 'defines the target environment'
|
||||
|
|
|
@ -195,7 +195,7 @@ inline std::vector<spv_target_env> AllTargetEnvironments() {
|
|||
SPV_ENV_OPENGL_4_1, SPV_ENV_OPENGL_4_2,
|
||||
SPV_ENV_OPENGL_4_3, SPV_ENV_OPENGL_4_5,
|
||||
SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3,
|
||||
SPV_ENV_VULKAN_1_1, SPV_ENV_WEBGPU_0,
|
||||
SPV_ENV_VULKAN_1_1,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,6 @@ add_spvtools_unittest(TARGET val_stuvw
|
|||
val_type_unique_test.cpp
|
||||
val_validation_state_test.cpp
|
||||
val_version_test.cpp
|
||||
val_webgpu_test.cpp
|
||||
${VAL_TEST_COMMON_SRCS}
|
||||
LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}
|
||||
PCH_FILE pch_test_val
|
||||
|
|
|
@ -113,23 +113,6 @@ std::string GenerateShaderCode(
|
|||
memory_model);
|
||||
}
|
||||
|
||||
std::string GenerateWebGPUShaderCode(
|
||||
const std::string& body,
|
||||
const std::string& capabilities_and_extensions = "") {
|
||||
const std::string vulkan_memory_capability = R"(
|
||||
OpCapability VulkanMemoryModelDeviceScopeKHR
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
)";
|
||||
const std::string vulkan_memory_extension = R"(
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
)";
|
||||
return GenerateShaderCodeImpl(body,
|
||||
vulkan_memory_capability +
|
||||
capabilities_and_extensions +
|
||||
vulkan_memory_extension,
|
||||
"", "VulkanKHR");
|
||||
}
|
||||
|
||||
std::string GenerateKernelCode(
|
||||
const std::string& body,
|
||||
const std::string& capabilities_and_extensions = "") {
|
||||
|
@ -518,39 +501,6 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanInt64) {
|
|||
"AtomicLoad: 64-bit atomics require the Int64Atomics capability"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicLoadWebGPUSuccess) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicLoadWebGPUNonQueueFamilyFailure) {
|
||||
const std::string body = R"(
|
||||
%val3 = OpAtomicLoad %u32 %u32_var %invocation %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Memory Scope is limited to QueueFamilyKHR for "
|
||||
"OpAtomic* operations"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicLoadWebGPUNonRelaxedFailure) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %acquire
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("no bits may be set for Memory Semantics of OpAtomic* "
|
||||
"instructions"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, VK_KHR_shader_atomic_int64Success) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicUMin %u64 %u64_var %device %relaxed %u64_1
|
||||
|
@ -727,38 +677,6 @@ OpAtomicStore %u32_var %device %sequentially_consistent %u32_1
|
|||
"Acquire, AcquireRelease and SequentiallyConsistent"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicStoreWebGPUSuccess) {
|
||||
const std::string body = R"(
|
||||
OpAtomicStore %u32_var %queuefamily %relaxed %u32_1
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
TEST_F(ValidateAtomics, AtomicStoreWebGPUNonQueueFamilyFailure) {
|
||||
const std::string body = R"(
|
||||
OpAtomicStore %u32_var %workgroup %relaxed %u32_1
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Memory Scope is limited to QueueFamilyKHR for "
|
||||
"OpAtomic* operations"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicStoreWebGPUNonRelaxedFailure) {
|
||||
const std::string body = R"(
|
||||
OpAtomicStore %u32_var %queuefamily %release %u32_1
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("no bits may be set for Memory Semantics of OpAtomic* "
|
||||
"instructions"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) {
|
||||
const std::string body = R"(
|
||||
OpAtomicStore %f32_1 %device %relaxed %f32_1
|
||||
|
@ -2049,75 +1967,6 @@ OpExtension "SPV_KHR_vulkan_memory_model"
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, WebGPUCrossDeviceMemoryScopeBad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %cross_device %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("in WebGPU environment Memory Scope is limited to "
|
||||
"QueueFamilyKHR for OpAtomic* operations"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, WebGPUDeviceMemoryScopeBad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("in WebGPU environment Memory Scope is limited to "
|
||||
"QueueFamilyKHR for OpAtomic* operations"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, WebGPUWorkgroupMemoryScopeBad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %workgroup %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("in WebGPU environment Memory Scope is limited to "
|
||||
"QueueFamilyKHR for OpAtomic* operations"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, WebGPUSubgroupMemoryScopeBad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %subgroup %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("in WebGPU environment Memory Scope is limited to "
|
||||
"QueueFamilyKHR for OpAtomic* operations"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, WebGPUInvocationMemoryScopeBad) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %invocation %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("in WebGPU environment Memory Scope is limited to "
|
||||
"QueueFamilyKHR for OpAtomic* operations"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, WebGPUQueueFamilyMemoryScopeGood) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, CompareExchangeWeakV13ValV14Good) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0
|
||||
|
|
|
@ -121,42 +121,6 @@ OpCapability Int64
|
|||
execution_model, memory_model);
|
||||
}
|
||||
|
||||
std::string GenerateWebGPUComputeShaderCode(
|
||||
const std::string& body,
|
||||
const std::string& capabilities_and_extensions = "",
|
||||
const std::string& execution_model = "GLCompute") {
|
||||
const std::string vulkan_memory_capability = R"(
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
)";
|
||||
const std::string vulkan_memory_extension = R"(
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
)";
|
||||
const std::string memory_model = "OpMemoryModel Logical VulkanKHR";
|
||||
return GenerateShaderCodeImpl(body,
|
||||
vulkan_memory_capability +
|
||||
capabilities_and_extensions +
|
||||
vulkan_memory_extension,
|
||||
"", execution_model, memory_model);
|
||||
}
|
||||
|
||||
std::string GenerateWebGPUVertexShaderCode(
|
||||
const std::string& body,
|
||||
const std::string& capabilities_and_extensions = "",
|
||||
const std::string& execution_model = "Vertex") {
|
||||
const std::string vulkan_memory_capability = R"(
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
)";
|
||||
const std::string vulkan_memory_extension = R"(
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
)";
|
||||
const std::string memory_model = "OpMemoryModel Logical VulkanKHR";
|
||||
return GenerateShaderCodeImpl(body,
|
||||
vulkan_memory_capability +
|
||||
capabilities_and_extensions +
|
||||
vulkan_memory_extension,
|
||||
"", execution_model, memory_model);
|
||||
}
|
||||
|
||||
std::string GenerateKernelCode(
|
||||
const std::string& body,
|
||||
const std::string& capabilities_and_extensions = "") {
|
||||
|
@ -271,64 +235,6 @@ OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
|
|||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUAcquireReleaseSuccess) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %acquire_release_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPURelaxedFailure) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, AcquireRelease must be set for Memory "
|
||||
"Semantics of OpControlBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUMissingWorkgroupFailure) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %acquire_release
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, WorkgroupMemory must be set for Memory "
|
||||
"Semantics"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUUniformFailure) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("For WebGPU only WorkgroupMemory and AcquireRelease may be set "
|
||||
"for Memory Semantics of OpControlBarrier."));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUReleaseFailure) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %release_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, AcquireRelease must be set for Memory "
|
||||
"Semantics of OpControlBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv12) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %device %device %none
|
||||
|
@ -435,44 +341,6 @@ OpControlBarrier %device %workgroup %none
|
|||
"is limited to Workgroup and Subgroup"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeDeviceBad) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %device %workgroup %none
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("ControlBarrier: in WebGPU environment Execution Scope "
|
||||
"is limited to Workgroup"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeSubgroupBad) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %subgroup %workgroup %none
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("ControlBarrier: in WebGPU environment Execution Scope "
|
||||
"is limited to Workgroup"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers,
|
||||
OpControlBarrierWebGPUExecutionScopeWorkgroupNonComputeBad) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %workgroup %acquire_release_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Workgroup Execution Scope is limited to GLCompute execution model"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroup) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %subgroup %subgroup %none
|
||||
|
@ -507,18 +375,6 @@ OpControlBarrier %subgroup %cross_device %none
|
|||
"cannot be CrossDevice"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierWebGPUMemoryScopeNonWorkgroup) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %workgroup %subgroup %acquire_release_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("ControlBarrier: in WebGPU environment Memory Scope is "
|
||||
"limited to Workgroup for OpControlBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) {
|
||||
const std::string body = R"(
|
||||
OpControlBarrier %device %device %acquire_and_release_uniform
|
||||
|
@ -738,100 +594,6 @@ OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup
|
|||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUImageMemorySuccess) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %image_memory
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUDeviceFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %subgroup %image_memory
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("in WebGPU environment Memory Scope is limited to "
|
||||
"Workgroup for OpMemoryBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireReleaseFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("ImageMemory must be set for Memory Semantics of "
|
||||
"OpMemoryBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPURelaxedFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("ImageMemory must be set for Memory Semantics of "
|
||||
"OpMemoryBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %acquire_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("ImageMemory must be set for Memory Semantics of "
|
||||
"OpMemoryBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUReleaseFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %release_uniform_workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("ImageMemory must be set for Memory Semantics of "
|
||||
"OpMemoryBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUUniformFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %uniform_image_memory
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("only ImageMemory may be set for Memory Semantics of "
|
||||
"OpMemoryBarrier"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUWorkgroupNonComputeFailure) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %workgroup %image_memory
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Workgroup Memory Scope is limited to GLCompute execution model"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) {
|
||||
const std::string body = R"(
|
||||
OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -116,8 +116,6 @@ using ValidateCapabilityOpenGL40 = spvtest::ValidateBase<CapTestParameter>;
|
|||
using ValidateCapabilityVulkan11 = spvtest::ValidateBase<CapTestParameter>;
|
||||
// Always assembles using Vulkan 1.2.
|
||||
using ValidateCapabilityVulkan12 = spvtest::ValidateBase<CapTestParameter>;
|
||||
// Always assembles using WebGPU.
|
||||
using ValidateCapabilityWebGPU = spvtest::ValidateBase<CapTestParameter>;
|
||||
|
||||
TEST_F(ValidateCapability, Default) {
|
||||
const char str[] = R"(
|
||||
|
@ -588,18 +586,6 @@ const std::vector<std::string>& AllVulkan12Capabilities() {
|
|||
return *r;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& AllWebGPUCapabilities() {
|
||||
static const auto r = new std::vector<std::string>{
|
||||
"",
|
||||
"Shader",
|
||||
"Matrix",
|
||||
"Sampled1D",
|
||||
"Image1D",
|
||||
"ImageQuery",
|
||||
"DerivativeControl"};
|
||||
return *r;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& MatrixDependencies() {
|
||||
static const auto r = new std::vector<std::string>{
|
||||
"Matrix",
|
||||
|
@ -790,12 +776,6 @@ const char kGLSL450MemoryModel[] = \
|
|||
" OpCapability Shader"
|
||||
" OpMemoryModel Logical GLSL450 ";
|
||||
|
||||
const char kVulkanMemoryModel[] = \
|
||||
" OpCapability Shader"
|
||||
" OpCapability VulkanMemoryModelKHR"
|
||||
" OpExtension \"SPV_KHR_vulkan_memory_model\""
|
||||
" OpMemoryModel Logical VulkanKHR ";
|
||||
|
||||
const char kVoidFVoid[] = \
|
||||
" %void = OpTypeVoid"
|
||||
" %void_f = OpTypeFunction %void"
|
||||
|
@ -1814,16 +1794,6 @@ std::make_pair(std::string(kGLSL450MemoryModel) +
|
|||
AllSpirV10Capabilities())
|
||||
)));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityWebGPU,
|
||||
Combine(
|
||||
// All capabilities to try.
|
||||
ValuesIn(AllCapabilities()),
|
||||
Values(
|
||||
std::make_pair(std::string(kVulkanMemoryModel) +
|
||||
"OpEntryPoint Vertex %func \"shader\" \n" + std::string(kVoidFVoid),
|
||||
AllWebGPUCapabilities())
|
||||
)));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan11,
|
||||
Combine(
|
||||
// All capabilities to try.
|
||||
|
@ -2047,17 +2017,6 @@ TEST_P(ValidateCapabilityOpenGL40, Capability) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_P(ValidateCapabilityWebGPU, Capability) {
|
||||
const std::string capability = Capability(GetParam());
|
||||
if (Exists(capability, SPV_ENV_WEBGPU_0)) {
|
||||
const std::string test_code = MakeAssembly(GetParam());
|
||||
CompileSuccessfully(test_code, SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(ExpectedResult(GetParam()),
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0))
|
||||
<< test_code;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ValidateCapability, SemanticsIdIsAnIdNotALiteral) {
|
||||
// From https://github.com/KhronosGroup/SPIRV-Tools/issues/248
|
||||
// The validator was interpreting the memory semantics ID number
|
||||
|
|
|
@ -168,15 +168,6 @@ const std::string& GetDefaultHeader(SpvCapability cap) {
|
|||
return (cap == SpvCapabilityShader) ? shader_header : kernel_header;
|
||||
}
|
||||
|
||||
const std::string& GetWebGPUHeader() {
|
||||
static const std::string header =
|
||||
"OpCapability Shader\n"
|
||||
"OpCapability VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\n";
|
||||
return header;
|
||||
}
|
||||
|
||||
const std::string& types_consts() {
|
||||
static const std::string types =
|
||||
"%voidt = OpTypeVoid\n"
|
||||
|
@ -714,10 +705,8 @@ TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string GetUnreachableMergeNoMergeInst(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableMergeNoMergeInst(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranchConditional);
|
||||
Block t("t", SpvOpReturn);
|
||||
|
@ -725,17 +714,11 @@ std::string GetUnreachableMergeNoMergeInst(SpvCapability cap,
|
|||
Block merge("merge", SpvOpReturn);
|
||||
|
||||
entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
|
||||
if (!spvIsWebGPUEnv(env) && cap == SpvCapabilityShader)
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpSelectionMerge %merge None\n");
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
str += types_consts() + "%func = OpFunction %voidt None %funct\n";
|
||||
str += entry >> branch;
|
||||
str += branch >> std::vector<Block>({t, f});
|
||||
|
@ -748,23 +731,12 @@ std::string GetUnreachableMergeNoMergeInst(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableMergeNoMergeInst) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeNoMergeInst(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableMergeNoMergeInst(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableMergeNoMergeInst) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeNoMergeInst(SpvCapabilityShader, SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, all blocks must be reachable"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableMergeTerminatedBy(SpvCapability cap,
|
||||
spv_target_env env, SpvOp op) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableMergeTerminatedBy(SpvCapability cap, SpvOp op) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranchConditional);
|
||||
|
@ -774,16 +746,10 @@ std::string GetUnreachableMergeTerminatedBy(SpvCapability cap,
|
|||
|
||||
entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpSelectionMerge %merge None\n");
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += entry >> branch;
|
||||
|
@ -797,49 +763,24 @@ std::string GetUnreachableMergeTerminatedBy(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpUnreachable) {
|
||||
CompileSuccessfully(GetUnreachableMergeTerminatedBy(
|
||||
GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpUnreachable));
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeTerminatedBy(GetParam(), SpvOpUnreachable));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, UnreachableMergeTerminatedByOpKill) {
|
||||
CompileSuccessfully(GetUnreachableMergeTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_UNIVERSAL_1_0, SpvOpKill));
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeTerminatedBy(SpvCapabilityShader, SpvOpKill));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpReturn) {
|
||||
CompileSuccessfully(GetUnreachableMergeTerminatedBy(
|
||||
GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpReturn));
|
||||
CompileSuccessfully(GetUnreachableMergeTerminatedBy(GetParam(), SpvOpReturn));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpUnreachable) {
|
||||
CompileSuccessfully(GetUnreachableMergeTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpUnreachable));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpKill) {
|
||||
CompileSuccessfully(GetUnreachableMergeTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpKill));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("must terminate with OpUnreachable"));
|
||||
}
|
||||
|
||||
TEST_P(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpReturn) {
|
||||
CompileSuccessfully(GetUnreachableMergeTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpReturn));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("must terminate with OpUnreachable"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableContinueTerminatedBy(SpvCapability cap,
|
||||
spv_target_env env, SpvOp op) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableContinueTerminatedBy(SpvCapability cap, SpvOp op) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranch);
|
||||
|
@ -849,16 +790,10 @@ std::string GetUnreachableContinueTerminatedBy(SpvCapability cap,
|
|||
if (op == SpvOpBranch) target >> branch;
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpLoopMerge %merge %target None\n");
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += entry >> branch;
|
||||
|
@ -871,8 +806,8 @@ std::string GetUnreachableContinueTerminatedBy(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpUnreachable) {
|
||||
CompileSuccessfully(GetUnreachableContinueTerminatedBy(
|
||||
GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpUnreachable));
|
||||
CompileSuccessfully(
|
||||
GetUnreachableContinueTerminatedBy(GetParam(), SpvOpUnreachable));
|
||||
if (GetParam() == SpvCapabilityShader) {
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
|
@ -883,16 +818,16 @@ TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpUnreachable) {
|
|||
}
|
||||
|
||||
TEST_F(ValidateCFG, UnreachableContinueTerminatedBySpvOpKill) {
|
||||
CompileSuccessfully(GetUnreachableContinueTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_UNIVERSAL_1_0, SpvOpKill));
|
||||
CompileSuccessfully(
|
||||
GetUnreachableContinueTerminatedBy(SpvCapabilityShader, SpvOpKill));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("targeted by 0 back-edge blocks"));
|
||||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpReturn) {
|
||||
CompileSuccessfully(GetUnreachableContinueTerminatedBy(
|
||||
GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpReturn));
|
||||
CompileSuccessfully(
|
||||
GetUnreachableContinueTerminatedBy(GetParam(), SpvOpReturn));
|
||||
if (GetParam() == SpvCapabilityShader) {
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
|
@ -903,48 +838,13 @@ TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpReturn) {
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpBranch) {
|
||||
CompileSuccessfully(GetUnreachableContinueTerminatedBy(
|
||||
GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpBranch));
|
||||
CompileSuccessfully(
|
||||
GetUnreachableContinueTerminatedBy(GetParam(), SpvOpBranch));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpUnreachable) {
|
||||
CompileSuccessfully(GetUnreachableContinueTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpUnreachable));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, unreachable continue-target must "
|
||||
"terminate with OpBranch.\n %12 = OpLabel\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpKill) {
|
||||
CompileSuccessfully(GetUnreachableContinueTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpKill));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, unreachable continue-target must "
|
||||
"terminate with OpBranch.\n %12 = OpLabel\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpReturn) {
|
||||
CompileSuccessfully(GetUnreachableContinueTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpReturn));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, unreachable continue-target must "
|
||||
"terminate with OpBranch.\n %12 = OpLabel\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpBranch) {
|
||||
CompileSuccessfully(GetUnreachableContinueTerminatedBy(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpBranch));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block body("body", SpvOpReturn);
|
||||
Block entry("entry");
|
||||
|
@ -955,16 +855,10 @@ std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap,
|
|||
|
||||
entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpSelectionMerge %merge None\n");
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += body;
|
||||
|
@ -979,23 +873,12 @@ std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableMergeUnreachableMergeInst) {
|
||||
CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst(
|
||||
GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableMergeUnreachableMergeInst) {
|
||||
CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("must be referenced by a reachable merge instruction"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block body("body", SpvOpReturn);
|
||||
Block entry("entry");
|
||||
|
@ -1006,16 +889,10 @@ std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap,
|
|||
target >> branch;
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpLoopMerge %merge %target None\n");
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += body;
|
||||
|
@ -1029,8 +906,7 @@ std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) {
|
||||
CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(
|
||||
GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(GetParam()));
|
||||
if (GetParam() == SpvCapabilityShader) {
|
||||
// Shader causes additional structured CFG checks that cause a failure.
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
|
@ -1043,18 +919,8 @@ TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableContinueUnreachableLoopInst) {
|
||||
CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst(
|
||||
SpvCapabilityShader, SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("must be referenced by a reachable loop instruction"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableMergeWithComplexBody(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableMergeWithComplexBody(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranchConditional);
|
||||
|
@ -1062,23 +928,15 @@ std::string GetUnreachableMergeWithComplexBody(SpvCapability cap,
|
|||
Block f("f", SpvOpReturn);
|
||||
Block merge("merge", SpvOpUnreachable);
|
||||
|
||||
entry.AppendBody(spvIsWebGPUEnv(env)
|
||||
? "%placeholder = OpVariable %intptrt Function %two\n"
|
||||
: "%placeholder = OpVariable %intptrt Function\n");
|
||||
entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
|
||||
entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
|
||||
merge.AppendBody("OpStore %placeholder %one\n");
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpSelectionMerge %merge None\n");
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%intptrt = OpTypePointer Function %intt\n";
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
|
@ -1093,24 +951,12 @@ std::string GetUnreachableMergeWithComplexBody(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableMergeWithComplexBody) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeWithComplexBody(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableMergeWithComplexBody(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableMergeWithComplexBody) {
|
||||
CompileSuccessfully(GetUnreachableMergeWithComplexBody(SpvCapabilityShader,
|
||||
SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("must only contain an OpLabel and OpUnreachable instruction"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableContinueWithComplexBody(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableContinueWithComplexBody(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranch);
|
||||
|
@ -1119,22 +965,14 @@ std::string GetUnreachableContinueWithComplexBody(SpvCapability cap,
|
|||
|
||||
target >> branch;
|
||||
|
||||
entry.AppendBody(spvIsWebGPUEnv(env)
|
||||
? "%placeholder = OpVariable %intptrt Function %two\n"
|
||||
: "%placeholder = OpVariable %intptrt Function\n");
|
||||
entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
|
||||
target.AppendBody("OpStore %placeholder %one\n");
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpLoopMerge %merge %target None\n");
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%intptrt = OpTypePointer Function %intt\n";
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
|
@ -1148,24 +986,12 @@ std::string GetUnreachableContinueWithComplexBody(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableContinueWithComplexBody) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableContinueWithComplexBody(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableContinueWithComplexBody(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableContinueWithComplexBody) {
|
||||
CompileSuccessfully(GetUnreachableContinueWithComplexBody(SpvCapabilityShader,
|
||||
SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("must only contain an OpLabel and an OpBranch instruction"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableMergeWithBranchUse(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableMergeWithBranchUse(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranchConditional);
|
||||
|
@ -1176,16 +1002,10 @@ std::string GetUnreachableMergeWithBranchUse(SpvCapability cap,
|
|||
entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpSelectionMerge %merge None\n");
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += entry >> branch;
|
||||
|
@ -1199,15 +1019,12 @@ std::string GetUnreachableMergeWithBranchUse(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableMergeWithBranchUse(GetParam()));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranchConditional);
|
||||
|
@ -1219,18 +1036,12 @@ std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap,
|
|||
entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n");
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader) {
|
||||
branch.AppendBody("OpSelectionMerge %merge None\n");
|
||||
duplicate.AppendBody("OpSelectionMerge %merge None\n");
|
||||
}
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += entry >> branch;
|
||||
|
@ -1245,8 +1056,7 @@ std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeWithMultipleUses(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableMergeWithMultipleUses(GetParam()));
|
||||
if (GetParam() == SpvCapabilityShader) {
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
|
@ -1256,18 +1066,8 @@ TEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableMergeWithMultipleUses) {
|
||||
CompileSuccessfully(GetUnreachableMergeWithMultipleUses(SpvCapabilityShader,
|
||||
SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("is already a merge block for another header"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableContinueWithBranchUse(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableContinueWithBranchUse(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block foo("foo", SpvOpBranch);
|
||||
|
@ -1278,21 +1078,13 @@ std::string GetUnreachableContinueWithBranchUse(SpvCapability cap,
|
|||
foo >> target;
|
||||
target >> branch;
|
||||
|
||||
entry.AppendBody(spvIsWebGPUEnv(env)
|
||||
? "%placeholder = OpVariable %intptrt Function %two\n"
|
||||
: "%placeholder = OpVariable %intptrt Function\n");
|
||||
entry.AppendBody("%placeholder = OpVariable %intptrt Function\n");
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader)
|
||||
branch.AppendBody("OpLoopMerge %merge %target None\n");
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", "target", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%intptrt = OpTypePointer Function %intt\n";
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
|
@ -1307,23 +1099,12 @@ std::string GetUnreachableContinueWithBranchUse(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableContinueWithBranchUse) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableContinueWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableContinueWithBranchUse(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableContinueWithBranchUse) {
|
||||
CompileSuccessfully(GetUnreachableContinueWithBranchUse(SpvCapabilityShader,
|
||||
SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("cannot be the target of a branch."));
|
||||
}
|
||||
|
||||
std::string GetReachableMergeAndContinue(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetReachableMergeAndContinue(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranch);
|
||||
|
@ -1339,20 +1120,13 @@ std::string GetReachableMergeAndContinue(SpvCapability cap,
|
|||
f >> target;
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader) {
|
||||
branch.AppendBody("OpLoopMerge %merge %target None\n");
|
||||
body.AppendBody("OpSelectionMerge %f None\n");
|
||||
}
|
||||
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", "target", "body", "t", "f",
|
||||
std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", "target", "body", "t", "f",
|
||||
std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += entry >> branch;
|
||||
|
@ -1368,21 +1142,12 @@ std::string GetReachableMergeAndContinue(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, ReachableMergeAndContinue) {
|
||||
CompileSuccessfully(
|
||||
GetReachableMergeAndContinue(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetReachableMergeAndContinue(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUReachableMergeAndContinue) {
|
||||
CompileSuccessfully(
|
||||
GetReachableMergeAndContinue(SpvCapabilityShader, SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
std::string GetUnreachableMergeAndContinue(SpvCapability cap,
|
||||
spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableMergeAndContinue(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block branch("branch", SpvOpBranch);
|
||||
|
@ -1396,20 +1161,13 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap,
|
|||
body.SetBody("%cond = OpSLessThan %boolt %one %two\n");
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (cap == SpvCapabilityShader) {
|
||||
branch.AppendBody("OpLoopMerge %merge %target None\n");
|
||||
body.AppendBody("OpSelectionMerge %target None\n");
|
||||
}
|
||||
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("branch", "merge", "target", "body", "t", "f",
|
||||
std::make_pair("func", "Main"));
|
||||
|
||||
str += nameOps("branch", "merge", "target", "body", "t", "f",
|
||||
std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += entry >> branch;
|
||||
|
@ -1425,36 +1183,19 @@ std::string GetUnreachableMergeAndContinue(SpvCapability cap,
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableMergeAndContinue) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeAndContinue(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableMergeAndContinue(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableMergeAndContinue) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeAndContinue(SpvCapabilityShader, SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("unreachable merge-blocks must terminate with OpUnreachable"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableBlock(SpvCapability cap, spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableBlock(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block unreachable("unreachable");
|
||||
Block exit("exit", SpvOpReturn);
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
|
||||
str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
str += entry >> exit;
|
||||
|
@ -1466,20 +1207,12 @@ std::string GetUnreachableBlock(SpvCapability cap, spv_target_env env) {
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableBlock) {
|
||||
CompileSuccessfully(GetUnreachableBlock(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableBlock(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableBlock) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableBlock(SpvCapabilityShader, SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("all blocks must be reachable"));
|
||||
}
|
||||
|
||||
std::string GetUnreachableBranch(SpvCapability cap, spv_target_env env) {
|
||||
std::string header =
|
||||
spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap);
|
||||
std::string GetUnreachableBranch(SpvCapability cap) {
|
||||
std::string header = GetDefaultHeader(cap);
|
||||
|
||||
Block entry("entry");
|
||||
Block unreachable("unreachable", SpvOpBranchConditional);
|
||||
|
@ -1493,13 +1226,7 @@ std::string GetUnreachableBranch(SpvCapability cap, spv_target_env env) {
|
|||
unreachable.AppendBody("OpSelectionMerge %merge None\n");
|
||||
|
||||
std::string str = header;
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
str +=
|
||||
"OpEntryPoint Fragment %func \"func\"\n"
|
||||
"OpExecutionMode %func OriginUpperLeft\n";
|
||||
}
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
|
||||
str += nameOps("unreachable", "exit", std::make_pair("func", "Main"));
|
||||
str += types_consts();
|
||||
str += "%func = OpFunction %voidt None %funct\n";
|
||||
|
||||
|
@ -1516,17 +1243,10 @@ std::string GetUnreachableBranch(SpvCapability cap, spv_target_env env) {
|
|||
}
|
||||
|
||||
TEST_P(ValidateCFG, UnreachableBranch) {
|
||||
CompileSuccessfully(GetUnreachableBranch(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
CompileSuccessfully(GetUnreachableBranch(GetParam()));
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableBranch) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableBranch(SpvCapabilityShader, SPV_ENV_WEBGPU_0));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("all blocks must be reachable"));
|
||||
}
|
||||
|
||||
TEST_P(ValidateCFG, EmptyFunction) {
|
||||
std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) +
|
||||
R"(%func = OpFunction %voidt None %funct
|
||||
|
|
|
@ -32,13 +32,6 @@ OpCapability SampleRateShading
|
|||
)";
|
||||
}
|
||||
|
||||
std::string GetWebGPUShaderCapabilities() {
|
||||
return R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
)";
|
||||
}
|
||||
|
||||
std::string GetDefaultShaderTypes() {
|
||||
return R"(
|
||||
%void = OpTypeVoid
|
||||
|
@ -127,55 +120,6 @@ std::string GetDefaultShaderTypes() {
|
|||
)";
|
||||
}
|
||||
|
||||
std::string GetWebGPUShaderTypes() {
|
||||
return R"(
|
||||
%void = OpTypeVoid
|
||||
%func = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%f32 = OpTypeFloat 32
|
||||
%u32 = OpTypeInt 32 0
|
||||
%f32vec2 = OpTypeVector %f32 2
|
||||
%f32vec3 = OpTypeVector %f32 3
|
||||
%f32vec4 = OpTypeVector %f32 4
|
||||
%u32vec2 = OpTypeVector %u32 2
|
||||
%u32vec3 = OpTypeVector %u32 3
|
||||
%u32vec4 = OpTypeVector %u32 4
|
||||
|
||||
%f32_0 = OpConstant %f32 0
|
||||
%f32_1 = OpConstant %f32 1
|
||||
%f32_2 = OpConstant %f32 2
|
||||
%f32_3 = OpConstant %f32 3
|
||||
%f32_4 = OpConstant %f32 4
|
||||
%f32_h = OpConstant %f32 0.5
|
||||
%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
|
||||
%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
|
||||
%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2
|
||||
%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3
|
||||
%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3
|
||||
%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4
|
||||
|
||||
%u32_0 = OpConstant %u32 0
|
||||
%u32_1 = OpConstant %u32 1
|
||||
%u32_2 = OpConstant %u32 2
|
||||
%u32_3 = OpConstant %u32 3
|
||||
%u32_4 = OpConstant %u32 4
|
||||
|
||||
%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
|
||||
%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
|
||||
%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
|
||||
|
||||
%u32arr2 = OpTypeArray %u32 %u32_2
|
||||
%u32arr3 = OpTypeArray %u32 %u32_3
|
||||
%u32arr4 = OpTypeArray %u32 %u32_4
|
||||
%f32arr2 = OpTypeArray %f32 %u32_2
|
||||
%f32arr3 = OpTypeArray %f32 %u32_3
|
||||
%f32arr4 = OpTypeArray %f32 %u32_4
|
||||
|
||||
%f32vec3arr3 = OpTypeArray %f32vec3 %u32_3
|
||||
%f32vec4arr3 = OpTypeArray %f32vec4 %u32_3
|
||||
)";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CodeGenerator CodeGenerator::GetDefaultShaderCodeGenerator() {
|
||||
|
@ -186,15 +130,6 @@ CodeGenerator CodeGenerator::GetDefaultShaderCodeGenerator() {
|
|||
return generator;
|
||||
}
|
||||
|
||||
CodeGenerator CodeGenerator::GetWebGPUShaderCodeGenerator() {
|
||||
CodeGenerator generator;
|
||||
generator.capabilities_ = GetWebGPUShaderCapabilities();
|
||||
generator.memory_model_ = "OpMemoryModel Logical VulkanKHR\n";
|
||||
generator.extensions_ = "OpExtension \"SPV_KHR_vulkan_memory_model\"\n";
|
||||
generator.types_ = GetWebGPUShaderTypes();
|
||||
return generator;
|
||||
}
|
||||
|
||||
std::string CodeGenerator::Build() const {
|
||||
std::ostringstream ss;
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ struct EntryPoint {
|
|||
class CodeGenerator {
|
||||
public:
|
||||
static CodeGenerator GetDefaultShaderCodeGenerator();
|
||||
static CodeGenerator GetWebGPUShaderCodeGenerator();
|
||||
|
||||
std::string Build() const;
|
||||
|
||||
|
|
|
@ -36,24 +36,6 @@ std::string HeaderWith(std::string cap) {
|
|||
cap + " OpMemoryModel Logical GLSL450 ";
|
||||
}
|
||||
|
||||
std::string WebGPUHeaderWith(std::string cap) {
|
||||
return R"(
|
||||
OpCapability Shader
|
||||
OpCapability )" +
|
||||
cap + R"(
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
)";
|
||||
}
|
||||
|
||||
std::string webgpu_header = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
)";
|
||||
|
||||
std::string header = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
|
@ -267,18 +249,6 @@ TEST_F(ValidateData, int8_with_storage_push_constant_8_good) {
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_int8_bad) {
|
||||
std::string str = WebGPUHeaderWith("Int8") + "%2 = OpTypeInt 8 0";
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Capability Int8 is not allowed by WebGPU specification (or "
|
||||
"requires extension)\n"
|
||||
" OpCapability Int8\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, int16_good) {
|
||||
std::string str = header_with_int16 + "%2 = OpTypeInt 16 1";
|
||||
CompileSuccessfully(str.c_str());
|
||||
|
@ -338,34 +308,6 @@ TEST_F(ValidateData, int16_bad) {
|
|||
EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int16_cap_error));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_int16_bad) {
|
||||
std::string str = WebGPUHeaderWith("Int16") + "%2 = OpTypeInt 16 1";
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Capability Int16 is not allowed by WebGPU specification (or "
|
||||
"requires extension)\n"
|
||||
" OpCapability Int16\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_int32_good) {
|
||||
std::string str = webgpu_header + R"(
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, int64_good) {
|
||||
std::string str = header_with_int64 + "%2 = OpTypeInt 64 1";
|
||||
CompileSuccessfully(str.c_str());
|
||||
|
@ -379,18 +321,6 @@ TEST_F(ValidateData, int64_bad) {
|
|||
EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int64_cap_error));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_int64_bad) {
|
||||
std::string str = WebGPUHeaderWith("Int64") + "%2 = OpTypeInt 64 1";
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Capability Int64 is not allowed by WebGPU specification (or "
|
||||
"requires extension)\n"
|
||||
" OpCapability Int64\n"));
|
||||
}
|
||||
|
||||
// Number of bits in an integer may be only one of: {8,16,32,64}
|
||||
TEST_F(ValidateData, int_invalid_num_bits) {
|
||||
std::string str = header + "%2 = OpTypeInt 48 1";
|
||||
|
@ -418,34 +348,6 @@ TEST_F(ValidateData, float16_bad) {
|
|||
EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float16_cap_error));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_float16_bad) {
|
||||
std::string str = WebGPUHeaderWith("Float16") + "%2 = OpTypeFloat 16";
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Capability Float16 is not allowed by WebGPU specification (or "
|
||||
"requires extension)\n"
|
||||
" OpCapability Float16\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_float32_good) {
|
||||
std::string str = webgpu_header + R"(
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%float_t = OpTypeFloat 32
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, float64_good) {
|
||||
std::string str = header_with_float64 + "%2 = OpTypeFloat 64";
|
||||
CompileSuccessfully(str.c_str());
|
||||
|
@ -459,18 +361,6 @@ TEST_F(ValidateData, float64_bad) {
|
|||
EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float64_cap_error));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_float64_bad) {
|
||||
std::string str = WebGPUHeaderWith("Float64") + "%2 = OpTypeFloat 64";
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Capability Float64 is not allowed by WebGPU specification (or "
|
||||
"requires extension)\n"
|
||||
" OpCapability Float64\n"));
|
||||
}
|
||||
|
||||
// Number of bits in a float may be only one of: {16,32,64}
|
||||
TEST_F(ValidateData, float_invalid_num_bits) {
|
||||
std::string str = header + "%2 = OpTypeFloat 48";
|
||||
|
@ -882,68 +772,6 @@ TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) {
|
|||
"OpTypeStruct %_runtimearr_uint %uint\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_RTA_array_at_end_of_struct) {
|
||||
std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
OpDecorate %array_t ArrayStride 4
|
||||
OpMemberDecorate %struct_t 0 Offset 0
|
||||
OpMemberDecorate %struct_t 1 Offset 4
|
||||
OpDecorate %struct_t Block
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %uint_t %array_t
|
||||
%struct_ptr = OpTypePointer StorageBuffer %struct_t
|
||||
%2 = OpVariable %struct_ptr StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_RTA_not_at_end_of_struct) {
|
||||
std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
OpDecorate %array_t ArrayStride 4
|
||||
OpMemberDecorate %struct_t 0 Offset 0
|
||||
OpMemberDecorate %struct_t 1 Offset 4
|
||||
OpDecorate %struct_t Block
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t %uint_t
|
||||
%struct_ptr = OpTypePointer StorageBuffer %struct_t
|
||||
%2 = OpVariable %struct_ptr StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("In WebGPU, OpTypeRuntimeArray must only be used for "
|
||||
"the last member of an OpTypeStruct\n %_struct_3 = "
|
||||
"OpTypeStruct %_runtimearr_uint %uint\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, TypeForwardReference) {
|
||||
std::string test = R"(
|
||||
OpCapability Shader
|
||||
|
|
|
@ -42,8 +42,6 @@ struct TestResult {
|
|||
};
|
||||
|
||||
using ValidateDecorations = spvtest::ValidateBase<bool>;
|
||||
using ValidateWebGPUCombineDecorationResult =
|
||||
spvtest::ValidateBase<std::tuple<const char*, TestResult>>;
|
||||
using ValidateVulkanCombineDecorationResult =
|
||||
spvtest::ValidateBase<std::tuple<const char*, const char*, TestResult>>;
|
||||
|
||||
|
@ -165,44 +163,6 @@ TEST_F(ValidateDecorations, ValidateGroupDecorateRegistration) {
|
|||
EXPECT_THAT(vstate_->id_decorations(4), Eq(expected_decorations));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WebGPUOpDecorationGroupBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpDecorate %1 DescriptorSet 0
|
||||
OpDecorate %1 NonWritable
|
||||
OpDecorate %1 Restrict
|
||||
%1 = OpDecorationGroup
|
||||
OpGroupDecorate %1 %2 %3
|
||||
OpGroupDecorate %1 %4
|
||||
%float = OpTypeFloat 32
|
||||
%_runtimearr_float = OpTypeRuntimeArray %float
|
||||
%_struct_9 = OpTypeStruct %_runtimearr_float
|
||||
%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9
|
||||
%2 = OpVariable %_ptr_Uniform__struct_9 Uniform
|
||||
%_struct_10 = OpTypeStruct %_runtimearr_float
|
||||
%_ptr_Uniform__struct_10 = OpTypePointer Uniform %_struct_10
|
||||
%3 = OpVariable %_ptr_Uniform__struct_10 Uniform
|
||||
%_struct_11 = OpTypeStruct %_runtimearr_float
|
||||
%_ptr_Uniform__struct_11 = OpTypePointer Uniform %_struct_11
|
||||
%4 = OpVariable %_ptr_Uniform__struct_11 Uniform
|
||||
)";
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpDecorationGroup is not allowed in the WebGPU "
|
||||
"execution environment.\n %1 = OpDecorationGroup\n"));
|
||||
}
|
||||
|
||||
// For WebGPU, OpGroupDecorate does not have a test case, because it requires
|
||||
// being preceded by OpDecorationGroup, which will cause a validation error.
|
||||
|
||||
// For WebGPU, OpGroupMemberDecorate does not have a test case, because it
|
||||
// requires being preceded by OpDecorationGroup, which will cause a validation
|
||||
// error.
|
||||
|
||||
TEST_F(ValidateDecorations, ValidateGroupMemberDecorateRegistration) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -6390,67 +6350,6 @@ TEST_F(ValidateDecorations, NonWritableRuntimeArrayGood) {
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_P(ValidateWebGPUCombineDecorationResult, Decorate) {
|
||||
const char* const decoration = std::get<0>(GetParam());
|
||||
const TestResult& test_result = std::get<1>(GetParam());
|
||||
|
||||
CodeGenerator generator = CodeGenerator::GetWebGPUShaderCodeGenerator();
|
||||
generator.before_types_ = "OpDecorate %u32 ";
|
||||
generator.before_types_ += decoration;
|
||||
generator.before_types_ += "\n";
|
||||
|
||||
EntryPoint entry_point;
|
||||
entry_point.name = "main";
|
||||
entry_point.execution_model = "Vertex";
|
||||
generator.entry_points_.push_back(std::move(entry_point));
|
||||
|
||||
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(test_result.validation_result,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
if (test_result.error_str != "") {
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ValidateWebGPUCombineDecorationResult, DecorateMember) {
|
||||
const char* const decoration = std::get<0>(GetParam());
|
||||
const TestResult& test_result = std::get<1>(GetParam());
|
||||
|
||||
CodeGenerator generator = CodeGenerator::GetWebGPUShaderCodeGenerator();
|
||||
generator.before_types_ = "OpMemberDecorate %struct_type 0 ";
|
||||
generator.before_types_ += decoration;
|
||||
generator.before_types_ += "\n";
|
||||
|
||||
generator.after_types_ = "%struct_type = OpTypeStruct %u32\n";
|
||||
|
||||
EntryPoint entry_point;
|
||||
entry_point.name = "main";
|
||||
entry_point.execution_model = "Vertex";
|
||||
generator.entry_points_.push_back(std::move(entry_point));
|
||||
|
||||
CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(test_result.validation_result,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
if (!test_result.error_str.empty()) {
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DecorationCapabilityFailure, ValidateWebGPUCombineDecorationResult,
|
||||
Combine(Values("CPacked", "Patch", "Sample", "Constant",
|
||||
"SaturatedConversion", "NonUniformEXT"),
|
||||
Values(TestResult(SPV_ERROR_INVALID_CAPABILITY,
|
||||
"requires one of these capabilities"))));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DecorationAllowListFailure, ValidateWebGPUCombineDecorationResult,
|
||||
Combine(Values("RelaxedPrecision", "BufferBlock", "GLSLShared",
|
||||
"GLSLPacked", "Invariant", "Volatile", "Coherent"),
|
||||
Values(TestResult(
|
||||
SPV_ERROR_INVALID_ID,
|
||||
"is not valid for the WebGPU execution environment."))));
|
||||
|
||||
TEST_P(ValidateVulkanCombineDecorationResult, Decorate) {
|
||||
const char* const decoration = std::get<0>(GetParam());
|
||||
const char* const vuid = std::get<1>(GetParam());
|
||||
|
|
|
@ -954,31 +954,6 @@ TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InWebGPU) {
|
||||
env_ = SPV_ENV_WEBGPU_0;
|
||||
const int width = GetParam();
|
||||
// WebGPU only has 32 bit integers.
|
||||
if (width != 32) return;
|
||||
const int max_int_width = 32;
|
||||
const auto module = CompileSuccessfully(MakeArrayLength(
|
||||
big_num_ending_0(width), kUnsigned, width, max_int_width, true));
|
||||
EXPECT_EQ(SPV_SUCCESS, Val(module));
|
||||
}
|
||||
|
||||
TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InWebGPU) {
|
||||
env_ = SPV_ENV_WEBGPU_0;
|
||||
const int width = GetParam();
|
||||
// WebGPU only has 32 bit integers.
|
||||
if (width != 32) return;
|
||||
const int max_int_width = 32;
|
||||
const auto module = CompileSuccessfully(MakeArrayLength(
|
||||
big_num_ending_1(width), kUnsigned, width, max_int_width, true));
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID,
|
||||
Val(module,
|
||||
"OpTypeArray Length <id> '3\\[%.*\\]' size exceeds max value "
|
||||
"2147483648 permitted by WebGPU: got 2147483649"));
|
||||
}
|
||||
|
||||
// The only valid widths for integers are 8, 16, 32, and 64.
|
||||
// Since the Int8 capability requires the Kernel capability, and the Kernel
|
||||
// capability prohibits usage of signed integers, we can skip 8-bit integers
|
||||
|
@ -4731,39 +4706,6 @@ TEST_F(ValidateIdWithMessage, OpVectorShuffleLiterals) {
|
|||
"size of 5."));
|
||||
}
|
||||
|
||||
TEST_F(ValidateIdWithMessage, WebGPUOpVectorShuffle0xFFFFFFFFLiteralBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%float = OpTypeFloat 32
|
||||
%vec2 = OpTypeVector %float 2
|
||||
%vec3 = OpTypeVector %float 3
|
||||
%vec4 = OpTypeVector %float 4
|
||||
%ptr_vec2 = OpTypePointer Function %vec2
|
||||
%ptr_vec3 = OpTypePointer Function %vec3
|
||||
%float_1 = OpConstant %float 1
|
||||
%float_2 = OpConstant %float 2
|
||||
%1 = OpConstantComposite %vec2 %float_2 %float_1
|
||||
%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2
|
||||
%3 = OpTypeFunction %vec4
|
||||
%4 = OpFunction %vec4 None %3
|
||||
%5 = OpLabel
|
||||
%var = OpVariable %ptr_vec2 Function %1
|
||||
%var2 = OpVariable %ptr_vec3 Function %2
|
||||
%6 = OpLoad %vec2 %var
|
||||
%7 = OpLoad %vec3 %var2
|
||||
%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff
|
||||
OpReturnValue %8
|
||||
OpFunctionEnd)";
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Component literal at operand 3 cannot be 0xFFFFFFFF in"
|
||||
" WebGPU execution environment."));
|
||||
}
|
||||
|
||||
// TODO: OpCompositeConstruct
|
||||
// TODO: OpCompositeExtract
|
||||
// TODO: OpCompositeInsert
|
||||
|
|
|
@ -454,26 +454,6 @@ std::string TrivialMain() {
|
|||
)";
|
||||
}
|
||||
|
||||
std::string GetWebGPUShaderHeader() {
|
||||
return R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
%void = OpTypeVoid
|
||||
%func = OpTypeFunction %void
|
||||
%f32 = OpTypeFloat 32
|
||||
%u32 = OpTypeInt 32 0
|
||||
%f32vec2 = OpTypeVector %f32 2
|
||||
%f32vec3 = OpTypeVector %f32 3
|
||||
%f32vec4 = OpTypeVector %f32 4
|
||||
%u32vec2 = OpTypeVector %u32 2
|
||||
%u32vec3 = OpTypeVector %u32 3
|
||||
%u32vec4 = OpTypeVector %u32 4
|
||||
%u32vec2null = OpConstantNull %u32vec2
|
||||
)";
|
||||
}
|
||||
|
||||
std::string GetShaderHeader(const std::string& capabilities_and_extensions = "",
|
||||
bool include_entry_point = true) {
|
||||
std::ostringstream ss;
|
||||
|
@ -833,37 +813,6 @@ TEST_F(ValidateImage, TypeImage_Vulkan_Sampled0_Invalid) {
|
|||
HasSubstr("Sampled must be 1 or 2 in the Vulkan environment."));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, TypeImage_WebGPU_Sampled1_OK) {
|
||||
const std::string code = GetWebGPUShaderHeader() + R"(
|
||||
%img_type = OpTypeImage %f32 2D 0 0 0 1 Unknown
|
||||
)" + TrivialMain();
|
||||
|
||||
CompileSuccessfully(code.c_str());
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, TypeImage_WebGPU_Sampled2_OK) {
|
||||
const std::string code = GetWebGPUShaderHeader() + R"(
|
||||
%img_type = OpTypeImage %f32 2D 0 0 0 2 Rgba32f
|
||||
)" + TrivialMain();
|
||||
|
||||
CompileSuccessfully(code.c_str());
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(), Eq(""));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, TypeImage_WebGPU_Sampled0_Invalid) {
|
||||
const std::string code = GetWebGPUShaderHeader() + R"(
|
||||
%img_type = OpTypeImage %f32 2D 0 0 0 0 Unknown
|
||||
)" + TrivialMain();
|
||||
|
||||
CompileSuccessfully(code.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Sampled must be 1 or 2 in the WebGPU environment."));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, TypeImageWrongFormatForSubpassData) {
|
||||
const std::string code =
|
||||
GetShaderHeader("OpCapability InputAttachment\n", false) +
|
||||
|
@ -3322,26 +3271,6 @@ TEST_F(ValidateImage, ReadWrongNumComponentsResultType_Vulkan) {
|
|||
HasSubstr("Expected Result Type to have 4 components"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ReadWrongNumComponentsResultType_WebGPU) {
|
||||
const std::string code = GetWebGPUShaderHeader() + R"(
|
||||
%img_type = OpTypeImage %f32 2D 0 0 0 2 Unknown
|
||||
%ptr_type = OpTypePointer UniformConstant %img_type
|
||||
%var = OpVariable %ptr_type UniformConstant
|
||||
%main = OpFunction %void None %func
|
||||
%entry = OpLabel
|
||||
%img = OpLoad %img_type %var
|
||||
%res1 = OpImageRead %u32vec3 %img %u32vec2null
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(code.c_str());
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0))
|
||||
<< getDiagnosticString();
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Expected Result Type to have 4 components"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateImage, ReadNotImage) {
|
||||
const std::string body = R"(
|
||||
%sampler = OpLoad %type_sampler %uniform_sampler
|
||||
|
|
|
@ -667,57 +667,6 @@ TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) {
|
|||
HasSubstr("ModuleProcessed cannot appear in a function declaration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLayout, WebGPUCallerBeforeCalleeBad) {
|
||||
char str[] = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
%void = OpTypeVoid
|
||||
%voidfn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %voidfn
|
||||
%1 = OpLabel
|
||||
%2 = OpFunctionCall %void %callee
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%callee = OpFunction %void None %voidfn
|
||||
%3 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str, SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, functions need to be defined before being "
|
||||
"called.\n %5 = OpFunctionCall %void %6\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateLayout, WebGPUCalleeBeforeCallerGood) {
|
||||
char str[] = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
%void = OpTypeVoid
|
||||
%voidfn = OpTypeFunction %void
|
||||
%callee = OpFunction %void None %voidfn
|
||||
%3 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %voidfn
|
||||
%1 = OpLabel
|
||||
%2 = OpFunctionCall %void %callee
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str, SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
// TODO(umar): Test optional instructions
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -362,192 +362,6 @@ OpFunctionEnd
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPUInitializerWithOutputStorageClassesGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%float = OpTypeFloat 32
|
||||
%float_ptr = OpTypePointer Output %float
|
||||
%init_val = OpConstant %float 1.0
|
||||
%1 = OpVariable %float_ptr Output %init_val
|
||||
%void = OpTypeVoid
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPUInitializerWithFunctionStorageClassesGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%float = OpTypeFloat 32
|
||||
%float_ptr = OpTypePointer Function %float
|
||||
%init_val = OpConstant %float 1.0
|
||||
%void = OpTypeVoid
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%1 = OpLabel
|
||||
%2 = OpVariable %float_ptr Function %init_val
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPUInitializerWithPrivateStorageClassesGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%float = OpTypeFloat 32
|
||||
%float_ptr = OpTypePointer Private %float
|
||||
%init_val = OpConstant %float 1.0
|
||||
%1 = OpVariable %float_ptr Private %init_val
|
||||
%void = OpTypeVoid
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPUInitializerWithDisallowedStorageClassesBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%float = OpTypeFloat 32
|
||||
%float_ptr = OpTypePointer Uniform %float
|
||||
%init_val = OpConstant %float 1.0
|
||||
%1 = OpVariable %float_ptr Uniform %init_val
|
||||
%void = OpTypeVoid
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpVariable, <id> '5[%5]', has a disallowed initializer & "
|
||||
"storage class combination.\nFrom WebGPU spec:\nVariable "
|
||||
"declarations that include initializers must have one of the "
|
||||
"following storage classes: Output, Private, or Function\n %5 "
|
||||
"= OpVariable %_ptr_Uniform_float Uniform %float_1\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPUOutputStorageClassWithoutInitializerBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%float = OpTypeFloat 32
|
||||
%float_ptr = OpTypePointer Output %float
|
||||
%1 = OpVariable %float_ptr Output
|
||||
%void = OpTypeVoid
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpVariable, <id> '4[%4]', must have an initializer.\n"
|
||||
"From WebGPU execution environment spec:\n"
|
||||
"All variables in the following storage classes must have an "
|
||||
"initializer: Output, Private, or Function\n"
|
||||
" %4 = OpVariable %_ptr_Output_float Output\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPUFunctionStorageClassWithoutInitializerBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%float = OpTypeFloat 32
|
||||
%float_ptr = OpTypePointer Function %float
|
||||
%void = OpTypeVoid
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%1 = OpLabel
|
||||
%2 = OpVariable %float_ptr Function
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpVariable, <id> '7[%7]', must have an initializer.\n"
|
||||
"From WebGPU execution environment spec:\n"
|
||||
"All variables in the following storage classes must have an "
|
||||
"initializer: Output, Private, or Function\n"
|
||||
" %7 = OpVariable %_ptr_Function_float Function\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPUPrivateStorageClassWithoutInitializerBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%float = OpTypeFloat 32
|
||||
%float_ptr = OpTypePointer Private %float
|
||||
%1 = OpVariable %float_ptr Private
|
||||
%void = OpTypeVoid
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpVariable, <id> '4[%4]', must have an initializer.\n"
|
||||
"From WebGPU execution environment spec:\n"
|
||||
"All variables in the following storage classes must have an "
|
||||
"initializer: Output, Private, or Function\n"
|
||||
" %4 = OpVariable %_ptr_Private_float Private\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanInitializerWithOutputStorageClassesGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -2282,38 +2096,6 @@ OpFunctionEnd
|
|||
"%_ptr_UniformConstant__runtimearr_2 UniformConstant\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAOutsideOfStructBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%sampler_t = OpTypeSampler
|
||||
%array_t = OpTypeRuntimeArray %sampler_t
|
||||
%array_ptr = OpTypePointer UniformConstant %array_t
|
||||
%2 = OpVariable %array_ptr UniformConstant
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"OpVariable, <id> '5[%5]', is attempting to create memory for an "
|
||||
"illegal type, OpTypeRuntimeArray.\nFor WebGPU OpTypeRuntimeArray "
|
||||
"can only appear as the final member of an OpTypeStruct, thus cannot "
|
||||
"be instantiated via OpVariable\n %5 = OpVariable "
|
||||
"%_ptr_UniformConstant__runtimearr_2 UniformConstant\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAOutsideOfStructWithRuntimeDescriptorArrayGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -2403,34 +2185,6 @@ OpFunctionEnd
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideStorageBufferStructGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
OpDecorate %array_t ArrayStride 4
|
||||
OpMemberDecorate %struct_t 0 Offset 0
|
||||
OpDecorate %struct_t Block
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t
|
||||
%struct_ptr = OpTypePointer StorageBuffer %struct_t
|
||||
%2 = OpVariable %struct_ptr StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideWrongStorageClassStructBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -2460,36 +2214,6 @@ OpFunctionEnd
|
|||
"OpVariable %_ptr_Workgroup__struct_4 Workgroup\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideWrongStorageClassStructBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t
|
||||
%struct_ptr = OpTypePointer Workgroup %struct_t
|
||||
%2 = OpVariable %struct_ptr Workgroup
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, OpTypeStruct variables containing "
|
||||
"OpTypeRuntimeArray must have storage class of StorageBuffer\n "
|
||||
" %6 = OpVariable %_ptr_Workgroup__struct_4 Workgroup\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideStorageBufferStructWithoutBlockBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -2518,36 +2242,6 @@ OpFunctionEnd
|
|||
"%_ptr_StorageBuffer__struct_4 StorageBuffer\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideStorageBufferStructWithoutBlockBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t
|
||||
%struct_ptr = OpTypePointer StorageBuffer %struct_t
|
||||
%2 = OpVariable %struct_ptr StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, an OpTypeStruct variable containing an "
|
||||
"OpTypeRuntimeArray must be decorated with Block if it "
|
||||
"has storage class StorageBuffer.\n %6 = OpVariable "
|
||||
"%_ptr_StorageBuffer__struct_4 StorageBuffer\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideUniformStructGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -2574,39 +2268,6 @@ OpFunctionEnd
|
|||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideUniformStructBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
OpDecorate %array_t ArrayStride 4
|
||||
OpMemberDecorate %struct_t 0 Offset 0
|
||||
OpDecorate %struct_t Block
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t
|
||||
%struct_ptr = OpTypePointer Uniform %struct_t
|
||||
%2 = OpVariable %struct_ptr Uniform
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, OpTypeStruct variables containing "
|
||||
"OpTypeRuntimeArray must have storage class of StorageBuffer\n "
|
||||
" %6 = OpVariable %_ptr_Uniform__struct_3 Uniform\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlockBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
@ -2664,37 +2325,6 @@ OpFunctionEnd
|
|||
"OpTypeRuntimeArray %_runtimearr_2\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideRTABad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%sampler_t = OpTypeSampler
|
||||
%inner_array_t = OpTypeRuntimeArray %sampler_t
|
||||
%array_t = OpTypeRuntimeArray %inner_array_t
|
||||
%array_ptr = OpTypePointer UniformConstant %array_t
|
||||
%2 = OpVariable %array_ptr UniformConstant
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"OpTypeRuntimeArray Element Type <id> '3[%_runtimearr_2]' is not "
|
||||
"valid in WebGPU environments.\n %_runtimearr__runtimearr_2 = "
|
||||
"OpTypeRuntimeArray %_runtimearr_2\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideRTAWithRuntimeDescriptorArrayBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability RuntimeDescriptorArrayEXT
|
||||
|
@ -2855,38 +2485,6 @@ OpFunctionEnd
|
|||
"OpTypeArray %_runtimearr_4 %uint_1\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideArrayBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%dim = OpConstant %uint_t 1
|
||||
%sampler_t = OpTypeSampler
|
||||
%inner_array_t = OpTypeRuntimeArray %sampler_t
|
||||
%array_t = OpTypeArray %inner_array_t %dim
|
||||
%array_ptr = OpTypePointer UniformConstant %array_t
|
||||
%2 = OpVariable %array_ptr UniformConstant
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not "
|
||||
"valid in WebGPU environments.\n %_arr__runtimearr_4_uint_1 = "
|
||||
"OpTypeArray %_runtimearr_4 %uint_1\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideArrayWithRuntimeDescriptorArrayBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability RuntimeDescriptorArrayEXT
|
||||
|
|
|
@ -535,24 +535,16 @@ TEST_P(ValidateModeExecution, ExecutionMode) {
|
|||
|
||||
std::ostringstream sstr;
|
||||
sstr << "OpCapability Shader\n";
|
||||
if (!spvIsWebGPUEnv(env)) {
|
||||
sstr << "OpCapability Geometry\n";
|
||||
sstr << "OpCapability Tessellation\n";
|
||||
sstr << "OpCapability TransformFeedback\n";
|
||||
}
|
||||
if (!spvIsVulkanOrWebGPUEnv(env)) {
|
||||
sstr << "OpCapability Geometry\n";
|
||||
sstr << "OpCapability Tessellation\n";
|
||||
sstr << "OpCapability TransformFeedback\n";
|
||||
if (!spvIsVulkanEnv(env)) {
|
||||
sstr << "OpCapability Kernel\n";
|
||||
if (env == SPV_ENV_UNIVERSAL_1_3) {
|
||||
sstr << "OpCapability SubgroupDispatch\n";
|
||||
}
|
||||
}
|
||||
if (spvIsWebGPUEnv(env)) {
|
||||
sstr << "OpCapability VulkanMemoryModelKHR\n";
|
||||
sstr << "OpExtension \"SPV_KHR_vulkan_memory_model\"\n";
|
||||
sstr << "OpMemoryModel Logical VulkanKHR\n";
|
||||
} else {
|
||||
sstr << "OpMemoryModel Logical GLSL450\n";
|
||||
}
|
||||
sstr << "OpMemoryModel Logical GLSL450\n";
|
||||
sstr << "OpEntryPoint " << model << " %main \"main\"\n";
|
||||
if (mode.find("LocalSizeId") == 0 || mode.find("LocalSizeHintId") == 0 ||
|
||||
mode.find("SubgroupsPerWorkgroupId") == 0) {
|
||||
|
@ -718,39 +710,6 @@ INSTANTIATE_TEST_SUITE_P(
|
|||
"SubgroupsPerWorkgroup 1", "SubgroupsPerWorkgroupId %int1"),
|
||||
Values(SPV_ENV_UNIVERSAL_1_3)));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ValidateModeGLComputeWebGPUAllowListGood,
|
||||
ValidateModeExecution,
|
||||
Combine(Values(SPV_SUCCESS), Values(""),
|
||||
Values("GLCompute"), Values("LocalSize 1 1 1"),
|
||||
Values(SPV_ENV_WEBGPU_0)));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ValidateModeGLComputeWebGPUAllowListBad, ValidateModeExecution,
|
||||
Combine(Values(SPV_ERROR_INVALID_DATA),
|
||||
Values("Execution mode must be one of OriginUpperLeft, "
|
||||
"DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
|
||||
"LocalSize, or LocalSizeHint for WebGPU environment"),
|
||||
Values("GLCompute"), Values("LocalSizeId %int1 %int1 %int1"),
|
||||
Values(SPV_ENV_WEBGPU_0)));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ValidateModeFragmentWebGPUAllowListGood, ValidateModeExecution,
|
||||
Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"),
|
||||
Values("OriginUpperLeft", "DepthReplacing", "DepthGreater",
|
||||
"DepthLess", "DepthUnchanged"),
|
||||
Values(SPV_ENV_WEBGPU_0)));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ValidateModeFragmentWebGPUAllowListBad, ValidateModeExecution,
|
||||
Combine(Values(SPV_ERROR_INVALID_DATA),
|
||||
Values("Execution mode must be one of OriginUpperLeft, "
|
||||
"DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
|
||||
"LocalSize, or LocalSizeHint for WebGPU environment"),
|
||||
Values("Fragment"),
|
||||
Values("PixelCenterInteger", "OriginLowerLeft",
|
||||
"EarlyFragmentTests"),
|
||||
Values(SPV_ENV_WEBGPU_0)));
|
||||
|
||||
TEST_F(ValidateModeExecution, MeshNVLocalSize) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
|
|
|
@ -250,70 +250,6 @@ TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParamBad) {
|
|||
HasSubstr("OpFunctionCall Argument <id> '"));
|
||||
}
|
||||
|
||||
std::string GetVarDeclStr(const std::string& storage_class) {
|
||||
if (storage_class != "Output" && storage_class != "Private" &&
|
||||
storage_class != "Function") {
|
||||
return "%var = OpVariable %ptrt " + storage_class + "\n";
|
||||
} else {
|
||||
return "%var = OpVariable %ptrt " + storage_class + " %null\n";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ValidateStorageClass, WebGPU) {
|
||||
std::string storage_class = std::get<0>(GetParam());
|
||||
bool is_local = std::get<1>(GetParam());
|
||||
bool is_valid = std::get<2>(GetParam());
|
||||
std::string error = std::get<3>(GetParam());
|
||||
|
||||
std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%intt = OpTypeInt 32 1
|
||||
%voidt = OpTypeVoid
|
||||
%vfunct = OpTypeFunction %voidt
|
||||
%null = OpConstantNull %intt
|
||||
)";
|
||||
str += "%ptrt = OpTypePointer " + storage_class + " %intt\n";
|
||||
if (!is_local) str += GetVarDeclStr(storage_class);
|
||||
str += R"(
|
||||
%func = OpFunction %voidt None %vfunct
|
||||
%funcl = OpLabel
|
||||
)";
|
||||
if (is_local) str += GetVarDeclStr(storage_class);
|
||||
str += R"(
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str, SPV_ENV_WEBGPU_0);
|
||||
if (is_valid) {
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
} else {
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr(error));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
StorageClass, ValidateStorageClass,
|
||||
Values(std::make_tuple("UniformConstant", false, true, ""),
|
||||
std::make_tuple("Uniform", false, true, ""),
|
||||
std::make_tuple("StorageBuffer", false, true, ""),
|
||||
std::make_tuple("Input", false, true, ""),
|
||||
std::make_tuple("Output", false, true, ""),
|
||||
std::make_tuple("Image", false, true, ""),
|
||||
std::make_tuple("Workgroup", false, true, ""),
|
||||
std::make_tuple("Private", false, true, ""),
|
||||
std::make_tuple("Function", true, true, ""),
|
||||
std::make_tuple("CrossWorkgroup", false, false,
|
||||
"Invalid storage class for target environment"),
|
||||
std::make_tuple("PushConstant", false, false,
|
||||
"Invalid storage class for target environment")));
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -245,12 +245,6 @@ TEST_F(ValidationStateTest, CheckVulkanNonRecursiveBodyGood) {
|
|||
ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
|
||||
}
|
||||
|
||||
TEST_F(ValidationStateTest, CheckWebGPUNonRecursiveBodyGood) {
|
||||
std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody;
|
||||
CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidationStateTest, CheckDirectlyRecursiveBodyGood) {
|
||||
std::string spirv = std::string(kHeader) + kDirectlyRecursiveBody;
|
||||
CompileSuccessfully(spirv);
|
||||
|
@ -267,16 +261,6 @@ TEST_F(ValidationStateTest, CheckVulkanDirectlyRecursiveBodyBad) {
|
|||
" %1 = OpFunction %void Pure|Const %3\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidationStateTest, CheckWebGPUDirectlyRecursiveBodyBad) {
|
||||
std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody;
|
||||
CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Entry points may not have a call graph with cycles.\n "
|
||||
" %1 = OpFunction %void Pure|Const %3\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidationStateTest, CheckIndirectlyRecursiveBodyGood) {
|
||||
std::string spirv = std::string(kHeader) + kIndirectlyRecursiveBody;
|
||||
CompileSuccessfully(spirv);
|
||||
|
@ -294,68 +278,6 @@ TEST_F(ValidationStateTest, CheckVulkanIndirectlyRecursiveBodyBad) {
|
|||
" %1 = OpFunction %void Pure|Const %3\n"));
|
||||
}
|
||||
|
||||
// Indirectly recursive functions are caught by the function definition layout
|
||||
// rules, because they cause a situation where there are 2 functions that have
|
||||
// to be before each other, and layout is checked earlier.
|
||||
TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) {
|
||||
std::string spirv =
|
||||
std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody;
|
||||
CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, functions need to be defined before being "
|
||||
"called.\n %10 = OpFunctionCall %_struct_5 %11\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidationStateTest,
|
||||
CheckWebGPUDuplicateEntryNamesDifferentFunctionsBad) {
|
||||
std::string spirv = std::string(kVulkanMemoryHeader) + R"(
|
||||
OpEntryPoint Fragment %func_1 "main"
|
||||
OpEntryPoint Vertex %func_2 "main"
|
||||
OpExecutionMode %func_1 OriginUpperLeft
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func_1 = OpFunction %void None %void_f
|
||||
%label_1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%func_2 = OpFunction %void None %void_f
|
||||
%label_2 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Entry point name \"main\" is not unique, which is not allow "
|
||||
"in WebGPU env.\n %1 = OpFunction %void None %4\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidationStateTest, CheckWebGPUDuplicateEntryNamesSameFunctionBad) {
|
||||
std::string spirv = std::string(kVulkanMemoryHeader) + R"(
|
||||
OpEntryPoint GLCompute %func_1 "main"
|
||||
OpEntryPoint Vertex %func_1 "main"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func_1 = OpFunction %void None %void_f
|
||||
%label_1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Entry point name \"main\" is not unique, which is not allow "
|
||||
"in WebGPU env.\n %1 = OpFunction %void None %3\n"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
|
|
@ -40,21 +40,6 @@ OpReturn
|
|||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string webgpu_spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%void = OpTypeVoid
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string opencl_spirv = R"(
|
||||
OpCapability Addresses
|
||||
OpCapability Kernel
|
||||
|
@ -85,7 +70,6 @@ std::string version(spv_target_env env) {
|
|||
return "1.2";
|
||||
case SPV_ENV_UNIVERSAL_1_3:
|
||||
case SPV_ENV_VULKAN_1_1:
|
||||
case SPV_ENV_WEBGPU_0:
|
||||
return "1.3";
|
||||
case SPV_ENV_UNIVERSAL_1_4:
|
||||
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
|
||||
|
@ -126,7 +110,6 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion,
|
|||
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_WEBGPU_0, webgpu_spirv, true),
|
||||
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true),
|
||||
|
@ -139,7 +122,6 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion,
|
|||
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_WEBGPU_0, webgpu_spirv, true),
|
||||
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
|
||||
|
@ -152,7 +134,6 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion,
|
|||
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_WEBGPU_0, webgpu_spirv, true),
|
||||
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false),
|
||||
|
@ -164,8 +145,7 @@ INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion,
|
|||
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false),
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_WEBGPU_0, webgpu_spirv, true)
|
||||
std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false)
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -1,424 +0,0 @@
|
|||
// Copyright (c) 2018 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.
|
||||
|
||||
// Validation tests for WebGPU env specific checks
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "test/val/val_fixtures.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
using testing::HasSubstr;
|
||||
|
||||
using ValidateWebGPU = spvtest::ValidateBase<bool>;
|
||||
|
||||
TEST_F(ValidateWebGPU, OpUndefIsDisallowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%float = OpTypeFloat 32
|
||||
%1 = OpUndef %float
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
// Control case: OpUndef is allowed in SPIR-V 1.3
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
|
||||
|
||||
// Control case: OpUndef is disallowed in the WebGPU env
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpNameIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpName %1 "foo"
|
||||
%1 = OpTypeFloat 32
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpMemberName %2 0 "foo"
|
||||
%1 = OpTypeFloat 32
|
||||
%2 = OpTypeStruct %1
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpSourceIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpSource GLSL 450
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpSource GLSL 450
|
||||
OpSourceContinued "I am a happy shader! Yay! ;"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpSourceExtension "bar"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpStringIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%1 = OpString "foo"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpLineIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%1 = OpString "minimal.vert"
|
||||
OpLine %1 1 1
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpNoLineIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpNoLine
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, LogicalAddressingGLSL450MemoryGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, LogicalAddressingSimpleMemoryGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, KernelIsBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Kernel
|
||||
OpMemoryModel Logical Simple
|
||||
OpNoLine
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Capability Kernel is not allowed by WebGPU "
|
||||
"specification (or requires extension)"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpenCLMemoryModelBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical OpenCL
|
||||
OpNoLine
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY,
|
||||
ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Operand 2 of MemoryModel requires one of these "
|
||||
"capabilities: Kernel"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, AllowListedExtendedInstructionsImportGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, NonAllowListedExtendedInstructionsImportBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
%1 = OpExtInstImport "OpenCL.std"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, the only valid parameter to "
|
||||
"OpExtInstImport is \"GLSL.std.450\".\n %1 = "
|
||||
"OpExtInstImport \"OpenCL.std\"\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelExtensionBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_8bit_storage"
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, the only valid parameter to OpExtension "
|
||||
"is \"SPV_KHR_vulkan_memory_model\".\n OpExtension "
|
||||
"\"SPV_KHR_8bit_storage\"\n"));
|
||||
}
|
||||
|
||||
spv_binary GenerateTrivialBinary(bool need_little_endian) {
|
||||
// Smallest possible valid WebGPU SPIR-V binary in little endian. Contains all
|
||||
// the required boilerplate and a trivial entry point function.
|
||||
static const uint8_t binary_bytes[] = {
|
||||
// clang-format off
|
||||
0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00,
|
||||
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
|
||||
0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0xE1, 0x14, 0x00, 0x00,
|
||||
0x0A, 0x00, 0x08, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F,
|
||||
0x76, 0x75, 0x6C, 0x6B, 0x61, 0x6E, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72,
|
||||
0x79, 0x5F, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x00, 0x0E, 0x00, 0x03, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x05, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64,
|
||||
0x65, 0x72, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
|
||||
// clang-format on
|
||||
};
|
||||
static const size_t word_count = sizeof(binary_bytes) / sizeof(uint32_t);
|
||||
std::unique_ptr<spv_binary_t> result(new spv_binary_t);
|
||||
if (!result) return nullptr;
|
||||
|
||||
result->wordCount = word_count;
|
||||
result->code = new uint32_t[word_count];
|
||||
if (!result->code) return nullptr;
|
||||
|
||||
if (need_little_endian) {
|
||||
memcpy(result->code, binary_bytes, sizeof(binary_bytes));
|
||||
} else {
|
||||
uint8_t* code_bytes = reinterpret_cast<uint8_t*>(result->code);
|
||||
for (size_t word = 0; word < word_count; ++word) {
|
||||
code_bytes[4 * word] = binary_bytes[4 * word + 3];
|
||||
code_bytes[4 * word + 1] = binary_bytes[4 * word + 2];
|
||||
code_bytes[4 * word + 2] = binary_bytes[4 * word + 1];
|
||||
code_bytes[4 * word + 3] = binary_bytes[4 * word];
|
||||
}
|
||||
}
|
||||
|
||||
return result.release();
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, LittleEndianGood) {
|
||||
DestroyBinary();
|
||||
binary_ = GenerateTrivialBinary(true);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, BigEndianBad) {
|
||||
DestroyBinary();
|
||||
binary_ = GenerateTrivialBinary(false);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("WebGPU requires SPIR-V to be little endian."));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
|
@ -79,18 +79,6 @@ std::string GetSizePasses() {
|
|||
return GetListOfPassesAsString(optimizer);
|
||||
}
|
||||
|
||||
std::string GetVulkanToWebGPUPasses() {
|
||||
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1);
|
||||
optimizer.RegisterVulkanToWebGPUPasses();
|
||||
return GetListOfPassesAsString(optimizer);
|
||||
}
|
||||
|
||||
std::string GetWebGPUToVulkanPasses() {
|
||||
spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0);
|
||||
optimizer.RegisterWebGPUToVulkanPasses();
|
||||
return GetListOfPassesAsString(optimizer);
|
||||
}
|
||||
|
||||
void PrintUsage(const char* program) {
|
||||
std::string target_env_list = spvTargetEnvList(16, 80);
|
||||
// NOTE: Please maintain flags in lexicographical order.
|
||||
|
@ -239,10 +227,6 @@ Options (in lexicographical order):)",
|
|||
values, providing guarantees that satisfy Vulkan's
|
||||
robustBufferAccess rules.)");
|
||||
printf(R"(
|
||||
--generate-webgpu-initializers
|
||||
Adds initial values to OpVariable instructions that are missing
|
||||
them, due to their storage type requiring them for WebGPU.)");
|
||||
printf(R"(
|
||||
--if-conversion
|
||||
Convert if-then-else like assignments into OpSelect.)");
|
||||
printf(R"(
|
||||
|
@ -261,11 +245,6 @@ Options (in lexicographical order):)",
|
|||
option --relax-logical-pointer to the validator.)",
|
||||
GetLegalizationPasses().c_str());
|
||||
printf(R"(
|
||||
--legalize-vector-shuffle
|
||||
Converts any usages of 0xFFFFFFFF for the literals in
|
||||
OpVectorShuffle to a literal 0. This is done since 0xFFFFFFFF is
|
||||
forbidden in WebGPU.)");
|
||||
printf(R"(
|
||||
--local-redundancy-elimination
|
||||
Looks for instructions in the same basic block that compute the
|
||||
same value, and deletes the redundant ones.)");
|
||||
|
@ -463,13 +442,6 @@ Options (in lexicographical order):)",
|
|||
Forwards this option to the validator. See the validator help
|
||||
for details.)");
|
||||
printf(R"(
|
||||
--split-invalid-unreachable
|
||||
Attempts to legalize for WebGPU cases where an unreachable
|
||||
merge-block is also a continue-target by splitting it into two
|
||||
separate blocks. There exist legal, for Vulkan, instances of this
|
||||
pattern that cannot be converted into legal WebGPU, so this
|
||||
conversion may not succeed.)");
|
||||
printf(R"(
|
||||
--skip-validation
|
||||
Will not validate the SPIR-V before optimizing. If the SPIR-V
|
||||
is invalid, the optimizer may fail or generate incorrect code.
|
||||
|
@ -513,34 +485,6 @@ Options (in lexicographical order):)",
|
|||
removes them from the vector. Note this would still leave around
|
||||
lots of dead code that a pass of ADCE will be able to remove.)");
|
||||
printf(R"(
|
||||
--vulkan-to-webgpu
|
||||
Turns on the prescribed passes for converting from Vulkan to
|
||||
WebGPU and sets the target environment to webgpu0. Other passes
|
||||
may be turned on via additional flags, but such combinations are
|
||||
not tested.
|
||||
Using --target-env with this flag is not allowed.
|
||||
|
||||
This flag is the equivalent of passing in --target-env=webgpu0
|
||||
and specifying the following optimization code names:
|
||||
%s
|
||||
|
||||
NOTE: This flag is a WIP and its behaviour is subject to change.)",
|
||||
GetVulkanToWebGPUPasses().c_str());
|
||||
printf(R"(
|
||||
--webgpu-to-vulkan
|
||||
Turns on the prescribed passes for converting from WebGPU to
|
||||
Vulkan and sets the target environment to vulkan1.1. Other passes
|
||||
may be turned on via additional flags, but such combinations are
|
||||
not tested.
|
||||
Using --target-env with this flag is not allowed.
|
||||
|
||||
This flag is the equivalent of passing in --target-env=vulkan1.1
|
||||
and specifying the following optimization code names:
|
||||
%s
|
||||
|
||||
NOTE: This flag is a WIP and its behaviour is subject to change.)",
|
||||
GetWebGPUToVulkanPasses().c_str());
|
||||
printf(R"(
|
||||
--workaround-1209
|
||||
Rewrites instructions for which there are known driver bugs to
|
||||
avoid triggering those bugs.
|
||||
|
@ -714,9 +658,6 @@ OptStatus ParseFlags(int argc, const char** argv,
|
|||
spvtools::ValidatorOptions* validator_options,
|
||||
spvtools::OptimizerOptions* optimizer_options) {
|
||||
std::vector<std::string> pass_flags;
|
||||
bool target_env_set = false;
|
||||
bool vulkan_to_webgpu_set = false;
|
||||
bool webgpu_to_vulkan_set = false;
|
||||
for (int argi = 1; argi < argc; ++argi) {
|
||||
const char* cur_arg = argv[argi];
|
||||
if ('-' == cur_arg[0]) {
|
||||
|
@ -781,19 +722,6 @@ OptStatus ParseFlags(int argc, const char** argv,
|
|||
max_id_bound);
|
||||
} else if (0 == strncmp(cur_arg,
|
||||
"--target-env=", sizeof("--target-env=") - 1)) {
|
||||
target_env_set = true;
|
||||
if (vulkan_to_webgpu_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"--vulkan-to-webgpu defines the target environment, "
|
||||
"so --target-env cannot be set at the same time");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
if (webgpu_to_vulkan_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"--webgpu-to-vulkan defines the target environment, "
|
||||
"so --target-env cannot be set at the same time");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg);
|
||||
const auto target_env_str = split_flag.second.c_str();
|
||||
spv_target_env target_env;
|
||||
|
@ -803,42 +731,6 @@ OptStatus ParseFlags(int argc, const char** argv,
|
|||
return {OPT_STOP, 1};
|
||||
}
|
||||
optimizer->SetTargetEnv(target_env);
|
||||
} else if (0 == strcmp(cur_arg, "--vulkan-to-webgpu")) {
|
||||
vulkan_to_webgpu_set = true;
|
||||
if (target_env_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"--vulkan-to-webgpu defines the target environment, "
|
||||
"so --target-env cannot be set at the same time");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
if (webgpu_to_vulkan_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"Cannot use both --webgpu-to-vulkan and "
|
||||
"--vulkan-to-webgpu at the same time, invoke twice "
|
||||
"if you are wanting to go to and from");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
|
||||
optimizer->SetTargetEnv(SPV_ENV_VULKAN_1_1);
|
||||
optimizer->RegisterVulkanToWebGPUPasses();
|
||||
} else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) {
|
||||
webgpu_to_vulkan_set = true;
|
||||
if (target_env_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"--webgpu-to-vulkan defines the target environment, "
|
||||
"so --target-env cannot be set at the same time");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
if (vulkan_to_webgpu_set) {
|
||||
spvtools::Error(opt_diagnostic, nullptr, {},
|
||||
"Cannot use both --webgpu-to-vulkan and "
|
||||
"--vulkan-to-webgpu at the same time, invoke twice "
|
||||
"if you are wanting to go to and from");
|
||||
return {OPT_STOP, 1};
|
||||
}
|
||||
|
||||
optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0);
|
||||
optimizer->RegisterWebGPUToVulkanPasses();
|
||||
} else if (0 == strcmp(cur_arg, "--validate-after-all")) {
|
||||
optimizer->SetValidateAfterAll(true);
|
||||
} else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) {
|
||||
|
|
|
@ -108,7 +108,7 @@ int main(int argc, char** argv) {
|
|||
printf("%s\n", spvSoftwareVersionDetailsString());
|
||||
printf(
|
||||
"Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n "
|
||||
"%s\n %s\n",
|
||||
"%s\n",
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2),
|
||||
|
@ -118,8 +118,7 @@ int main(int argc, char** argv) {
|
|||
spvTargetEnvDescription(SPV_ENV_OPENCL_2_2),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_0),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1),
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4),
|
||||
spvTargetEnvDescription(SPV_ENV_WEBGPU_0));
|
||||
spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
|
||||
continue_processing = false;
|
||||
return_code = 0;
|
||||
} else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче