Updated spirv-tools.
This commit is contained in:
parent
65c43ee311
commit
84f36f2def
@ -1 +1 @@
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-178-g85f3e93d"
|
||||
"v2019.5-dev", "SPIRV-Tools v2019.5-dev v2019.4-186-gb334829a"
|
||||
|
@ -627,6 +627,11 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed(
|
||||
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
|
||||
spv_fuzzer_options options, uint32_t shrinker_step_limit);
|
||||
|
||||
// Enables running the validator after every pass is applied during a fuzzing
|
||||
// run.
|
||||
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
|
||||
spv_fuzzer_options options);
|
||||
|
||||
// Encodes the given SPIR-V assembly text to its binary representation. The
|
||||
// length parameter specifies the number of bytes for text. Encoded binary will
|
||||
// be stored into *binary. Any error will be written into *diagnostic if
|
||||
|
@ -229,6 +229,11 @@ class FuzzerOptions {
|
||||
spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit);
|
||||
}
|
||||
|
||||
// See spvFuzzerOptionsEnableFuzzerPassValidation.
|
||||
void enable_fuzzer_pass_validation() {
|
||||
spvFuzzerOptionsEnableFuzzerPassValidation(options_);
|
||||
}
|
||||
|
||||
private:
|
||||
spv_fuzzer_options options_;
|
||||
};
|
||||
|
76
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
76
3rdparty/spirv-tools/source/fuzz/fuzzer.cpp
vendored
@ -65,13 +65,26 @@ void MaybeAddPass(
|
||||
} // namespace
|
||||
|
||||
struct Fuzzer::Impl {
|
||||
explicit Impl(spv_target_env env) : target_env(env) {}
|
||||
explicit Impl(spv_target_env env, uint32_t random_seed,
|
||||
bool validate_after_each_pass)
|
||||
: target_env(env),
|
||||
seed(random_seed),
|
||||
validate_after_each_fuzzer_pass(validate_after_each_pass) {}
|
||||
|
||||
const spv_target_env target_env; // Target environment.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
bool ApplyPassAndCheckValidity(FuzzerPass* pass,
|
||||
const opt::IRContext& ir_context,
|
||||
const spvtools::SpirvTools& tools) const;
|
||||
|
||||
const spv_target_env target_env; // Target environment.
|
||||
const uint32_t seed; // Seed for random number generator.
|
||||
bool validate_after_each_fuzzer_pass; // Determines whether the validator
|
||||
// should be invoked after every fuzzer pass.
|
||||
MessageConsumer consumer; // Message consumer.
|
||||
};
|
||||
|
||||
Fuzzer::Fuzzer(spv_target_env env) : impl_(MakeUnique<Impl>(env)) {}
|
||||
Fuzzer::Fuzzer(spv_target_env env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass)
|
||||
: impl_(MakeUnique<Impl>(env, seed, validate_after_each_fuzzer_pass)) {}
|
||||
|
||||
Fuzzer::~Fuzzer() = default;
|
||||
|
||||
@ -79,10 +92,27 @@ void Fuzzer::SetMessageConsumer(MessageConsumer c) {
|
||||
impl_->consumer = std::move(c);
|
||||
}
|
||||
|
||||
bool Fuzzer::Impl::ApplyPassAndCheckValidity(
|
||||
FuzzerPass* pass, const opt::IRContext& ir_context,
|
||||
const spvtools::SpirvTools& tools) const {
|
||||
pass->Apply();
|
||||
if (validate_after_each_fuzzer_pass) {
|
||||
std::vector<uint32_t> binary_to_validate;
|
||||
ir_context.module()->ToBinary(&binary_to_validate, false);
|
||||
if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size())) {
|
||||
consumer(SPV_MSG_INFO, nullptr, {},
|
||||
"Binary became invalid during fuzzing (set a breakpoint to "
|
||||
"inspect); stopping.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const {
|
||||
// Check compatibility between the library version being linked with and the
|
||||
// header files being used.
|
||||
@ -108,10 +138,8 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
impl_->target_env, impl_->consumer, binary_in.data(), binary_in.size());
|
||||
assert(ir_context);
|
||||
|
||||
// Make a PRNG, either from a given seed or from a random device.
|
||||
PseudoRandomGenerator random_generator(
|
||||
options->has_random_seed ? options->random_seed
|
||||
: static_cast<uint32_t>(std::random_device()()));
|
||||
// Make a PRNG from the seed passed to the fuzzer on creation.
|
||||
PseudoRandomGenerator random_generator(impl_->seed);
|
||||
|
||||
// The fuzzer will introduce new ids into the module. The module's id bound
|
||||
// gives the smallest id that can be used for this purpose. We add an offset
|
||||
@ -128,9 +156,13 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
|
||||
// Add some essential ingredients to the module if they are not already
|
||||
// present, such as boolean constants.
|
||||
FuzzerPassAddUsefulConstructs(ir_context.get(), &fact_manager,
|
||||
&fuzzer_context, transformation_sequence_out)
|
||||
.Apply();
|
||||
FuzzerPassAddUsefulConstructs add_useful_constructs(
|
||||
ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
if (!impl_->ApplyPassAndCheckValidity(&add_useful_constructs, *ir_context,
|
||||
tools)) {
|
||||
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
|
||||
}
|
||||
|
||||
// Apply some semantics-preserving passes.
|
||||
std::vector<std::unique_ptr<FuzzerPass>> passes;
|
||||
@ -168,7 +200,11 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
(is_first ||
|
||||
fuzzer_context.ChoosePercentage(kChanceOfApplyingAnotherPass))) {
|
||||
is_first = false;
|
||||
passes[fuzzer_context.RandomIndex(passes)]->Apply();
|
||||
if (!impl_->ApplyPassAndCheckValidity(
|
||||
passes[fuzzer_context.RandomIndex(passes)].get(), *ir_context,
|
||||
tools)) {
|
||||
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
|
||||
}
|
||||
}
|
||||
|
||||
// Now apply some passes that it does not make sense to apply repeatedly,
|
||||
@ -186,15 +222,13 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
||||
MaybeAddPass<FuzzerPassAdjustSelectionControls>(
|
||||
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
|
||||
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||
transformation_sequence_out);
|
||||
for (auto& pass : final_passes) {
|
||||
pass->Apply();
|
||||
}
|
||||
|
||||
if (fuzzer_context.ChooseEven()) {
|
||||
FuzzerPassAddNoContractionDecorations(ir_context.get(), &fact_manager,
|
||||
&fuzzer_context,
|
||||
transformation_sequence_out)
|
||||
.Apply();
|
||||
if (!impl_->ApplyPassAndCheckValidity(pass.get(), *ir_context, tools)) {
|
||||
return Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the module as a binary.
|
||||
|
18
3rdparty/spirv-tools/source/fuzz/fuzzer.h
vendored
18
3rdparty/spirv-tools/source/fuzz/fuzzer.h
vendored
@ -32,11 +32,16 @@ class Fuzzer {
|
||||
enum class FuzzerResultStatus {
|
||||
kComplete,
|
||||
kFailedToCreateSpirvToolsInterface,
|
||||
kFuzzerPassLedToInvalidModule,
|
||||
kInitialBinaryInvalid,
|
||||
};
|
||||
|
||||
// Constructs a fuzzer from the given target environment.
|
||||
explicit Fuzzer(spv_target_env env);
|
||||
// Constructs a fuzzer from the given target environment |env|. |seed| is a
|
||||
// seed for pseudo-random number generation.
|
||||
// |validate_after_each_fuzzer_pass| controls whether the validator will be
|
||||
// invoked after every fuzzer pass is applied.
|
||||
explicit Fuzzer(spv_target_env env, uint32_t seed,
|
||||
bool validate_after_each_fuzzer_pass);
|
||||
|
||||
// Disables copy/move constructor/assignment operations.
|
||||
Fuzzer(const Fuzzer&) = delete;
|
||||
@ -51,14 +56,13 @@ class Fuzzer {
|
||||
void SetMessageConsumer(MessageConsumer consumer);
|
||||
|
||||
// Transforms |binary_in| to |binary_out| by running a number of randomized
|
||||
// fuzzer passes, controlled via |options|. Initial facts about the input
|
||||
// binary and the context in which it will execute are provided via
|
||||
// |initial_facts|. The transformation sequence that was applied is returned
|
||||
// via |transformation_sequence_out|.
|
||||
// fuzzer passes. Initial facts about the input binary and the context in
|
||||
// which it will execute are provided via |initial_facts|. The transformation
|
||||
// sequence that was applied is returned via |transformation_sequence_out|.
|
||||
FuzzerResultStatus Run(
|
||||
const std::vector<uint32_t>& binary_in,
|
||||
const protobufs::FactSequence& initial_facts,
|
||||
spv_const_fuzzer_options options, std::vector<uint32_t>* binary_out,
|
||||
std::vector<uint32_t>* binary_out,
|
||||
protobufs::TransformationSequence* transformation_sequence_out) const;
|
||||
|
||||
private:
|
||||
|
@ -49,6 +49,18 @@ bool IsMerge(IRContext* context, BasicBlock* block) {
|
||||
return IsMerge(context, block->id());
|
||||
}
|
||||
|
||||
// Returns true if |id| is the continue target of a merge instruction.
|
||||
bool IsContinue(IRContext* context, uint32_t id) {
|
||||
return !context->get_def_use_mgr()->WhileEachUse(
|
||||
id, [](Instruction* user, uint32_t index) {
|
||||
SpvOp op = user->opcode();
|
||||
if (op == SpvOpLoopMerge && index == 1u) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Removes any OpPhi instructions in |block|, which should have exactly one
|
||||
// predecessor, replacing uses of OpPhi ids with the ids associated with the
|
||||
// predecessor.
|
||||
@ -86,6 +98,11 @@ bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pred_is_merge && IsContinue(context, lab_id)) {
|
||||
// Cannot merge a continue target with a merge block.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't bother trying to merge unreachable blocks.
|
||||
if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) {
|
||||
if (!dominators->IsReachable(block)) return false;
|
||||
|
22
3rdparty/spirv-tools/source/opt/inline_pass.cpp
vendored
22
3rdparty/spirv-tools/source/opt/inline_pass.cpp
vendored
@ -27,7 +27,6 @@
|
||||
static const int kSpvFunctionCallFunctionId = 2;
|
||||
static const int kSpvFunctionCallArgumentId = 3;
|
||||
static const int kSpvReturnValueId = 0;
|
||||
static const int kSpvLoopMergeContinueTargetIdInIdx = 1;
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
@ -285,19 +284,14 @@ bool InlinePass::GenInlineCode(
|
||||
if (rid != 0) callee_result_ids.insert(rid);
|
||||
});
|
||||
|
||||
// If the caller is in a single-block loop, and the callee has multiple
|
||||
// blocks, then the normal inlining logic will place the OpLoopMerge in
|
||||
// the last of several blocks in the loop. Instead, it should be placed
|
||||
// at the end of the first block. First determine if the caller is in a
|
||||
// single block loop. We'll wait to move the OpLoopMerge until the end
|
||||
// of the regular inlining logic, and only if necessary.
|
||||
bool caller_is_single_block_loop = false;
|
||||
// If the caller is a loop header and the callee has multiple blocks, then the
|
||||
// normal inlining logic will place the OpLoopMerge in the last of several
|
||||
// blocks in the loop. Instead, it should be placed at the end of the first
|
||||
// block. We'll wait to move the OpLoopMerge until the end of the regular
|
||||
// inlining logic, and only if necessary.
|
||||
bool caller_is_loop_header = false;
|
||||
if (auto* loop_merge = call_block_itr->GetLoopMergeInst()) {
|
||||
if (call_block_itr->GetLoopMergeInst()) {
|
||||
caller_is_loop_header = true;
|
||||
caller_is_single_block_loop =
|
||||
call_block_itr->id() ==
|
||||
loop_merge->GetSingleWordInOperand(kSpvLoopMergeContinueTargetIdInIdx);
|
||||
}
|
||||
|
||||
bool callee_begins_with_structured_header =
|
||||
@ -611,10 +605,6 @@ bool InlinePass::GenInlineCode(
|
||||
--loop_merge_itr;
|
||||
assert(loop_merge_itr->opcode() == SpvOpLoopMerge);
|
||||
std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
|
||||
if (caller_is_single_block_loop) {
|
||||
// Also, update its continue target to point to the last block.
|
||||
cp_inst->SetInOperand(kSpvLoopMergeContinueTargetIdInIdx, {last->id()});
|
||||
}
|
||||
first->tail().InsertBefore(std::move(cp_inst));
|
||||
|
||||
// Remove the loop merge from the last block.
|
||||
|
35
3rdparty/spirv-tools/source/opt/optimizer.cpp
vendored
35
3rdparty/spirv-tools/source/opt/optimizer.cpp
vendored
@ -198,28 +198,35 @@ Optimizer& Optimizer::RegisterSizePasses() {
|
||||
.RegisterPass(CreateDeadBranchElimPass())
|
||||
.RegisterPass(CreateMergeReturnPass())
|
||||
.RegisterPass(CreateInlineExhaustivePass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateEliminateDeadFunctionsPass())
|
||||
.RegisterPass(CreatePrivateToLocalPass())
|
||||
.RegisterPass(CreateScalarReplacementPass())
|
||||
.RegisterPass(CreateLocalAccessChainConvertPass())
|
||||
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
||||
.RegisterPass(CreateLocalSingleStoreElimPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateSimplificationPass())
|
||||
.RegisterPass(CreateDeadInsertElimPass())
|
||||
.RegisterPass(CreateScalarReplacementPass(0))
|
||||
.RegisterPass(CreateLocalMultiStoreElimPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateCCPPass())
|
||||
.RegisterPass(CreateLoopUnrollPass(true))
|
||||
.RegisterPass(CreateDeadBranchElimPass())
|
||||
.RegisterPass(CreateSimplificationPass())
|
||||
.RegisterPass(CreateScalarReplacementPass(0))
|
||||
.RegisterPass(CreateLocalSingleStoreElimPass())
|
||||
.RegisterPass(CreateIfConversionPass())
|
||||
.RegisterPass(CreateSimplificationPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateDeadBranchElimPass())
|
||||
.RegisterPass(CreateIfConversionPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateBlockMergePass())
|
||||
.RegisterPass(CreateSimplificationPass())
|
||||
.RegisterPass(CreateLocalAccessChainConvertPass())
|
||||
.RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateCopyPropagateArraysPass())
|
||||
.RegisterPass(CreateVectorDCEPass())
|
||||
.RegisterPass(CreateDeadInsertElimPass())
|
||||
.RegisterPass(CreateEliminateDeadMembersPass())
|
||||
.RegisterPass(CreateLocalSingleStoreElimPass())
|
||||
.RegisterPass(CreateBlockMergePass())
|
||||
.RegisterPass(CreateLocalMultiStoreElimPass())
|
||||
.RegisterPass(CreateRedundancyEliminationPass())
|
||||
.RegisterPass(CreateCFGCleanupPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass());
|
||||
.RegisterPass(CreateSimplificationPass())
|
||||
.RegisterPass(CreateAggressiveDCEPass())
|
||||
.RegisterPass(CreateCFGCleanupPass());
|
||||
}
|
||||
|
||||
Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
|
||||
|
@ -78,9 +78,16 @@ class ComputeRegisterLiveness {
|
||||
// - Second, walk loop forest to propagate registers crossing back-edges
|
||||
// (add iterative values into the liveness set).
|
||||
void Compute() {
|
||||
cfg_.ForEachBlockInPostOrder(&*function_->begin(), [this](BasicBlock* bb) {
|
||||
ComputePartialLiveness(bb);
|
||||
});
|
||||
for (BasicBlock& start_bb : *function_) {
|
||||
if (reg_pressure_->Get(start_bb.id()) != nullptr) {
|
||||
continue;
|
||||
}
|
||||
cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) {
|
||||
if (reg_pressure_->Get(bb->id()) == nullptr) {
|
||||
ComputePartialLiveness(bb);
|
||||
}
|
||||
});
|
||||
}
|
||||
DoLoopLivenessUnification();
|
||||
EvaluateRegisterRequirements();
|
||||
}
|
||||
|
3
3rdparty/spirv-tools/source/print.cpp
vendored
3
3rdparty/spirv-tools/source/print.cpp
vendored
@ -15,7 +15,8 @@
|
||||
#include "source/print.h"
|
||||
|
||||
#if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \
|
||||
defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || defined(SPIRV_EMSCRIPTEN)
|
||||
defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || \
|
||||
defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA)
|
||||
namespace spvtools {
|
||||
|
||||
clr::reset::operator const char*() { return "\x1b[0m"; }
|
||||
|
@ -23,7 +23,8 @@ spv_fuzzer_options_t::spv_fuzzer_options_t()
|
||||
: has_random_seed(false),
|
||||
random_seed(0),
|
||||
replay_validation_enabled(false),
|
||||
shrinker_step_limit(kDefaultStepLimit) {}
|
||||
shrinker_step_limit(kDefaultStepLimit),
|
||||
fuzzer_pass_validation_enabled(false) {}
|
||||
|
||||
SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() {
|
||||
return new spv_fuzzer_options_t();
|
||||
@ -48,3 +49,8 @@ SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit(
|
||||
spv_fuzzer_options options, uint32_t shrinker_step_limit) {
|
||||
options->shrinker_step_limit = shrinker_step_limit;
|
||||
}
|
||||
|
||||
SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation(
|
||||
spv_fuzzer_options options) {
|
||||
options->fuzzer_pass_validation_enabled = true;
|
||||
}
|
||||
|
@ -34,6 +34,9 @@ struct spv_fuzzer_options_t {
|
||||
|
||||
// See spvFuzzerOptionsSetShrinkerStepLimit.
|
||||
uint32_t shrinker_step_limit;
|
||||
|
||||
// See spvFuzzerOptionsValidateAfterEveryPass.
|
||||
bool fuzzer_pass_validation_enabled;
|
||||
};
|
||||
|
||||
#endif // SOURCE_SPIRV_FUZZER_OPTIONS_H_
|
||||
|
@ -15,8 +15,8 @@
|
||||
#ifndef SOURCE_VAL_BASIC_BLOCK_H_
|
||||
#define SOURCE_VAL_BASIC_BLOCK_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -28,7 +28,7 @@ namespace val {
|
||||
|
||||
enum BlockType : uint32_t {
|
||||
kBlockTypeUndefined,
|
||||
kBlockTypeHeader,
|
||||
kBlockTypeSelection,
|
||||
kBlockTypeLoop,
|
||||
kBlockTypeMerge,
|
||||
kBlockTypeBreak,
|
||||
|
7
3rdparty/spirv-tools/source/val/function.cpp
vendored
7
3rdparty/spirv-tools/source/val/function.cpp
vendored
@ -14,9 +14,8 @@
|
||||
|
||||
#include "source/val/function.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@ -99,7 +98,7 @@ spv_result_t Function::RegisterLoopMerge(uint32_t merge_id,
|
||||
spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
|
||||
RegisterBlock(merge_id, false);
|
||||
BasicBlock& merge_block = blocks_.at(merge_id);
|
||||
current_block_->set_type(kBlockTypeHeader);
|
||||
current_block_->set_type(kBlockTypeSelection);
|
||||
merge_block.set_type(kBlockTypeMerge);
|
||||
merge_block_header_[&merge_block] = current_block_;
|
||||
|
||||
@ -344,7 +343,7 @@ int Function::GetBlockDepth(BasicBlock* bb) {
|
||||
BasicBlock* header = merge_block_header_[bb];
|
||||
assert(header);
|
||||
block_depth_[bb] = GetBlockDepth(header);
|
||||
} else if (bb_dom->is_type(kBlockTypeHeader) ||
|
||||
} else if (bb_dom->is_type(kBlockTypeSelection) ||
|
||||
bb_dom->is_type(kBlockTypeLoop)) {
|
||||
// The dominator of the given block is a header block. So, the nesting
|
||||
// depth of this block is: 1 + nesting depth of the header.
|
||||
|
23
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
23
3rdparty/spirv-tools/source/val/validate_cfg.cpp
vendored
@ -12,8 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/val/validate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
@ -34,6 +32,7 @@
|
||||
#include "source/val/basic_block.h"
|
||||
#include "source/val/construct.h"
|
||||
#include "source/val/function.h"
|
||||
#include "source/val/validate.h"
|
||||
#include "source/val/validation_state.h"
|
||||
|
||||
namespace spvtools {
|
||||
@ -755,6 +754,26 @@ spv_result_t StructuredControlFlowChecks(
|
||||
<< header_name << " <ID> " << header->id();
|
||||
}
|
||||
}
|
||||
|
||||
if (block->is_type(BlockType::kBlockTypeSelection) ||
|
||||
block->is_type(BlockType::kBlockTypeLoop)) {
|
||||
size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1;
|
||||
const auto& merge_inst = _.ordered_instructions()[index];
|
||||
if (merge_inst.opcode() == SpvOpSelectionMerge ||
|
||||
merge_inst.opcode() == SpvOpLoopMerge) {
|
||||
uint32_t merge_id = merge_inst.GetOperandAs<uint32_t>(0);
|
||||
auto merge_block = function->GetBlock(merge_id).first;
|
||||
if (merge_block->reachable() &&
|
||||
!construct_blocks.count(merge_block)) {
|
||||
return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id()))
|
||||
<< "Header block " << _.getIdName(block->id())
|
||||
<< " is contained in the " << construct_name
|
||||
<< " construct headed by " << _.getIdName(header->id())
|
||||
<< ", but it's merge block " << _.getIdName(merge_id)
|
||||
<< " is not";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks rules for case constructs.
|
||||
|
@ -56,13 +56,6 @@ spv_result_t ValidateLine(ValidationState_t& _, const Instruction* inst) {
|
||||
} // namespace
|
||||
|
||||
spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (spvIsWebGPUEnv(_.context()->target_env) &&
|
||||
spvOpcodeIsDebug(inst->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, inst)
|
||||
<< "Debugging instructions are not allowed in the WebGPU execution "
|
||||
<< "environment.";
|
||||
}
|
||||
|
||||
switch (inst->opcode()) {
|
||||
case SpvOpMemberName:
|
||||
if (auto error = ValidateMemberName(_, inst)) return error;
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "test/fuzz/fuzz_test_util.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "tools/io.h"
|
||||
@ -105,5 +106,20 @@ void DumpShader(const std::vector<uint32_t>& binary, const char* filename) {
|
||||
}
|
||||
}
|
||||
|
||||
void DumpTransformationsJson(
|
||||
const protobufs::TransformationSequence& transformations,
|
||||
const char* filename) {
|
||||
std::string json_string;
|
||||
auto json_options = google::protobuf::util::JsonOptions();
|
||||
json_options.add_whitespace = true;
|
||||
auto json_generation_status = google::protobuf::util::MessageToJsonString(
|
||||
transformations, &json_string, json_options);
|
||||
if (json_generation_status == google::protobuf::util::Status::OK) {
|
||||
std::ofstream transformations_json_file(filename);
|
||||
transformations_json_file << json_string;
|
||||
transformations_json_file.close();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||
#include "source/opt/build_module.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "spirv-tools/libspirv.h"
|
||||
@ -100,6 +101,12 @@ void DumpShader(opt::IRContext* context, const char* filename);
|
||||
// Dumps |binary| to file |filename|. Useful for interactive debugging.
|
||||
void DumpShader(const std::vector<uint32_t>& binary, const char* filename);
|
||||
|
||||
// Dumps |transformations| to file |filename| in JSON format. Useful for
|
||||
// interactive debugging.
|
||||
void DumpTransformationsJson(
|
||||
const protobufs::TransformationSequence& transformations,
|
||||
const char* filename);
|
||||
|
||||
} // namespace fuzz
|
||||
} // namespace spvtools
|
||||
|
||||
|
@ -62,13 +62,11 @@ void RunFuzzerAndReplayer(const std::string& shader,
|
||||
for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) {
|
||||
std::vector<uint32_t> fuzzer_binary_out;
|
||||
protobufs::TransformationSequence fuzzer_transformation_sequence_out;
|
||||
spvtools::FuzzerOptions fuzzer_options;
|
||||
spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
|
||||
|
||||
Fuzzer fuzzer(env);
|
||||
Fuzzer fuzzer(env, seed, true);
|
||||
fuzzer.SetMessageConsumer(kSilentConsumer);
|
||||
auto fuzzer_result_status =
|
||||
fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out,
|
||||
fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
|
||||
&fuzzer_transformation_sequence_out);
|
||||
ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
|
||||
ASSERT_TRUE(t.Validate(fuzzer_binary_out));
|
||||
|
@ -165,12 +165,10 @@ void RunFuzzerAndShrinker(const std::string& shader,
|
||||
// Run the fuzzer and check that it successfully yields a valid binary.
|
||||
std::vector<uint32_t> fuzzer_binary_out;
|
||||
protobufs::TransformationSequence fuzzer_transformation_sequence_out;
|
||||
spvtools::FuzzerOptions fuzzer_options;
|
||||
spvFuzzerOptionsSetRandomSeed(fuzzer_options, seed);
|
||||
Fuzzer fuzzer(env);
|
||||
Fuzzer fuzzer(env, seed, true);
|
||||
fuzzer.SetMessageConsumer(kSilentConsumer);
|
||||
auto fuzzer_result_status =
|
||||
fuzzer.Run(binary_in, initial_facts, fuzzer_options, &fuzzer_binary_out,
|
||||
fuzzer.Run(binary_in, initial_facts, &fuzzer_binary_out,
|
||||
&fuzzer_transformation_sequence_out);
|
||||
ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result_status);
|
||||
ASSERT_TRUE(t.Validate(fuzzer_binary_out));
|
||||
|
@ -209,117 +209,6 @@ TEST(TransformationAddDeadContinueTest, SimpleExample) {
|
||||
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadContinueTest,
|
||||
DoNotAllowContinueToMergeBlockOfAnotherLoop) {
|
||||
// A loop header must dominate its merge block if that merge block is
|
||||
// reachable. We are thus not allowed to add a dead continue that would result
|
||||
// in violation of this property. This test checks for such a scenario.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %16 %139
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%8 = OpTypeBool
|
||||
%14 = OpTypeVector %6 4
|
||||
%15 = OpTypePointer Input %14
|
||||
%16 = OpVariable %15 Input
|
||||
%138 = OpTypePointer Output %14
|
||||
%139 = OpVariable %138 Output
|
||||
%400 = OpConstantTrue %8
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %500
|
||||
%500 = OpLabel
|
||||
OpLoopMerge %501 %502 None
|
||||
OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502
|
||||
%503 = OpLabel
|
||||
OpLoopMerge %502 %504 None
|
||||
OpBranchConditional %400 %505 %504
|
||||
%505 = OpLabel
|
||||
OpBranch %502
|
||||
%504 = OpLabel
|
||||
OpBranch %503
|
||||
%502 = OpLabel
|
||||
OpBranchConditional %400 %501 %500
|
||||
%501 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
FactManager fact_manager;
|
||||
|
||||
ASSERT_FALSE(TransformationAddDeadContinue(500, true, {})
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
ASSERT_FALSE(TransformationAddDeadContinue(500, false, {})
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadContinueTest, DoNotAllowContinueToSelectionMerge) {
|
||||
// A selection header must dominate its merge block if that merge block is
|
||||
// reachable. We are thus not allowed to add a dead continue that would result
|
||||
// in violation of this property. This test checks for such a scenario.
|
||||
|
||||
std::string shader = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %4 "main" %16 %139
|
||||
OpExecutionMode %4 OriginUpperLeft
|
||||
OpSource ESSL 310
|
||||
%2 = OpTypeVoid
|
||||
%3 = OpTypeFunction %2
|
||||
%6 = OpTypeFloat 32
|
||||
%7 = OpTypePointer Function %6
|
||||
%8 = OpTypeBool
|
||||
%14 = OpTypeVector %6 4
|
||||
%15 = OpTypePointer Input %14
|
||||
%16 = OpVariable %15 Input
|
||||
%138 = OpTypePointer Output %14
|
||||
%139 = OpVariable %138 Output
|
||||
%400 = OpConstantTrue %8
|
||||
%4 = OpFunction %2 None %3
|
||||
%5 = OpLabel
|
||||
OpBranch %500
|
||||
%500 = OpLabel
|
||||
OpLoopMerge %501 %502 None
|
||||
OpBranch %503 ; We are not allowed to change this to OpBranchConditional %400 %503 %502
|
||||
%503 = OpLabel
|
||||
OpSelectionMerge %502 None
|
||||
OpBranchConditional %400 %505 %504
|
||||
%505 = OpLabel
|
||||
OpBranch %502
|
||||
%504 = OpLabel
|
||||
OpBranch %502
|
||||
%502 = OpLabel
|
||||
OpBranchConditional %400 %501 %500
|
||||
%501 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const auto env = SPV_ENV_UNIVERSAL_1_3;
|
||||
const auto consumer = nullptr;
|
||||
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||
ASSERT_TRUE(IsValid(env, context.get()));
|
||||
FactManager fact_manager;
|
||||
|
||||
ASSERT_FALSE(TransformationAddDeadContinue(500, true, {})
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
ASSERT_FALSE(TransformationAddDeadContinue(500, false, {})
|
||||
.IsApplicable(context.get(), fact_manager));
|
||||
}
|
||||
|
||||
TEST(TransformationAddDeadContinueTest, LoopNest) {
|
||||
// Checks some allowed and disallowed scenarios for a nest of loops, including
|
||||
// continuing a loop from an if or switch.
|
||||
@ -1420,7 +1309,6 @@ TEST(TransformationAddDeadContinueTest, Miscellaneous2) {
|
||||
OpLoopMerge %1557 %1570 None
|
||||
OpBranchConditional %395 %1562 %1557
|
||||
%1562 = OpLabel
|
||||
OpSelectionMerge %1570 None
|
||||
OpBranchConditional %395 %1571 %1570
|
||||
%1571 = OpLabel
|
||||
OpBranch %1557
|
||||
|
@ -291,7 +291,7 @@ TEST(TransformationCopyObjectTest, CheckIllegalCases) {
|
||||
%31 = OpLabel
|
||||
%42 = OpAccessChain %36 %18 %41
|
||||
%43 = OpLoad %11 %42
|
||||
OpSelectionMerge %47 None
|
||||
OpSelectionMerge %45 None
|
||||
OpSwitch %43 %46 0 %44 1 %45
|
||||
%46 = OpLabel
|
||||
%69 = OpIAdd %11 %96 %27
|
||||
|
@ -159,18 +159,20 @@ const std::string kComplexShader = R"(
|
||||
%65 = OpAccessChain %13 %11 %64
|
||||
%66 = OpLoad %6 %65
|
||||
%67 = OpSGreaterThan %29 %84 %66
|
||||
OpSelectionMerge %69 None
|
||||
OpSelectionMerge %1000 None
|
||||
OpBranchConditional %67 %68 %72
|
||||
%68 = OpLabel
|
||||
%71 = OpIAdd %6 %84 %26
|
||||
OpBranch %69
|
||||
OpBranch %1000
|
||||
%72 = OpLabel
|
||||
%74 = OpIAdd %6 %84 %64
|
||||
%205 = OpCopyObject %6 %74
|
||||
OpBranch %69
|
||||
%69 = OpLabel
|
||||
OpBranch %1000
|
||||
%1000 = OpLabel
|
||||
%86 = OpPhi %6 %71 %68 %74 %72
|
||||
%301 = OpPhi %6 %71 %68 %15 %72
|
||||
OpBranch %69
|
||||
%69 = OpLabel
|
||||
OpBranch %20
|
||||
%22 = OpLabel
|
||||
%75 = OpAccessChain %46 %42 %50
|
||||
@ -421,18 +423,20 @@ TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) {
|
||||
%65 = OpAccessChain %13 %11 %64
|
||||
%66 = OpLoad %6 %65
|
||||
%67 = OpSGreaterThan %29 %84 %66
|
||||
OpSelectionMerge %69 None
|
||||
OpSelectionMerge %1000 None
|
||||
OpBranchConditional %67 %68 %72
|
||||
%68 = OpLabel
|
||||
%71 = OpIAdd %6 %84 %26
|
||||
OpBranch %69
|
||||
OpBranch %1000
|
||||
%72 = OpLabel
|
||||
%74 = OpIAdd %6 %84 %64
|
||||
%205 = OpCopyObject %6 %74
|
||||
OpBranch %69
|
||||
%69 = OpLabel
|
||||
OpBranch %1000
|
||||
%1000 = OpLabel
|
||||
%86 = OpPhi %6 %71 %68 %205 %72
|
||||
%301 = OpPhi %6 %71 %68 %15 %72
|
||||
OpBranch %69
|
||||
%69 = OpLabel
|
||||
OpBranch %20
|
||||
%22 = OpLabel
|
||||
%75 = OpAccessChain %46 %42 %50
|
||||
|
@ -5932,7 +5932,6 @@ OpBranch %42
|
||||
%42 = OpLabel
|
||||
%43 = OpLoad %int %i
|
||||
%44 = OpSLessThan %bool %43 %int_1
|
||||
OpSelectionMerge %45 None
|
||||
OpBranchConditional %44 %46 %40
|
||||
%46 = OpLabel
|
||||
%47 = OpLoad %int %i
|
||||
|
@ -447,10 +447,53 @@ OpBranch %header
|
||||
OpLoopMerge %merge %continue None
|
||||
OpBranch %inner_header
|
||||
%inner_header = OpLabel
|
||||
OpSelectionMerge %continue None
|
||||
OpBranchConditional %true %then %continue
|
||||
OpSelectionMerge %if_merge None
|
||||
OpBranchConditional %true %then %if_merge
|
||||
%then = OpLabel
|
||||
OpBranch %continue
|
||||
%if_merge = OpLabel
|
||||
OpBranch %continue
|
||||
%continue = OpLabel
|
||||
OpBranchConditional %false %merge %header
|
||||
%merge = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<BlockMergePass>(text, true);
|
||||
}
|
||||
|
||||
TEST_F(BlockMergeTest, CannotMergeContinue) {
|
||||
const std::string text = R"(
|
||||
; CHECK: OpBranch [[loop_header:%\w+]]
|
||||
; CHECK: [[loop_header]] = OpLabel
|
||||
; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue:%\w+]]
|
||||
; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_header:%\w+]]
|
||||
; CHECK: [[if_header]] = OpLabel
|
||||
; CHECK-NEXT: OpSelectionMerge
|
||||
; CHECK: [[continue]] = OpLabel
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%void = OpTypeVoid
|
||||
%bool = OpTypeBool
|
||||
%true = OpConstantTrue %bool
|
||||
%false = OpConstantFalse %bool
|
||||
%functy = OpTypeFunction %void
|
||||
%func = OpFunction %void None %functy
|
||||
%entry = OpLabel
|
||||
OpBranch %header
|
||||
%header = OpLabel
|
||||
OpLoopMerge %merge %continue None
|
||||
OpBranchConditional %true %inner_header %merge
|
||||
%inner_header = OpLabel
|
||||
OpSelectionMerge %if_merge None
|
||||
OpBranchConditional %true %then %if_merge
|
||||
%then = OpLabel
|
||||
OpBranch %continue
|
||||
%if_merge = OpLabel
|
||||
OpBranch %continue
|
||||
%continue = OpLabel
|
||||
OpBranchConditional %false %merge %header
|
||||
%merge = OpLabel
|
||||
|
@ -2798,7 +2798,9 @@ OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, true);
|
||||
// The selection merge in the loop naming the continue target as merge is
|
||||
// invalid, but handled by this pass so validation is disabled.
|
||||
SinglePassRunAndMatch<DeadBranchElimPass>(predefs + body, false);
|
||||
}
|
||||
|
||||
TEST_F(DeadBranchElimTest, SelectionMergeWithNestedLoop) {
|
||||
@ -2942,7 +2944,9 @@ OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndMatch<DeadBranchElimPass>(body, true);
|
||||
// The selection merge in the loop naming the continue target as merge is
|
||||
// invalid, but handled by this pass so validation is disabled.
|
||||
SinglePassRunAndMatch<DeadBranchElimPass>(body, false);
|
||||
}
|
||||
|
||||
TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) {
|
||||
|
@ -1819,7 +1819,7 @@ OpFunctionEnd
|
||||
%9 = OpLabel
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %12 %13 None
|
||||
OpLoopMerge %12 %10 None
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpBranchConditional %true %10 %12
|
||||
@ -1980,7 +1980,7 @@ OpFunctionEnd
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
%14 = OpCopyObject %bool %false
|
||||
OpLoopMerge %16 %19 None
|
||||
OpLoopMerge %16 %13 None
|
||||
OpBranch %17
|
||||
%17 = OpLabel
|
||||
%18 = OpCopyObject %bool %true
|
||||
@ -2145,7 +2145,7 @@ OpBranch %19
|
||||
%19 = OpLabel
|
||||
%20 = OpCopyObject %int %int_2
|
||||
%25 = OpCopyObject %int %int_0
|
||||
OpLoopMerge %23 %26 None
|
||||
OpLoopMerge %23 %19 None
|
||||
OpBranch %26
|
||||
%27 = OpLabel
|
||||
%28 = OpCopyObject %int %int_1
|
||||
|
@ -193,7 +193,24 @@ OpDecorate %fo Location 0
|
||||
)";
|
||||
|
||||
const std::string before =
|
||||
R"(%main = OpFunction %void None %9
|
||||
R"(
|
||||
; CHECK: = OpFunction
|
||||
; CHECK-NEXT: [[entry:%\w+]] = OpLabel
|
||||
; CHECK: [[outer_header:%\w+]] = OpLabel
|
||||
; CHECK-NEXT: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]]
|
||||
; CHECK-NEXT: [[i:%\w+]] = OpPhi %int %int_0 [[entry]] [[i_next:%\w+]] [[outer_be]]
|
||||
; CHECK-NEXT: OpSLessThan {{%\w+}} [[i]]
|
||||
; CHECK: [[inner_pre_header:%\w+]] = OpLabel
|
||||
; CHECK: [[inner_header:%\w+]] = OpLabel
|
||||
; CHECK-NEXT: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]]
|
||||
; CHECK-NEXT: [[j:%\w+]] = OpPhi %int %int_0 [[inner_pre_header]] [[j_next:%\w+]] [[inner_be]]
|
||||
; CHECK: [[inner_be]] = OpLabel
|
||||
; CHECK: [[f_next]] = OpFAdd %float [[inner_f]]
|
||||
; CHECK: [[j_next]] = OpIAdd %int [[j]] %int_1
|
||||
; CHECK: [[outer_be]] = OpLabel
|
||||
; CHECK: [[i_next]] = OpIAdd
|
||||
; CHECK: OpStore %fo [[outer_f]]
|
||||
%main = OpFunction %void None %9
|
||||
%24 = OpLabel
|
||||
%f = OpVariable %_ptr_Function_float Function
|
||||
%i = OpVariable %_ptr_Function_int Function
|
||||
@ -212,8 +229,8 @@ OpBranch %31
|
||||
%31 = OpLabel
|
||||
%32 = OpLoad %int %j
|
||||
%33 = OpSLessThan %bool %32 %int_4
|
||||
OpLoopMerge %29 %34 None
|
||||
OpBranchConditional %33 %34 %29
|
||||
OpLoopMerge %50 %34 None
|
||||
OpBranchConditional %33 %34 %50
|
||||
%34 = OpLabel
|
||||
%35 = OpLoad %float %f
|
||||
%36 = OpLoad %int %i
|
||||
@ -226,6 +243,8 @@ OpStore %f %40
|
||||
%42 = OpIAdd %int %41 %int_1
|
||||
OpStore %j %42
|
||||
OpBranch %31
|
||||
%50 = OpLabel
|
||||
OpBranch %29
|
||||
%29 = OpLabel
|
||||
%43 = OpLoad %int %i
|
||||
%44 = OpIAdd %int %43 %int_1
|
||||
@ -238,50 +257,7 @@ OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
const std::string after =
|
||||
R"(%main = OpFunction %void None %9
|
||||
%24 = OpLabel
|
||||
%f = OpVariable %_ptr_Function_float Function
|
||||
%i = OpVariable %_ptr_Function_int Function
|
||||
%j = OpVariable %_ptr_Function_int Function
|
||||
OpStore %f %float_0
|
||||
OpStore %i %int_0
|
||||
OpBranch %25
|
||||
%25 = OpLabel
|
||||
%47 = OpPhi %float %float_0 %24 %50 %29
|
||||
%46 = OpPhi %int %int_0 %24 %44 %29
|
||||
%27 = OpSLessThan %bool %46 %int_4
|
||||
OpLoopMerge %28 %29 None
|
||||
OpBranchConditional %27 %30 %28
|
||||
%30 = OpLabel
|
||||
OpStore %j %int_0
|
||||
OpBranch %31
|
||||
%31 = OpLabel
|
||||
%50 = OpPhi %float %47 %30 %40 %34
|
||||
%48 = OpPhi %int %int_0 %30 %42 %34
|
||||
%33 = OpSLessThan %bool %48 %int_4
|
||||
OpLoopMerge %29 %34 None
|
||||
OpBranchConditional %33 %34 %29
|
||||
%34 = OpLabel
|
||||
%38 = OpAccessChain %_ptr_Input_float %BC %46 %48
|
||||
%39 = OpLoad %float %38
|
||||
%40 = OpFAdd %float %50 %39
|
||||
OpStore %f %40
|
||||
%42 = OpIAdd %int %48 %int_1
|
||||
OpStore %j %42
|
||||
OpBranch %31
|
||||
%29 = OpLabel
|
||||
%44 = OpIAdd %int %46 %int_1
|
||||
OpStore %i %44
|
||||
OpBranch %25
|
||||
%28 = OpLabel
|
||||
OpStore %fo %47
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SinglePassRunAndCheck<SSARewritePass>(predefs + before, predefs + after, true,
|
||||
true);
|
||||
SinglePassRunAndMatch<SSARewritePass>(predefs + before, true);
|
||||
}
|
||||
|
||||
TEST_F(LocalSSAElimTest, ForLoopWithContinue) {
|
||||
|
@ -3177,7 +3177,7 @@ TEST_F(FusionLegalTest, OuterloopWithBreakContinueInInner) {
|
||||
%21 = OpLabel
|
||||
%29 = OpSMod %6 %96 %28
|
||||
%30 = OpIEqual %17 %29 %9
|
||||
OpSelectionMerge %23 None
|
||||
OpSelectionMerge %sel_merge None
|
||||
OpBranchConditional %30 %31 %48
|
||||
%31 = OpLabel
|
||||
%44 = OpAccessChain %7 %41 %91 %96
|
||||
@ -3187,8 +3187,10 @@ TEST_F(FusionLegalTest, OuterloopWithBreakContinueInInner) {
|
||||
OpStore %47 %46
|
||||
OpBranch %32
|
||||
%48 = OpLabel
|
||||
OpBranch %23
|
||||
OpBranch %sel_merge
|
||||
%32 = OpLabel
|
||||
OpBranch %sel_merge
|
||||
%sel_merge = OpLabel
|
||||
OpBranch %23
|
||||
%23 = OpLabel
|
||||
%52 = OpIAdd %6 %96 %51
|
||||
|
@ -1390,7 +1390,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) {
|
||||
OpLoopMerge %36 %40 None
|
||||
OpBranch %35
|
||||
%35 = OpLabel
|
||||
OpSelectionMerge %40 None
|
||||
OpBranchConditional %77 %39 %40
|
||||
%39 = OpLabel
|
||||
OpReturnValue %16
|
||||
@ -1402,7 +1401,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) {
|
||||
OpLoopMerge %45 %49 None
|
||||
OpBranch %44
|
||||
%44 = OpLabel
|
||||
OpSelectionMerge %49 None
|
||||
OpBranchConditional %77 %48 %49
|
||||
%48 = OpLabel
|
||||
OpReturnValue %16
|
||||
@ -1415,7 +1413,6 @@ TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) {
|
||||
OpLoopMerge %64 %68 None
|
||||
OpBranch %63
|
||||
%63 = OpLabel
|
||||
OpSelectionMerge %68 None
|
||||
OpBranchConditional %77 %67 %68
|
||||
%67 = OpLabel
|
||||
OpReturnValue %16
|
||||
@ -1813,12 +1810,14 @@ TEST_F(MergeReturnPassTest, PhiInSecondMerge) {
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpLoopMerge %12 %14 None
|
||||
OpBranchConditional %8 %15 %12
|
||||
OpLoopMerge %18 %14 None
|
||||
OpBranchConditional %8 %15 %18
|
||||
%15 = OpLabel
|
||||
OpReturn
|
||||
%14 = OpLabel
|
||||
OpBranch %13
|
||||
%18 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
%16 = OpUndef %float
|
||||
OpBranchConditional %8 %10 %11
|
||||
|
@ -1277,6 +1277,46 @@ TEST_F(PassClassTest, FissionSimulation) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test that register liveness does not fail when there is an unreachable block.
|
||||
// We are not testing if the liveness is computed correctly because the specific
|
||||
// results do not matter for unreachable blocks.
|
||||
TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
%1 = OpExtInstImport "GLSL.std.450"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %2 "main"
|
||||
OpExecutionMode %2 OriginLowerLeft
|
||||
OpSource GLSL 330
|
||||
OpSourceExtension "GL_ARB_shading_language_420pack"
|
||||
%void = OpTypeVoid
|
||||
%4 = OpTypeFunction %void
|
||||
%2 = OpFunction %void None %4
|
||||
%5 = OpLabel
|
||||
OpBranch %6
|
||||
%6 = OpLabel
|
||||
OpLoopMerge %7 %8 None
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
OpBranch %7
|
||||
%8 = OpLabel
|
||||
OpBranch %6
|
||||
%7 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
std::unique_ptr<IRContext> context =
|
||||
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
|
||||
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
Module* module = context->module();
|
||||
EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
|
||||
<< text << std::endl;
|
||||
Function* f = &*module->begin();
|
||||
LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis();
|
||||
liveness_analysis->Get(f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opt
|
||||
} // namespace spvtools
|
||||
|
@ -13,6 +13,9 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <string>
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "spirv-tools/optimizer.hpp"
|
||||
|
||||
#include "test/opt/pass_fixture.h"
|
||||
#include "test/opt/pass_utils.h"
|
||||
@ -23,6 +26,50 @@ namespace {
|
||||
|
||||
using StripLineReflectInfoTest = PassTest<::testing::Test>;
|
||||
|
||||
// This test acts as an end-to-end code example on how to strip
|
||||
// reflection info from a SPIR-V module. Use this code pattern
|
||||
// when you have compiled HLSL code with Glslang or DXC using
|
||||
// option -fhlsl_functionality1 to insert reflection information,
|
||||
// but then want to filter out the extra instructions before sending
|
||||
// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1.
|
||||
TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) {
|
||||
// This is a non-sensical example, but exercises the instructions.
|
||||
std::string before = R"(OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpExtension "SPV_GOOGLE_decorate_string"
|
||||
OpExtension "SPV_GOOGLE_hlsl_functionality1"
|
||||
OpMemoryModel Logical Simple
|
||||
OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar"
|
||||
OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness"
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
)";
|
||||
SpirvTools tools(SPV_ENV_UNIVERSAL_1_1);
|
||||
std::vector<uint32_t> binary_in;
|
||||
tools.Assemble(before, &binary_in);
|
||||
|
||||
// Instantiate the optimizer, and run the strip-reflection-info
|
||||
// pass over the |binary_in| module, and place the modified module
|
||||
// into |binary_out|.
|
||||
spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1);
|
||||
optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass());
|
||||
std::vector<uint32_t> binary_out;
|
||||
optimizer.Run(binary_in.data(), binary_in.size(), &binary_out);
|
||||
|
||||
// Check results
|
||||
std::string disassembly;
|
||||
tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly);
|
||||
std::string after = R"(OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical Simple
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
)";
|
||||
EXPECT_THAT(disassembly, testing::Eq(after));
|
||||
}
|
||||
|
||||
// This test is functionally the same as the end-to-end test above,
|
||||
// but uses the test SinglePassRunAndCheck test fixture instead.
|
||||
TEST_F(StripLineReflectInfoTest, StripHlslSemantic) {
|
||||
// This is a non-sensical example, but exercises the instructions.
|
||||
std::string before = R"(OpCapability Shader
|
||||
|
@ -125,19 +125,21 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
%29 = OpAccessChain %28 %27 %9
|
||||
%30 = OpLoad %24 %29
|
||||
%32 = OpFOrdGreaterThan %22 %30 %31
|
||||
OpSelectionMerge %34 None
|
||||
OpSelectionMerge %90 None
|
||||
OpBranchConditional %32 %33 %46
|
||||
%33 = OpLabel
|
||||
%40 = OpFAdd %24 %71 %30
|
||||
%45 = OpISub %6 %73 %21
|
||||
OpBranch %34
|
||||
OpBranch %90
|
||||
%46 = OpLabel
|
||||
%50 = OpFMul %24 %71 %30
|
||||
%54 = OpSDiv %6 %73 %21
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
OpBranch %90
|
||||
%90 = OpLabel
|
||||
%77 = OpPhi %6 %45 %33 %54 %46
|
||||
%76 = OpPhi %24 %40 %33 %50 %46
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
%57 = OpIAdd %6 %70 %56
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
@ -193,11 +195,13 @@ TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) {
|
||||
OpLoopMerge %12 %34 None
|
||||
OpBranchConditional %100 %11 %12
|
||||
%11 = OpLabel
|
||||
OpSelectionMerge %34 None
|
||||
OpSelectionMerge %90 None
|
||||
OpBranchConditional %100 %33 %46
|
||||
%33 = OpLabel
|
||||
OpBranch %34
|
||||
OpBranch %90
|
||||
%46 = OpLabel
|
||||
OpBranch %90
|
||||
%90 = OpLabel
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
OpBranch %10
|
||||
@ -345,7 +349,6 @@ const std::string kShaderWithLoopsDivAndMul = R"(
|
||||
OpLoopMerge %33 %38 None
|
||||
OpBranch %32
|
||||
%32 = OpLabel
|
||||
OpSelectionMerge %38 None
|
||||
OpBranchConditional %30 %37 %38
|
||||
%37 = OpLabel
|
||||
OpSelectionMerge %42 None
|
||||
|
@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "source/reduce/reducer.h"
|
||||
|
||||
#include "source/reduce/reduction_opportunity.h"
|
||||
#include "source/reduce/remove_instruction_reduction_opportunity.h"
|
||||
#include "test/reduce/reduce_test_util.h"
|
||||
@ -23,8 +22,8 @@ namespace reduce {
|
||||
namespace {
|
||||
|
||||
using opt::Function;
|
||||
using opt::IRContext;
|
||||
using opt::Instruction;
|
||||
using opt::IRContext;
|
||||
|
||||
// A dumb reduction opportunity finder that finds opportunities to remove global
|
||||
// values regardless of whether they are referenced. This is very likely to make
|
||||
@ -181,19 +180,21 @@ TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) {
|
||||
%29 = OpAccessChain %28 %27 %9
|
||||
%30 = OpLoad %24 %29
|
||||
%32 = OpFOrdGreaterThan %22 %30 %31
|
||||
OpSelectionMerge %34 None
|
||||
OpSelectionMerge %90 None
|
||||
OpBranchConditional %32 %33 %46
|
||||
%33 = OpLabel
|
||||
%40 = OpFAdd %24 %71 %30
|
||||
%45 = OpISub %6 %73 %21
|
||||
OpBranch %34
|
||||
OpBranch %90
|
||||
%46 = OpLabel
|
||||
%50 = OpFMul %24 %71 %30
|
||||
%54 = OpSDiv %6 %73 %21
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
OpBranch %90
|
||||
%90 = OpLabel
|
||||
%77 = OpPhi %6 %45 %33 %54 %46
|
||||
%76 = OpPhi %24 %40 %33 %50 %46
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
%57 = OpIAdd %6 %70 %56
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
@ -303,19 +304,21 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) {
|
||||
%29 = OpAccessChain %28 %27 %9
|
||||
%30 = OpLoad %24 %29
|
||||
%32 = OpFOrdGreaterThan %22 %30 %31
|
||||
OpSelectionMerge %34 None
|
||||
OpSelectionMerge %90 None
|
||||
OpBranchConditional %32 %33 %46
|
||||
%33 = OpLabel
|
||||
%40 = OpFAdd %24 %71 %30
|
||||
%45 = OpISub %6 %73 %21
|
||||
OpBranch %34
|
||||
OpBranch %90
|
||||
%46 = OpLabel
|
||||
%50 = OpFMul %24 %71 %30
|
||||
%54 = OpSDiv %6 %73 %21
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
OpBranch %90
|
||||
%90 = OpLabel
|
||||
%77 = OpPhi %6 %45 %33 %54 %46
|
||||
%76 = OpPhi %24 %40 %33 %50 %46
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
%57 = OpIAdd %6 %70 %56
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
@ -392,19 +395,21 @@ TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) {
|
||||
%29 = OpAccessChain %28 %27 %9
|
||||
%30 = OpLoad %24 %29
|
||||
%32 = OpFOrdGreaterThan %22 %30 %31
|
||||
OpSelectionMerge %34 None
|
||||
OpSelectionMerge %90 None
|
||||
OpBranchConditional %32 %33 %46
|
||||
%33 = OpLabel
|
||||
%40 = OpFAdd %24 %71 %30
|
||||
%45 = OpISub %6 %73 %21
|
||||
OpBranch %34
|
||||
OpBranch %90
|
||||
%46 = OpLabel
|
||||
%50 = OpFMul %24 %71 %30
|
||||
%54 = OpSDiv %6 %73 %21
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
OpBranch %90
|
||||
%90 = OpLabel
|
||||
%77 = OpPhi %6 %45 %33 %54 %46
|
||||
%76 = OpPhi %24 %40 %33 %50 %46
|
||||
OpBranch %34
|
||||
%34 = OpLabel
|
||||
%57 = OpIAdd %6 %70 %56
|
||||
OpBranch %10
|
||||
%12 = OpLabel
|
||||
|
37
3rdparty/spirv-tools/test/tools/opt/flags.py
vendored
37
3rdparty/spirv-tools/test/tools/opt/flags.py
vendored
@ -187,28 +187,35 @@ class TestSizeOptimizationPasses(expect.ValidObjectFile1_5,
|
||||
'eliminate-dead-branches',
|
||||
'merge-return',
|
||||
'inline-entry-points-exhaustive',
|
||||
'eliminate-dead-code-aggressive',
|
||||
'eliminate-dead-functions',
|
||||
'private-to-local',
|
||||
'scalar-replacement=100',
|
||||
'convert-local-access-chains',
|
||||
'eliminate-local-single-block',
|
||||
'eliminate-local-single-store',
|
||||
'eliminate-dead-code-aggressive',
|
||||
'simplify-instructions',
|
||||
'eliminate-dead-inserts',
|
||||
'scalar-replacement=0',
|
||||
'ssa-rewrite',
|
||||
'eliminate-dead-code-aggressive',
|
||||
'ccp',
|
||||
'loop-unroll',
|
||||
'eliminate-dead-branches',
|
||||
'simplify-instructions',
|
||||
'scalar-replacement=0',
|
||||
'eliminate-local-single-store',
|
||||
'if-conversion',
|
||||
'simplify-instructions',
|
||||
'eliminate-dead-code-aggressive',
|
||||
'eliminate-dead-branches',
|
||||
'if-conversion',
|
||||
'eliminate-dead-code-aggressive',
|
||||
'merge-blocks',
|
||||
'simplify-instructions',
|
||||
'eliminate-dead-inserts',
|
||||
'redundancy-elimination',
|
||||
'cfg-cleanup',
|
||||
'convert-local-access-chains',
|
||||
'eliminate-local-single-block',
|
||||
'eliminate-dead-code-aggressive',
|
||||
'copy-propagate-arrays',
|
||||
'vector-dce',
|
||||
'eliminate-dead-inserts',
|
||||
'eliminate-dead-members',
|
||||
'eliminate-local-single-store',
|
||||
'merge-blocks',
|
||||
'ssa-rewrite',
|
||||
'redundancy-elimination',
|
||||
'simplify-instructions',
|
||||
'eliminate-dead-code-aggressive',
|
||||
'cfg-cleanup',
|
||||
]
|
||||
shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm')
|
||||
output = placeholder.TempFileName('output.spv')
|
||||
|
143
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
143
3rdparty/spirv-tools/test/val/val_cfg_test.cpp
vendored
@ -23,7 +23,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "source/diagnostic.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/val/validate.h"
|
||||
@ -1355,7 +1354,7 @@ std::string GetReachableMergeAndContinue(SpvCapability cap,
|
||||
}
|
||||
if (cap == SpvCapabilityShader) {
|
||||
branch.AppendBody("OpLoopMerge %merge %target None\n");
|
||||
body.AppendBody("OpSelectionMerge %target None\n");
|
||||
body.AppendBody("OpSelectionMerge %f None\n");
|
||||
}
|
||||
|
||||
if (!spvIsWebGPUEnv(env))
|
||||
@ -1650,25 +1649,27 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
|
||||
Block entry("entry");
|
||||
Block loop1("loop1", SpvOpBranchConditional);
|
||||
Block loop2("loop2", SpvOpBranchConditional);
|
||||
Block loop2_merge("loop2_merge", SpvOpBranchConditional);
|
||||
Block loop2_merge("loop2_merge");
|
||||
Block loop1_cont("loop1_cont", SpvOpBranchConditional);
|
||||
Block be_block("be_block");
|
||||
Block exit("exit", SpvOpReturn);
|
||||
|
||||
entry.SetBody("%cond = OpSLessThan %boolt %one %two\n");
|
||||
if (is_shader) {
|
||||
loop1.SetBody("OpLoopMerge %exit %loop2_merge None\n");
|
||||
loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n");
|
||||
loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n");
|
||||
}
|
||||
|
||||
std::string str = GetDefaultHeader(GetParam()) +
|
||||
nameOps("loop1", "loop2", "be_block", "loop2_merge") +
|
||||
types_consts() +
|
||||
"%func = OpFunction %voidt None %funct\n";
|
||||
std::string str =
|
||||
GetDefaultHeader(GetParam()) +
|
||||
nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") +
|
||||
types_consts() + "%func = OpFunction %voidt None %funct\n";
|
||||
|
||||
str += entry >> loop1;
|
||||
str += loop1 >> std::vector<Block>({loop2, exit});
|
||||
str += loop2 >> std::vector<Block>({loop2, loop2_merge});
|
||||
str += loop2_merge >> std::vector<Block>({be_block, exit});
|
||||
str += loop2_merge >> loop1_cont;
|
||||
str += loop1_cont >> std::vector<Block>({be_block, exit});
|
||||
str += be_block >> loop1;
|
||||
str += exit;
|
||||
str += "OpFunctionEnd";
|
||||
@ -1678,7 +1679,7 @@ TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) {
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
MatchesRegex("The continue construct with the continue target "
|
||||
".\\[%loop2_merge\\] is not post dominated by the "
|
||||
".\\[%loop1_cont\\] is not post dominated by the "
|
||||
"back-edge block .\\[%be_block\\]\n"
|
||||
" %be_block = OpLabel\n"));
|
||||
} else {
|
||||
@ -2037,13 +2038,10 @@ TEST_P(ValidateCFG,
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
|
||||
}
|
||||
|
||||
TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) {
|
||||
// This example is valid. It shows that the validator can't just add
|
||||
// an edge from the loop head to the continue target. If that edge
|
||||
// is added, then the "if_merge" block is both the continue target
|
||||
// for the loop and also the merge block for the nested selection, but
|
||||
// then it wouldn't be dominated by "if_head", the header block for the
|
||||
// nested selection.
|
||||
TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) {
|
||||
// The continue construct cannot be the merge target of a nested selection
|
||||
// because the loop construct must contain "if_merge" because it contains
|
||||
// "if_head".
|
||||
bool is_shader = GetParam() == SpvCapabilityShader;
|
||||
Block entry("entry");
|
||||
Block loop("loop");
|
||||
@ -2072,7 +2070,16 @@ TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructureGood) {
|
||||
str += "OpFunctionEnd";
|
||||
|
||||
CompileSuccessfully(str);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString();
|
||||
if (is_shader) {
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Header block 3[%if_head] is contained in the loop construct "
|
||||
"headed "
|
||||
"by 2[%loop], but it's merge block 5[%if_merge] is not"));
|
||||
} else {
|
||||
EXPECT_THAT(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) {
|
||||
@ -3681,17 +3688,21 @@ OpBranch %7
|
||||
OpLoopMerge %8 %9 None
|
||||
OpBranch %10
|
||||
%10 = OpLabel
|
||||
OpLoopMerge %9 %11 None
|
||||
OpLoopMerge %11 %12 None
|
||||
OpBranch %13
|
||||
%13 = OpLabel
|
||||
OpSelectionMerge %14 None
|
||||
OpBranchConditional %3 %14 %15
|
||||
%15 = OpLabel
|
||||
OpBranch %8
|
||||
%14 = OpLabel
|
||||
OpBranch %12
|
||||
%12 = OpLabel
|
||||
OpSelectionMerge %11 None
|
||||
OpBranchConditional %3 %11 %13
|
||||
%13 = OpLabel
|
||||
OpBranch %8
|
||||
OpBranchConditional %3 %10 %11
|
||||
%11 = OpLabel
|
||||
OpBranchConditional %3 %9 %10
|
||||
OpBranch %9
|
||||
%9 = OpLabel
|
||||
OpBranchConditional %3 %8 %7
|
||||
OpBranchConditional %3 %7 %8
|
||||
%8 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
@ -3700,7 +3711,7 @@ OpFunctionEnd
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("block <ID> 13[%13] exits the loop headed by <ID> "
|
||||
HasSubstr("block <ID> 15[%15] exits the loop headed by <ID> "
|
||||
"10[%10], but not via a structured exit"));
|
||||
}
|
||||
|
||||
@ -3726,9 +3737,11 @@ OpBranchConditional %undef %11 %12
|
||||
OpSelectionMerge %31 None
|
||||
OpBranchConditional %undef %30 %31
|
||||
%30 = OpLabel
|
||||
OpSelectionMerge %37 None
|
||||
OpBranchConditional %undef %36 %37
|
||||
OpSelectionMerge %38 None
|
||||
OpBranchConditional %undef %36 %38
|
||||
%36 = OpLabel
|
||||
OpBranch %38
|
||||
%38 = OpLabel
|
||||
OpBranch %37
|
||||
%37 = OpLabel
|
||||
OpBranch %10
|
||||
@ -4100,6 +4113,80 @@ OpFunctionEnd
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %loop "loop"
|
||||
OpName %continue "continue"
|
||||
OpName %body "body"
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpBranch %loop
|
||||
%loop = OpLabel
|
||||
OpLoopMerge %exit %continue None
|
||||
OpBranch %body
|
||||
%body = OpLabel
|
||||
OpSelectionMerge %continue None
|
||||
OpBranchConditional %undef %exit %continue
|
||||
%continue = OpLabel
|
||||
OpBranch %loop
|
||||
%exit = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Header block 3[%body] is contained in the loop construct headed by "
|
||||
"1[%loop], but it's merge block 2[%continue] is not"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpName %loop "loop"
|
||||
OpName %continue "continue"
|
||||
OpName %inner "inner"
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%bool = OpTypeBool
|
||||
%undef = OpUndef %bool
|
||||
%func = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpBranch %loop
|
||||
%loop = OpLabel
|
||||
OpLoopMerge %exit %continue None
|
||||
OpBranchConditional %undef %exit %inner
|
||||
%inner = OpLabel
|
||||
OpLoopMerge %continue %inner None
|
||||
OpBranchConditional %undef %inner %continue
|
||||
%continue = OpLabel
|
||||
OpBranch %loop
|
||||
%exit = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Header block 3[%inner] is contained in the loop construct headed by "
|
||||
"1[%loop], but it's merge block 2[%continue] is not"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
206
3rdparty/spirv-tools/test/val/val_webgpu_test.cpp
vendored
206
3rdparty/spirv-tools/test/val/val_webgpu_test.cpp
vendored
@ -54,117 +54,170 @@ TEST_F(ValidateWebGPU, OpUndefIsDisallowed) {
|
||||
EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpNameIsDisallowed) {
|
||||
TEST_F(ValidateWebGPU, OpNameIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpName %1 "foo"
|
||||
%1 = OpTypeFloat 32
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpName %1 "foo"
|
||||
%1 = OpTypeFloat 32
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Debugging instructions are not allowed in the WebGPU "
|
||||
"execution environment.\n OpName %foo \"foo\"\n"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpMemberNameIsDisallowed) {
|
||||
TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpMemberName %2 0 "foo"
|
||||
%1 = OpTypeFloat 32
|
||||
%2 = OpTypeStruct %1
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpMemberName %2 0 "foo"
|
||||
%1 = OpTypeFloat 32
|
||||
%2 = OpTypeStruct %1
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Debugging instructions are not allowed in the WebGPU "
|
||||
"execution environment.\n OpMemberName %_struct_1 0 "
|
||||
"\"foo\"\n"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpSourceIsDisallowed) {
|
||||
TEST_F(ValidateWebGPU, OpSourceIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpSource GLSL 450
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpSource GLSL 450
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Debugging instructions are not allowed in the WebGPU "
|
||||
"execution environment.\n OpSource GLSL 450\n"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
// OpSourceContinued does not have a test case, because it requires being
|
||||
// preceded by OpSource, which will cause a validation error.
|
||||
|
||||
TEST_F(ValidateWebGPU, OpSourceExtensionIsDisallowed) {
|
||||
TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpSourceExtension "bar"
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpSource GLSL 450
|
||||
OpSourceContinued "I am a happy shader! Yay! ;"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Debugging instructions are not allowed in the WebGPU "
|
||||
"execution environment.\n OpSourceExtension "
|
||||
"\"bar\"\n"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpStringIsDisallowed) {
|
||||
TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
%1 = OpString "foo"
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpSourceExtension "bar"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Debugging instructions are not allowed in the WebGPU "
|
||||
"execution environment.\n %1 = OpString \"foo\"\n"));
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
// OpLine does not have a test case, because it requires being preceded by
|
||||
// OpString, which will cause a validation error.
|
||||
|
||||
TEST_F(ValidateWebGPU, OpNoLineDisallowed) {
|
||||
TEST_F(ValidateWebGPU, OpStringIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpNoLine
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%1 = OpString "foo"
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Debugging instructions are not allowed in the WebGPU "
|
||||
"execution environment.\n OpNoLine\n"));
|
||||
TEST_F(ValidateWebGPU, OpLineIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
%1 = OpString "minimal.vert"
|
||||
OpLine %1 1 1
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, OpNoLineIsAllowed) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Vertex %func "shader"
|
||||
OpNoLine
|
||||
%void = OpTypeVoid
|
||||
%void_f = OpTypeFunction %void
|
||||
%func = OpFunction %void None %void_f
|
||||
%label = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
|
||||
@ -183,7 +236,6 @@ TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user