mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated spirv-tools.
This commit is contained in:
1
3rdparty/spirv-tools/Android.mk
vendored
1
3rdparty/spirv-tools/Android.mk
vendored
@@ -92,6 +92,7 @@ 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/def_use_manager.cpp \
|
||||
source/opt/dominator_analysis.cpp \
|
||||
|
||||
2
3rdparty/spirv-tools/BUILD.gn
vendored
2
3rdparty/spirv-tools/BUILD.gn
vendored
@@ -484,6 +484,8 @@ 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/def_use_manager.cpp",
|
||||
|
||||
@@ -1 +1 @@
|
||||
"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-140-g102e430a"
|
||||
"v2019.3-dev", "SPIRV-Tools v2019.3-dev v2019.1-147-g7aad9653"
|
||||
|
||||
@@ -768,6 +768,10 @@ Optimizer::PassToken CreateFixStorageClassPass();
|
||||
// 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();
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_
|
||||
|
||||
18
3rdparty/spirv-tools/source/CMakeLists.txt
vendored
18
3rdparty/spirv-tools/source/CMakeLists.txt
vendored
@@ -101,7 +101,7 @@ macro(spvtools_opencl_tables CONFIG_VERSION)
|
||||
list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE})
|
||||
endmacro(spvtools_opencl_tables)
|
||||
|
||||
macro(spvtools_vendor_tables VENDOR_TABLE)
|
||||
macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME)
|
||||
set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc")
|
||||
set(GRAMMAR_FILE "${spirv-tools_SOURCE_DIR}/source/extinst.${VENDOR_TABLE}.grammar.json")
|
||||
add_custom_command(OUTPUT ${INSTS_FILE}
|
||||
@@ -110,9 +110,9 @@ macro(spvtools_vendor_tables VENDOR_TABLE)
|
||||
--vendor-insts-output=${INSTS_FILE}
|
||||
DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
|
||||
COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.")
|
||||
add_custom_target(spirv-tools-${VENDOR_TABLE} DEPENDS ${INSTS_FILE})
|
||||
set_property(TARGET spirv-tools-${VENDOR_TABLE} PROPERTY FOLDER "SPIRV-Tools build")
|
||||
list(APPEND EXTINST_CPP_DEPENDS spirv-tools-${VENDOR_TABLE})
|
||||
add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE})
|
||||
set_property(TARGET spv-tools-${SHORT_NAME} PROPERTY FOLDER "SPIRV-Tools build")
|
||||
list(APPEND EXTINST_CPP_DEPENDS spv-tools-${SHORT_NAME})
|
||||
endmacro(spvtools_vendor_tables)
|
||||
|
||||
macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE)
|
||||
@@ -134,11 +134,11 @@ spvtools_core_tables("unified1")
|
||||
spvtools_enum_string_mapping("unified1")
|
||||
spvtools_opencl_tables("unified1")
|
||||
spvtools_glsl_tables("unified1")
|
||||
spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter")
|
||||
spvtools_vendor_tables("spv-amd-shader-trinary-minmax")
|
||||
spvtools_vendor_tables("spv-amd-gcn-shader")
|
||||
spvtools_vendor_tables("spv-amd-shader-ballot")
|
||||
spvtools_vendor_tables("debuginfo")
|
||||
spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp")
|
||||
spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm")
|
||||
spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs")
|
||||
spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb")
|
||||
spvtools_vendor_tables("debuginfo" "debuginfo")
|
||||
spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
|
||||
|
||||
spvtools_vimsyntax("unified1" "1.0")
|
||||
|
||||
@@ -31,6 +31,7 @@ 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
|
||||
def_use_manager.h
|
||||
dominator_analysis.h
|
||||
@@ -130,6 +131,7 @@ 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
|
||||
def_use_manager.cpp
|
||||
dominator_analysis.cpp
|
||||
|
||||
77
3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp
vendored
Normal file
77
3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// 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();
|
||||
bool changed = false;
|
||||
|
||||
// TODO(zoddicus): Handle 'Output' variables
|
||||
// TODO(zoddicus): Handle 'Private' variables
|
||||
|
||||
// Handle 'Function' variables
|
||||
for (auto func = module->begin(); func != module->end(); ++func) {
|
||||
auto block = func->entry().get();
|
||||
std::vector<Instruction*> new_stores;
|
||||
|
||||
auto last_var = block->begin();
|
||||
for (auto iter = block->begin();
|
||||
iter != block->end() && iter->opcode() == SpvOpVariable; ++iter) {
|
||||
last_var = iter;
|
||||
Instruction* inst = &(*iter);
|
||||
if (!HasInitializer(inst)) continue;
|
||||
|
||||
changed = true;
|
||||
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}}});
|
||||
new_stores.push_back(store_inst);
|
||||
iter->RemoveOperand(3);
|
||||
get_def_use_mgr()->UpdateDefUse(&*iter);
|
||||
}
|
||||
|
||||
for (auto store = new_stores.begin(); store != new_stores.end(); ++store) {
|
||||
context()->AnalyzeDefUse(*store);
|
||||
context()->set_instr_block(*store, block);
|
||||
(*store)->InsertAfter(&*last_var);
|
||||
last_var = *store;
|
||||
}
|
||||
}
|
||||
|
||||
return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
57
3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.h
vendored
Normal file
57
3rdparty/spirv-tools/source/opt/decompose_initialized_variables_pass.h
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
// 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,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
@@ -231,7 +231,9 @@ Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
|
||||
.RegisterPass(CreateDeadBranchElimPass());
|
||||
}
|
||||
|
||||
Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() { return *this; }
|
||||
Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() {
|
||||
return RegisterPass(CreateDecomposeInitializedVariablesPass());
|
||||
}
|
||||
|
||||
bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
|
||||
for (const auto& flag : flags) {
|
||||
@@ -866,4 +868,9 @@ Optimizer::PassToken CreateLegalizeVectorShufflePass() {
|
||||
MakeUnique<opt::LegalizeVectorShufflePass>());
|
||||
}
|
||||
|
||||
Optimizer::PassToken CreateDecomposeInitializedVariablesPass() {
|
||||
return MakeUnique<Optimizer::PassToken::Impl>(
|
||||
MakeUnique<opt::DecomposeInitializedVariablesPass>());
|
||||
}
|
||||
|
||||
} // namespace spvtools
|
||||
|
||||
1
3rdparty/spirv-tools/source/opt/passes.h
vendored
1
3rdparty/spirv-tools/source/opt/passes.h
vendored
@@ -29,6 +29,7 @@
|
||||
#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/eliminate_dead_constant_pass.h"
|
||||
#include "source/opt/eliminate_dead_functions_pass.h"
|
||||
#include "source/opt/eliminate_dead_members_pass.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
@@ -35,6 +35,10 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
remove_unreferenced_instruction_reduction_opportunity_finder.h
|
||||
structured_loop_to_selection_reduction_opportunity.h
|
||||
structured_loop_to_selection_reduction_opportunity_finder.h
|
||||
conditional_branch_to_simple_conditional_branch_opportunity_finder.h
|
||||
conditional_branch_to_simple_conditional_branch_reduction_opportunity.h
|
||||
simple_conditional_branch_to_branch_opportunity_finder.h
|
||||
simple_conditional_branch_to_branch_reduction_opportunity.h
|
||||
|
||||
change_operand_reduction_opportunity.cpp
|
||||
change_operand_to_undef_reduction_opportunity.cpp
|
||||
@@ -58,7 +62,11 @@ set(SPIRV_TOOLS_REDUCE_SOURCES
|
||||
remove_opname_instruction_reduction_opportunity_finder.cpp
|
||||
structured_loop_to_selection_reduction_opportunity.cpp
|
||||
structured_loop_to_selection_reduction_opportunity_finder.cpp
|
||||
)
|
||||
conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp
|
||||
conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp
|
||||
simple_conditional_branch_to_branch_opportunity_finder.cpp
|
||||
simple_conditional_branch_to_branch_reduction_opportunity.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
# Enable parallel builds across four cores for this lib
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h"
|
||||
|
||||
#include "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
ConditionalBranchToSimpleConditionalBranchOpportunityFinder::
|
||||
GetAvailableOpportunities(IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
// Find the opportunities for redirecting all false targets before the
|
||||
// opportunities for redirecting all true targets because the former
|
||||
// opportunities disable the latter, and vice versa, and the efficiency of the
|
||||
// reducer is improved by avoiding contiguous opportunities that disable one
|
||||
// another.
|
||||
for (bool redirect_to_true : {true, false}) {
|
||||
// Consider every function.
|
||||
for (auto& function : *context->module()) {
|
||||
// Consider every block in the function.
|
||||
for (auto& block : function) {
|
||||
// The terminator must be SpvOpBranchConditional.
|
||||
Instruction* terminator = block.terminator();
|
||||
if (terminator->opcode() != SpvOpBranchConditional) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t true_block_id =
|
||||
terminator->GetSingleWordInOperand(kTrueBranchOperandIndex);
|
||||
uint32_t false_block_id =
|
||||
terminator->GetSingleWordInOperand(kFalseBranchOperandIndex);
|
||||
|
||||
// The conditional branch must not already be simplified.
|
||||
if (true_block_id == false_block_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The redirected target must not be a back-edge to a structured loop
|
||||
// header.
|
||||
uint32_t redirected_block_id =
|
||||
redirect_to_true ? false_block_id : true_block_id;
|
||||
uint32_t containing_loop_header =
|
||||
context->GetStructuredCFGAnalysis()->ContainingLoop(block.id());
|
||||
// The structured CFG analysis does not include a loop header as part
|
||||
// of the loop construct, but we want to include it, so handle this
|
||||
// special case:
|
||||
if (block.GetLoopMergeInst() != nullptr) {
|
||||
containing_loop_header = block.id();
|
||||
}
|
||||
if (redirected_block_id == containing_loop_header) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(
|
||||
MakeUnique<
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity>(
|
||||
block.terminator(), redirect_to_true));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string
|
||||
ConditionalBranchToSimpleConditionalBranchOpportunityFinder::GetName() const {
|
||||
return "ConditionalBranchToSimpleConditionalBranchOpportunityFinder";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_
|
||||
#define SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_
|
||||
|
||||
#include "source/reduce/reduction_opportunity_finder.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A finder for opportunities to simplify conditional branches into simple
|
||||
// conditional branches (conditional branches with one target).
|
||||
class ConditionalBranchToSimpleConditionalBranchOpportunityFinder
|
||||
: public ReductionOpportunityFinder {
|
||||
public:
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const override;
|
||||
|
||||
std::string GetName() const override;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h"
|
||||
|
||||
#include "source/reduce/reduction_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::Instruction;
|
||||
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
|
||||
ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
|
||||
Instruction* conditional_branch_instruction, bool redirect_to_true)
|
||||
: conditional_branch_instruction_(conditional_branch_instruction),
|
||||
redirect_to_true_(redirect_to_true) {}
|
||||
|
||||
bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity::
|
||||
PreconditionHolds() {
|
||||
// Another opportunity may have already simplified this conditional branch,
|
||||
// which should disable this opportunity.
|
||||
return conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kTrueBranchOperandIndex) !=
|
||||
conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kFalseBranchOperandIndex);
|
||||
}
|
||||
|
||||
void ConditionalBranchToSimpleConditionalBranchReductionOpportunity::Apply() {
|
||||
uint32_t operand_to_modify =
|
||||
redirect_to_true_ ? kFalseBranchOperandIndex : kTrueBranchOperandIndex;
|
||||
uint32_t operand_to_copy =
|
||||
redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex;
|
||||
|
||||
// Do the branch redirection.
|
||||
conditional_branch_instruction_->SetInOperand(
|
||||
operand_to_modify,
|
||||
{conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
operand_to_copy)});
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
#define SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
|
||||
#include "source/opt/basic_block.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// An opportunity to simplify a conditional branch to a simple conditional
|
||||
// branch (a conditional branch with one target).
|
||||
class ConditionalBranchToSimpleConditionalBranchReductionOpportunity
|
||||
: public ReductionOpportunity {
|
||||
public:
|
||||
// Constructs an opportunity to simplify |conditional_branch_instruction|. If
|
||||
// |redirect_to_true| is true, the false target will be changed to also point
|
||||
// to the true target; otherwise, the true target will be changed to also
|
||||
// point to the false target.
|
||||
explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity(
|
||||
opt::Instruction* conditional_branch_instruction, bool redirect_to_true);
|
||||
|
||||
bool PreconditionHolds() override;
|
||||
|
||||
protected:
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
opt::Instruction* conditional_branch_instruction_;
|
||||
|
||||
// If true, the false target will be changed to point to the true target;
|
||||
// otherwise, the true target will be changed to point to the false target.
|
||||
bool redirect_to_true_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
#include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h"
|
||||
#include "source/reduce/merge_blocks_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/operand_to_const_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h"
|
||||
@@ -26,6 +27,7 @@
|
||||
#include "source/reduce/remove_opname_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_selection_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.h"
|
||||
#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
|
||||
#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h"
|
||||
#include "source/spirv_reducer_options.h"
|
||||
|
||||
@@ -191,6 +193,11 @@ void Reducer::AddDefaultReductionPasses() {
|
||||
spvtools::MakeUnique<RemoveBlockReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<RemoveSelectionReductionOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<
|
||||
ConditionalBranchToSimpleConditionalBranchOpportunityFinder>());
|
||||
AddReductionPass(
|
||||
spvtools::MakeUnique<SimpleConditionalBranchToBranchOpportunityFinder>());
|
||||
}
|
||||
|
||||
void Reducer::AddReductionPass(
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace reduce {
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
|
||||
const uint32_t kTrueBranchOperandIndex = 1;
|
||||
const uint32_t kFalseBranchOperandIndex = 2;
|
||||
|
||||
uint32_t FindOrCreateGlobalUndef(IRContext* context, uint32_t type_id) {
|
||||
for (auto& inst : context->module()->types_values()) {
|
||||
if (inst.opcode() != SpvOpUndef) {
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
extern const uint32_t kTrueBranchOperandIndex;
|
||||
extern const uint32_t kFalseBranchOperandIndex;
|
||||
|
||||
// Returns an OpUndef id from the global value list that is of the given type,
|
||||
// adding one if it does not exist.
|
||||
uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
65
3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp
vendored
Normal file
65
3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
|
||||
|
||||
#include "source/reduce/reduction_util.h"
|
||||
#include "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>>
|
||||
SimpleConditionalBranchToBranchOpportunityFinder::GetAvailableOpportunities(
|
||||
IRContext* context) const {
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> result;
|
||||
|
||||
// Consider every function.
|
||||
for (auto& function : *context->module()) {
|
||||
// Consider every block in the function.
|
||||
for (auto& block : function) {
|
||||
// The terminator must be SpvOpBranchConditional.
|
||||
Instruction* terminator = block.terminator();
|
||||
if (terminator->opcode() != SpvOpBranchConditional) {
|
||||
continue;
|
||||
}
|
||||
// It must not be a selection header, as these cannot be followed by
|
||||
// OpBranch.
|
||||
if (block.GetMergeInst() &&
|
||||
block.GetMergeInst()->opcode() == SpvOpSelectionMerge) {
|
||||
continue;
|
||||
}
|
||||
// The conditional branch must be simplified.
|
||||
if (terminator->GetSingleWordInOperand(kTrueBranchOperandIndex) !=
|
||||
terminator->GetSingleWordInOperand(kFalseBranchOperandIndex)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(
|
||||
MakeUnique<SimpleConditionalBranchToBranchReductionOpportunity>(
|
||||
block.terminator()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string SimpleConditionalBranchToBranchOpportunityFinder::GetName() const {
|
||||
return "SimpleConditionalBranchToBranchOpportunityFinder";
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
37
3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h
vendored
Normal file
37
3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_
|
||||
#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_
|
||||
|
||||
#include "source/reduce/reduction_opportunity_finder.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// A finder for opportunities to change simple conditional branches (conditional
|
||||
// branches with one target) to an OpBranch.
|
||||
class SimpleConditionalBranchToBranchOpportunityFinder
|
||||
: public ReductionOpportunityFinder {
|
||||
public:
|
||||
std::vector<std::unique_ptr<ReductionOpportunity>> GetAvailableOpportunities(
|
||||
opt::IRContext* context) const override;
|
||||
|
||||
std::string GetName() const override;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_
|
||||
59
3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp
vendored
Normal file
59
3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h"
|
||||
|
||||
#include "source/reduce/reduction_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
using namespace opt;
|
||||
|
||||
SimpleConditionalBranchToBranchReductionOpportunity::
|
||||
SimpleConditionalBranchToBranchReductionOpportunity(
|
||||
Instruction* conditional_branch_instruction)
|
||||
: conditional_branch_instruction_(conditional_branch_instruction) {}
|
||||
|
||||
bool SimpleConditionalBranchToBranchReductionOpportunity::PreconditionHolds() {
|
||||
// We find at most one opportunity per conditional branch and simplifying
|
||||
// another branch cannot disable this opportunity.
|
||||
return true;
|
||||
}
|
||||
|
||||
void SimpleConditionalBranchToBranchReductionOpportunity::Apply() {
|
||||
assert(conditional_branch_instruction_->opcode() == SpvOpBranchConditional &&
|
||||
"SimpleConditionalBranchToBranchReductionOpportunity: branch was not "
|
||||
"a conditional branch");
|
||||
|
||||
assert(conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kTrueBranchOperandIndex) ==
|
||||
conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kFalseBranchOperandIndex) &&
|
||||
"SimpleConditionalBranchToBranchReductionOpportunity: branch was not "
|
||||
"simple");
|
||||
|
||||
// OpBranchConditional %condition %block_id %block_id ...
|
||||
// ->
|
||||
// OpBranch %block_id
|
||||
|
||||
conditional_branch_instruction_->SetOpcode(SpvOpBranch);
|
||||
conditional_branch_instruction_->ReplaceOperands(
|
||||
{{SPV_OPERAND_TYPE_ID,
|
||||
{conditional_branch_instruction_->GetSingleWordInOperand(
|
||||
kTrueBranchOperandIndex)}}});
|
||||
}
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
45
3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h
vendored
Normal file
45
3rdparty/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
|
||||
// An opportunity to change simple conditional branches (conditional branches
|
||||
// with one target) to an OpBranch.
|
||||
class SimpleConditionalBranchToBranchReductionOpportunity
|
||||
: public ReductionOpportunity {
|
||||
public:
|
||||
// Constructs an opportunity to simplify |conditional_branch_instruction|.
|
||||
explicit SimpleConditionalBranchToBranchReductionOpportunity(
|
||||
opt::Instruction* conditional_branch_instruction);
|
||||
|
||||
bool PreconditionHolds() override;
|
||||
|
||||
protected:
|
||||
void Apply() override;
|
||||
|
||||
private:
|
||||
opt::Instruction* conditional_branch_instruction_;
|
||||
};
|
||||
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
|
||||
#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_
|
||||
48
3rdparty/spirv-tools/source/val/validate.cpp
vendored
48
3rdparty/spirv-tools/source/val/validate.cpp
vendored
@@ -110,48 +110,6 @@ spv_result_t ProcessInstruction(void* user_data,
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
void printDot(const ValidationState_t& _, const BasicBlock& other) {
|
||||
std::string block_string;
|
||||
if (other.successors()->empty()) {
|
||||
block_string += "end ";
|
||||
} else {
|
||||
for (auto block : *other.successors()) {
|
||||
block_string += _.getIdName(block->id()) + " ";
|
||||
}
|
||||
}
|
||||
printf("%10s -> {%s\b}\n", _.getIdName(other.id()).c_str(),
|
||||
block_string.c_str());
|
||||
}
|
||||
|
||||
void PrintBlocks(ValidationState_t& _, Function func) {
|
||||
assert(func.first_block());
|
||||
|
||||
printf("%10s -> %s\n", _.getIdName(func.id()).c_str(),
|
||||
_.getIdName(func.first_block()->id()).c_str());
|
||||
for (const auto& block : func.ordered_blocks()) {
|
||||
printDot(_, *block);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#define UNUSED(func) [[gnu::unused]] func
|
||||
#elif defined(__GNUC__)
|
||||
#define UNUSED(func) \
|
||||
func __attribute__((unused)); \
|
||||
func
|
||||
#elif defined(_MSC_VER)
|
||||
#define UNUSED(func) func
|
||||
#endif
|
||||
|
||||
UNUSED(void PrintDotGraph(ValidationState_t& _, Function func)) {
|
||||
if (func.first_block()) {
|
||||
std::string func_name(_.getIdName(func.id()));
|
||||
printf("digraph %s {\n", func_name.c_str());
|
||||
PrintBlocks(_, func);
|
||||
printf("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
spv_result_t ValidateForwardDecls(ValidationState_t& _) {
|
||||
if (_.unresolved_forward_id_count() == 0) return SPV_SUCCESS;
|
||||
|
||||
@@ -281,6 +239,12 @@ 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, "",
|
||||
|
||||
32
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
32
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
@@ -725,36 +725,6 @@ spv_result_t PerformWebGPUCfgChecks(ValidationState_t& _, Function* function) {
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
// Checks that there are no OpUnreachable instructions reachable from the entry
|
||||
// block of |function|.
|
||||
spv_result_t ValidateUnreachableBlocks(ValidationState_t& _,
|
||||
Function& function) {
|
||||
auto* entry_block = function.first_block();
|
||||
std::vector<BasicBlock*> stack;
|
||||
std::unordered_set<BasicBlock*> seen;
|
||||
if (entry_block) stack.push_back(entry_block);
|
||||
|
||||
while (!stack.empty()) {
|
||||
auto* block = stack.back();
|
||||
stack.pop_back();
|
||||
|
||||
if (!seen.insert(block).second) continue;
|
||||
|
||||
auto* terminator = block->terminator();
|
||||
if (terminator->opcode() == SpvOpUnreachable) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, block->label())
|
||||
<< "Statically reachable blocks cannot be terminated by "
|
||||
"OpUnreachable";
|
||||
}
|
||||
|
||||
for (auto* succ : *block->successors()) {
|
||||
stack.push_back(succ);
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t PerformCfgChecks(ValidationState_t& _) {
|
||||
for (auto& function : _.functions()) {
|
||||
// Check all referenced blocks are defined within a function
|
||||
@@ -857,8 +827,6 @@ spv_result_t PerformCfgChecks(ValidationState_t& _) {
|
||||
}
|
||||
}
|
||||
|
||||
if (auto error = ValidateUnreachableBlocks(_, function)) return error;
|
||||
|
||||
/// Structured control flow checks are only required for shader capabilities
|
||||
if (_.HasCapability(SpvCapabilityShader)) {
|
||||
if (auto error = StructuredControlFlowChecks(_, &function, back_edges))
|
||||
|
||||
@@ -48,21 +48,31 @@ spv_result_t ValidateMemorySemantics(ValidationState_t& _,
|
||||
}
|
||||
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
uint32_t valid_bits = SpvMemorySemanticsAcquireMask |
|
||||
SpvMemorySemanticsReleaseMask |
|
||||
SpvMemorySemanticsAcquireReleaseMask |
|
||||
SpvMemorySemanticsUniformMemoryMask |
|
||||
uint32_t valid_bits = SpvMemorySemanticsUniformMemoryMask |
|
||||
SpvMemorySemanticsWorkgroupMemoryMask |
|
||||
SpvMemorySemanticsImageMemoryMask |
|
||||
SpvMemorySemanticsOutputMemoryKHRMask |
|
||||
SpvMemorySemanticsMakeAvailableKHRMask |
|
||||
SpvMemorySemanticsMakeVisibleKHRMask;
|
||||
if (!spvOpcodeIsAtomicOp(inst->opcode())) {
|
||||
valid_bits |= SpvMemorySemanticsAcquireMask |
|
||||
SpvMemorySemanticsReleaseMask |
|
||||
SpvMemorySemanticsAcquireReleaseMask;
|
||||
}
|
||||
|
||||
if (value & ~valid_bits) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "WebGPU spec disallows any bit masks in Memory Semantics that "
|
||||
"are not Acquire, Release, AcquireRelease, UniformMemory, "
|
||||
"WorkgroupMemory, ImageMemory, OutputMemoryKHR, "
|
||||
"MakeAvailableKHR, or MakeVisibleKHR";
|
||||
if (spvOpcodeIsAtomicOp(inst->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "WebGPU spec disallows, for OpAtomic*, any bit masks in "
|
||||
"Memory Semantics that are not UniformMemory, "
|
||||
"WorkgroupMemory, ImageMemory, or OutputMemoryKHR";
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "WebGPU spec disallows any bit masks in Memory Semantics "
|
||||
"that are not Acquire, Release, AcquireRelease, "
|
||||
"UniformMemory, WorkgroupMemory, ImageMemory, "
|
||||
"OutputMemoryKHR, MakeAvailableKHR, or MakeVisibleKHR";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
3rdparty/spirv-tools/test/opt/CMakeLists.txt
vendored
1
3rdparty/spirv-tools/test/opt/CMakeLists.txt
vendored
@@ -30,6 +30,7 @@ add_spvtools_unittest(TARGET opt
|
||||
dead_branch_elim_test.cpp
|
||||
dead_insert_elim_test.cpp
|
||||
dead_variable_elim_test.cpp
|
||||
decompose_initialized_variables_test.cpp
|
||||
decoration_manager_test.cpp
|
||||
def_use_test.cpp
|
||||
eliminate_dead_const_test.cpp
|
||||
|
||||
85
3rdparty/spirv-tools/test/opt/decompose_initialized_variables_test.cpp
vendored
Normal file
85
3rdparty/spirv-tools/test/opt/decompose_initialized_variables_test.cpp
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// 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>;
|
||||
|
||||
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 VulkanMemoryModelKHR",
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"",
|
||||
"OpMemoryModel Logical VulkanKHR",
|
||||
"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::vector<const char*> body) {
|
||||
auto result = header;
|
||||
result += {"%_ptr_Function_uint = OpTypePointer Function %uint",
|
||||
"%1 = OpFunction %void None %6", "%8 = OpLabel"};
|
||||
result += body;
|
||||
result += {"OpReturn", "OpFunctionEnd"};
|
||||
return JoinAllInsts(result);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionChanged) {
|
||||
std::string input =
|
||||
GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function %uint_1"});
|
||||
std::string expected = GetFunctionTest(
|
||||
{"%9 = OpVariable %_ptr_Function_uint Function", "OpStore %9 %uint_1"});
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionUnchanged) {
|
||||
std::string input =
|
||||
GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function"});
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, input, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
TEST_F(DecomposeInitializedVariablesTest, FunctionMultipleVariables) {
|
||||
std::string input =
|
||||
GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function %uint_1",
|
||||
"%10 = OpVariable %_ptr_Function_uint Function %4"});
|
||||
std::string expected =
|
||||
GetFunctionTest({"%9 = OpVariable %_ptr_Function_uint Function",
|
||||
"%10 = OpVariable %_ptr_Function_uint Function",
|
||||
"OpStore %9 %uint_1", "OpStore %10 %4"});
|
||||
|
||||
SinglePassRunAndCheck<DecomposeInitializedVariablesPass>(
|
||||
input, expected, /* skip_nop = */ false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
108
3rdparty/spirv-tools/test/opt/optimizer_test.cpp
vendored
108
3rdparty/spirv-tools/test/opt/optimizer_test.cpp
vendored
@@ -222,7 +222,7 @@ TEST(Optimizer, CanRegisterPassesFromFlags) {
|
||||
EXPECT_EQ(msg_level, SPV_MSG_ERROR);
|
||||
}
|
||||
|
||||
TEST(Optimizer, VulkanToWebGPUModeSetsCorrectPasses) {
|
||||
TEST(Optimizer, VulkanToWebGPUSetsCorrectPasses) {
|
||||
Optimizer opt(SPV_ENV_WEBGPU_0);
|
||||
opt.RegisterVulkanToWebGPUPasses();
|
||||
std::vector<const char*> pass_names = opt.GetPassNames();
|
||||
@@ -260,9 +260,11 @@ using VulkanToWebGPUPassTest =
|
||||
PassTest<::testing::TestWithParam<VulkanToWebGPUPassCase>>;
|
||||
|
||||
TEST_P(VulkanToWebGPUPassTest, Ran) {
|
||||
SpirvTools tools(SPV_ENV_WEBGPU_0);
|
||||
std::vector<uint32_t> binary;
|
||||
tools.Assemble(GetParam().input, &binary);
|
||||
{
|
||||
SpirvTools tools(SPV_ENV_VULKAN_1_1);
|
||||
tools.Assemble(GetParam().input, &binary);
|
||||
}
|
||||
|
||||
Optimizer opt(SPV_ENV_WEBGPU_0);
|
||||
opt.RegisterVulkanToWebGPUPasses();
|
||||
@@ -272,7 +274,10 @@ TEST_P(VulkanToWebGPUPassTest, Ran) {
|
||||
ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized,
|
||||
validator_options, true));
|
||||
std::string disassembly;
|
||||
tools.Disassemble(optimized.data(), optimized.size(), &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";
|
||||
@@ -521,6 +526,101 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
// pass
|
||||
"legalize-vector-shuffle"}}));
|
||||
|
||||
TEST(Optimizer, WebGPUToVulkanSetsCorrectPasses) {
|
||||
Optimizer opt(SPV_ENV_VULKAN_1_1);
|
||||
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"};
|
||||
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_VULKAN_1_1);
|
||||
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 VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\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 VulkanMemoryModelKHR\n"
|
||||
"OpExtension \"SPV_KHR_vulkan_memory_model\"\n"
|
||||
"OpMemoryModel Logical VulkanKHR\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"}}));
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2019 Google Inc.
|
||||
// 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.
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
add_spvtools_unittest(TARGET reduce
|
||||
SRCS merge_blocks_test.cpp
|
||||
SRCS
|
||||
merge_blocks_test.cpp
|
||||
operand_to_constant_test.cpp
|
||||
operand_to_undef_test.cpp
|
||||
operand_to_dominating_id_test.cpp
|
||||
@@ -27,6 +28,8 @@ add_spvtools_unittest(TARGET reduce
|
||||
remove_unreferenced_instruction_test.cpp
|
||||
structured_loop_to_selection_test.cpp
|
||||
validation_during_reduction_test.cpp
|
||||
conditional_branch_to_simple_conditional_branch_test.cpp
|
||||
simple_conditional_branch_to_branch_test.cpp
|
||||
LIBS SPIRV-Tools-reduce
|
||||
)
|
||||
|
||||
|
||||
501
3rdparty/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp
vendored
Normal file
501
3rdparty/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp
vendored
Normal file
@@ -0,0 +1,501 @@
|
||||
// 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/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_pass.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest, Diamond) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// | |
|
||||
// b b
|
||||
// | |
|
||||
// selection merge
|
||||
//
|
||||
// There should be two opportunities for redirecting the OpBranchConditional
|
||||
// targets: redirecting the true to false, and vice-versa. E.g. false to true:
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// b b
|
||||
// | |
|
||||
// selection merge
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %13
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(2, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ASSERT_TRUE(ops[1]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
// The other opportunity should now be disabled.
|
||||
ASSERT_FALSE(ops[1]->PreconditionHolds());
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
{
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
}
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
|
||||
// Start again, and apply the other op.
|
||||
context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(2, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ASSERT_TRUE(ops[1]->PreconditionHolds());
|
||||
ops[1]->TryToApply();
|
||||
// The other opportunity should now be disabled.
|
||||
ASSERT_FALSE(ops[0]->PreconditionHolds());
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
{
|
||||
std::string after2 = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %13 %13
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after2, context.get());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest, AlreadySimplified) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// b b
|
||||
// | |
|
||||
// selection merge
|
||||
//
|
||||
// There should be no opportunities for redirecting the OpBranchConditional
|
||||
// as it is already simplified.
|
||||
//
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest, DontRemoveBackEdge) {
|
||||
// A test with the following structure. The loop has a continue construct that
|
||||
// ends with OpBranchConditional. The OpBranchConditional can be simplified,
|
||||
// but only to point to the loop header, otherwise we have removed the
|
||||
// back-edge. Thus, there should be one opportunity instead of two.
|
||||
//
|
||||
// loop header
|
||||
// |
|
||||
// loop continue target and back-edge block
|
||||
// OpBranchConditional
|
||||
// | |
|
||||
// loop merge (to loop header^)
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %8 %11 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest,
|
||||
DontRemoveBackEdgeCombinedHeaderContinue) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// loop header and continue target and back-edge block
|
||||
// OpBranchConditional
|
||||
// | |
|
||||
// loop merge (to loop header^)
|
||||
//
|
||||
// The OpBranchConditional-to-header edge must not be removed, so there should
|
||||
// only be one opportunity. It should change both targets to be to the loop
|
||||
// header.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %10 None
|
||||
OpBranchConditional %8 %11 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %10 None
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(ConditionalBranchToSimpleConditionalBranchTest, BackEdgeUnreachable) {
|
||||
// A test with the following structure. I.e. a loop with an unreachable
|
||||
// continue construct that ends with OpBranchConditional.
|
||||
//
|
||||
// loop header
|
||||
// |
|
||||
// | loop continue target (unreachable)
|
||||
// | |
|
||||
// | back-edge block (unreachable)
|
||||
// | OpBranchConditional
|
||||
// | | |
|
||||
// loop merge (to loop header^)
|
||||
//
|
||||
// The branch to the loop header must not be removed, even though the continue
|
||||
// construct is unreachable. So there should only be one opportunity to make
|
||||
// the true and false targets of the OpBranchConditional to point to the loop
|
||||
// header.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %11
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %8 %11 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %11
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
486
3rdparty/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp
vendored
Normal file
486
3rdparty/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
// 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/reduce/simple_conditional_branch_to_branch_opportunity_finder.h"
|
||||
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/reduction_pass.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace reduce {
|
||||
namespace {
|
||||
|
||||
const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3;
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, Diamond) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// b b
|
||||
// | |
|
||||
// selection merge
|
||||
//
|
||||
// The conditional branch cannot be simplified because selection headers
|
||||
// cannot end with OpBranch.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, DiamondNoSelection) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// b b
|
||||
// | /
|
||||
// b
|
||||
//
|
||||
// The conditional branch can be simplified.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpBranchConditional %8 %12 %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, ConditionalBranchesButNotSimple) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// selection header
|
||||
// OpBranchConditional
|
||||
// | |
|
||||
// b OpBranchConditional
|
||||
// | | |
|
||||
// | b |
|
||||
// | | |
|
||||
// selection merge
|
||||
//
|
||||
// None of the conditional branches can be simplified; the first is not simple
|
||||
// AND part of a selection header; the second is just not simple (where
|
||||
// "simple" means it only has one target).
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %8 %12 %13
|
||||
%12 = OpLabel
|
||||
OpBranch %11
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %8 %14 %11
|
||||
%14 = OpLabel
|
||||
OpBranch %11
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, SimplifyBackEdge) {
|
||||
// A test with the following structure. The loop has a continue construct that
|
||||
// ends with OpBranchConditional. The OpBranchConditional can be simplified.
|
||||
//
|
||||
// loop header
|
||||
// |
|
||||
// loop continue target and back-edge block
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// loop merge (to loop header^)
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpBranch %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest,
|
||||
DontRemoveBackEdgeCombinedHeaderContinue) {
|
||||
// A test with the following structure.
|
||||
//
|
||||
// loop header and continue target and back-edge block
|
||||
// OpBranchConditional
|
||||
// ||
|
||||
// loop merge (to loop header^)
|
||||
//
|
||||
// The conditional branch can be simplified.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %10 None
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %10 None
|
||||
OpBranch %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
TEST(SimpleConditionalBranchToBranchTest, BackEdgeUnreachable) {
|
||||
// A test with the following structure. I.e. a loop with an unreachable
|
||||
// continue construct that ends with OpBranchConditional.
|
||||
//
|
||||
// loop header
|
||||
// |
|
||||
// | loop continue target (unreachable)
|
||||
// | |
|
||||
// | back-edge block (unreachable)
|
||||
// | OpBranchConditional
|
||||
// | ||
|
||||
// loop merge (to loop header^)
|
||||
//
|
||||
// The conditional branch can be simplified.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %11
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %8 %10 %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto context =
|
||||
BuildModule(kEnv, nullptr, shader, kReduceAssembleOption);
|
||||
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
auto ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
|
||||
ASSERT_EQ(1, ops.size());
|
||||
|
||||
ASSERT_TRUE(ops[0]->PreconditionHolds());
|
||||
ops[0]->TryToApply();
|
||||
CheckValid(kEnv, context.get());
|
||||
|
||||
std::string after = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
OpName %2 "main"
|
||||
%3 = OpTypeVoid
|
||||
%4 = OpTypeFunction %3
|
||||
%5 = OpTypeInt 32 1
|
||||
%6 = OpTypePointer Function %5
|
||||
%7 = OpTypeBool
|
||||
%8 = OpConstantTrue %7
|
||||
%2 = OpFunction %3 None %4
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %11
|
||||
%12 = OpLabel
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranch %10
|
||||
%11 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CheckEqual(kEnv, after, context.get());
|
||||
|
||||
ops = SimpleConditionalBranchToBranchOpportunityFinder()
|
||||
.GetAvailableOpportunities(context.get());
|
||||
ASSERT_EQ(0, ops.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace reduce
|
||||
} // namespace spvtools
|
||||
@@ -373,30 +373,37 @@ TEST_F(ValidateAtomics, AtomicLoadVulkanInt64) {
|
||||
"AtomicLoad: 64-bit atomics require the Int64Atomics capability"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicLoadWebGPUShaderSuccess) {
|
||||
TEST_F(ValidateAtomics, AtomicLoadWebGPUSuccess) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %relaxed
|
||||
%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire
|
||||
%val2 = OpAtomicLoad %u32 %u32_var %workgroup %relaxed
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicLoadWebGPUShaderSequentiallyConsistentFailure) {
|
||||
TEST_F(ValidateAtomics, AtomicLoadWebGPUNonRelaxedFailure) {
|
||||
const std::string body = R"(
|
||||
%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %acquire
|
||||
%val2 = OpAtomicLoad %u32 %u32_var %workgroup %release
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec disallows, for OpAtomic*, any bit masks"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicLoadWebGPUSequentiallyConsistentFailure) {
|
||||
const std::string body = R"(
|
||||
%val3 = OpAtomicLoad %u32 %u32_var %subgroup %sequentially_consistent
|
||||
)";
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"WebGPU spec disallows any bit masks in Memory Semantics that are "
|
||||
"not Acquire, Release, AcquireRelease, UniformMemory, "
|
||||
"WorkgroupMemory, ImageMemory, OutputMemoryKHR, MakeAvailableKHR, or "
|
||||
"MakeVisibleKHR\n %34 = OpAtomicLoad %uint %29 %uint_3 %uint_16\n"));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec disallows, for OpAtomic*, any bit masks"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, VK_KHR_shader_atomic_int64Success) {
|
||||
@@ -579,13 +586,24 @@ OpAtomicStore %u32_var %device %sequentially_consistent %u32_1
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicStoreWebGPUSuccess) {
|
||||
const std::string body = R"(
|
||||
OpAtomicStore %u32_var %queuefamily %release %u32_1
|
||||
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, 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("WebGPU spec disallows, for OpAtomic*, any bit masks"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicStoreWebGPUSequentiallyConsistent) {
|
||||
const std::string body = R"(
|
||||
OpAtomicStore %u32_var %queuefamily %sequentially_consistent %u32_1
|
||||
@@ -593,14 +611,8 @@ OpAtomicStore %u32_var %queuefamily %sequentially_consistent %u32_1
|
||||
|
||||
CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"WebGPU spec disallows any bit masks in Memory Semantics that are "
|
||||
"not Acquire, Release, AcquireRelease, UniformMemory, "
|
||||
"WorkgroupMemory, ImageMemory, OutputMemoryKHR, MakeAvailableKHR, or "
|
||||
"MakeVisibleKHR\n"
|
||||
" OpAtomicStore %29 %uint_5 %uint_16 %uint_1\n"));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("WebGPU spec disallows, for OpAtomic*, any bit masks"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) {
|
||||
|
||||
41
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
41
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
@@ -1203,11 +1203,7 @@ std::string GetUnreachableMergeWithBranchUse(SpvCapability cap,
|
||||
TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) {
|
||||
CompileSuccessfully(
|
||||
GetUnreachableMergeWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0));
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Statically reachable blocks cannot be terminated by OpUnreachable"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, WebGPUUnreachableMergeWithBranchUse) {
|
||||
@@ -1942,10 +1938,7 @@ OpDecorate %id BuiltIn GlobalInvocationId
|
||||
str += "OpFunctionEnd";
|
||||
|
||||
CompileSuccessfully(str);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Statically reachable blocks cannot be terminated by "
|
||||
"OpUnreachable"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) {
|
||||
@@ -2873,11 +2866,7 @@ OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Statically reachable blocks cannot be terminated by OpUnreachable"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, ReachableOpUnreachableOpBranch) {
|
||||
@@ -2896,11 +2885,7 @@ OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Statically reachable blocks cannot be terminated by OpUnreachable"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, ReachableOpUnreachableOpBranchConditional) {
|
||||
@@ -2923,11 +2908,7 @@ OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Statically reachable blocks cannot be terminated by OpUnreachable"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, ReachableOpUnreachableOpSwitch) {
|
||||
@@ -2952,11 +2933,7 @@ OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Statically reachable blocks cannot be terminated by OpUnreachable"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, ReachableOpUnreachableLoop) {
|
||||
@@ -2980,11 +2957,7 @@ OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Statically reachable blocks cannot be terminated by OpUnreachable"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, UnreachableLoopBadBackedge) {
|
||||
|
||||
@@ -276,6 +276,63 @@ TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelExtensionBad) {
|
||||
"\"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
|
||||
|
||||
Reference in New Issue
Block a user