mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-26 13:20:05 +00:00
spirv-fuzz: fuzzer pass to adjust memory access operands (#2968)
A new pass that gives spirv-fuzz the ability to adjust the memory operand masks associated with memory access instructions (such as OpLoad and OpCopy Memory). Fixes #2940.
This commit is contained in:
parent
02910ffdff
commit
570582d8d6
@ -41,6 +41,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
fuzzer_pass_add_useful_constructs.h
|
fuzzer_pass_add_useful_constructs.h
|
||||||
fuzzer_pass_adjust_function_controls.h
|
fuzzer_pass_adjust_function_controls.h
|
||||||
fuzzer_pass_adjust_loop_controls.h
|
fuzzer_pass_adjust_loop_controls.h
|
||||||
|
fuzzer_pass_adjust_memory_operands_masks.h
|
||||||
fuzzer_pass_adjust_selection_controls.h
|
fuzzer_pass_adjust_selection_controls.h
|
||||||
fuzzer_pass_apply_id_synonyms.h
|
fuzzer_pass_apply_id_synonyms.h
|
||||||
fuzzer_pass_construct_composites.h
|
fuzzer_pass_construct_composites.h
|
||||||
@ -74,6 +75,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
transformation_replace_id_with_synonym.h
|
transformation_replace_id_with_synonym.h
|
||||||
transformation_set_function_control.h
|
transformation_set_function_control.h
|
||||||
transformation_set_loop_control.h
|
transformation_set_loop_control.h
|
||||||
|
transformation_set_memory_operands_mask.h
|
||||||
transformation_set_selection_control.h
|
transformation_set_selection_control.h
|
||||||
transformation_split_block.h
|
transformation_split_block.h
|
||||||
uniform_buffer_element_descriptor.h
|
uniform_buffer_element_descriptor.h
|
||||||
@ -91,6 +93,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
fuzzer_pass_add_useful_constructs.cpp
|
fuzzer_pass_add_useful_constructs.cpp
|
||||||
fuzzer_pass_adjust_function_controls.cpp
|
fuzzer_pass_adjust_function_controls.cpp
|
||||||
fuzzer_pass_adjust_loop_controls.cpp
|
fuzzer_pass_adjust_loop_controls.cpp
|
||||||
|
fuzzer_pass_adjust_memory_operands_masks.cpp
|
||||||
fuzzer_pass_adjust_selection_controls.cpp
|
fuzzer_pass_adjust_selection_controls.cpp
|
||||||
fuzzer_pass_apply_id_synonyms.cpp
|
fuzzer_pass_apply_id_synonyms.cpp
|
||||||
fuzzer_pass_construct_composites.cpp
|
fuzzer_pass_construct_composites.cpp
|
||||||
@ -123,6 +126,7 @@ if(SPIRV_BUILD_FUZZER)
|
|||||||
transformation_replace_id_with_synonym.cpp
|
transformation_replace_id_with_synonym.cpp
|
||||||
transformation_set_function_control.cpp
|
transformation_set_function_control.cpp
|
||||||
transformation_set_loop_control.cpp
|
transformation_set_loop_control.cpp
|
||||||
|
transformation_set_memory_operands_mask.cpp
|
||||||
transformation_set_selection_control.cpp
|
transformation_set_selection_control.cpp
|
||||||
transformation_split_block.cpp
|
transformation_split_block.cpp
|
||||||
uniform_buffer_element_descriptor.cpp
|
uniform_buffer_element_descriptor.cpp
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "fuzzer_pass_adjust_memory_operands_masks.h"
|
||||||
#include "source/fuzz/fact_manager.h"
|
#include "source/fuzz/fact_manager.h"
|
||||||
#include "source/fuzz/fuzzer_context.h"
|
#include "source/fuzz/fuzzer_context.h"
|
||||||
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
|
#include "source/fuzz/fuzzer_pass_add_dead_breaks.h"
|
||||||
@ -173,14 +174,17 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
|
|||||||
// Now apply some passes that it does not make sense to apply repeatedly,
|
// Now apply some passes that it does not make sense to apply repeatedly,
|
||||||
// as they do not unlock other passes.
|
// as they do not unlock other passes.
|
||||||
std::vector<std::unique_ptr<FuzzerPass>> final_passes;
|
std::vector<std::unique_ptr<FuzzerPass>> final_passes;
|
||||||
MaybeAddPass<FuzzerPassAdjustFunctionControls>(&passes, ir_context.get(),
|
MaybeAddPass<FuzzerPassAdjustFunctionControls>(
|
||||||
&fact_manager, &fuzzer_context,
|
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
MaybeAddPass<FuzzerPassAdjustLoopControls>(&passes, ir_context.get(),
|
MaybeAddPass<FuzzerPassAdjustLoopControls>(&final_passes, ir_context.get(),
|
||||||
&fact_manager, &fuzzer_context,
|
&fact_manager, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
|
MaybeAddPass<FuzzerPassAdjustMemoryOperandsMasks>(
|
||||||
|
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||||
|
transformation_sequence_out);
|
||||||
MaybeAddPass<FuzzerPassAdjustSelectionControls>(
|
MaybeAddPass<FuzzerPassAdjustSelectionControls>(
|
||||||
&passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
&final_passes, ir_context.get(), &fact_manager, &fuzzer_context,
|
||||||
transformation_sequence_out);
|
transformation_sequence_out);
|
||||||
for (auto& pass : final_passes) {
|
for (auto& pass : final_passes) {
|
||||||
pass->Apply();
|
pass->Apply();
|
||||||
|
@ -30,6 +30,8 @@ const std::pair<uint32_t, uint32_t> kChanceOfAddingNoContractionDecoration = {
|
|||||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
|
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingFunctionControl = {20,
|
||||||
70};
|
70};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
|
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingLoopControl = {20, 90};
|
||||||
|
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingMemoryOperandsMask = {20,
|
||||||
|
90};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
|
const std::pair<uint32_t, uint32_t> kChanceOfAdjustingSelectionControl = {20,
|
||||||
90};
|
90};
|
||||||
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
|
const std::pair<uint32_t, uint32_t> kChanceOfCopyingObject = {20, 50};
|
||||||
@ -72,6 +74,8 @@ FuzzerContext::FuzzerContext(RandomGenerator* random_generator,
|
|||||||
ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
|
ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl);
|
||||||
chance_of_adjusting_loop_control_ =
|
chance_of_adjusting_loop_control_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
|
ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl);
|
||||||
|
chance_of_adjusting_memory_operands_mask_ =
|
||||||
|
ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask);
|
||||||
chance_of_adjusting_selection_control_ =
|
chance_of_adjusting_selection_control_ =
|
||||||
ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
|
ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl);
|
||||||
chance_of_constructing_composite_ =
|
chance_of_constructing_composite_ =
|
||||||
|
@ -71,6 +71,9 @@ class FuzzerContext {
|
|||||||
uint32_t GetChanceOfAdjustingLoopControl() {
|
uint32_t GetChanceOfAdjustingLoopControl() {
|
||||||
return chance_of_adjusting_loop_control_;
|
return chance_of_adjusting_loop_control_;
|
||||||
}
|
}
|
||||||
|
uint32_t GetChanceOfAdjustingMemoryOperandsMask() {
|
||||||
|
return chance_of_adjusting_memory_operands_mask_;
|
||||||
|
}
|
||||||
uint32_t GetChanceOfAdjustingSelectionControl() {
|
uint32_t GetChanceOfAdjustingSelectionControl() {
|
||||||
return chance_of_adjusting_selection_control_;
|
return chance_of_adjusting_selection_control_;
|
||||||
}
|
}
|
||||||
@ -112,6 +115,7 @@ class FuzzerContext {
|
|||||||
uint32_t chance_of_adding_no_contraction_decoration_;
|
uint32_t chance_of_adding_no_contraction_decoration_;
|
||||||
uint32_t chance_of_adjusting_function_control_;
|
uint32_t chance_of_adjusting_function_control_;
|
||||||
uint32_t chance_of_adjusting_loop_control_;
|
uint32_t chance_of_adjusting_loop_control_;
|
||||||
|
uint32_t chance_of_adjusting_memory_operands_mask_;
|
||||||
uint32_t chance_of_adjusting_selection_control_;
|
uint32_t chance_of_adjusting_selection_control_;
|
||||||
uint32_t chance_of_constructing_composite_;
|
uint32_t chance_of_constructing_composite_;
|
||||||
uint32_t chance_of_copying_object_;
|
uint32_t chance_of_copying_object_;
|
||||||
|
113
source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
Normal file
113
source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
// 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/fuzz/fuzzer_pass_adjust_memory_operands_masks.h"
|
||||||
|
|
||||||
|
#include "source/fuzz/instruction_descriptor.h"
|
||||||
|
#include "source/fuzz/transformation_set_memory_operands_mask.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks(
|
||||||
|
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||||
|
FuzzerContext* fuzzer_context,
|
||||||
|
protobufs::TransformationSequence* transformations)
|
||||||
|
: FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
|
||||||
|
|
||||||
|
FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() =
|
||||||
|
default;
|
||||||
|
|
||||||
|
void FuzzerPassAdjustMemoryOperandsMasks::Apply() {
|
||||||
|
// Consider every block in every function.
|
||||||
|
for (auto& function : *GetIRContext()->module()) {
|
||||||
|
for (auto& block : function) {
|
||||||
|
// Consider every instruction in this block, using an explicit iterator so
|
||||||
|
// that when we find an instruction of interest we can search backwards to
|
||||||
|
// create an id descriptor for it.
|
||||||
|
for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) {
|
||||||
|
if (!TransformationSetMemoryOperandsMask::IsMemoryAccess(*inst_it)) {
|
||||||
|
// We are only interested in memory access instructions.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> indices_of_available_masks_to_adjust;
|
||||||
|
// All memory instructions have at least one memory operands mask.
|
||||||
|
indices_of_available_masks_to_adjust.push_back(0);
|
||||||
|
// From SPIR-V 1.4 onwards, OpCopyMemory and OpCopyMemorySized have a
|
||||||
|
// second mask.
|
||||||
|
switch (inst_it->opcode()) {
|
||||||
|
case SpvOpCopyMemory:
|
||||||
|
case SpvOpCopyMemorySized:
|
||||||
|
if (TransformationSetMemoryOperandsMask::
|
||||||
|
MultipleMemoryOperandMasksAreSupported(GetIRContext())) {
|
||||||
|
indices_of_available_masks_to_adjust.push_back(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consider the available masks
|
||||||
|
for (auto mask_index : indices_of_available_masks_to_adjust) {
|
||||||
|
// Randomly decide whether to adjust this mask.
|
||||||
|
if (!GetFuzzerContext()->ChoosePercentage(
|
||||||
|
GetFuzzerContext()
|
||||||
|
->GetChanceOfAdjustingMemoryOperandsMask())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Get the existing mask, using None if there was no mask present at
|
||||||
|
// all.
|
||||||
|
auto existing_mask_in_operand_index =
|
||||||
|
TransformationSetMemoryOperandsMask::GetInOperandIndexForMask(
|
||||||
|
*inst_it, mask_index);
|
||||||
|
auto existing_mask =
|
||||||
|
existing_mask_in_operand_index < inst_it->NumInOperands()
|
||||||
|
? inst_it->GetSingleWordOperand(
|
||||||
|
existing_mask_in_operand_index)
|
||||||
|
: static_cast<uint32_t>(SpvMemoryAccessMaskNone);
|
||||||
|
|
||||||
|
// There are two things we can do to a mask:
|
||||||
|
// - add Volatile if not already present
|
||||||
|
// - toggle Nontemporal
|
||||||
|
// The following ensures that we do at least one of these
|
||||||
|
bool add_volatile = !(existing_mask & SpvMemoryAccessVolatileMask) &&
|
||||||
|
GetFuzzerContext()->ChooseEven();
|
||||||
|
bool toggle_nontemporal =
|
||||||
|
!add_volatile || GetFuzzerContext()->ChooseEven();
|
||||||
|
|
||||||
|
// These bitwise operations use '|' to add Volatile if desired, and
|
||||||
|
// '^' to toggle Nontemporal if desired.
|
||||||
|
uint32_t new_mask =
|
||||||
|
(existing_mask | (add_volatile ? SpvMemoryAccessVolatileMask
|
||||||
|
: SpvMemoryAccessMaskNone)) ^
|
||||||
|
(toggle_nontemporal ? SpvMemoryAccessNontemporalMask
|
||||||
|
: SpvMemoryAccessMaskNone);
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask transformation(
|
||||||
|
MakeInstructionDescriptor(block, inst_it), new_mask, mask_index);
|
||||||
|
assert(
|
||||||
|
transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
|
||||||
|
"Transformation should be applicable by construction.");
|
||||||
|
transformation.Apply(GetIRContext(), GetFactManager());
|
||||||
|
*GetTransformations()->add_transformation() =
|
||||||
|
transformation.ToMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
40
source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
Normal file
40
source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// 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_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_
|
||||||
|
#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_
|
||||||
|
|
||||||
|
#include "source/fuzz/fuzzer_pass.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
// A fuzzer pass to adjust the memory operand masks in memory access
|
||||||
|
// instructions.
|
||||||
|
class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass {
|
||||||
|
public:
|
||||||
|
FuzzerPassAdjustMemoryOperandsMasks(
|
||||||
|
opt::IRContext* ir_context, FactManager* fact_manager,
|
||||||
|
FuzzerContext* fuzzer_context,
|
||||||
|
protobufs::TransformationSequence* transformations);
|
||||||
|
|
||||||
|
~FuzzerPassAdjustMemoryOperandsMasks();
|
||||||
|
|
||||||
|
void Apply() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_
|
@ -66,5 +66,40 @@ protobufs::InstructionDescriptor MakeInstructionDescriptor(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protobufs::InstructionDescriptor MakeInstructionDescriptor(
|
||||||
|
const opt::BasicBlock& block,
|
||||||
|
const opt::BasicBlock::const_iterator& inst_it) {
|
||||||
|
const SpvOp opcode =
|
||||||
|
inst_it->opcode(); // The opcode of the instruction being described.
|
||||||
|
uint32_t skip_count = 0; // The number of these opcodes we have skipped when
|
||||||
|
// searching backwards.
|
||||||
|
|
||||||
|
// Consider instructions in the block in reverse order, starting from
|
||||||
|
// |inst_it|.
|
||||||
|
for (opt::BasicBlock::const_iterator backwards_iterator = inst_it;;
|
||||||
|
--backwards_iterator) {
|
||||||
|
if (backwards_iterator->HasResultId()) {
|
||||||
|
// As soon as we find an instruction with a result id, we can return a
|
||||||
|
// descriptor for |inst_it|.
|
||||||
|
return MakeInstructionDescriptor(backwards_iterator->result_id(), opcode,
|
||||||
|
skip_count);
|
||||||
|
}
|
||||||
|
if (backwards_iterator != inst_it &&
|
||||||
|
backwards_iterator->opcode() == opcode) {
|
||||||
|
// We are skipping over an instruction with the same opcode as |inst_it|;
|
||||||
|
// we increase our skip count to reflect this.
|
||||||
|
skip_count++;
|
||||||
|
}
|
||||||
|
if (backwards_iterator == block.begin()) {
|
||||||
|
// We exit the loop when we reach the start of the block, but only after
|
||||||
|
// we have processed the first instruction in the block.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We did not find an instruction inside the block with a result id, so we use
|
||||||
|
// the block's label's id.
|
||||||
|
return MakeInstructionDescriptor(block.id(), opcode, skip_count);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fuzz
|
} // namespace fuzz
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
|
#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
|
||||||
|
|
||||||
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||||
|
#include "source/opt/basic_block.h"
|
||||||
#include "source/opt/ir_context.h"
|
#include "source/opt/ir_context.h"
|
||||||
|
|
||||||
namespace spvtools {
|
namespace spvtools {
|
||||||
@ -34,6 +35,14 @@ protobufs::InstructionDescriptor MakeInstructionDescriptor(
|
|||||||
uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
|
uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
|
||||||
uint32_t num_opcodes_to_ignore);
|
uint32_t num_opcodes_to_ignore);
|
||||||
|
|
||||||
|
// Returns an instruction descriptor that describing the instruction at
|
||||||
|
// |inst_it|, which must be inside |block|. The descriptor will be with
|
||||||
|
// respect to the first instruction at or before |inst_it| that has a result
|
||||||
|
// id.
|
||||||
|
protobufs::InstructionDescriptor MakeInstructionDescriptor(
|
||||||
|
const opt::BasicBlock& block,
|
||||||
|
const opt::BasicBlock::const_iterator& inst_it);
|
||||||
|
|
||||||
} // namespace fuzz
|
} // namespace fuzz
|
||||||
} // namespace spvtools
|
} // namespace spvtools
|
||||||
|
|
||||||
|
@ -190,6 +190,7 @@ message Transformation {
|
|||||||
TransformationSetLoopControl set_loop_control = 17;
|
TransformationSetLoopControl set_loop_control = 17;
|
||||||
TransformationSetFunctionControl set_function_control = 18;
|
TransformationSetFunctionControl set_function_control = 18;
|
||||||
TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
|
TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
|
||||||
|
TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
|
||||||
// Add additional option using the next available number.
|
// Add additional option using the next available number.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,6 +465,25 @@ message TransformationSetLoopControl {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message TransformationSetMemoryOperandsMask {
|
||||||
|
|
||||||
|
// A transformation that sets the memory operands mask of a memory access
|
||||||
|
// instruction.
|
||||||
|
|
||||||
|
// A descriptor for a memory access instruction, e.g. an OpLoad
|
||||||
|
InstructionDescriptor memory_access_instruction = 1;
|
||||||
|
|
||||||
|
// A mask of memory operands to be applied to the instruction. It must be the
|
||||||
|
// same as the original mask, except that Volatile can be added, and
|
||||||
|
// Nontemporal can be added or removed.
|
||||||
|
uint32 memory_operands_mask = 2;
|
||||||
|
|
||||||
|
// Some memory access instructions allow more than one mask to be specified;
|
||||||
|
// this field indicates which mask should be set
|
||||||
|
uint32 memory_operands_mask_index = 3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
message TransformationSetSelectionControl {
|
message TransformationSetSelectionControl {
|
||||||
|
|
||||||
// A transformation that sets the selection control operand of an
|
// A transformation that sets the selection control operand of an
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
#include "source/fuzz/transformation_replace_id_with_synonym.h"
|
||||||
#include "source/fuzz/transformation_set_function_control.h"
|
#include "source/fuzz/transformation_set_function_control.h"
|
||||||
#include "source/fuzz/transformation_set_loop_control.h"
|
#include "source/fuzz/transformation_set_loop_control.h"
|
||||||
|
#include "source/fuzz/transformation_set_memory_operands_mask.h"
|
||||||
#include "source/fuzz/transformation_set_selection_control.h"
|
#include "source/fuzz/transformation_set_selection_control.h"
|
||||||
#include "source/fuzz/transformation_split_block.h"
|
#include "source/fuzz/transformation_split_block.h"
|
||||||
#include "source/util/make_unique.h"
|
#include "source/util/make_unique.h"
|
||||||
@ -94,6 +95,9 @@ std::unique_ptr<Transformation> Transformation::FromMessage(
|
|||||||
case protobufs::Transformation::TransformationCase::kSetLoopControl:
|
case protobufs::Transformation::TransformationCase::kSetLoopControl:
|
||||||
return MakeUnique<TransformationSetLoopControl>(
|
return MakeUnique<TransformationSetLoopControl>(
|
||||||
message.set_loop_control());
|
message.set_loop_control());
|
||||||
|
case protobufs::Transformation::TransformationCase::kSetMemoryOperandsMask:
|
||||||
|
return MakeUnique<TransformationSetMemoryOperandsMask>(
|
||||||
|
message.set_memory_operands_mask());
|
||||||
case protobufs::Transformation::TransformationCase::kSetSelectionControl:
|
case protobufs::Transformation::TransformationCase::kSetSelectionControl:
|
||||||
return MakeUnique<TransformationSetSelectionControl>(
|
return MakeUnique<TransformationSetSelectionControl>(
|
||||||
message.set_selection_control());
|
message.set_selection_control());
|
||||||
|
201
source/fuzz/transformation_set_memory_operands_mask.cpp
Normal file
201
source/fuzz/transformation_set_memory_operands_mask.cpp
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
// 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/fuzz/transformation_set_memory_operands_mask.h"
|
||||||
|
|
||||||
|
#include "source/fuzz/instruction_descriptor.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const uint32_t kOpLoadMemoryOperandsMaskIndex = 1;
|
||||||
|
const uint32_t kOpStoreMemoryOperandsMaskIndex = 2;
|
||||||
|
const uint32_t kOpCopyMemoryFirstMemoryOperandsMaskIndex = 2;
|
||||||
|
const uint32_t kOpCopyMemorySizedFirstMemoryOperandsMaskIndex = 3;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask(
|
||||||
|
const spvtools::fuzz::protobufs::TransformationSetMemoryOperandsMask&
|
||||||
|
message)
|
||||||
|
: message_(message) {}
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask(
|
||||||
|
const protobufs::InstructionDescriptor& memory_access_instruction,
|
||||||
|
uint32_t memory_operands_mask, uint32_t memory_operands_mask_index) {
|
||||||
|
*message_.mutable_memory_access_instruction() = memory_access_instruction;
|
||||||
|
message_.set_memory_operands_mask(memory_operands_mask);
|
||||||
|
message_.set_memory_operands_mask_index(memory_operands_mask_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformationSetMemoryOperandsMask::IsApplicable(
|
||||||
|
opt::IRContext* context,
|
||||||
|
const spvtools::fuzz::FactManager& /*unused*/) const {
|
||||||
|
if (message_.memory_operands_mask_index() != 0) {
|
||||||
|
// The following conditions should never be violated, even if
|
||||||
|
// transformations end up being replayed in a different way to the manner in
|
||||||
|
// which they were applied during fuzzing, hence why these are assertions
|
||||||
|
// rather than applicability checks.
|
||||||
|
assert(message_.memory_operands_mask_index() == 1);
|
||||||
|
assert(message_.memory_access_instruction().target_instruction_opcode() ==
|
||||||
|
SpvOpCopyMemory ||
|
||||||
|
message_.memory_access_instruction().target_instruction_opcode() ==
|
||||||
|
SpvOpCopyMemorySized);
|
||||||
|
assert(MultipleMemoryOperandMasksAreSupported(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto instruction =
|
||||||
|
FindInstruction(message_.memory_access_instruction(), context);
|
||||||
|
if (!instruction) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!IsMemoryAccess(*instruction)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto original_mask_in_operand_index = GetInOperandIndexForMask(
|
||||||
|
*instruction, message_.memory_operands_mask_index());
|
||||||
|
assert(original_mask_in_operand_index != 0 &&
|
||||||
|
"The given mask index is not valid.");
|
||||||
|
uint32_t original_mask =
|
||||||
|
original_mask_in_operand_index < instruction->NumInOperands()
|
||||||
|
? instruction->GetSingleWordInOperand(original_mask_in_operand_index)
|
||||||
|
: static_cast<uint32_t>(SpvMemoryAccessMaskNone);
|
||||||
|
uint32_t new_mask = message_.memory_operands_mask();
|
||||||
|
|
||||||
|
// Volatile must not be removed
|
||||||
|
if ((original_mask & SpvMemoryAccessVolatileMask) &&
|
||||||
|
!(new_mask & SpvMemoryAccessVolatileMask)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nontemporal can be added or removed, and no other flag is allowed to
|
||||||
|
// change. We do this by checking that the masks are equal once we set
|
||||||
|
// their Volatile and Nontemporal flags to the same value (this works
|
||||||
|
// because valid manipulation of Volatile is checked above, and the manner
|
||||||
|
// in which Nontemporal is manipulated does not matter).
|
||||||
|
return (original_mask | SpvMemoryAccessVolatileMask |
|
||||||
|
SpvMemoryAccessNontemporalMask) ==
|
||||||
|
(new_mask | SpvMemoryAccessVolatileMask |
|
||||||
|
SpvMemoryAccessNontemporalMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformationSetMemoryOperandsMask::Apply(
|
||||||
|
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
|
||||||
|
auto instruction =
|
||||||
|
FindInstruction(message_.memory_access_instruction(), context);
|
||||||
|
auto original_mask_in_operand_index = GetInOperandIndexForMask(
|
||||||
|
*instruction, message_.memory_operands_mask_index());
|
||||||
|
// Either add a new operand, if no mask operand was already present, or
|
||||||
|
// replace an existing mask operand.
|
||||||
|
if (original_mask_in_operand_index >= instruction->NumInOperands()) {
|
||||||
|
instruction->AddOperand(
|
||||||
|
{SPV_OPERAND_TYPE_MEMORY_ACCESS, {message_.memory_operands_mask()}});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
instruction->SetInOperand(original_mask_in_operand_index,
|
||||||
|
{message_.memory_operands_mask()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protobufs::Transformation TransformationSetMemoryOperandsMask::ToMessage()
|
||||||
|
const {
|
||||||
|
protobufs::Transformation result;
|
||||||
|
*result.mutable_set_memory_operands_mask() = message_;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformationSetMemoryOperandsMask::IsMemoryAccess(
|
||||||
|
const opt::Instruction& instruction) {
|
||||||
|
switch (instruction.opcode()) {
|
||||||
|
case SpvOpLoad:
|
||||||
|
case SpvOpStore:
|
||||||
|
case SpvOpCopyMemory:
|
||||||
|
case SpvOpCopyMemorySized:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TransformationSetMemoryOperandsMask::GetInOperandIndexForMask(
|
||||||
|
const opt::Instruction& instruction, uint32_t mask_index) {
|
||||||
|
// Get the input operand index associated with the first memory operands mask
|
||||||
|
// for the instruction.
|
||||||
|
uint32_t first_mask_in_operand_index = 0;
|
||||||
|
switch (instruction.opcode()) {
|
||||||
|
case SpvOpLoad:
|
||||||
|
first_mask_in_operand_index = kOpLoadMemoryOperandsMaskIndex;
|
||||||
|
break;
|
||||||
|
case SpvOpStore:
|
||||||
|
first_mask_in_operand_index = kOpStoreMemoryOperandsMaskIndex;
|
||||||
|
break;
|
||||||
|
case SpvOpCopyMemory:
|
||||||
|
first_mask_in_operand_index = kOpCopyMemoryFirstMemoryOperandsMaskIndex;
|
||||||
|
break;
|
||||||
|
case SpvOpCopyMemorySized:
|
||||||
|
first_mask_in_operand_index =
|
||||||
|
kOpCopyMemorySizedFirstMemoryOperandsMaskIndex;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false && "Unknown memory instruction.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// If we are looking for the input operand index of the first mask, return it.
|
||||||
|
if (mask_index == 0) {
|
||||||
|
return first_mask_in_operand_index;
|
||||||
|
}
|
||||||
|
assert(mask_index == 1 && "Memory operands mask index must be 0 or 1.");
|
||||||
|
|
||||||
|
// We are looking for the input operand index of the second mask. This is a
|
||||||
|
// little complicated because, depending on the contents of the first mask,
|
||||||
|
// there may be some input operands separating the two masks.
|
||||||
|
uint32_t first_mask =
|
||||||
|
instruction.GetSingleWordInOperand(first_mask_in_operand_index);
|
||||||
|
|
||||||
|
// Consider each bit that might have an associated extra input operand, and
|
||||||
|
// count how many there are expected to be.
|
||||||
|
uint32_t first_mask_extra_operand_count = 0;
|
||||||
|
for (auto mask_bit :
|
||||||
|
{SpvMemoryAccessAlignedMask, SpvMemoryAccessMakePointerAvailableMask,
|
||||||
|
SpvMemoryAccessMakePointerAvailableKHRMask,
|
||||||
|
SpvMemoryAccessMakePointerVisibleMask,
|
||||||
|
SpvMemoryAccessMakePointerVisibleKHRMask}) {
|
||||||
|
if (first_mask & mask_bit) {
|
||||||
|
first_mask_extra_operand_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return first_mask_in_operand_index + first_mask_extra_operand_count + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformationSetMemoryOperandsMask::
|
||||||
|
MultipleMemoryOperandMasksAreSupported(opt::IRContext* context) {
|
||||||
|
// TODO(afd): We capture the universal environments for which this loop
|
||||||
|
// control is definitely not supported. The check should be refined on
|
||||||
|
// demand for other target environments.
|
||||||
|
switch (context->grammar().target_env()) {
|
||||||
|
case SPV_ENV_UNIVERSAL_1_0:
|
||||||
|
case SPV_ENV_UNIVERSAL_1_1:
|
||||||
|
case SPV_ENV_UNIVERSAL_1_2:
|
||||||
|
case SPV_ENV_UNIVERSAL_1_3:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
76
source/fuzz/transformation_set_memory_operands_mask.h
Normal file
76
source/fuzz/transformation_set_memory_operands_mask.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_
|
||||||
|
#define SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_
|
||||||
|
|
||||||
|
#include "source/fuzz/fact_manager.h"
|
||||||
|
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
|
||||||
|
#include "source/fuzz/transformation.h"
|
||||||
|
#include "source/opt/ir_context.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
|
||||||
|
class TransformationSetMemoryOperandsMask : public Transformation {
|
||||||
|
public:
|
||||||
|
explicit TransformationSetMemoryOperandsMask(
|
||||||
|
const protobufs::TransformationSetMemoryOperandsMask& message);
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask(
|
||||||
|
const protobufs::InstructionDescriptor& memory_access_instruction,
|
||||||
|
uint32_t memory_operands_mask, uint32_t memory_operands_mask_index);
|
||||||
|
|
||||||
|
// - |message_.memory_access_instruction| must describe a memory access
|
||||||
|
// instruction.
|
||||||
|
// - |message_.memory_operands_mask_index| must be suitable for this memory
|
||||||
|
// access instruction, e.g. it must be 0 in the case of OpLoad, and may be
|
||||||
|
// 1 in the case of OpCopyMemory if the SPIR-V version is 1.4 or higher.
|
||||||
|
// - |message_.memory_operands_mask| must be identical to the original memory
|
||||||
|
// operands mask, except that Volatile may be added, and Nontemporal may be
|
||||||
|
// toggled.
|
||||||
|
bool IsApplicable(opt::IRContext* context,
|
||||||
|
const FactManager& fact_manager) const override;
|
||||||
|
|
||||||
|
// Replaces the operands mask identified by
|
||||||
|
// |message_.memory_operands_mask_index| in the instruction described by
|
||||||
|
// |message_.memory_access_instruction| with |message_.memory_operands_mask|,
|
||||||
|
// creating an input operand for the mask if no such operand was present.
|
||||||
|
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
|
||||||
|
|
||||||
|
protobufs::Transformation ToMessage() const override;
|
||||||
|
|
||||||
|
// Helper function that determines whether |instruction| is a memory
|
||||||
|
// instruction (e.g. OpLoad).
|
||||||
|
static bool IsMemoryAccess(const opt::Instruction& instruction);
|
||||||
|
|
||||||
|
// Does the version of SPIR-V being used support multiple memory operand
|
||||||
|
// masks on relevant memory access instructions?
|
||||||
|
static bool MultipleMemoryOperandMasksAreSupported(opt::IRContext* context);
|
||||||
|
|
||||||
|
// Helper function to get the input operand index associated with mask number
|
||||||
|
// |mask_index|. This is a bit tricky if there are multiple masks, because the
|
||||||
|
// index associated with the second mask depends on whether the first mask
|
||||||
|
// includes any flags such as Aligned that have corresponding operands.
|
||||||
|
static uint32_t GetInOperandIndexForMask(const opt::Instruction& instruction,
|
||||||
|
uint32_t mask_index);
|
||||||
|
|
||||||
|
private:
|
||||||
|
protobufs::TransformationSetMemoryOperandsMask message_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
||||||
|
|
||||||
|
#endif // SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_
|
@ -20,6 +20,7 @@ if (${SPIRV_BUILD_FUZZER})
|
|||||||
fact_manager_test.cpp
|
fact_manager_test.cpp
|
||||||
fuzz_test_util.cpp
|
fuzz_test_util.cpp
|
||||||
fuzzer_pass_add_useful_constructs_test.cpp
|
fuzzer_pass_add_useful_constructs_test.cpp
|
||||||
|
instruction_descriptor_test.cpp
|
||||||
transformation_add_constant_boolean_test.cpp
|
transformation_add_constant_boolean_test.cpp
|
||||||
transformation_add_constant_scalar_test.cpp
|
transformation_add_constant_scalar_test.cpp
|
||||||
transformation_add_dead_break_test.cpp
|
transformation_add_dead_break_test.cpp
|
||||||
@ -37,6 +38,7 @@ if (${SPIRV_BUILD_FUZZER})
|
|||||||
transformation_replace_id_with_synonym_test.cpp
|
transformation_replace_id_with_synonym_test.cpp
|
||||||
transformation_set_function_control_test.cpp
|
transformation_set_function_control_test.cpp
|
||||||
transformation_set_loop_control_test.cpp
|
transformation_set_loop_control_test.cpp
|
||||||
|
transformation_set_memory_operands_mask_test.cpp
|
||||||
transformation_set_selection_control_test.cpp
|
transformation_set_selection_control_test.cpp
|
||||||
transformation_split_block_test.cpp
|
transformation_split_block_test.cpp
|
||||||
uniform_buffer_element_descriptor_test.cpp)
|
uniform_buffer_element_descriptor_test.cpp)
|
||||||
|
69
test/fuzz/instruction_descriptor_test.cpp
Normal file
69
test/fuzz/instruction_descriptor_test.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// 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/fuzz/instruction_descriptor.h"
|
||||||
|
#include "test/fuzz/fuzz_test_util.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(InstructionDescriptorTest, BasicTest) {
|
||||||
|
std::string shader = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %4 "main"
|
||||||
|
OpExecutionMode %4 OriginUpperLeft
|
||||||
|
OpSource ESSL 310
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeInt 32 0
|
||||||
|
%7 = OpTypePointer Function %6
|
||||||
|
%9 = OpConstant %6 0
|
||||||
|
%10 = OpTypeInt 32 1
|
||||||
|
%11 = OpTypePointer Function %10
|
||||||
|
%13 = OpConstant %10 2
|
||||||
|
%32 = OpConstant %10 0
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
%164 = OpVariable %11 Function
|
||||||
|
%165 = OpVariable %11 Function
|
||||||
|
OpBranch %16
|
||||||
|
%16 = OpLabel
|
||||||
|
OpStore %164 %32
|
||||||
|
OpStore %165 %13
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
||||||
|
const auto consumer = nullptr;
|
||||||
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
|
for (auto& function : *context->module()) {
|
||||||
|
for (auto& block : function) {
|
||||||
|
for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) {
|
||||||
|
ASSERT_EQ(&*inst_it,
|
||||||
|
FindInstruction(MakeInstructionDescriptor(block, inst_it),
|
||||||
|
context.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
432
test/fuzz/transformation_set_memory_operands_mask_test.cpp
Normal file
432
test/fuzz/transformation_set_memory_operands_mask_test.cpp
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
// 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/fuzz/transformation_set_memory_operands_mask.h"
|
||||||
|
#include "source/fuzz/instruction_descriptor.h"
|
||||||
|
#include "test/fuzz/fuzz_test_util.h"
|
||||||
|
|
||||||
|
namespace spvtools {
|
||||||
|
namespace fuzz {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(TransformationSetMemoryOperandsMaskTest, PreSpirv14) {
|
||||||
|
std::string shader = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %4 "main"
|
||||||
|
OpExecutionMode %4 OriginUpperLeft
|
||||||
|
OpSource ESSL 310
|
||||||
|
OpName %4 "main"
|
||||||
|
OpName %7 "Point3D"
|
||||||
|
OpMemberName %7 0 "x"
|
||||||
|
OpMemberName %7 1 "y"
|
||||||
|
OpMemberName %7 2 "z"
|
||||||
|
OpName %12 "global_points"
|
||||||
|
OpName %15 "block"
|
||||||
|
OpMemberName %15 0 "in_points"
|
||||||
|
OpMemberName %15 1 "in_point"
|
||||||
|
OpName %17 ""
|
||||||
|
OpName %133 "local_points"
|
||||||
|
OpMemberDecorate %7 0 Offset 0
|
||||||
|
OpMemberDecorate %7 1 Offset 4
|
||||||
|
OpMemberDecorate %7 2 Offset 8
|
||||||
|
OpDecorate %10 ArrayStride 16
|
||||||
|
OpMemberDecorate %15 0 Offset 0
|
||||||
|
OpMemberDecorate %15 1 Offset 192
|
||||||
|
OpDecorate %15 Block
|
||||||
|
OpDecorate %17 DescriptorSet 0
|
||||||
|
OpDecorate %17 Binding 0
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeFloat 32
|
||||||
|
%7 = OpTypeStruct %6 %6 %6
|
||||||
|
%8 = OpTypeInt 32 0
|
||||||
|
%9 = OpConstant %8 12
|
||||||
|
%10 = OpTypeArray %7 %9
|
||||||
|
%11 = OpTypePointer Private %10
|
||||||
|
%12 = OpVariable %11 Private
|
||||||
|
%15 = OpTypeStruct %10 %7
|
||||||
|
%16 = OpTypePointer Uniform %15
|
||||||
|
%17 = OpVariable %16 Uniform
|
||||||
|
%18 = OpTypeInt 32 1
|
||||||
|
%19 = OpConstant %18 0
|
||||||
|
%20 = OpTypePointer Uniform %10
|
||||||
|
%24 = OpTypePointer Private %7
|
||||||
|
%27 = OpTypePointer Private %6
|
||||||
|
%30 = OpConstant %18 1
|
||||||
|
%132 = OpTypePointer Function %10
|
||||||
|
%135 = OpTypePointer Uniform %7
|
||||||
|
%145 = OpTypePointer Function %7
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
%133 = OpVariable %132 Function
|
||||||
|
%21 = OpAccessChain %20 %17 %19
|
||||||
|
OpCopyMemory %12 %21 Aligned 16
|
||||||
|
OpCopyMemory %133 %12 Volatile
|
||||||
|
%136 = OpAccessChain %135 %17 %30
|
||||||
|
%138 = OpAccessChain %24 %12 %19
|
||||||
|
OpCopyMemory %138 %136 None
|
||||||
|
%146 = OpAccessChain %145 %133 %30
|
||||||
|
%147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16
|
||||||
|
%148 = OpAccessChain %24 %12 %19
|
||||||
|
OpStore %148 %147 Nontemporal
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Not OK: the instruction is not a memory access.
|
||||||
|
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpAccessChain, 0),
|
||||||
|
SpvMemoryAccessMaskNone, 0)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
|
||||||
|
// Not OK to remove Aligned
|
||||||
|
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(147, SpvOpLoad, 0),
|
||||||
|
SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask,
|
||||||
|
0)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask transformation1(
|
||||||
|
MakeInstructionDescriptor(147, SpvOpLoad, 0),
|
||||||
|
SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0);
|
||||||
|
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation1.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
// Not OK to remove Aligned
|
||||||
|
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
|
||||||
|
SpvMemoryAccessMaskNone, 0)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
|
||||||
|
// OK: leaves the mask as is
|
||||||
|
ASSERT_TRUE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
|
||||||
|
SpvMemoryAccessAlignedMask, 0)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
|
||||||
|
// OK: adds Nontemporal and Volatile
|
||||||
|
TransformationSetMemoryOperandsMask transformation2(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
|
||||||
|
SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask |
|
||||||
|
SpvMemoryAccessVolatileMask,
|
||||||
|
0);
|
||||||
|
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation2.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
// Not OK to remove Volatile
|
||||||
|
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
|
||||||
|
SpvMemoryAccessNontemporalMask, 0)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
|
||||||
|
// Not OK to add Aligned
|
||||||
|
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
|
||||||
|
SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
|
||||||
|
// OK: adds Nontemporal
|
||||||
|
TransformationSetMemoryOperandsMask transformation3(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
|
||||||
|
SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
|
||||||
|
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation3.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
// OK: adds Nontemporal and Volatile
|
||||||
|
TransformationSetMemoryOperandsMask transformation4(
|
||||||
|
MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
|
||||||
|
SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0);
|
||||||
|
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation4.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
// OK: removes Nontemporal, adds Volatile
|
||||||
|
TransformationSetMemoryOperandsMask transformation5(
|
||||||
|
MakeInstructionDescriptor(148, SpvOpStore, 0),
|
||||||
|
SpvMemoryAccessVolatileMask, 0);
|
||||||
|
ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation5.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
std::string after_transformation = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %4 "main"
|
||||||
|
OpExecutionMode %4 OriginUpperLeft
|
||||||
|
OpSource ESSL 310
|
||||||
|
OpName %4 "main"
|
||||||
|
OpName %7 "Point3D"
|
||||||
|
OpMemberName %7 0 "x"
|
||||||
|
OpMemberName %7 1 "y"
|
||||||
|
OpMemberName %7 2 "z"
|
||||||
|
OpName %12 "global_points"
|
||||||
|
OpName %15 "block"
|
||||||
|
OpMemberName %15 0 "in_points"
|
||||||
|
OpMemberName %15 1 "in_point"
|
||||||
|
OpName %17 ""
|
||||||
|
OpName %133 "local_points"
|
||||||
|
OpMemberDecorate %7 0 Offset 0
|
||||||
|
OpMemberDecorate %7 1 Offset 4
|
||||||
|
OpMemberDecorate %7 2 Offset 8
|
||||||
|
OpDecorate %10 ArrayStride 16
|
||||||
|
OpMemberDecorate %15 0 Offset 0
|
||||||
|
OpMemberDecorate %15 1 Offset 192
|
||||||
|
OpDecorate %15 Block
|
||||||
|
OpDecorate %17 DescriptorSet 0
|
||||||
|
OpDecorate %17 Binding 0
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeFloat 32
|
||||||
|
%7 = OpTypeStruct %6 %6 %6
|
||||||
|
%8 = OpTypeInt 32 0
|
||||||
|
%9 = OpConstant %8 12
|
||||||
|
%10 = OpTypeArray %7 %9
|
||||||
|
%11 = OpTypePointer Private %10
|
||||||
|
%12 = OpVariable %11 Private
|
||||||
|
%15 = OpTypeStruct %10 %7
|
||||||
|
%16 = OpTypePointer Uniform %15
|
||||||
|
%17 = OpVariable %16 Uniform
|
||||||
|
%18 = OpTypeInt 32 1
|
||||||
|
%19 = OpConstant %18 0
|
||||||
|
%20 = OpTypePointer Uniform %10
|
||||||
|
%24 = OpTypePointer Private %7
|
||||||
|
%27 = OpTypePointer Private %6
|
||||||
|
%30 = OpConstant %18 1
|
||||||
|
%132 = OpTypePointer Function %10
|
||||||
|
%135 = OpTypePointer Uniform %7
|
||||||
|
%145 = OpTypePointer Function %7
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
%133 = OpVariable %132 Function
|
||||||
|
%21 = OpAccessChain %20 %17 %19
|
||||||
|
OpCopyMemory %12 %21 Aligned|Nontemporal|Volatile 16
|
||||||
|
OpCopyMemory %133 %12 Nontemporal|Volatile
|
||||||
|
%136 = OpAccessChain %135 %17 %30
|
||||||
|
%138 = OpAccessChain %24 %12 %19
|
||||||
|
OpCopyMemory %138 %136 Nontemporal|Volatile
|
||||||
|
%146 = OpAccessChain %145 %133 %30
|
||||||
|
%147 = OpLoad %7 %146 Aligned|Volatile 16
|
||||||
|
%148 = OpAccessChain %24 %12 %19
|
||||||
|
OpStore %148 %147 Volatile
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) {
|
||||||
|
std::string shader = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %4 "main" %12 %17
|
||||||
|
OpExecutionMode %4 OriginUpperLeft
|
||||||
|
OpSource ESSL 310
|
||||||
|
OpName %4 "main"
|
||||||
|
OpName %7 "Point3D"
|
||||||
|
OpMemberName %7 0 "x"
|
||||||
|
OpMemberName %7 1 "y"
|
||||||
|
OpMemberName %7 2 "z"
|
||||||
|
OpName %12 "global_points"
|
||||||
|
OpName %15 "block"
|
||||||
|
OpMemberName %15 0 "in_points"
|
||||||
|
OpMemberName %15 1 "in_point"
|
||||||
|
OpName %17 ""
|
||||||
|
OpName %133 "local_points"
|
||||||
|
OpMemberDecorate %7 0 Offset 0
|
||||||
|
OpMemberDecorate %7 1 Offset 4
|
||||||
|
OpMemberDecorate %7 2 Offset 8
|
||||||
|
OpDecorate %10 ArrayStride 16
|
||||||
|
OpMemberDecorate %15 0 Offset 0
|
||||||
|
OpMemberDecorate %15 1 Offset 192
|
||||||
|
OpDecorate %15 Block
|
||||||
|
OpDecorate %17 DescriptorSet 0
|
||||||
|
OpDecorate %17 Binding 0
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeFloat 32
|
||||||
|
%7 = OpTypeStruct %6 %6 %6
|
||||||
|
%8 = OpTypeInt 32 0
|
||||||
|
%9 = OpConstant %8 12
|
||||||
|
%10 = OpTypeArray %7 %9
|
||||||
|
%11 = OpTypePointer Private %10
|
||||||
|
%12 = OpVariable %11 Private
|
||||||
|
%15 = OpTypeStruct %10 %7
|
||||||
|
%16 = OpTypePointer Uniform %15
|
||||||
|
%17 = OpVariable %16 Uniform
|
||||||
|
%18 = OpTypeInt 32 1
|
||||||
|
%19 = OpConstant %18 0
|
||||||
|
%20 = OpTypePointer Uniform %10
|
||||||
|
%24 = OpTypePointer Private %7
|
||||||
|
%27 = OpTypePointer Private %6
|
||||||
|
%30 = OpConstant %18 1
|
||||||
|
%132 = OpTypePointer Function %10
|
||||||
|
%135 = OpTypePointer Uniform %7
|
||||||
|
%145 = OpTypePointer Function %7
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
%133 = OpVariable %132 Function
|
||||||
|
%21 = OpAccessChain %20 %17 %19
|
||||||
|
OpCopyMemory %12 %21 Aligned 16 Nontemporal|Aligned 16
|
||||||
|
OpCopyMemory %133 %12 Volatile
|
||||||
|
%136 = OpAccessChain %135 %17 %30
|
||||||
|
%138 = OpAccessChain %24 %12 %19
|
||||||
|
OpCopyMemory %138 %136 None Aligned 16
|
||||||
|
OpCopyMemory %138 %136 Aligned 16
|
||||||
|
%146 = OpAccessChain %145 %133 %30
|
||||||
|
%147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16
|
||||||
|
%148 = OpAccessChain %24 %12 %19
|
||||||
|
OpStore %148 %147 Nontemporal
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
|
||||||
|
const auto env = SPV_ENV_UNIVERSAL_1_4;
|
||||||
|
const auto consumer = nullptr;
|
||||||
|
const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
|
||||||
|
ASSERT_TRUE(IsValid(env, context.get()));
|
||||||
|
|
||||||
|
FactManager fact_manager;
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask transformation1(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
|
||||||
|
SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1);
|
||||||
|
// Bad: cannot remove aligned
|
||||||
|
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 0),
|
||||||
|
SpvMemoryAccessVolatileMask, 1)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation1.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask transformation2(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
|
||||||
|
SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1);
|
||||||
|
// Bad: cannot remove volatile
|
||||||
|
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(21, SpvOpCopyMemory, 1),
|
||||||
|
SpvMemoryAccessNontemporalMask, 0)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation2.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask transformation3(
|
||||||
|
MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
|
||||||
|
SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1);
|
||||||
|
// Bad: the first mask is None, so Aligned cannot be added to it.
|
||||||
|
ASSERT_FALSE(TransformationSetMemoryOperandsMask(
|
||||||
|
MakeInstructionDescriptor(138, SpvOpCopyMemory, 0),
|
||||||
|
SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask,
|
||||||
|
0)
|
||||||
|
.IsApplicable(context.get(), fact_manager));
|
||||||
|
ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation3.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask transformation4(
|
||||||
|
MakeInstructionDescriptor(138, SpvOpCopyMemory, 1),
|
||||||
|
SpvMemoryAccessVolatileMask, 1);
|
||||||
|
ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation4.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask transformation5(
|
||||||
|
MakeInstructionDescriptor(147, SpvOpLoad, 0),
|
||||||
|
SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0);
|
||||||
|
ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation5.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
TransformationSetMemoryOperandsMask transformation6(
|
||||||
|
MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone,
|
||||||
|
0);
|
||||||
|
ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
|
||||||
|
transformation6.Apply(context.get(), &fact_manager);
|
||||||
|
|
||||||
|
std::string after_transformation = R"(
|
||||||
|
OpCapability Shader
|
||||||
|
%1 = OpExtInstImport "GLSL.std.450"
|
||||||
|
OpMemoryModel Logical GLSL450
|
||||||
|
OpEntryPoint Fragment %4 "main" %12 %17
|
||||||
|
OpExecutionMode %4 OriginUpperLeft
|
||||||
|
OpSource ESSL 310
|
||||||
|
OpName %4 "main"
|
||||||
|
OpName %7 "Point3D"
|
||||||
|
OpMemberName %7 0 "x"
|
||||||
|
OpMemberName %7 1 "y"
|
||||||
|
OpMemberName %7 2 "z"
|
||||||
|
OpName %12 "global_points"
|
||||||
|
OpName %15 "block"
|
||||||
|
OpMemberName %15 0 "in_points"
|
||||||
|
OpMemberName %15 1 "in_point"
|
||||||
|
OpName %17 ""
|
||||||
|
OpName %133 "local_points"
|
||||||
|
OpMemberDecorate %7 0 Offset 0
|
||||||
|
OpMemberDecorate %7 1 Offset 4
|
||||||
|
OpMemberDecorate %7 2 Offset 8
|
||||||
|
OpDecorate %10 ArrayStride 16
|
||||||
|
OpMemberDecorate %15 0 Offset 0
|
||||||
|
OpMemberDecorate %15 1 Offset 192
|
||||||
|
OpDecorate %15 Block
|
||||||
|
OpDecorate %17 DescriptorSet 0
|
||||||
|
OpDecorate %17 Binding 0
|
||||||
|
%2 = OpTypeVoid
|
||||||
|
%3 = OpTypeFunction %2
|
||||||
|
%6 = OpTypeFloat 32
|
||||||
|
%7 = OpTypeStruct %6 %6 %6
|
||||||
|
%8 = OpTypeInt 32 0
|
||||||
|
%9 = OpConstant %8 12
|
||||||
|
%10 = OpTypeArray %7 %9
|
||||||
|
%11 = OpTypePointer Private %10
|
||||||
|
%12 = OpVariable %11 Private
|
||||||
|
%15 = OpTypeStruct %10 %7
|
||||||
|
%16 = OpTypePointer Uniform %15
|
||||||
|
%17 = OpVariable %16 Uniform
|
||||||
|
%18 = OpTypeInt 32 1
|
||||||
|
%19 = OpConstant %18 0
|
||||||
|
%20 = OpTypePointer Uniform %10
|
||||||
|
%24 = OpTypePointer Private %7
|
||||||
|
%27 = OpTypePointer Private %6
|
||||||
|
%30 = OpConstant %18 1
|
||||||
|
%132 = OpTypePointer Function %10
|
||||||
|
%135 = OpTypePointer Uniform %7
|
||||||
|
%145 = OpTypePointer Function %7
|
||||||
|
%4 = OpFunction %2 None %3
|
||||||
|
%5 = OpLabel
|
||||||
|
%133 = OpVariable %132 Function
|
||||||
|
%21 = OpAccessChain %20 %17 %19
|
||||||
|
OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16
|
||||||
|
OpCopyMemory %133 %12 Volatile Nontemporal|Volatile
|
||||||
|
%136 = OpAccessChain %135 %17 %30
|
||||||
|
%138 = OpAccessChain %24 %12 %19
|
||||||
|
OpCopyMemory %138 %136 None Aligned|Nontemporal 16
|
||||||
|
OpCopyMemory %138 %136 Aligned 16 Volatile
|
||||||
|
%146 = OpAccessChain %145 %133 %30
|
||||||
|
%147 = OpLoad %7 %146 Volatile|Aligned 16
|
||||||
|
%148 = OpAccessChain %24 %12 %19
|
||||||
|
OpStore %148 %147 None
|
||||||
|
OpReturn
|
||||||
|
OpFunctionEnd
|
||||||
|
)";
|
||||||
|
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace fuzz
|
||||||
|
} // namespace spvtools
|
Loading…
Reference in New Issue
Block a user