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:
Ryan Harrison 2021-01-14 16:45:18 -05:00 коммит произвёл GitHub
Родитель b2cfc5d1ce
Коммит 9150cd441f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
73 изменённых файлов: 205 добавлений и 6565 удалений

Просмотреть файл

@ -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 \

Просмотреть файл

@ -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")) {