Updated spirv-tools.

This commit is contained in:
Бранимир Караџић 2019-07-03 08:08:41 -07:00
parent 95926756e0
commit 8580112ecb
60 changed files with 2418 additions and 2252 deletions

View File

@ -1 +1 @@
"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-60-gdf86bb44"
"v2019.4-dev", "SPIRV-Tools v2019.4-dev v2019.3-67-g9702d47c"

View File

@ -3,6 +3,7 @@ static const SpvCapability pygen_variable_caps_AddressesPhysicalStorageBufferAdd
static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBuffer[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer};
static const SpvCapability pygen_variable_caps_AddressesVariablePointersVariablePointersStorageBufferPhysicalStorageBufferAddressesEXT[] = {SpvCapabilityAddresses, SpvCapabilityVariablePointers, SpvCapabilityVariablePointersStorageBuffer, SpvCapabilityPhysicalStorageBufferAddressesEXT};
static const SpvCapability pygen_variable_caps_CooperativeMatrixNV[] = {SpvCapabilityCooperativeMatrixNV};
static const SpvCapability pygen_variable_caps_DemoteToHelperInvocationEXT[] = {SpvCapabilityDemoteToHelperInvocationEXT};
static const SpvCapability pygen_variable_caps_DerivativeControl[] = {SpvCapabilityDerivativeControl};
static const SpvCapability pygen_variable_caps_DeviceEnqueue[] = {SpvCapabilityDeviceEnqueue};
static const SpvCapability pygen_variable_caps_FragmentMaskAMD[] = {SpvCapabilityFragmentMaskAMD};
@ -45,6 +46,7 @@ static const SpvCapability pygen_variable_caps_SubgroupVoteKHR[] = {SpvCapabilit
static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_ballot[] = {spvtools::Extension::kSPV_AMD_shader_ballot};
static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_fragment_mask[] = {spvtools::Extension::kSPV_AMD_shader_fragment_mask};
static const spvtools::Extension pygen_variable_exts_SPV_EXT_demote_to_helper_invocation[] = {spvtools::Extension::kSPV_EXT_demote_to_helper_invocation};
static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_shader_interlock[] = {spvtools::Extension::kSPV_EXT_fragment_shader_interlock};
static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_decorate_stringSPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_decorate_string, spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
static const spvtools::Extension pygen_variable_exts_SPV_GOOGLE_hlsl_functionality1[] = {spvtools::Extension::kSPV_GOOGLE_hlsl_functionality1};
@ -433,6 +435,8 @@ static const spv_opcode_desc_t kOpcodeTableEntries[] = {
{"CooperativeMatrixLengthNV", SpvOpCooperativeMatrixLengthNV, 1, pygen_variable_caps_CooperativeMatrixNV, 3, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 1, pygen_variable_exts_SPV_NV_cooperative_matrix, 0xffffffffu, 0xffffffffu},
{"BeginInvocationInterlockEXT", SpvOpBeginInvocationInterlockEXT, 3, pygen_variable_caps_FragmentShaderSampleInterlockEXTFragmentShaderPixelInterlockEXTFragmentShaderShadingRateInterlockEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, 0xffffffffu, 0xffffffffu},
{"EndInvocationInterlockEXT", SpvOpEndInvocationInterlockEXT, 3, pygen_variable_caps_FragmentShaderSampleInterlockEXTFragmentShaderPixelInterlockEXTFragmentShaderShadingRateInterlockEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, 0xffffffffu, 0xffffffffu},
{"DemoteToHelperInvocationEXT", SpvOpDemoteToHelperInvocationEXT, 1, pygen_variable_caps_DemoteToHelperInvocationEXT, 0, {}, 0, 0, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, 0xffffffffu, 0xffffffffu},
{"IsHelperInvocationEXT", SpvOpIsHelperInvocationEXT, 1, pygen_variable_caps_DemoteToHelperInvocationEXT, 2, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}, 1, 1, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, 0xffffffffu, 0xffffffffu},
{"SubgroupShuffleINTEL", SpvOpSubgroupShuffleINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 4, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
{"SubgroupShuffleDownINTEL", SpvOpSubgroupShuffleDownINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},
{"SubgroupShuffleUpINTEL", SpvOpSubgroupShuffleUpINTEL, 1, pygen_variable_caps_SubgroupShuffleINTEL, 5, {SPV_OPERAND_TYPE_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_ID}, 1, 1, 0, nullptr, 0xffffffffu, 0xffffffffu},

View File

@ -20,6 +20,8 @@ const char* ExtensionToString(Extension extension) {
return "SPV_AMD_shader_trinary_minmax";
case Extension::kSPV_AMD_texture_gather_bias_lod:
return "SPV_AMD_texture_gather_bias_lod";
case Extension::kSPV_EXT_demote_to_helper_invocation:
return "SPV_EXT_demote_to_helper_invocation";
case Extension::kSPV_EXT_descriptor_indexing:
return "SPV_EXT_descriptor_indexing";
case Extension::kSPV_EXT_fragment_fully_covered:
@ -113,8 +115,8 @@ const char* ExtensionToString(Extension extension) {
bool GetExtensionFromString(const char* str, Extension* extension) {
static const char* known_ext_strs[] = { "SPV_AMD_gcn_shader", "SPV_AMD_gpu_shader_half_float", "SPV_AMD_gpu_shader_half_float_fetch", "SPV_AMD_gpu_shader_int16", "SPV_AMD_shader_ballot", "SPV_AMD_shader_explicit_vertex_parameter", "SPV_AMD_shader_fragment_mask", "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_trinary_minmax", "SPV_AMD_texture_gather_bias_lod", "SPV_EXT_descriptor_indexing", "SPV_EXT_fragment_fully_covered", "SPV_EXT_fragment_invocation_density", "SPV_EXT_fragment_shader_interlock", "SPV_EXT_physical_storage_buffer", "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_media_block_io", "SPV_INTEL_shader_integer_functions2", "SPV_INTEL_subgroups", "SPV_KHR_16bit_storage", "SPV_KHR_8bit_storage", "SPV_KHR_device_group", "SPV_KHR_float_controls", "SPV_KHR_multiview", "SPV_KHR_no_integer_wrap_decoration", "SPV_KHR_post_depth_coverage", "SPV_KHR_shader_atomic_counter_ops", "SPV_KHR_shader_ballot", "SPV_KHR_shader_draw_parameters", "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_subgroup_vote", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_compute_shader_derivatives", "SPV_NV_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_sample_mask_override_coverage", "SPV_NV_shader_image_footprint", "SPV_NV_shader_sm_builtins", "SPV_NV_shader_subgroup_partitioned", "SPV_NV_shading_rate", "SPV_NV_stereo_view_rendering", "SPV_NV_viewport_array2", "SPV_VALIDATOR_ignore_type_decl_unique" };
static const Extension known_ext_ids[] = { Extension::kSPV_AMD_gcn_shader, Extension::kSPV_AMD_gpu_shader_half_float, Extension::kSPV_AMD_gpu_shader_half_float_fetch, Extension::kSPV_AMD_gpu_shader_int16, Extension::kSPV_AMD_shader_ballot, Extension::kSPV_AMD_shader_explicit_vertex_parameter, Extension::kSPV_AMD_shader_fragment_mask, Extension::kSPV_AMD_shader_image_load_store_lod, Extension::kSPV_AMD_shader_trinary_minmax, Extension::kSPV_AMD_texture_gather_bias_lod, Extension::kSPV_EXT_descriptor_indexing, Extension::kSPV_EXT_fragment_fully_covered, Extension::kSPV_EXT_fragment_invocation_density, Extension::kSPV_EXT_fragment_shader_interlock, Extension::kSPV_EXT_physical_storage_buffer, Extension::kSPV_EXT_shader_stencil_export, Extension::kSPV_EXT_shader_viewport_index_layer, Extension::kSPV_GOOGLE_decorate_string, Extension::kSPV_GOOGLE_hlsl_functionality1, Extension::kSPV_GOOGLE_user_type, Extension::kSPV_INTEL_device_side_avc_motion_estimation, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_shader_integer_functions2, Extension::kSPV_INTEL_subgroups, Extension::kSPV_KHR_16bit_storage, Extension::kSPV_KHR_8bit_storage, Extension::kSPV_KHR_device_group, Extension::kSPV_KHR_float_controls, Extension::kSPV_KHR_multiview, Extension::kSPV_KHR_no_integer_wrap_decoration, Extension::kSPV_KHR_post_depth_coverage, Extension::kSPV_KHR_shader_atomic_counter_ops, Extension::kSPV_KHR_shader_ballot, Extension::kSPV_KHR_shader_draw_parameters, Extension::kSPV_KHR_storage_buffer_storage_class, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_variable_pointers, Extension::kSPV_KHR_vulkan_memory_model, Extension::kSPV_NVX_multiview_per_view_attributes, Extension::kSPV_NV_compute_shader_derivatives, Extension::kSPV_NV_cooperative_matrix, Extension::kSPV_NV_fragment_shader_barycentric, Extension::kSPV_NV_geometry_shader_passthrough, Extension::kSPV_NV_mesh_shader, Extension::kSPV_NV_ray_tracing, Extension::kSPV_NV_sample_mask_override_coverage, Extension::kSPV_NV_shader_image_footprint, Extension::kSPV_NV_shader_sm_builtins, Extension::kSPV_NV_shader_subgroup_partitioned, Extension::kSPV_NV_shading_rate, Extension::kSPV_NV_stereo_view_rendering, Extension::kSPV_NV_viewport_array2, Extension::kSPV_VALIDATOR_ignore_type_decl_unique };
static const char* known_ext_strs[] = { "SPV_AMD_gcn_shader", "SPV_AMD_gpu_shader_half_float", "SPV_AMD_gpu_shader_half_float_fetch", "SPV_AMD_gpu_shader_int16", "SPV_AMD_shader_ballot", "SPV_AMD_shader_explicit_vertex_parameter", "SPV_AMD_shader_fragment_mask", "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_trinary_minmax", "SPV_AMD_texture_gather_bias_lod", "SPV_EXT_demote_to_helper_invocation", "SPV_EXT_descriptor_indexing", "SPV_EXT_fragment_fully_covered", "SPV_EXT_fragment_invocation_density", "SPV_EXT_fragment_shader_interlock", "SPV_EXT_physical_storage_buffer", "SPV_EXT_shader_stencil_export", "SPV_EXT_shader_viewport_index_layer", "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", "SPV_GOOGLE_user_type", "SPV_INTEL_device_side_avc_motion_estimation", "SPV_INTEL_media_block_io", "SPV_INTEL_shader_integer_functions2", "SPV_INTEL_subgroups", "SPV_KHR_16bit_storage", "SPV_KHR_8bit_storage", "SPV_KHR_device_group", "SPV_KHR_float_controls", "SPV_KHR_multiview", "SPV_KHR_no_integer_wrap_decoration", "SPV_KHR_post_depth_coverage", "SPV_KHR_shader_atomic_counter_ops", "SPV_KHR_shader_ballot", "SPV_KHR_shader_draw_parameters", "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_subgroup_vote", "SPV_KHR_variable_pointers", "SPV_KHR_vulkan_memory_model", "SPV_NVX_multiview_per_view_attributes", "SPV_NV_compute_shader_derivatives", "SPV_NV_cooperative_matrix", "SPV_NV_fragment_shader_barycentric", "SPV_NV_geometry_shader_passthrough", "SPV_NV_mesh_shader", "SPV_NV_ray_tracing", "SPV_NV_sample_mask_override_coverage", "SPV_NV_shader_image_footprint", "SPV_NV_shader_sm_builtins", "SPV_NV_shader_subgroup_partitioned", "SPV_NV_shading_rate", "SPV_NV_stereo_view_rendering", "SPV_NV_viewport_array2", "SPV_VALIDATOR_ignore_type_decl_unique" };
static const Extension known_ext_ids[] = { Extension::kSPV_AMD_gcn_shader, Extension::kSPV_AMD_gpu_shader_half_float, Extension::kSPV_AMD_gpu_shader_half_float_fetch, Extension::kSPV_AMD_gpu_shader_int16, Extension::kSPV_AMD_shader_ballot, Extension::kSPV_AMD_shader_explicit_vertex_parameter, Extension::kSPV_AMD_shader_fragment_mask, Extension::kSPV_AMD_shader_image_load_store_lod, Extension::kSPV_AMD_shader_trinary_minmax, Extension::kSPV_AMD_texture_gather_bias_lod, Extension::kSPV_EXT_demote_to_helper_invocation, Extension::kSPV_EXT_descriptor_indexing, Extension::kSPV_EXT_fragment_fully_covered, Extension::kSPV_EXT_fragment_invocation_density, Extension::kSPV_EXT_fragment_shader_interlock, Extension::kSPV_EXT_physical_storage_buffer, Extension::kSPV_EXT_shader_stencil_export, Extension::kSPV_EXT_shader_viewport_index_layer, Extension::kSPV_GOOGLE_decorate_string, Extension::kSPV_GOOGLE_hlsl_functionality1, Extension::kSPV_GOOGLE_user_type, Extension::kSPV_INTEL_device_side_avc_motion_estimation, Extension::kSPV_INTEL_media_block_io, Extension::kSPV_INTEL_shader_integer_functions2, Extension::kSPV_INTEL_subgroups, Extension::kSPV_KHR_16bit_storage, Extension::kSPV_KHR_8bit_storage, Extension::kSPV_KHR_device_group, Extension::kSPV_KHR_float_controls, Extension::kSPV_KHR_multiview, Extension::kSPV_KHR_no_integer_wrap_decoration, Extension::kSPV_KHR_post_depth_coverage, Extension::kSPV_KHR_shader_atomic_counter_ops, Extension::kSPV_KHR_shader_ballot, Extension::kSPV_KHR_shader_draw_parameters, Extension::kSPV_KHR_storage_buffer_storage_class, Extension::kSPV_KHR_subgroup_vote, Extension::kSPV_KHR_variable_pointers, Extension::kSPV_KHR_vulkan_memory_model, Extension::kSPV_NVX_multiview_per_view_attributes, Extension::kSPV_NV_compute_shader_derivatives, Extension::kSPV_NV_cooperative_matrix, Extension::kSPV_NV_fragment_shader_barycentric, Extension::kSPV_NV_geometry_shader_passthrough, Extension::kSPV_NV_mesh_shader, Extension::kSPV_NV_ray_tracing, Extension::kSPV_NV_sample_mask_override_coverage, Extension::kSPV_NV_shader_image_footprint, Extension::kSPV_NV_shader_sm_builtins, Extension::kSPV_NV_shader_subgroup_partitioned, Extension::kSPV_NV_shading_rate, Extension::kSPV_NV_stereo_view_rendering, Extension::kSPV_NV_viewport_array2, Extension::kSPV_VALIDATOR_ignore_type_decl_unique };
const auto b = std::begin(known_ext_strs);
const auto e = std::end(known_ext_strs);
const auto found = std::equal_range(
@ -386,6 +388,8 @@ const char* CapabilityToString(SpvCapability capability) {
return "ShaderSMBuiltinsNV";
case SpvCapabilityFragmentShaderPixelInterlockEXT:
return "FragmentShaderPixelInterlockEXT";
case SpvCapabilityDemoteToHelperInvocationEXT:
return "DemoteToHelperInvocationEXT";
case SpvCapabilitySubgroupShuffleINTEL:
return "SubgroupShuffleINTEL";
case SpvCapabilitySubgroupBufferBlockIOINTEL:

View File

@ -8,6 +8,7 @@ kSPV_AMD_shader_fragment_mask,
kSPV_AMD_shader_image_load_store_lod,
kSPV_AMD_shader_trinary_minmax,
kSPV_AMD_texture_gather_bias_lod,
kSPV_EXT_demote_to_helper_invocation,
kSPV_EXT_descriptor_indexing,
kSPV_EXT_fragment_fully_covered,
kSPV_EXT_fragment_invocation_density,

View File

@ -90,6 +90,7 @@ static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_explicit_ver
static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_fragment_mask[] = {spvtools::Extension::kSPV_AMD_shader_fragment_mask};
static const spvtools::Extension pygen_variable_exts_SPV_AMD_shader_image_load_store_lod[] = {spvtools::Extension::kSPV_AMD_shader_image_load_store_lod};
static const spvtools::Extension pygen_variable_exts_SPV_AMD_texture_gather_bias_lod[] = {spvtools::Extension::kSPV_AMD_texture_gather_bias_lod};
static const spvtools::Extension pygen_variable_exts_SPV_EXT_demote_to_helper_invocation[] = {spvtools::Extension::kSPV_EXT_demote_to_helper_invocation};
static const spvtools::Extension pygen_variable_exts_SPV_EXT_descriptor_indexing[] = {spvtools::Extension::kSPV_EXT_descriptor_indexing};
static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_fully_covered[] = {spvtools::Extension::kSPV_EXT_fragment_fully_covered};
static const spvtools::Extension pygen_variable_exts_SPV_EXT_fragment_invocation_densitySPV_NV_shading_rate[] = {spvtools::Extension::kSPV_EXT_fragment_invocation_density, spvtools::Extension::kSPV_NV_shading_rate};
@ -817,6 +818,7 @@ static const spv_operand_desc_t pygen_variable_CapabilityEntries[] = {
{"FragmentShaderShadingRateInterlockEXT", 5372, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, {}, 0xffffffffu, 0xffffffffu},
{"ShaderSMBuiltinsNV", 5373, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_NV_shader_sm_builtins, {}, 0xffffffffu, 0xffffffffu},
{"FragmentShaderPixelInterlockEXT", 5378, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_fragment_shader_interlock, {}, 0xffffffffu, 0xffffffffu},
{"DemoteToHelperInvocationEXT", 5379, 1, pygen_variable_caps_Shader, 1, pygen_variable_exts_SPV_EXT_demote_to_helper_invocation, {}, 0xffffffffu, 0xffffffffu},
{"SubgroupShuffleINTEL", 5568, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
{"SubgroupBufferBlockIOINTEL", 5569, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},
{"SubgroupImageBlockIOINTEL", 5570, 0, nullptr, 1, pygen_variable_exts_SPV_INTEL_subgroups, {}, 0xffffffffu, 0xffffffffu},

View File

@ -41,6 +41,7 @@ if(SPIRV_BUILD_FUZZER)
pseudo_random_generator.h
random_generator.h
replayer.h
transformation.h
transformation_add_constant_boolean.h
transformation_add_constant_scalar.h
transformation_add_dead_break.h
@ -69,6 +70,7 @@ if(SPIRV_BUILD_FUZZER)
pseudo_random_generator.cpp
random_generator.cpp
replayer.cpp
transformation.cpp
transformation_add_constant_boolean.cpp
transformation_add_constant_scalar.cpp
transformation_add_dead_break.cpp

View File

@ -14,12 +14,60 @@
#include "source/fuzz/fact_manager.h"
#include <sstream>
#include "source/fuzz/uniform_buffer_element_descriptor.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
namespace {
std::string ToString(const protobufs::Fact& fact) {
assert(fact.fact_case() == protobufs::Fact::kConstantUniformFact &&
"Right now this is the only fact.");
std::stringstream stream;
stream << "("
<< fact.constant_uniform_fact()
.uniform_buffer_element_descriptor()
.descriptor_set()
<< ", "
<< fact.constant_uniform_fact()
.uniform_buffer_element_descriptor()
.binding()
<< ")[";
bool first = true;
for (auto index : fact.constant_uniform_fact()
.uniform_buffer_element_descriptor()
.index()) {
if (first) {
first = false;
} else {
stream << ", ";
}
stream << index;
}
stream << "] == [";
first = true;
for (auto constant_word : fact.constant_uniform_fact().constant_word()) {
if (first) {
first = false;
} else {
stream << ", ";
}
stream << constant_word;
}
stream << "]";
return stream.str();
}
} // namespace
// The purpose of this struct is to group the fields and data used to represent
// facts about uniform constants.
struct FactManager::ConstantUniformFacts {
@ -288,16 +336,16 @@ FactManager::FactManager() {
FactManager::~FactManager() = default;
bool FactManager::AddFacts(const protobufs::FactSequence& initial_facts,
void FactManager::AddFacts(const MessageConsumer& message_consumer,
const protobufs::FactSequence& initial_facts,
opt::IRContext* context) {
for (auto& fact : initial_facts.fact()) {
if (!AddFact(fact, context)) {
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2621) Provide
// information about the fact that could not be added.
return false;
message_consumer(
SPV_MSG_WARNING, nullptr, {},
("Invalid fact " + ToString(fact) + " ignored.").c_str());
}
}
return true;
}
bool FactManager::AddFact(const spvtools::fuzz::protobufs::Fact& fact,

View File

@ -41,11 +41,14 @@ class FactManager {
~FactManager();
// Adds all the facts from |facts|, checking them for validity with respect to
// |context|. Returns true if and only if all facts are valid.
bool AddFacts(const protobufs::FactSequence& facts, opt::IRContext* context);
// |context|. Warnings about invalid facts are communicated via
// |message_consumer|; such facts are otherwise ignored.
void AddFacts(const MessageConsumer& message_consumer,
const protobufs::FactSequence& facts, opt::IRContext* context);
// Adds |fact| to the fact manager, checking it for validity with respect to
// |context|. Returns true if and only if the fact is valid.
// Checks the fact for validity with respect to |context|. Returns false,
// with no side effects, if the fact is invalid. Otherwise adds |fact| to the
// fact manager.
bool AddFact(const protobufs::Fact& fact, opt::IRContext* context);
// The fact manager will ultimately be responsible for managing a few distinct

View File

@ -97,9 +97,7 @@ Fuzzer::FuzzerResultStatus Fuzzer::Run(
FuzzerContext fuzzer_context(&random_generator, minimum_fresh_id);
FactManager fact_manager;
if (!fact_manager.AddFacts(initial_facts, ir_context.get())) {
return Fuzzer::FuzzerResultStatus::kInitialFactsInvalid;
}
fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
// Add some essential ingredients to the module if they are not already
// present, such as boolean constants.

View File

@ -33,7 +33,6 @@ class Fuzzer {
kComplete,
kFailedToCreateSpirvToolsInterface,
kInitialBinaryInvalid,
kInitialFactsInvalid,
};
// Constructs a fuzzer from the given target environment.

View File

@ -30,7 +30,7 @@ FuzzerPassAddDeadBreaks::~FuzzerPassAddDeadBreaks() = default;
void FuzzerPassAddDeadBreaks::Apply() {
// We first collect up lots of possibly-applicable transformations.
std::vector<protobufs::TransformationAddDeadBreak> candidate_transformations;
std::vector<TransformationAddDeadBreak> candidate_transformations;
// We consider each function separately.
for (auto& function : *GetIRContext()->module()) {
// For a given function, we find all the merge blocks in that function.
@ -51,13 +51,12 @@ void FuzzerPassAddDeadBreaks::Apply() {
// merge blocks. This will lead to interesting opportunities being
// missed.
std::vector<uint32_t> phi_ids;
auto candidate_transformation =
transformation::MakeTransformationAddDeadBreak(
block.id(), merge_block_id,
GetFuzzerContext()->GetRandomGenerator()->RandomBool(),
std::move(phi_ids));
if (transformation::IsApplicable(candidate_transformation,
GetIRContext(), *GetFactManager())) {
auto candidate_transformation = TransformationAddDeadBreak(
block.id(), merge_block_id,
GetFuzzerContext()->GetRandomGenerator()->RandomBool(),
std::move(phi_ids));
if (candidate_transformation.IsApplicable(GetIRContext(),
*GetFactManager())) {
// Only consider a transformation as a candidate if it is applicable.
candidate_transformations.push_back(
std::move(candidate_transformation));
@ -92,11 +91,9 @@ void FuzzerPassAddDeadBreaks::Apply() {
}
// If the transformation can be applied, apply it and add it to the
// sequence of transformations that have been applied.
if (transformation::IsApplicable(transformation, GetIRContext(),
*GetFactManager())) {
transformation::Apply(transformation, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_add_dead_break() =
transformation;
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
transformation.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = transformation.ToMessage();
}
}
}

View File

@ -48,15 +48,13 @@ void FuzzerPassAddUsefulConstructs::MaybeAddIntConstant(
"The relevant int type should have been added to the module already.");
opt::analysis::IntConstant int_constant(registered_int_type, data);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&int_constant)) {
protobufs::TransformationAddConstantScalar add_constant_int =
transformation::MakeTransformationAddConstantScalar(
GetFuzzerContext()->GetFreshId(), int_type_id, data);
assert(transformation::IsApplicable(add_constant_int, GetIRContext(),
*GetFactManager()) &&
TransformationAddConstantScalar add_constant_int =
TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
int_type_id, data);
assert(add_constant_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
transformation::Apply(add_constant_int, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_add_constant_scalar() =
add_constant_int;
add_constant_int.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = add_constant_int.ToMessage();
}
}
@ -76,15 +74,14 @@ void FuzzerPassAddUsefulConstructs::MaybeAddFloatConstant(
"The relevant float type should have been added to the module already.");
opt::analysis::FloatConstant float_constant(registered_float_type, data);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&float_constant)) {
protobufs::TransformationAddConstantScalar add_constant_float =
transformation::MakeTransformationAddConstantScalar(
GetFuzzerContext()->GetFreshId(), float_type_id, data);
assert(transformation::IsApplicable(add_constant_float, GetIRContext(),
*GetFactManager()) &&
TransformationAddConstantScalar add_constant_float =
TransformationAddConstantScalar(GetFuzzerContext()->GetFreshId(),
float_type_id, data);
assert(add_constant_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
transformation::Apply(add_constant_float, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_add_constant_scalar() =
add_constant_float;
add_constant_float.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
add_constant_float.ToMessage();
}
}
@ -93,15 +90,13 @@ void FuzzerPassAddUsefulConstructs::Apply() {
// Add boolean type if not present.
opt::analysis::Bool temp_bool_type;
if (!GetIRContext()->get_type_mgr()->GetId(&temp_bool_type)) {
protobufs::TransformationAddTypeBoolean add_type_boolean =
transformation::MakeTransformationAddTypeBoolean(
GetFuzzerContext()->GetFreshId());
assert(transformation::IsApplicable(add_type_boolean, GetIRContext(),
*GetFactManager()) &&
auto add_type_boolean =
TransformationAddTypeBoolean(GetFuzzerContext()->GetFreshId());
assert(add_type_boolean.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
transformation::Apply(add_type_boolean, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_add_type_boolean() =
add_type_boolean;
add_type_boolean.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
add_type_boolean.ToMessage();
}
}
@ -110,15 +105,12 @@ void FuzzerPassAddUsefulConstructs::Apply() {
for (auto is_signed : {true, false}) {
opt::analysis::Integer temp_int_type(32, is_signed);
if (!GetIRContext()->get_type_mgr()->GetId(&temp_int_type)) {
protobufs::TransformationAddTypeInt add_type_int =
transformation::MakeTransformationAddTypeInt(
GetFuzzerContext()->GetFreshId(), 32, is_signed);
assert(transformation::IsApplicable(add_type_int, GetIRContext(),
*GetFactManager()) &&
TransformationAddTypeInt add_type_int = TransformationAddTypeInt(
GetFuzzerContext()->GetFreshId(), 32, is_signed);
assert(add_type_int.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
transformation::Apply(add_type_int, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_add_type_int() =
add_type_int;
add_type_int.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = add_type_int.ToMessage();
}
}
}
@ -127,15 +119,12 @@ void FuzzerPassAddUsefulConstructs::Apply() {
// Add 32-bit float type if not present.
opt::analysis::Float temp_float_type(32);
if (!GetIRContext()->get_type_mgr()->GetId(&temp_float_type)) {
protobufs::TransformationAddTypeFloat add_type_float =
transformation::MakeTransformationAddTypeFloat(
GetFuzzerContext()->GetFreshId(), 32);
assert(transformation::IsApplicable(add_type_float, GetIRContext(),
*GetFactManager()) &&
TransformationAddTypeFloat add_type_float =
TransformationAddTypeFloat(GetFuzzerContext()->GetFreshId(), 32);
assert(add_type_float.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
transformation::Apply(add_type_float, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_add_type_float() =
add_type_float;
add_type_float.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = add_type_float.ToMessage();
}
}
@ -149,17 +138,14 @@ void FuzzerPassAddUsefulConstructs::Apply() {
// Add OpConstantTrue/False if not already there.
opt::analysis::BoolConstant bool_constant(bool_type, boolean_value);
if (!GetIRContext()->get_constant_mgr()->FindConstant(&bool_constant)) {
protobufs::TransformationAddConstantBoolean add_constant_boolean =
transformation::MakeTransformationAddConstantBoolean(
GetFuzzerContext()->GetFreshId(), boolean_value);
assert(transformation::IsApplicable(add_constant_boolean, GetIRContext(),
*GetFactManager()) &&
TransformationAddConstantBoolean add_constant_boolean(
GetFuzzerContext()->GetFreshId(), boolean_value);
assert(add_constant_boolean.IsApplicable(GetIRContext(),
*GetFactManager()) &&
"Should be applicable by construction.");
transformation::Apply(add_constant_boolean, GetIRContext(),
GetFactManager());
*GetTransformations()
->add_transformation()
->mutable_add_constant_boolean() = add_constant_boolean;
add_constant_boolean.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
add_constant_boolean.ToMessage();
}
}
@ -196,15 +182,13 @@ void FuzzerPassAddUsefulConstructs::Apply() {
opt::analysis::Pointer uniform_pointer(element_type,
SpvStorageClassUniform);
if (!GetIRContext()->get_type_mgr()->GetId(&uniform_pointer)) {
auto add_pointer = transformation::MakeTransformationAddTypePointer(
GetFuzzerContext()->GetFreshId(), SpvStorageClassUniform,
element_type_id);
assert(transformation::IsApplicable(add_pointer, GetIRContext(),
*GetFactManager()) &&
auto add_pointer =
TransformationAddTypePointer(GetFuzzerContext()->GetFreshId(),
SpvStorageClassUniform, element_type_id);
assert(add_pointer.IsApplicable(GetIRContext(), *GetFactManager()) &&
"Should be applicable by construction.");
transformation::Apply(add_pointer, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_add_type_pointer() =
add_pointer;
add_pointer.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = add_pointer.ToMessage();
}
std::vector<uint32_t> words;
for (auto word : fact_and_type_id.first.constant_word()) {

View File

@ -80,25 +80,20 @@ void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
// We can now make a transformation that will replace |bool_constant_use|
// with an expression of the form (written using infix notation):
// |lhs_id| |comparison_opcode| |rhs_id|
auto transformation = transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
bool_constant_use, lhs_id, rhs_id, comparison_opcode,
GetFuzzerContext()->GetFreshId());
auto transformation = TransformationReplaceBooleanConstantWithConstantBinary(
bool_constant_use, lhs_id, rhs_id, comparison_opcode,
GetFuzzerContext()->GetFreshId());
// The transformation should be applicable by construction.
assert(transformation::IsApplicable(transformation, GetIRContext(),
*GetFactManager()));
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()));
// Applying this transformation yields a pointer to the new instruction that
// computes the result of the binary expression.
auto binary_operator_instruction =
transformation::Apply(transformation, GetIRContext(), GetFactManager());
transformation.ApplyWithResult(GetIRContext(), GetFactManager());
// Add this transformation to the sequence of transformations that have been
// applied.
*GetTransformations()
->add_transformation()
->mutable_replace_boolean_constant_with_constant_binary() =
transformation;
*GetTransformations()->add_transformation() = transformation.ToMessage();
// Having made a binary expression, there may now be opportunities to further
// obfuscate the constants used as the LHS and RHS of the expression (e.g. by
@ -331,17 +326,13 @@ void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
static_cast<uint32_t>(uniform_descriptors.size()))];
// Create, apply and record a transformation to replace the constant use with
// the result of a load from the chosen uniform.
auto transformation =
transformation::MakeTransformationReplaceConstantWithUniform(
constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
GetFuzzerContext()->GetFreshId());
auto transformation = TransformationReplaceConstantWithUniform(
constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
GetFuzzerContext()->GetFreshId());
// Transformation should be applicable by construction.
assert(transformation::IsApplicable(transformation, GetIRContext(),
*GetFactManager()));
transformation::Apply(transformation, GetIRContext(), GetFactManager());
*GetTransformations()
->add_transformation()
->mutable_replace_constant_with_uniform() = transformation;
assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()));
transformation.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = transformation.ToMessage();
}
void FuzzerPassObfuscateConstants::ObfuscateConstant(

View File

@ -65,14 +65,11 @@ void FuzzerPassPermuteBlocks::Apply() {
// The loop is guaranteed to terminate because a block cannot be pushed
// down indefinitely.
while (true) {
protobufs::TransformationMoveBlockDown message;
message.set_block_id(*id);
if (transformation::IsApplicable(message, GetIRContext(),
*GetFactManager())) {
transformation::Apply(message, GetIRContext(), GetFactManager());
*GetTransformations()
->add_transformation()
->mutable_move_block_down() = message;
TransformationMoveBlockDown transformation(*id);
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
transformation.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() =
transformation.ToMessage();
} else {
break;
}

View File

@ -80,17 +80,15 @@ void FuzzerPassSplitBlocks::Apply() {
auto base_offset = base_offset_pairs
[GetFuzzerContext()->GetRandomGenerator()->RandomUint32(
static_cast<uint32_t>(base_offset_pairs.size()))];
auto message = transformation::MakeTransformationSplitBlock(
base_offset.first, base_offset.second,
GetFuzzerContext()->GetFreshId());
auto transformation =
TransformationSplitBlock(base_offset.first, base_offset.second,
GetFuzzerContext()->GetFreshId());
// If the position we have chosen turns out to be a valid place to split
// the block, we apply the split. Otherwise the block just doesn't get
// split.
if (transformation::IsApplicable(message, GetIRContext(),
*GetFactManager())) {
transformation::Apply(message, GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation()->mutable_split_block() =
message;
if (transformation.IsApplicable(GetIRContext(), *GetFactManager())) {
transformation.Apply(GetIRContext(), GetFactManager());
*GetTransformations()->add_transformation() = transformation.ToMessage();
}
}
}

View File

@ -18,6 +18,7 @@
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/fuzz/transformation_add_constant_boolean.h"
#include "source/fuzz/transformation_add_constant_scalar.h"
#include "source/fuzz/transformation_add_dead_break.h"
@ -35,121 +36,6 @@
namespace spvtools {
namespace fuzz {
namespace {
// Returns true if and only if the precondition for |transformation| holds, with
// respect to the given |context| and |fact_manager|.
bool IsApplicable(const protobufs::Transformation& transformation,
opt::IRContext* context, const FactManager& fact_manager) {
switch (transformation.transformation_case()) {
case protobufs::Transformation::TransformationCase::kAddConstantBoolean:
return transformation::IsApplicable(transformation.add_constant_boolean(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
return transformation::IsApplicable(transformation.add_constant_scalar(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kAddDeadBreak:
return transformation::IsApplicable(transformation.add_dead_break(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
return transformation::IsApplicable(transformation.add_type_boolean(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kAddTypeFloat:
return transformation::IsApplicable(transformation.add_type_float(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kAddTypeInt:
return transformation::IsApplicable(transformation.add_type_int(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kAddTypePointer:
return transformation::IsApplicable(transformation.add_type_pointer(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
return transformation::IsApplicable(transformation.move_block_down(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return transformation::IsApplicable(
transformation.replace_boolean_constant_with_constant_binary(),
context, fact_manager);
case protobufs::Transformation::TransformationCase::
kReplaceConstantWithUniform:
return transformation::IsApplicable(
transformation.replace_constant_with_uniform(), context,
fact_manager);
case protobufs::Transformation::TransformationCase::kSplitBlock:
return transformation::IsApplicable(transformation.split_block(), context,
fact_manager);
default:
assert(transformation.transformation_case() ==
protobufs::Transformation::TRANSFORMATION_NOT_SET &&
"Unhandled transformation type.");
assert(false && "An unset transformation was encountered.");
return false;
}
}
// Requires that IsApplicable holds. Applies |transformation| to the given
// |context| and |fact_manager|.
void Apply(const protobufs::Transformation& transformation,
opt::IRContext* context, FactManager* fact_manager) {
switch (transformation.transformation_case()) {
case protobufs::Transformation::TransformationCase::kAddConstantBoolean:
transformation::Apply(transformation.add_constant_boolean(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
transformation::Apply(transformation.add_constant_scalar(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kAddDeadBreak:
transformation::Apply(transformation.add_dead_break(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
transformation::Apply(transformation.add_type_boolean(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kAddTypeFloat:
transformation::Apply(transformation.add_type_float(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kAddTypeInt:
transformation::Apply(transformation.add_type_int(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kAddTypePointer:
transformation::Apply(transformation.add_type_pointer(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
transformation::Apply(transformation.move_block_down(), context,
fact_manager);
break;
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
transformation::Apply(
transformation.replace_boolean_constant_with_constant_binary(),
context, fact_manager);
break;
case protobufs::Transformation::TransformationCase::
kReplaceConstantWithUniform:
transformation::Apply(transformation.replace_constant_with_uniform(),
context, fact_manager);
break;
case protobufs::Transformation::TransformationCase::kSplitBlock:
transformation::Apply(transformation.split_block(), context,
fact_manager);
break;
default:
assert(transformation.transformation_case() ==
protobufs::Transformation::TRANSFORMATION_NOT_SET &&
"Unhandled transformation type.");
assert(false && "An unset transformation was encountered.");
}
}
} // namespace
struct Replayer::Impl {
explicit Impl(spv_target_env env) : target_env(env) {}
@ -195,18 +81,18 @@ Replayer::ReplayerResultStatus Replayer::Run(
assert(ir_context);
FactManager fact_manager;
if (!fact_manager.AddFacts(initial_facts, ir_context.get())) {
return Replayer::ReplayerResultStatus::kInitialFactsInvalid;
}
fact_manager.AddFacts(impl_->consumer, initial_facts, ir_context.get());
// Consider the transformation proto messages in turn.
for (auto& transformation : transformation_sequence_in.transformation()) {
for (auto& message : transformation_sequence_in.transformation()) {
auto transformation = Transformation::FromMessage(message);
// Check whether the transformation can be applied.
if (IsApplicable(transformation, ir_context.get(), fact_manager)) {
if (transformation->IsApplicable(ir_context.get(), fact_manager)) {
// The transformation is applicable, so apply it, and copy it to the
// sequence of transformations that were applied.
Apply(transformation, ir_context.get(), &fact_manager);
*transformation_sequence_out->add_transformation() = transformation;
transformation->Apply(ir_context.get(), &fact_manager);
*transformation_sequence_out->add_transformation() = message;
}
}

View File

@ -33,7 +33,6 @@ class Replayer {
kComplete,
kFailedToCreateSpirvToolsInterface,
kInitialBinaryInvalid,
kInitialFactsInvalid,
};
// Constructs a replayer from the given target environment.

View File

@ -0,0 +1,80 @@
// 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.h"
#include <cassert>
#include "source/util/make_unique.h"
#include "transformation_add_constant_boolean.h"
#include "transformation_add_constant_scalar.h"
#include "transformation_add_dead_break.h"
#include "transformation_add_type_boolean.h"
#include "transformation_add_type_float.h"
#include "transformation_add_type_int.h"
#include "transformation_add_type_pointer.h"
#include "transformation_move_block_down.h"
#include "transformation_replace_boolean_constant_with_constant_binary.h"
#include "transformation_replace_constant_with_uniform.h"
#include "transformation_split_block.h"
namespace spvtools {
namespace fuzz {
Transformation::~Transformation() = default;
std::unique_ptr<Transformation> Transformation::FromMessage(
const protobufs::Transformation& message) {
switch (message.transformation_case()) {
case protobufs::Transformation::TransformationCase::kAddConstantBoolean:
return MakeUnique<TransformationAddConstantBoolean>(
message.add_constant_boolean());
case protobufs::Transformation::TransformationCase::kAddConstantScalar:
return MakeUnique<TransformationAddConstantScalar>(
message.add_constant_scalar());
case protobufs::Transformation::TransformationCase::kAddDeadBreak:
return MakeUnique<TransformationAddDeadBreak>(message.add_dead_break());
case protobufs::Transformation::TransformationCase::kAddTypeBoolean:
return MakeUnique<TransformationAddTypeBoolean>(
message.add_type_boolean());
case protobufs::Transformation::TransformationCase::kAddTypeFloat:
return MakeUnique<TransformationAddTypeFloat>(message.add_type_float());
case protobufs::Transformation::TransformationCase::kAddTypeInt:
return MakeUnique<TransformationAddTypeInt>(message.add_type_int());
case protobufs::Transformation::TransformationCase::kAddTypePointer:
return MakeUnique<TransformationAddTypePointer>(
message.add_type_pointer());
case protobufs::Transformation::TransformationCase::kMoveBlockDown:
return MakeUnique<TransformationMoveBlockDown>(message.move_block_down());
case protobufs::Transformation::TransformationCase::
kReplaceBooleanConstantWithConstantBinary:
return MakeUnique<TransformationReplaceBooleanConstantWithConstantBinary>(
message.replace_boolean_constant_with_constant_binary());
case protobufs::Transformation::TransformationCase::
kReplaceConstantWithUniform:
return MakeUnique<TransformationReplaceConstantWithUniform>(
message.replace_constant_with_uniform());
case protobufs::Transformation::TransformationCase::kSplitBlock:
return MakeUnique<TransformationSplitBlock>(message.split_block());
default:
assert(message.transformation_case() ==
protobufs::Transformation::TRANSFORMATION_NOT_SET &&
"Unhandled transformation type.");
assert(false && "An unset transformation was encountered.");
return nullptr;
}
}
} // namespace fuzz
} // namespace spvtools

View File

@ -0,0 +1,91 @@
// 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_H_
#define SOURCE_FUZZ_TRANSFORMATION_H_
#include <memory>
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
// Rules for transformations
// -------------------------
//
// - Immutability: a transformation must be immutable.
// - Ability to copy and serialize: to ensure that a copy of a transformation,
// possibly saved out to disk and read back again, is indistinguishable
// from the original transformation, thus a transformation must depend
// only on well-defined pieces of state, such as instruction ids. It must
// not rely on state such as pointers to instructions and blocks.
// - Determinism: the effect of a transformation on a module be a deterministic
// function of the module and the transformation. Any randomization should
// be applied before creating the transformation, not during its
// application.
// - Well-defined and precondition: the 'IsApplicable' method should only
// return true if the transformation can be cleanly applied to the given
// module, to mutate it into a valid and semantically-equivalent module, as
// long as the module is initially valid.
// - Ability to test precondition on any valid module: 'IsApplicable' should be
// designed so that it is safe to ask whether a transformation is
// applicable to an arbitrary valid module. For example, if a
// transformation involves a block id, 'IsApplicable' should check whether
// the module indeed has a block with that id, and return false if not. It
// must not assume that there is such a block.
// - Documented precondition: while the implementation of 'IsApplicable' should
// should codify the precondition, the method should be commented in the
// header file for a transformation with a precise English description of
// the precondition.
// - Documented effect: while the implementation of 'Apply' should codify the
// effect of the transformation, the method should be commented in the
// header file for a transformation with a precise English description of
// the effect.
class Transformation {
public:
// A precondition that determines whether the transformation can be cleanly
// applied in a semantics-preserving manner to the SPIR-V module given by
// |context|, in the presence of facts captured by |fact_manager|.
// Preconditions for individual transformations must be documented in the
// associated header file using precise English. The fact manager is used to
// provide access to facts about the module that are known to be true, on
// which the precondition may depend.
virtual bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const = 0;
// Requires that IsApplicable(context, fact_manager) holds. Applies the
// transformation, mutating |context| and possibly updating |fact_manager|
// with new facts established by the transformation.
virtual void Apply(opt::IRContext* context,
FactManager* fact_manager) const = 0;
// Turns the transformation into a protobuf message for serialization.
virtual protobufs::Transformation ToMessage() const = 0;
virtual ~Transformation();
// Factory method to obtain a transformation object from the protobuf
// representation of a transformation given by |message|.
static std::unique_ptr<Transformation> FromMessage(
const protobufs::Transformation& message);
};
} // namespace fuzz
} // namespace spvtools
#endif // SOURCE_FUZZ_TRANSFORMATION_H_

View File

@ -19,42 +19,46 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::IRContext;
TransformationAddConstantBoolean::TransformationAddConstantBoolean(
const protobufs::TransformationAddConstantBoolean& message)
: message_(message) {}
bool IsApplicable(const protobufs::TransformationAddConstantBoolean& message,
IRContext* context, const FactManager& /*unused*/) {
TransformationAddConstantBoolean::TransformationAddConstantBoolean(
uint32_t fresh_id, bool is_true) {
message_.set_fresh_id(fresh_id);
message_.set_is_true(is_true);
}
bool TransformationAddConstantBoolean::IsApplicable(
opt::IRContext* context, const FactManager& /*unused*/) const {
opt::analysis::Bool bool_type;
if (!context->get_type_mgr()->GetId(&bool_type)) {
// No OpTypeBool is present.
return false;
}
return fuzzerutil::IsFreshId(context, message.fresh_id());
return fuzzerutil::IsFreshId(context, message_.fresh_id());
}
void Apply(const protobufs::TransformationAddConstantBoolean& message,
IRContext* context, FactManager* /*unused*/) {
void TransformationAddConstantBoolean::Apply(opt::IRContext* context,
FactManager* /*unused*/) const {
opt::analysis::Bool bool_type;
// Add the boolean constant to the module, ensuring the module's id bound is
// high enough.
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
context->module()->AddGlobalValue(
message.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
message.fresh_id(), context->get_type_mgr()->GetId(&bool_type));
message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse,
message_.fresh_id(), context->get_type_mgr()->GetId(&bool_type));
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationAddConstantBoolean
MakeTransformationAddConstantBoolean(uint32_t fresh_id, bool is_true) {
protobufs::TransformationAddConstantBoolean result;
result.set_fresh_id(fresh_id);
result.set_is_true(is_true);
protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_constant_boolean() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -17,27 +17,34 @@
#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 {
namespace transformation {
// - |fresh_id| must not be used by the module.
// - The module must already contain OpTypeBool.
bool IsApplicable(const protobufs::TransformationAddConstantBoolean& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationAddConstantBoolean : public Transformation {
public:
explicit TransformationAddConstantBoolean(
const protobufs::TransformationAddConstantBoolean& message);
// - Adds OpConstantTrue (OpConstantFalse) to the module with id |fresh_id|
// if |is_true| holds (does not hold).
void Apply(const protobufs::TransformationAddConstantBoolean& message,
opt::IRContext* context, FactManager* fact_manager);
TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true);
// Helper factory to create a transformation message.
protobufs::TransformationAddConstantBoolean
MakeTransformationAddConstantBoolean(uint32_t fresh_id, bool is_true);
// - |message_.fresh_id| must not be used by the module.
// - The module must already contain OpTypeBool.
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// - Adds OpConstantTrue (OpConstantFalse) to the module with id
// |message_.fresh_id| if |message_.is_true| holds (does not hold).
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddConstantBoolean message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -18,19 +18,29 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::IRContext;
TransformationAddConstantScalar::TransformationAddConstantScalar(
const spvtools::fuzz::protobufs::TransformationAddConstantScalar& message)
: message_(message) {}
bool IsApplicable(const protobufs::TransformationAddConstantScalar& message,
IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) {
TransformationAddConstantScalar::TransformationAddConstantScalar(
uint32_t fresh_id, uint32_t type_id, std::vector<uint32_t> words) {
message_.set_fresh_id(fresh_id);
message_.set_type_id(type_id);
for (auto word : words) {
message_.add_word(word);
}
}
bool TransformationAddConstantScalar::IsApplicable(
opt::IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) const {
// The id needs to be fresh.
if (!fuzzerutil::IsFreshId(context, message.fresh_id())) {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
// The type id for the scalar must exist and be a type.
auto type = context->get_type_mgr()->GetType(message.type_id());
auto type = context->get_type_mgr()->GetType(message_.type_id());
if (!type) {
return false;
}
@ -47,37 +57,31 @@ bool IsApplicable(const protobufs::TransformationAddConstantScalar& message,
// The number of words provided by the transformation needs to match the
// width of the type.
return static_cast<uint32_t>(message.word().size()) == words;
return static_cast<uint32_t>(message_.word().size()) == words;
}
void Apply(const protobufs::TransformationAddConstantScalar& message,
IRContext* context, spvtools::fuzz::FactManager* /*unused*/) {
void TransformationAddConstantScalar::Apply(
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
opt::Instruction::OperandList operand_list;
for (auto word : message.word()) {
for (auto word : message_.word()) {
operand_list.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {word}});
}
context->module()->AddGlobalValue(
MakeUnique<opt::Instruction>(context, SpvOpConstant, message.type_id(),
message.fresh_id(), operand_list));
MakeUnique<opt::Instruction>(context, SpvOpConstant, message_.type_id(),
message_.fresh_id(), operand_list));
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationAddConstantScalar MakeTransformationAddConstantScalar(
uint32_t fresh_id, uint32_t type_id, std::vector<uint32_t> words) {
protobufs::TransformationAddConstantScalar result;
result.set_fresh_id(fresh_id);
result.set_type_id(type_id);
for (auto word : words) {
result.add_word(word);
}
protobufs::Transformation TransformationAddConstantScalar::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_constant_scalar() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -19,27 +19,36 @@
#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 {
namespace transformation {
// - |message.fresh_id| must not be used by the module
// - |message.type_id| must be the id of a floating-point or integer type
// - The size of |message.word| must be compatible with the width of this type
bool IsApplicable(const protobufs::TransformationAddConstantScalar& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationAddConstantScalar : public Transformation {
public:
explicit TransformationAddConstantScalar(
const protobufs::TransformationAddConstantScalar& message);
// Adds a new OpConstant instruction with the given type and words.
void Apply(const protobufs::TransformationAddConstantScalar& message,
opt::IRContext* context, FactManager* fact_manager);
TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id,
std::vector<uint32_t> words);
// Helper factory to create a transformation message.
protobufs::TransformationAddConstantScalar MakeTransformationAddConstantScalar(
uint32_t fresh_id, uint32_t type_id, std::vector<uint32_t> words);
// - |message_.fresh_id| must not be used by the module
// - |message_.type_id| must be the id of a floating-point or integer type
// - The size of |message_.word| must be compatible with the width of this
// type
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// Adds a new OpConstant instruction with the given type and words.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddConstantScalar message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -21,15 +21,24 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::BasicBlock;
using opt::IRContext;
using opt::Instruction;
TransformationAddDeadBreak::TransformationAddDeadBreak(
const spvtools::fuzz::protobufs::TransformationAddDeadBreak& message)
: message_(message) {}
namespace {
TransformationAddDeadBreak::TransformationAddDeadBreak(
uint32_t from_block, uint32_t to_block, bool break_condition_value,
std::vector<uint32_t> phi_id) {
message_.set_from_block(from_block);
message_.set_to_block(to_block);
message_.set_break_condition_value(break_condition_value);
for (auto id : phi_id) {
message_.add_phi_id(id);
}
}
BasicBlock* MaybeFindBlock(IRContext* context, uint32_t maybe_block_id) {
opt::BasicBlock* TransformationAddDeadBreak::MaybeFindBlock(
opt::IRContext* context, uint32_t maybe_block_id) const {
auto inst = context->get_def_use_mgr()->GetDef(maybe_block_id);
if (inst == nullptr) {
// No instruction defining this id was found.
@ -43,14 +52,15 @@ BasicBlock* MaybeFindBlock(IRContext* context, uint32_t maybe_block_id) {
return context->cfg()->block(maybe_block_id);
}
bool PhiIdsOk(const protobufs::TransformationAddDeadBreak& message,
IRContext* context, BasicBlock* bb_from, BasicBlock* bb_to) {
bool TransformationAddDeadBreak::PhiIdsOk(opt::IRContext* context,
opt::BasicBlock* bb_from,
opt::BasicBlock* bb_to) const {
if (bb_from->IsSuccessor(bb_to)) {
// There is already an edge from |from_block| to |to_block|, so there is
// no need to extend OpPhi instructions. Do not allow phi ids to be
// present. This might turn out to be too strict; perhaps it would be OK
// just to ignore the ids in this case.
return message.phi_id().empty();
return message_.phi_id().empty();
}
// The break would add a previously non-existent edge from |from_block| to
// |to_block|, so we go through the given phi ids and check that they exactly
@ -65,14 +75,14 @@ bool PhiIdsOk(const protobufs::TransformationAddDeadBreak& message,
// a non-OpPhi then we have seen them all.
break;
}
if (phi_index == static_cast<uint32_t>(message.phi_id().size())) {
if (phi_index == static_cast<uint32_t>(message_.phi_id().size())) {
// Not enough phi ids have been provided to account for the OpPhi
// instructions.
return false;
}
// Look for an instruction defining the next phi id.
Instruction* phi_extension =
context->get_def_use_mgr()->GetDef(message.phi_id()[phi_index]);
opt::Instruction* phi_extension =
context->get_def_use_mgr()->GetDef(message_.phi_id()[phi_index]);
if (!phi_extension) {
// The id given to extend this OpPhi does not exist.
return false;
@ -101,28 +111,26 @@ bool PhiIdsOk(const protobufs::TransformationAddDeadBreak& message,
// Reject the transformation if not all of the ids for extending OpPhi
// instructions are needed. This might turn out to be stricter than necessary;
// perhaps it would be OK just to not use the ids in this case.
return phi_index == static_cast<uint32_t>(message.phi_id().size());
return phi_index == static_cast<uint32_t>(message_.phi_id().size());
}
bool FromBlockIsInLoopContinueConstruct(
const protobufs::TransformationAddDeadBreak& message, IRContext* context,
uint32_t maybe_loop_header) {
bool TransformationAddDeadBreak::FromBlockIsInLoopContinueConstruct(
opt::IRContext* context, uint32_t maybe_loop_header) const {
// We deem a block to be part of a loop's continue construct if the loop's
// continue target dominates the block.
auto containing_construct_block = context->cfg()->block(maybe_loop_header);
if (containing_construct_block->IsLoopHeader()) {
auto continue_target = containing_construct_block->ContinueBlockId();
if (context->GetDominatorAnalysis(containing_construct_block->GetParent())
->Dominates(continue_target, message.from_block())) {
->Dominates(continue_target, message_.from_block())) {
return true;
}
}
return false;
}
bool AddingBreakRespectsStructuredControlFlow(
const protobufs::TransformationAddDeadBreak& message, IRContext* context,
BasicBlock* bb_from) {
bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow(
opt::IRContext* context, opt::BasicBlock* bb_from) const {
// Look at the structured control flow associated with |from_block| and
// check whether it is contained in an appropriate construct with merge id
// |to_block| such that a break from |from_block| to |to_block| is legal.
@ -146,7 +154,7 @@ bool AddingBreakRespectsStructuredControlFlow(
if (bb_from->IsLoopHeader()) {
// Case (1) holds if |to_block| is the merge block for the loop;
// otherwise no case holds
return bb_from->MergeBlockId() == message.to_block();
return bb_from->MergeBlockId() == message_.to_block();
}
// Both cases (2) and (3) require that |from_block| is inside some
@ -154,14 +162,14 @@ bool AddingBreakRespectsStructuredControlFlow(
auto containing_construct =
context->GetStructuredCFGAnalysis()->ContainingConstruct(
message.from_block());
message_.from_block());
if (!containing_construct) {
// |from_block| is not in a construct from which we can break.
return false;
}
// Consider case (2)
if (message.to_block() ==
if (message_.to_block() ==
context->cfg()->block(containing_construct)->MergeBlockId()) {
// This looks like an instance of case (2).
// However, the structured CFG analysis regards the continue construct of a
@ -172,27 +180,24 @@ bool AddingBreakRespectsStructuredControlFlow(
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2577): We do not
// currently allow a dead break from a back edge block, but we could and
// ultimately should.
return !FromBlockIsInLoopContinueConstruct(message, context,
containing_construct);
return !FromBlockIsInLoopContinueConstruct(context, containing_construct);
}
// Case (3) holds if and only if |to_block| is the merge block for this
// innermost loop that contains |from_block|
auto containing_loop_header =
context->GetStructuredCFGAnalysis()->ContainingLoop(message.from_block());
context->GetStructuredCFGAnalysis()->ContainingLoop(
message_.from_block());
if (containing_loop_header &&
message.to_block() ==
message_.to_block() ==
context->cfg()->block(containing_loop_header)->MergeBlockId()) {
return !FromBlockIsInLoopContinueConstruct(message, context,
containing_loop_header);
return !FromBlockIsInLoopContinueConstruct(context, containing_loop_header);
}
return false;
}
} // namespace
bool IsApplicable(const protobufs::TransformationAddDeadBreak& message,
IRContext* context, const FactManager& /*unused*/) {
bool TransformationAddDeadBreak::IsApplicable(
opt::IRContext* context, const FactManager& /*unused*/) const {
// First, we check that a constant with the same value as
// |break_condition_value| is present.
opt::analysis::Bool bool_type;
@ -202,7 +207,7 @@ bool IsApplicable(const protobufs::TransformationAddDeadBreak& message,
return false;
}
opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
message.break_condition_value());
message_.break_condition_value());
if (!context->get_constant_mgr()->FindConstant(&bool_constant)) {
// The required constant is not present, so the transformation cannot be
// applied.
@ -210,11 +215,11 @@ bool IsApplicable(const protobufs::TransformationAddDeadBreak& message,
}
// Check that |from_block| and |to_block| really are block ids
BasicBlock* bb_from = MaybeFindBlock(context, message.from_block());
opt::BasicBlock* bb_from = MaybeFindBlock(context, message_.from_block());
if (bb_from == nullptr) {
return false;
}
BasicBlock* bb_to = MaybeFindBlock(context, message.to_block());
opt::BasicBlock* bb_to = MaybeFindBlock(context, message_.to_block());
if (bb_to == nullptr) {
return false;
}
@ -229,36 +234,36 @@ bool IsApplicable(const protobufs::TransformationAddDeadBreak& message,
assert(bb_from != nullptr &&
"We should have found a block if this line of code is reached.");
assert(
bb_from->id() == message.from_block() &&
bb_from->id() == message_.from_block() &&
"The id of the block we found should match the source id for the break.");
assert(bb_to != nullptr &&
"We should have found a block if this line of code is reached.");
assert(
bb_to->id() == message.to_block() &&
bb_to->id() == message_.to_block() &&
"The id of the block we found should match the target id for the break.");
// Check whether the data passed to extend OpPhi instructions is appropriate.
if (!PhiIdsOk(message, context, bb_from, bb_to)) {
if (!PhiIdsOk(context, bb_from, bb_to)) {
return false;
}
// Finally, check that adding the break would respect the rules of structured
// control flow.
return AddingBreakRespectsStructuredControlFlow(message, context, bb_from);
return AddingBreakRespectsStructuredControlFlow(context, bb_from);
}
void Apply(const protobufs::TransformationAddDeadBreak& message,
IRContext* context, FactManager* /*unused*/) {
void TransformationAddDeadBreak::Apply(opt::IRContext* context,
FactManager* /*unused*/) const {
// Get the id of the boolean constant to be used as the break condition.
opt::analysis::Bool bool_type;
opt::analysis::BoolConstant bool_constant(
context->get_type_mgr()->GetRegisteredType(&bool_type)->AsBool(),
message.break_condition_value());
message_.break_condition_value());
uint32_t bool_id = context->get_constant_mgr()->FindDeclaredConstant(
&bool_constant, context->get_type_mgr()->GetId(&bool_type));
auto bb_from = context->cfg()->block(message.from_block());
auto bb_to = context->cfg()->block(message.to_block());
auto bb_from = context->cfg()->block(message_.from_block());
auto bb_to = context->cfg()->block(message_.to_block());
const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
assert(bb_from->terminator()->opcode() == SpvOpBranch &&
@ -272,9 +277,9 @@ void Apply(const protobufs::TransformationAddDeadBreak& message,
bb_from->terminator()->SetInOperands(
{{SPV_OPERAND_TYPE_ID, {bool_id}},
{SPV_OPERAND_TYPE_ID,
{message.break_condition_value() ? successor : message.to_block()}},
{message_.break_condition_value() ? successor : message_.to_block()}},
{SPV_OPERAND_TYPE_ID,
{message.break_condition_value() ? message.to_block() : successor}}});
{message_.break_condition_value() ? message_.to_block() : successor}}});
// Update OpPhi instructions in the target block if this break adds a
// previously non-existent edge from source to target.
@ -284,33 +289,25 @@ void Apply(const protobufs::TransformationAddDeadBreak& message,
if (inst.opcode() != SpvOpPhi) {
break;
}
assert(phi_index < static_cast<uint32_t>(message.phi_id().size()) &&
assert(phi_index < static_cast<uint32_t>(message_.phi_id().size()) &&
"There should be exactly one phi id per OpPhi instruction.");
inst.AddOperand({SPV_OPERAND_TYPE_ID, {message.phi_id()[phi_index]}});
inst.AddOperand({SPV_OPERAND_TYPE_ID, {message.from_block()}});
inst.AddOperand({SPV_OPERAND_TYPE_ID, {message_.phi_id()[phi_index]}});
inst.AddOperand({SPV_OPERAND_TYPE_ID, {message_.from_block()}});
phi_index++;
}
assert(phi_index == static_cast<uint32_t>(message.phi_id().size()) &&
assert(phi_index == static_cast<uint32_t>(message_.phi_id().size()) &&
"There should be exactly one phi id per OpPhi instruction.");
}
// Invalidate all analyses
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationAddDeadBreak MakeTransformationAddDeadBreak(
uint32_t from_block, uint32_t to_block, bool break_condition_value,
std::vector<uint32_t> phi_id) {
protobufs::TransformationAddDeadBreak result;
result.set_from_block(from_block);
result.set_to_block(to_block);
result.set_break_condition_value(break_condition_value);
for (auto id : phi_id) {
result.add_phi_id(id);
}
protobufs::Transformation TransformationAddDeadBreak::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_dead_break() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -19,42 +19,70 @@
#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 {
namespace transformation {
// - |message.from_block| must be the id of a block a in the given module.
// - |message.to_block| must be the id of a block b in the given module.
// - if |message.break_condition_value| holds (does not hold) then
// OpConstantTrue (OpConstantFalse) must be present in the module
// - |message.phi_ids| must be a list of ids that are all available at
// |message.from_block|
// - a and b must be in the same function.
// - b must be a merge block.
// - a must end with an unconditional branch to some block c.
// - replacing this branch with a conditional branch to b or c, with
// the boolean constant associated with |message.break_condition_value| as
// the condition, and the ids in |message.phi_ids| used to extend
// any OpPhi instructions at b as a result of the edge from a, must
// maintain validity of the module.
bool IsApplicable(const protobufs::TransformationAddDeadBreak& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationAddDeadBreak : public Transformation {
public:
explicit TransformationAddDeadBreak(
const protobufs::TransformationAddDeadBreak& message);
// Replaces the terminator of a with a conditional branch to b or c.
// The boolean constant associated with |message.break_condition_value| is used
// as the condition, and the order of b and c is arranged such that control is
// guaranteed to jump to c.
void Apply(const protobufs::TransformationAddDeadBreak& message,
opt::IRContext* context, FactManager* fact_manager);
TransformationAddDeadBreak(uint32_t from_block, uint32_t to_block,
bool break_condition_value,
std::vector<uint32_t> phi_id);
// Helper factory to create a transformation message.
protobufs::TransformationAddDeadBreak MakeTransformationAddDeadBreak(
uint32_t from_block, uint32_t to_block, bool break_condition_value,
std::vector<uint32_t> phi_ids);
// - |message.from_block| must be the id of a block a in the given module.
// - |message.to_block| must be the id of a block b in the given module.
// - if |message.break_condition_value| holds (does not hold) then
// OpConstantTrue (OpConstantFalse) must be present in the module
// - |message.phi_ids| must be a list of ids that are all available at
// |message.from_block|
// - a and b must be in the same function.
// - b must be a merge block.
// - a must end with an unconditional branch to some block c.
// - replacing this branch with a conditional branch to b or c, with
// the boolean constant associated with |message.break_condition_value| as
// the condition, and the ids in |message.phi_ids| used to extend
// any OpPhi instructions at b as a result of the edge from a, must
// maintain validity of the module.
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// Replaces the terminator of a with a conditional branch to b or c.
// The boolean constant associated with |message.break_condition_value| is
// used as the condition, and the order of b and c is arranged such that
// control is guaranteed to jump to c.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
// Return the block with id |maybe_block_id| if it exists, and nullptr
// otherwise.
opt::BasicBlock* MaybeFindBlock(opt::IRContext* context,
uint32_t maybe_block_id) const;
// Returns true if and only if the phi ids associated with |message_| are
// sufficient to allow an edge from |bb_from| to |bb_to| to be added.
bool PhiIdsOk(opt::IRContext* context, opt::BasicBlock* bb_from,
opt::BasicBlock* bb_to) const;
// Returns true if and only if |message_.from_block| is in the continue
// construct of a loop headed at |maybe_loop_header|.
bool FromBlockIsInLoopContinueConstruct(opt::IRContext* context,
uint32_t maybe_loop_header) const;
// Returns true if and only if adding an edge from |bb_from| to
// |message_.to_block| respects structured control flow.
bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* context,
opt::BasicBlock* bb_from) const;
protobufs::TransformationAddDeadBreak message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -18,15 +18,20 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::IRContext;
TransformationAddTypeBoolean::TransformationAddTypeBoolean(
const spvtools::fuzz::protobufs::TransformationAddTypeBoolean& message)
: message_(message) {}
bool IsApplicable(const protobufs::TransformationAddTypeBoolean& message,
IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) {
TransformationAddTypeBoolean::TransformationAddTypeBoolean(uint32_t fresh_id) {
message_.set_fresh_id(fresh_id);
}
bool TransformationAddTypeBoolean::IsApplicable(
opt::IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) const {
// The id must be fresh.
if (!fuzzerutil::IsFreshId(context, message.fresh_id())) {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
@ -35,24 +40,22 @@ bool IsApplicable(const protobufs::TransformationAddTypeBoolean& message,
return context->get_type_mgr()->GetId(&bool_type) == 0;
}
void Apply(const protobufs::TransformationAddTypeBoolean& message,
IRContext* context, spvtools::fuzz::FactManager* /*unused*/) {
void TransformationAddTypeBoolean::Apply(
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
opt::Instruction::OperandList empty_operands;
context->module()->AddType(MakeUnique<opt::Instruction>(
context, SpvOpTypeBool, 0, message.fresh_id(), empty_operands));
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands));
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationAddTypeBoolean MakeTransformationAddTypeBoolean(
uint32_t fresh_id) {
protobufs::TransformationAddTypeBoolean result;
result.set_fresh_id(fresh_id);
protobufs::Transformation TransformationAddTypeBoolean::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_type_boolean() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -17,26 +17,33 @@
#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 {
namespace transformation {
// - |message.fresh_id| must not be used by the module.
// - The module must not yet declare OpTypeBoolean
bool IsApplicable(const protobufs::TransformationAddTypeBoolean& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationAddTypeBoolean : public Transformation {
public:
explicit TransformationAddTypeBoolean(
const protobufs::TransformationAddTypeBoolean& message);
// Adds OpTypeBoolean with |message.fresh_id| as result id.
void Apply(const protobufs::TransformationAddTypeBoolean& message,
opt::IRContext* context, FactManager* fact_manager);
explicit TransformationAddTypeBoolean(uint32_t fresh_id);
// Helper factory to create a transformation message.
protobufs::TransformationAddTypeBoolean MakeTransformationAddTypeBoolean(
uint32_t fresh_id);
// - |message_.fresh_id| must not be used by the module.
// - The module must not yet declare OpTypeBoolean
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// Adds OpTypeBoolean with |message_.fresh_id| as result id.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddTypeBoolean message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -18,44 +18,48 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::IRContext;
TransformationAddTypeFloat::TransformationAddTypeFloat(uint32_t fresh_id,
uint32_t width) {
message_.set_fresh_id(fresh_id);
message_.set_width(width);
}
bool IsApplicable(const protobufs::TransformationAddTypeFloat& message,
IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) {
TransformationAddTypeFloat::TransformationAddTypeFloat(
const spvtools::fuzz::protobufs::TransformationAddTypeFloat& message)
: message_(message) {}
bool TransformationAddTypeFloat::IsApplicable(
opt::IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) const {
// The id must be fresh.
if (!fuzzerutil::IsFreshId(context, message.fresh_id())) {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
// Applicable if there is no float type with this width already declared in
// the module.
opt::analysis::Float float_type(message.width());
opt::analysis::Float float_type(message_.width());
return context->get_type_mgr()->GetId(&float_type) == 0;
}
void Apply(const protobufs::TransformationAddTypeFloat& message,
IRContext* context, spvtools::fuzz::FactManager* /*unused*/) {
void TransformationAddTypeFloat::Apply(
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
opt::Instruction::OperandList width = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message.width()}}};
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}}};
context->module()->AddType(MakeUnique<opt::Instruction>(
context, SpvOpTypeFloat, 0, message.fresh_id(), width));
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
context, SpvOpTypeFloat, 0, message_.fresh_id(), width));
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationAddTypeFloat MakeTransformationAddTypeFloat(
uint32_t fresh_id, uint32_t width) {
protobufs::TransformationAddTypeFloat result;
result.set_fresh_id(fresh_id);
result.set_width(width);
protobufs::Transformation TransformationAddTypeFloat::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_type_float() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -17,27 +17,34 @@
#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 {
namespace transformation {
// - |message.fresh_id| must not be used by the module
// - The module must not contain an OpTypeFloat instruction with width
// |message.width|
bool IsApplicable(const protobufs::TransformationAddTypeFloat& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationAddTypeFloat : public Transformation {
public:
explicit TransformationAddTypeFloat(
const protobufs::TransformationAddTypeFloat& message);
// Adds an OpTypeFloat instruction to the module with the given width
void Apply(const protobufs::TransformationAddTypeFloat& message,
opt::IRContext* context, FactManager* fact_manager);
TransformationAddTypeFloat(uint32_t fresh_id, uint32_t width);
// Helper factory to create a transformation message.
protobufs::TransformationAddTypeFloat MakeTransformationAddTypeFloat(
uint32_t fresh_id, uint32_t width);
// - |message_.fresh_id| must not be used by the module
// - The module must not contain an OpTypeFloat instruction with width
// |message_.width|
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// Adds an OpTypeFloat instruction to the module with the given width
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddTypeFloat message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -18,46 +18,51 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::IRContext;
TransformationAddTypeInt::TransformationAddTypeInt(
const spvtools::fuzz::protobufs::TransformationAddTypeInt& message)
: message_(message) {}
bool IsApplicable(const protobufs::TransformationAddTypeInt& message,
IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) {
TransformationAddTypeInt::TransformationAddTypeInt(uint32_t fresh_id,
uint32_t width,
bool is_signed) {
message_.set_fresh_id(fresh_id);
message_.set_width(width);
message_.set_is_signed(is_signed);
}
bool TransformationAddTypeInt::IsApplicable(
opt::IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) const {
// The id must be fresh.
if (!fuzzerutil::IsFreshId(context, message.fresh_id())) {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
// Applicable if there is no int type with this width and signedness already
// declared in the module.
opt::analysis::Integer int_type(message.width(), message.is_signed());
opt::analysis::Integer int_type(message_.width(), message_.is_signed());
return context->get_type_mgr()->GetId(&int_type) == 0;
}
void Apply(const protobufs::TransformationAddTypeInt& message,
IRContext* context, spvtools::fuzz::FactManager* /*unused*/) {
void TransformationAddTypeInt::Apply(
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
opt::Instruction::OperandList in_operands = {
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message.width()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message.is_signed() ? 1u : 0u}}};
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.width()}},
{SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.is_signed() ? 1u : 0u}}};
context->module()->AddType(MakeUnique<opt::Instruction>(
context, SpvOpTypeInt, 0, message.fresh_id(), in_operands));
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
context, SpvOpTypeInt, 0, message_.fresh_id(), in_operands));
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationAddTypeInt MakeTransformationAddTypeInt(
uint32_t fresh_id, uint32_t width, bool is_signed) {
protobufs::TransformationAddTypeInt result;
result.set_fresh_id(fresh_id);
result.set_width(width);
result.set_is_signed(is_signed);
protobufs::Transformation TransformationAddTypeInt::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_type_int() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -17,28 +17,35 @@
#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 {
namespace transformation {
// - |message.fresh_id| must not be used by the module
// - The module must not contain an OpTypeInt instruction with width
// |message.width| and signedness |message.is_signed|
bool IsApplicable(const protobufs::TransformationAddTypeInt& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationAddTypeInt : public Transformation {
public:
explicit TransformationAddTypeInt(
const protobufs::TransformationAddTypeInt& message);
// Adds an OpTypeInt instruction to the module with the given width and
// signedness.
void Apply(const protobufs::TransformationAddTypeInt& message,
opt::IRContext* context, FactManager* fact_manager);
TransformationAddTypeInt(uint32_t fresh_id, uint32_t width, bool is_signed);
// Helper factory to create a transformation message.
protobufs::TransformationAddTypeInt MakeTransformationAddTypeInt(
uint32_t fresh_id, uint32_t width, bool is_signed);
// - |message_.fresh_id| must not be used by the module
// - The module must not contain an OpTypeInt instruction with width
// |message_.width| and signedness |message.is_signed|
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// Adds an OpTypeInt instruction to the module with the given width and
// signedness.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddTypeInt message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -18,44 +18,48 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::IRContext;
TransformationAddTypePointer::TransformationAddTypePointer(
const spvtools::fuzz::protobufs::TransformationAddTypePointer& message)
: message_(message) {}
bool IsApplicable(const protobufs::TransformationAddTypePointer& message,
IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) {
TransformationAddTypePointer::TransformationAddTypePointer(
uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) {
message_.set_fresh_id(fresh_id);
message_.set_storage_class(storage_class);
message_.set_base_type_id(base_type_id);
}
bool TransformationAddTypePointer::IsApplicable(
opt::IRContext* context,
const spvtools::fuzz::FactManager& /*unused*/) const {
// The id must be fresh.
if (!fuzzerutil::IsFreshId(context, message.fresh_id())) {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
return false;
}
// The base type must be known.
return context->get_type_mgr()->GetType(message.base_type_id()) != nullptr;
return context->get_type_mgr()->GetType(message_.base_type_id()) != nullptr;
}
void Apply(const protobufs::TransformationAddTypePointer& message,
IRContext* context, spvtools::fuzz::FactManager* /*unused*/) {
void TransformationAddTypePointer::Apply(
opt::IRContext* context, spvtools::fuzz::FactManager* /*unused*/) const {
// Add the pointer type.
opt::Instruction::OperandList in_operands = {
{SPV_OPERAND_TYPE_STORAGE_CLASS, {message.storage_class()}},
{SPV_OPERAND_TYPE_ID, {message.base_type_id()}}};
{SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}},
{SPV_OPERAND_TYPE_ID, {message_.base_type_id()}}};
context->module()->AddType(MakeUnique<opt::Instruction>(
context, SpvOpTypePointer, 0, message.fresh_id(), in_operands));
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands));
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
// We have added an instruction to the module, so need to be careful about the
// validity of existing analyses.
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationAddTypePointer MakeTransformationAddTypePointer(
uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) {
protobufs::TransformationAddTypePointer result;
result.set_fresh_id(fresh_id);
result.set_storage_class(storage_class);
result.set_base_type_id(base_type_id);
protobufs::Transformation TransformationAddTypePointer::ToMessage() const {
protobufs::Transformation result;
*result.mutable_add_type_pointer() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -17,27 +17,36 @@
#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 {
namespace transformation {
// - |message.fresh_id| must not be used by the module
// - |message.base_type_id| must be the result id of an OpType[...] instruction
bool IsApplicable(const protobufs::TransformationAddTypePointer& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationAddTypePointer : public Transformation {
public:
explicit TransformationAddTypePointer(
const protobufs::TransformationAddTypePointer& message);
// Adds an OpTypePointer instruction with the given storage class and base type
// to the module.
void Apply(const protobufs::TransformationAddTypePointer& message,
opt::IRContext* context, FactManager* fact_manager);
TransformationAddTypePointer(uint32_t fresh_id, SpvStorageClass storage_class,
uint32_t base_type_id);
// Helper factory to create a transformation message.
protobufs::TransformationAddTypePointer MakeTransformationAddTypePointer(
uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id);
// - |message_.fresh_id| must not be used by the module
// - |message_.base_type_id| must be the result id of an OpType[...]
// instruction
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// Adds an OpTypePointer instruction with the given storage class and base
// type to the module.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationAddTypePointer message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -18,19 +18,23 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::BasicBlock;
using opt::IRContext;
TransformationMoveBlockDown::TransformationMoveBlockDown(
const spvtools::fuzz::protobufs::TransformationMoveBlockDown& message)
: message_(message) {}
bool IsApplicable(const protobufs::TransformationMoveBlockDown& message,
IRContext* context, const FactManager& /*unused*/) {
TransformationMoveBlockDown::TransformationMoveBlockDown(uint32_t id) {
message_.set_block_id(id);
}
bool TransformationMoveBlockDown::IsApplicable(
opt::IRContext* context, const FactManager& /*unused*/) const {
// Go through every block in every function, looking for a block whose id
// matches that of the block we want to consider moving down.
for (auto& function : *context->module()) {
for (auto block_it = function.begin(); block_it != function.end();
++block_it) {
if (block_it->id() == message.block_id()) {
if (block_it->id() == message_.block_id()) {
// We have found a match.
if (block_it == function.begin()) {
// The block is the first one appearing in the function. We are not
@ -38,7 +42,12 @@ bool IsApplicable(const protobufs::TransformationMoveBlockDown& message,
return false;
}
// Record the block we would like to consider moving down.
BasicBlock* block_matching_id = &*block_it;
opt::BasicBlock* block_matching_id = &*block_it;
if (!context->GetDominatorAnalysis(&function)->IsReachable(
block_matching_id)) {
// The block is not reachable. We are not allowed to move it down.
return false;
}
// Now see whether there is some block following that block in program
// order.
++block_it;
@ -48,7 +57,7 @@ bool IsApplicable(const protobufs::TransformationMoveBlockDown& message,
// apply.
return false;
}
BasicBlock* next_block_in_program_order = &*block_it;
opt::BasicBlock* next_block_in_program_order = &*block_it;
// We can move the block of interest down if and only if it does not
// dominate the block that comes next.
return !context->GetDominatorAnalysis(&function)->Dominates(
@ -62,26 +71,26 @@ bool IsApplicable(const protobufs::TransformationMoveBlockDown& message,
return false;
}
void Apply(const protobufs::TransformationMoveBlockDown& message,
IRContext* context, FactManager* /*unused*/) {
void TransformationMoveBlockDown::Apply(opt::IRContext* context,
FactManager* /*unused*/) const {
// Go through every block in every function, looking for a block whose id
// matches that of the block we want to move down.
for (auto& function : *context->module()) {
for (auto block_it = function.begin(); block_it != function.end();
++block_it) {
if (block_it->id() == message.block_id()) {
if (block_it->id() == message_.block_id()) {
++block_it;
assert(block_it != function.end() &&
"To be able to move a block down, it needs to have a "
"program-order successor.");
function.MoveBasicBlockToAfter(message.block_id(), &*block_it);
function.MoveBasicBlockToAfter(message_.block_id(), &*block_it);
// It is prudent to invalidate analyses after changing block ordering in
// case any of them depend on it, but the ones that definitely do not
// depend on ordering can be preserved. These include the following,
// which can likely be extended.
context->InvalidateAnalysesExceptFor(
IRContext::Analysis::kAnalysisDefUse |
IRContext::Analysis::kAnalysisDominatorAnalysis);
opt::IRContext::Analysis::kAnalysisDefUse |
opt::IRContext::Analysis::kAnalysisDominatorAnalysis);
return;
}
@ -90,13 +99,11 @@ void Apply(const protobufs::TransformationMoveBlockDown& message,
assert(false && "No block was found to move down.");
}
protobufs::TransformationMoveBlockDown MakeTransformationMoveBlockDown(
uint32_t id) {
protobufs::TransformationMoveBlockDown result;
result.set_block_id(id);
protobufs::Transformation TransformationMoveBlockDown::ToMessage() const {
protobufs::Transformation result;
*result.mutable_move_block_down() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -17,29 +17,37 @@
#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 {
namespace transformation {
// - |block_id| must be the id of a block b in the given module.
// - b must not be the first nor last block appearing, in program order,
// in a function.
// - b must not dominate the block that follows it in program order.
bool IsApplicable(const protobufs::TransformationMoveBlockDown& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationMoveBlockDown : public Transformation {
public:
explicit TransformationMoveBlockDown(
const protobufs::TransformationMoveBlockDown& message);
// The block with id |block_id| is moved down; i.e. the program order
// between it and the block that follows it is swapped.
void Apply(const protobufs::TransformationMoveBlockDown& message,
opt::IRContext* context, FactManager* fact_manager);
explicit TransformationMoveBlockDown(uint32_t id);
// Creates a protobuf message to move down the block with id |id|.
protobufs::TransformationMoveBlockDown MakeTransformationMoveBlockDown(
uint32_t id);
// - |message_.block_id| must be the id of a block b in the given module.
// - b must not be the first nor last block appearing, in program order,
// in a function.
// - b must not dominate the block that follows it in program order.
// - b must be reachable.
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// The block with id |message_.block_id| is moved down; i.e. the program order
// between it and the block that follows it is swapped.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationMoveBlockDown message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -21,7 +21,6 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
namespace {
@ -110,19 +109,35 @@ bool unsigned_int_binop_evaluates_to(T lhs, T rhs, SpvOp binop,
} // namespace
bool IsApplicable(
const protobufs::TransformationReplaceBooleanConstantWithConstantBinary&
message,
opt::IRContext* context, const FactManager& /*unused*/) {
TransformationReplaceBooleanConstantWithConstantBinary::
TransformationReplaceBooleanConstantWithConstantBinary(
const spvtools::fuzz::protobufs::
TransformationReplaceBooleanConstantWithConstantBinary& message)
: message_(message) {}
TransformationReplaceBooleanConstantWithConstantBinary::
TransformationReplaceBooleanConstantWithConstantBinary(
const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id,
uint32_t rhs_id, SpvOp comparison_opcode,
uint32_t fresh_id_for_binary_operation) {
*message_.mutable_id_use_descriptor() = id_use_descriptor;
message_.set_lhs_id(lhs_id);
message_.set_rhs_id(rhs_id);
message_.set_opcode(comparison_opcode);
message_.set_fresh_id_for_binary_operation(fresh_id_for_binary_operation);
}
bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable(
opt::IRContext* context, const FactManager& /*unused*/) const {
// The id for the binary result must be fresh
if (!fuzzerutil::IsFreshId(context,
message.fresh_id_for_binary_operation())) {
message_.fresh_id_for_binary_operation())) {
return false;
}
// The used id must be for a boolean constant
auto boolean_constant = context->get_def_use_mgr()->GetDef(
message.id_use_descriptor().id_of_interest());
message_.id_use_descriptor().id_of_interest());
if (!boolean_constant) {
return false;
}
@ -132,7 +147,8 @@ bool IsApplicable(
}
// The left-hand-side id must correspond to a constant instruction.
auto lhs_constant_inst = context->get_def_use_mgr()->GetDef(message.lhs_id());
auto lhs_constant_inst =
context->get_def_use_mgr()->GetDef(message_.lhs_id());
if (!lhs_constant_inst) {
return false;
}
@ -141,7 +157,8 @@ bool IsApplicable(
}
// The right-hand-side id must correspond to a constant instruction.
auto rhs_constant_inst = context->get_def_use_mgr()->GetDef(message.rhs_id());
auto rhs_constant_inst =
context->get_def_use_mgr()->GetDef(message_.rhs_id());
if (!rhs_constant_inst) {
return false;
}
@ -156,12 +173,12 @@ bool IsApplicable(
// The expression 'LHS opcode RHS' must evaluate to the boolean constant.
auto lhs_constant =
context->get_constant_mgr()->FindDeclaredConstant(message.lhs_id());
context->get_constant_mgr()->FindDeclaredConstant(message_.lhs_id());
auto rhs_constant =
context->get_constant_mgr()->FindDeclaredConstant(message.rhs_id());
context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id());
bool expected_result = (boolean_constant->opcode() == SpvOpConstantTrue);
const SpvOp binary_opcode = static_cast<SpvOp>(message.opcode());
const SpvOp binary_opcode = static_cast<SpvOp>(message_.opcode());
// We consider the floating point, signed and unsigned integer cases
// separately. In each case the logic is very similar.
@ -220,25 +237,29 @@ bool IsApplicable(
}
// The id use descriptor must identify some instruction
return transformation::FindInstruction(message.id_use_descriptor(),
return transformation::FindInstruction(message_.id_use_descriptor(),
context) != nullptr;
}
opt::Instruction* Apply(
const protobufs::TransformationReplaceBooleanConstantWithConstantBinary&
message,
opt::IRContext* context, FactManager* /*unused*/) {
void TransformationReplaceBooleanConstantWithConstantBinary::Apply(
opt::IRContext* context, FactManager* fact_manager) const {
ApplyWithResult(context, fact_manager);
}
opt::Instruction*
TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult(
opt::IRContext* context, FactManager* /*unused*/) const {
opt::analysis::Bool bool_type;
opt::Instruction::OperandList operands = {
{SPV_OPERAND_TYPE_ID, {message.lhs_id()}},
{SPV_OPERAND_TYPE_ID, {message.rhs_id()}}};
{SPV_OPERAND_TYPE_ID, {message_.lhs_id()}},
{SPV_OPERAND_TYPE_ID, {message_.rhs_id()}}};
auto binary_instruction = MakeUnique<opt::Instruction>(
context, static_cast<SpvOp>(message.opcode()),
context, static_cast<SpvOp>(message_.opcode()),
context->get_type_mgr()->GetId(&bool_type),
message.fresh_id_for_binary_operation(), operands);
message_.fresh_id_for_binary_operation(), operands);
opt::Instruction* result = binary_instruction.get();
auto instruction_containing_constant_use =
transformation::FindInstruction(message.id_use_descriptor(), context);
transformation::FindInstruction(message_.id_use_descriptor(), context);
// We want to insert the new instruction before the instruction that contains
// the use of the boolean, but we need to go backwards one more instruction if
@ -255,28 +276,20 @@ opt::Instruction* Apply(
instruction_before_which_to_insert->InsertBefore(
std::move(binary_instruction));
instruction_containing_constant_use->SetInOperand(
message.id_use_descriptor().in_operand_index(),
{message.fresh_id_for_binary_operation()});
message_.id_use_descriptor().in_operand_index(),
{message_.fresh_id_for_binary_operation()});
fuzzerutil::UpdateModuleIdBound(context,
message.fresh_id_for_binary_operation());
message_.fresh_id_for_binary_operation());
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
return result;
}
protobufs::TransformationReplaceBooleanConstantWithConstantBinary
MakeTransformationReplaceBooleanConstantWithConstantBinary(
const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id,
uint32_t rhs_id, SpvOp comparison_opcode,
uint32_t fresh_id_for_binary_operation) {
protobufs::TransformationReplaceBooleanConstantWithConstantBinary result;
*result.mutable_id_use_descriptor() = id_use_descriptor;
result.set_lhs_id(lhs_id);
result.set_rhs_id(rhs_id);
result.set_opcode(comparison_opcode);
result.set_fresh_id_for_binary_operation(fresh_id_for_binary_operation);
protobufs::Transformation
TransformationReplaceBooleanConstantWithConstantBinary::ToMessage() const {
protobufs::Transformation result;
*result.mutable_replace_boolean_constant_with_constant_binary() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -17,42 +17,53 @@
#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 {
namespace transformation {
// - |message.fresh_id_for_binary_operation| must not already be used by the
// module.
// - |message.id_use_descriptor| must identify a use of a boolean constant c.
// - |message.lhs_id| and |message.rhs_id| must be the ids of constant
// instructions with the same type
// - |message.opcode| must be suitable for applying to |message.lhs_id| and
// |message.rhs_id|, and the result must evaluate to the boolean constant c.
bool IsApplicable(
const protobufs::TransformationReplaceBooleanConstantWithConstantBinary&
message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationReplaceBooleanConstantWithConstantBinary
: public Transformation {
public:
explicit TransformationReplaceBooleanConstantWithConstantBinary(
const protobufs::TransformationReplaceBooleanConstantWithConstantBinary&
message);
// A new instruction is added before the boolean constant usage that computes
// the result of applying |message.opcode| to |message.lhs_id| and
// |message.rhs_id| is added, with result id
// |message.fresh_id_for_binary_operation|. The boolean constant usage is
// replaced with this result id.
opt::Instruction* Apply(
const protobufs::TransformationReplaceBooleanConstantWithConstantBinary&
message,
opt::IRContext* context, FactManager* fact_manager);
TransformationReplaceBooleanConstantWithConstantBinary(
const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id,
uint32_t rhs_id, SpvOp comparison_opcode,
uint32_t fresh_id_for_binary_operation);
// Helper factory to create a transformation message.
protobufs::TransformationReplaceBooleanConstantWithConstantBinary
MakeTransformationReplaceBooleanConstantWithConstantBinary(
const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id,
uint32_t rhs_id, SpvOp comparison_opcode,
uint32_t fresh_id_for_binary_operation);
// - |message_.fresh_id_for_binary_operation| must not already be used by the
// module.
// - |message_.id_use_descriptor| must identify a use of a boolean constant c.
// - |message_.lhs_id| and |message.rhs_id| must be the ids of constant
// instructions with the same type
// - |message_.opcode| must be suitable for applying to |message.lhs_id| and
// |message_.rhs_id|, and the result must evaluate to the boolean constant
// c.
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// A new instruction is added before the boolean constant usage that computes
// the result of applying |message_.opcode| to |message_.lhs_id| and
// |message_.rhs_id| is added, with result id
// |message_.fresh_id_for_binary_operation|. The boolean constant usage is
// replaced with this result id.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
// The same as Apply, except that the newly-added binary instruction is
// returned.
opt::Instruction* ApplyWithResult(opt::IRContext* context,
FactManager* fact_manager) const;
protobufs::Transformation ToMessage() const override;
private:
protobufs::TransformationReplaceBooleanConstantWithConstantBinary message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -19,18 +19,32 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
namespace {
TransformationReplaceConstantWithUniform::
TransformationReplaceConstantWithUniform(
const spvtools::fuzz::protobufs::
TransformationReplaceConstantWithUniform& message)
: message_(message) {}
std::unique_ptr<opt::Instruction> MakeAccessChainInstruction(
const protobufs::TransformationReplaceConstantWithUniform& message,
spvtools::opt::IRContext* context, uint32_t constant_type_id) {
TransformationReplaceConstantWithUniform::
TransformationReplaceConstantWithUniform(
protobufs::IdUseDescriptor id_use,
protobufs::UniformBufferElementDescriptor uniform_descriptor,
uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load) {
*message_.mutable_id_use_descriptor() = std::move(id_use);
*message_.mutable_uniform_descriptor() = std::move(uniform_descriptor);
message_.set_fresh_id_for_access_chain(fresh_id_for_access_chain);
message_.set_fresh_id_for_load(fresh_id_for_load);
}
std::unique_ptr<opt::Instruction>
TransformationReplaceConstantWithUniform::MakeAccessChainInstruction(
spvtools::opt::IRContext* context, uint32_t constant_type_id) const {
// The input operands for the access chain.
opt::Instruction::OperandList operands_for_access_chain;
opt::Instruction* uniform_variable =
FindUniformVariable(message.uniform_descriptor(), context, false);
FindUniformVariable(message_.uniform_descriptor(), context, false);
// The first input operand is the id of the uniform variable.
operands_for_access_chain.push_back(
@ -44,7 +58,7 @@ std::unique_ptr<opt::Instruction> MakeAccessChainInstruction(
auto registered_int_type =
context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
auto int_type_id = context->get_type_mgr()->GetId(&int_type);
for (auto index : message.uniform_descriptor().index()) {
for (auto index : message_.uniform_descriptor().index()) {
opt::analysis::IntConstant int_constant(registered_int_type, {index});
auto constant_id = context->get_constant_mgr()->FindDeclaredConstant(
&int_constant, int_type_id);
@ -62,43 +76,40 @@ std::unique_ptr<opt::Instruction> MakeAccessChainInstruction(
return MakeUnique<opt::Instruction>(
context, SpvOpAccessChain, pointer_to_uniform_constant_type_id,
message.fresh_id_for_access_chain(), operands_for_access_chain);
message_.fresh_id_for_access_chain(), operands_for_access_chain);
}
std::unique_ptr<opt::Instruction> MakeLoadInstruction(
const protobufs::TransformationReplaceConstantWithUniform& message,
spvtools::opt::IRContext* context, uint32_t constant_type_id) {
std::unique_ptr<opt::Instruction>
TransformationReplaceConstantWithUniform::MakeLoadInstruction(
spvtools::opt::IRContext* context, uint32_t constant_type_id) const {
opt::Instruction::OperandList operands_for_load = {
{SPV_OPERAND_TYPE_ID, {message.fresh_id_for_access_chain()}}};
{SPV_OPERAND_TYPE_ID, {message_.fresh_id_for_access_chain()}}};
return MakeUnique<opt::Instruction>(context, SpvOpLoad, constant_type_id,
message.fresh_id_for_load(),
message_.fresh_id_for_load(),
operands_for_load);
}
} // namespace
bool IsApplicable(
const protobufs::TransformationReplaceConstantWithUniform& message,
bool TransformationReplaceConstantWithUniform::IsApplicable(
spvtools::opt::IRContext* context,
const spvtools::fuzz::FactManager& fact_manager) {
const spvtools::fuzz::FactManager& fact_manager) const {
// The following is really an invariant of the transformation rather than
// merely a requirement of the precondition. We check it here since we cannot
// check it in the message constructor.
assert(message.fresh_id_for_access_chain() != message.fresh_id_for_load() &&
// check it in the message_ constructor.
assert(message_.fresh_id_for_access_chain() != message_.fresh_id_for_load() &&
"Fresh ids for access chain and load result cannot be the same.");
// The ids for the access chain and load instructions must both be fresh.
if (!fuzzerutil::IsFreshId(context, message.fresh_id_for_access_chain())) {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_access_chain())) {
return false;
}
if (!fuzzerutil::IsFreshId(context, message.fresh_id_for_load())) {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id_for_load())) {
return false;
}
// The id specified in the id use descriptor must be that of a declared scalar
// constant.
auto declared_constant = context->get_constant_mgr()->FindDeclaredConstant(
message.id_use_descriptor().id_of_interest());
message_.id_use_descriptor().id_of_interest());
if (!declared_constant) {
return false;
}
@ -110,7 +121,7 @@ bool IsApplicable(
// by the uniform buffer element descriptor will hold a scalar value.
auto constant_id_associated_with_uniform =
fact_manager.GetConstantFromUniformDescriptor(
context, message.uniform_descriptor());
context, message_.uniform_descriptor());
if (!constant_id_associated_with_uniform) {
return false;
}
@ -138,7 +149,7 @@ bool IsApplicable(
// The id use descriptor must identify some instruction with respect to the
// module.
auto instruction_using_constant =
transformation::FindInstruction(message.id_use_descriptor(), context);
transformation::FindInstruction(message_.id_use_descriptor(), context);
if (!instruction_using_constant) {
return false;
}
@ -161,7 +172,7 @@ bool IsApplicable(
auto registered_int_type =
context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger();
auto int_type_id = context->get_type_mgr()->GetId(&int_type);
for (auto index : message.uniform_descriptor().index()) {
for (auto index : message_.uniform_descriptor().index()) {
opt::analysis::IntConstant int_constant(registered_int_type, {index});
if (!context->get_constant_mgr()->FindDeclaredConstant(&int_constant,
int_type_id)) {
@ -172,59 +183,53 @@ bool IsApplicable(
return true;
}
void Apply(const protobufs::TransformationReplaceConstantWithUniform& message,
spvtools::opt::IRContext* context,
spvtools::fuzz::FactManager* /*unused*/) {
void TransformationReplaceConstantWithUniform::Apply(
spvtools::opt::IRContext* context,
spvtools::fuzz::FactManager* /*unused*/) const {
// Get the instruction that contains the id use we wish to replace.
auto instruction_containing_constant_use =
transformation::FindInstruction(message.id_use_descriptor(), context);
transformation::FindInstruction(message_.id_use_descriptor(), context);
assert(instruction_containing_constant_use &&
"Precondition requires that the id use can be found.");
assert(instruction_containing_constant_use->GetSingleWordInOperand(
message.id_use_descriptor().in_operand_index()) ==
message.id_use_descriptor().id_of_interest() &&
message_.id_use_descriptor().in_operand_index()) ==
message_.id_use_descriptor().id_of_interest() &&
"Does not appear to be a usage of the desired id.");
// The id of the type for the constant whose use we wish to replace.
auto constant_type_id =
context->get_def_use_mgr()
->GetDef(message.id_use_descriptor().id_of_interest())
->GetDef(message_.id_use_descriptor().id_of_interest())
->type_id();
// Add an access chain instruction to target the uniform element.
instruction_containing_constant_use->InsertBefore(
MakeAccessChainInstruction(message, context, constant_type_id));
MakeAccessChainInstruction(context, constant_type_id));
// Add a load from this access chain.
instruction_containing_constant_use->InsertBefore(
MakeLoadInstruction(message, context, constant_type_id));
MakeLoadInstruction(context, constant_type_id));
// Adjust the instruction containing the usage of the constant so that this
// usage refers instead to the result of the load.
instruction_containing_constant_use->SetInOperand(
message.id_use_descriptor().in_operand_index(),
{message.fresh_id_for_load()});
message_.id_use_descriptor().in_operand_index(),
{message_.fresh_id_for_load()});
// Update the module id bound to reflect the new instructions.
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id_for_load());
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id_for_access_chain());
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id_for_load());
fuzzerutil::UpdateModuleIdBound(context,
message_.fresh_id_for_access_chain());
context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
}
protobufs::TransformationReplaceConstantWithUniform
MakeTransformationReplaceConstantWithUniform(
protobufs::IdUseDescriptor id_use,
protobufs::UniformBufferElementDescriptor uniform_descriptor,
uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load) {
protobufs::TransformationReplaceConstantWithUniform result;
*result.mutable_id_use_descriptor() = std::move(id_use);
*result.mutable_uniform_descriptor() = std::move(uniform_descriptor);
result.set_fresh_id_for_access_chain(fresh_id_for_access_chain);
result.set_fresh_id_for_load(fresh_id_for_load);
protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage()
const {
protobufs::Transformation result;
*result.mutable_replace_constant_with_uniform() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -20,53 +20,71 @@
#include "source/fuzz/fact_manager.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
#include "source/fuzz/transformation.h"
#include "source/opt/ir_context.h"
namespace spvtools {
namespace fuzz {
namespace transformation {
// - |fresh_id_for_access_chain| and |fresh_id_for_load| must be distinct fresh
// ids.
// - |uniform_descriptor| specifies a result id and a list of integer literal
// indices.
// As an example, suppose |uniform_descriptor| is (18, [0, 1, 0])
// It is required that:
// - the result id (18 in our example) is the id of some uniform variable
// - the module contains an integer constant instruction corresponding to
// each of the literal indices; in our example there must thus be
// OpConstant instructions %A and %B say for each of 0 and 1
// - it is legitimate to index into the uniform variable using the
// sequence of indices; in our example this means indexing into %18 using
// the sequence %A %B %A
// - the module contains a uniform pointer type corresponding to the type
// of the uniform data element obtained by following these indices
// - |id_use_descriptor| identifies the use of some id %C. It is required that:
// - this use does indeed exist in the module
// - %C is an OpConstant
// - According to the fact manager, the uniform data element specified by
// |uniform_descriptor| holds a value with the same type and value as %C
bool IsApplicable(
const protobufs::TransformationReplaceConstantWithUniform& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationReplaceConstantWithUniform : public Transformation {
public:
explicit TransformationReplaceConstantWithUniform(
const protobufs::TransformationReplaceConstantWithUniform& message);
// - Introduces two new instructions:
// - An access chain targeting the uniform data element specified by
// |uniform_descriptor|, with result id |fresh_id_for_access_chain|
// - A load from this access chain, with id |fresh_id_for_load|
// - Replaces the id use specified by |id_use_descriptor| with
// |fresh_id_for_load|
void Apply(const protobufs::TransformationReplaceConstantWithUniform& message,
opt::IRContext* context, FactManager* fact_manager);
TransformationReplaceConstantWithUniform(
protobufs::IdUseDescriptor id_use,
protobufs::UniformBufferElementDescriptor uniform_descriptor,
uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load);
// Helper factory to create a transformation message.
protobufs::TransformationReplaceConstantWithUniform
MakeTransformationReplaceConstantWithUniform(
protobufs::IdUseDescriptor id_use,
protobufs::UniformBufferElementDescriptor uniform_descriptor,
uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load);
// - |message_.fresh_id_for_access_chain| and |message_.fresh_id_for_load|
// must be distinct fresh ids.
// - |message_.uniform_descriptor| specifies a result id and a list of integer
// literal indices.
// As an example, suppose |message_.uniform_descriptor| is (18, [0, 1, 0])
// It is required that:
// - the result id (18 in our example) is the id of some uniform variable
// - the module contains an integer constant instruction corresponding to
// each of the literal indices; in our example there must thus be
// OpConstant instructions %A and %B say for each of 0 and 1
// - it is legitimate to index into the uniform variable using the
// sequence of indices; in our example this means indexing into %18
// using the sequence %A %B %A
// - the module contains a uniform pointer type corresponding to the type
// of the uniform data element obtained by following these indices
// - |message_.id_use_descriptor| identifies the use of some id %C. It is
// required that:
// - this use does indeed exist in the module
// - %C is an OpConstant
// - According to the fact manager, the uniform data element specified by
// |message_.uniform_descriptor| holds a value with the same type and
// value as %C
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// - Introduces two new instructions:
// - An access chain targeting the uniform data element specified by
// |message_.uniform_descriptor|, with result id
// |message_.fresh_id_for_access_chain|
// - A load from this access chain, with id |message_.fresh_id_for_load|
// - Replaces the id use specified by |message_.id_use_descriptor| with
// |message_.fresh_id_for_load|
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
// Helper method to create an access chain for the uniform element associated
// with the transformation.
std::unique_ptr<opt::Instruction> MakeAccessChainInstruction(
spvtools::opt::IRContext* context, uint32_t constant_type_id) const;
// Helper to create a load instruction.
std::unique_ptr<opt::Instruction> MakeLoadInstruction(
spvtools::opt::IRContext* context, uint32_t constant_type_id) const;
protobufs::TransformationReplaceConstantWithUniform message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -21,32 +21,31 @@
namespace spvtools {
namespace fuzz {
namespace transformation {
using opt::BasicBlock;
using opt::IRContext;
using opt::Instruction;
using opt::Operand;
TransformationSplitBlock::TransformationSplitBlock(
const spvtools::fuzz::protobufs::TransformationSplitBlock& message)
: message_(message) {}
namespace {
TransformationSplitBlock::TransformationSplitBlock(uint32_t result_id,
uint32_t offset,
uint32_t fresh_id) {
message_.set_result_id(result_id);
message_.set_offset(offset);
message_.set_fresh_id(fresh_id);
}
// Returns:
// - (true, block->end()) if the relevant instruction is in this block
// but inapplicable
// - (true, it) if 'it' is an iterator for the relevant instruction
// - (false, _) otherwise.
std::pair<bool, BasicBlock::iterator> FindInstToSplitBefore(
const protobufs::TransformationSplitBlock& message, BasicBlock* block) {
std::pair<bool, opt::BasicBlock::iterator>
TransformationSplitBlock::FindInstToSplitBefore(opt::BasicBlock* block) const {
// There are three possibilities:
// (1) the transformation wants to split at some offset from the block's
// label.
// (2) the transformation wants to split at some offset from a
// non-label instruction inside the block.
// (3) the split associated with this transformation has nothing to do with
// (3) the split assocaiated with this transformation has nothing to do with
// this block
if (message.result_id() == block->id()) {
if (message_.result_id() == block->id()) {
// Case (1).
if (message.offset() == 0) {
if (message_.offset() == 0) {
// The offset is not allowed to be 0: this would mean splitting before the
// block's label.
// By returning (true, block->end()), we indicate that we did find the
@ -57,16 +56,17 @@ std::pair<bool, BasicBlock::iterator> FindInstToSplitBefore(
// Conceptually, the first instruction in the block is [label + 1].
// We thus start from 1 when applying the offset.
auto inst_it = block->begin();
for (uint32_t i = 1; i < message.offset() && inst_it != block->end(); i++) {
for (uint32_t i = 1; i < message_.offset() && inst_it != block->end();
i++) {
++inst_it;
}
// This is either the desired instruction, or the end of the block.
return {true, inst_it};
}
for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) {
if (message.result_id() == inst_it->result_id()) {
if (message_.result_id() == inst_it->result_id()) {
// Case (2): we have found the base instruction; we now apply the offset.
for (uint32_t i = 0; i < message.offset() && inst_it != block->end();
for (uint32_t i = 0; i < message_.offset() && inst_it != block->end();
i++) {
++inst_it;
}
@ -78,18 +78,16 @@ std::pair<bool, BasicBlock::iterator> FindInstToSplitBefore(
return {false, block->end()};
}
} // namespace
bool IsApplicable(const protobufs::TransformationSplitBlock& message,
IRContext* context, const FactManager& /*unused*/) {
if (!fuzzerutil::IsFreshId(context, message.fresh_id())) {
bool TransformationSplitBlock::IsApplicable(
opt::IRContext* context, const FactManager& /*unused*/) const {
if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
// We require the id for the new block to be unused.
return false;
}
// Consider every block in every function.
for (auto& function : *context->module()) {
for (auto& block : function) {
auto maybe_split_before = FindInstToSplitBefore(message, &block);
auto maybe_split_before = FindInstToSplitBefore(&block);
if (!maybe_split_before.first) {
continue;
}
@ -125,11 +123,11 @@ bool IsApplicable(const protobufs::TransformationSplitBlock& message,
return false;
}
void Apply(const protobufs::TransformationSplitBlock& message,
IRContext* context, FactManager* /*unused*/) {
void TransformationSplitBlock::Apply(opt::IRContext* context,
FactManager* /*unused*/) const {
for (auto& function : *context->module()) {
for (auto& block : function) {
auto maybe_split_before = FindInstToSplitBefore(message, &block);
auto maybe_split_before = FindInstToSplitBefore(&block);
if (!maybe_split_before.first) {
continue;
}
@ -138,20 +136,21 @@ void Apply(const protobufs::TransformationSplitBlock& message,
"instruction to split on.");
// We need to make sure the module's id bound is large enough to add the
// fresh id.
fuzzerutil::UpdateModuleIdBound(context, message.fresh_id());
fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
// Split the block.
auto new_bb = block.SplitBasicBlock(context, message.fresh_id(),
auto new_bb = block.SplitBasicBlock(context, message_.fresh_id(),
maybe_split_before.second);
// The split does not automatically add a branch between the two parts of
// the original block, so we add one.
block.AddInstruction(MakeUnique<Instruction>(
block.AddInstruction(MakeUnique<opt::Instruction>(
context, SpvOpBranch, 0, 0,
std::initializer_list<Operand>{Operand(
spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message.fresh_id()})}));
std::initializer_list<opt::Operand>{
opt::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
{message_.fresh_id()})}));
// If we split before OpPhi instructions, we need to update their
// predecessor operand so that the block they used to be inside is now the
// predecessor.
new_bb->ForEachPhiInst([&block](Instruction* phi_inst) {
new_bb->ForEachPhiInst([&block](opt::Instruction* phi_inst) {
// The following assertion is a sanity check. It is guaranteed to hold
// if IsApplicable holds.
assert(phi_inst->NumInOperands() == 2 &&
@ -160,7 +159,8 @@ void Apply(const protobufs::TransformationSplitBlock& message,
phi_inst->SetInOperand(1, {block.id()});
});
// Invalidate all analyses
context->InvalidateAnalysesExceptFor(IRContext::Analysis::kAnalysisNone);
context->InvalidateAnalysesExceptFor(
opt::IRContext::Analysis::kAnalysisNone);
return;
}
}
@ -169,15 +169,11 @@ void Apply(const protobufs::TransformationSplitBlock& message,
"transformation.");
}
protobufs::TransformationSplitBlock MakeTransformationSplitBlock(
uint32_t result_id, uint32_t offset, uint32_t fresh_id) {
protobufs::TransformationSplitBlock result;
result.set_result_id(result_id);
result.set_offset(offset);
result.set_fresh_id(fresh_id);
protobufs::Transformation TransformationSplitBlock::ToMessage() const {
protobufs::Transformation result;
*result.mutable_split_block() = message_;
return result;
}
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -17,35 +17,52 @@
#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 {
namespace transformation {
// - |result_id| must be the result id of an instruction 'base' in some
// block 'blk'.
// - 'blk' must contain an instruction 'inst' located |offset| instructions
// after 'inst' (if |offset| = 0 then 'inst' = 'base').
// - Splitting 'blk' at 'inst', so that all instructions from 'inst' onwards
// appear in a new block that 'blk' directly jumps to must be valid.
// - |fresh_id| must not be used by the module.
bool IsApplicable(const protobufs::TransformationSplitBlock& message,
opt::IRContext* context, const FactManager& fact_manager);
class TransformationSplitBlock : public Transformation {
public:
explicit TransformationSplitBlock(
const protobufs::TransformationSplitBlock& message);
// - A new block with label |fresh_id| is inserted right after 'blk' in
// program order.
// - All instructions of 'blk' from 'inst' onwards are moved into the new
// block.
// - 'blk' is made to jump unconditionally to the new block.
void Apply(const protobufs::TransformationSplitBlock& message,
opt::IRContext* context, FactManager* fact_manager);
TransformationSplitBlock(uint32_t result_id, uint32_t offset,
uint32_t fresh_id);
// Creates a protobuf message representing a block-splitting transformation.
protobufs::TransformationSplitBlock MakeTransformationSplitBlock(
uint32_t result_id, uint32_t offset, uint32_t fresh_id);
// - |message_.result_id| must be the result id of an instruction 'base' in
// some block 'blk'.
// - 'blk' must contain an instruction 'inst' located |message_.offset|
// instructions after 'inst' (if |message_.offset| = 0 then 'inst' =
// 'base').
// - Splitting 'blk' at 'inst', so that all instructions from 'inst' onwards
// appear in a new block that 'blk' directly jumps to must be valid.
// - |message_.fresh_id| must not be used by the module.
bool IsApplicable(opt::IRContext* context,
const FactManager& fact_manager) const override;
// - A new block with label |message_.fresh_id| is inserted right after 'blk'
// in program order.
// - All instructions of 'blk' from 'inst' onwards are moved into the new
// block.
// - 'blk' is made to jump unconditionally to the new block.
void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
protobufs::Transformation ToMessage() const override;
private:
// Returns:
// - (true, block->end()) if the relevant instruction is in this block
// but inapplicable
// - (true, it) if 'it' is an iterator for the relevant instruction
// - (false, _) otherwise.
std::pair<bool, opt::BasicBlock::iterator> FindInstToSplitBefore(
opt::BasicBlock* block) const;
protobufs::TransformationSplitBlock message_;
};
} // namespace transformation
} // namespace fuzz
} // namespace spvtools

View File

@ -678,12 +678,18 @@ uint32_t IRContext::GetBuiltinInputVarId(uint32_t builtin) {
case SpvBuiltInVertexIndex:
case SpvBuiltInInstanceIndex:
case SpvBuiltInPrimitiveId:
case SpvBuiltInInvocationId:
case SpvBuiltInGlobalInvocationId: {
case SpvBuiltInInvocationId: {
analysis::Integer uint_ty(32, false);
reg_type = type_mgr->GetRegisteredType(&uint_ty);
break;
}
case SpvBuiltInGlobalInvocationId: {
analysis::Integer uint_ty(32, false);
analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty);
analysis::Vector v3uint_ty(reg_uint_ty, 3);
reg_type = type_mgr->GetRegisteredType(&v3uint_ty);
break;
}
default: {
assert(false && "unhandled builtin");
return 0;

View File

@ -234,6 +234,36 @@ bool spvIsWebGPUEnv(spv_target_env env) {
return false;
}
bool spvIsOpenGLEnv(spv_target_env env) {
switch (env) {
case SPV_ENV_UNIVERSAL_1_0:
case SPV_ENV_VULKAN_1_0:
case SPV_ENV_UNIVERSAL_1_1:
case SPV_ENV_UNIVERSAL_1_2:
case SPV_ENV_UNIVERSAL_1_3:
case SPV_ENV_VULKAN_1_1:
case SPV_ENV_OPENCL_1_2:
case SPV_ENV_OPENCL_EMBEDDED_1_2:
case SPV_ENV_OPENCL_2_0:
case SPV_ENV_OPENCL_EMBEDDED_2_0:
case SPV_ENV_OPENCL_EMBEDDED_2_1:
case SPV_ENV_OPENCL_EMBEDDED_2_2:
case SPV_ENV_OPENCL_2_1:
case SPV_ENV_OPENCL_2_2:
case SPV_ENV_WEBGPU_0:
case SPV_ENV_UNIVERSAL_1_4:
case SPV_ENV_VULKAN_1_1_SPIRV_1_4:
return false;
case SPV_ENV_OPENGL_4_0:
case SPV_ENV_OPENGL_4_1:
case SPV_ENV_OPENGL_4_2:
case SPV_ENV_OPENGL_4_3:
case SPV_ENV_OPENGL_4_5:
return true;
}
return false;
}
bool spvIsVulkanOrWebGPUEnv(spv_target_env env) {
return spvIsVulkanEnv(env) || spvIsWebGPUEnv(env);
}

View File

@ -28,6 +28,9 @@ bool spvIsOpenCLEnv(spv_target_env env);
// Returns true if |env| is an WEBGPU environment, false otherwise.
bool spvIsWebGPUEnv(spv_target_env env);
// Returns true if |env| is an OPENGL environment, false otherwise.
bool spvIsOpenGLEnv(spv_target_env env);
// Returns true if |env| is a VULKAN or WEBGPU environment, false otherwise.
bool spvIsVulkanOrWebGPUEnv(spv_target_env env);

View File

@ -910,6 +910,25 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
}
}
if (spvIsOpenGLEnv(vstate.context()->target_env)) {
bool has_block = hasDecoration(var_id, SpvDecorationBlock, vstate);
bool has_buffer_block =
hasDecoration(var_id, SpvDecorationBufferBlock, vstate);
if ((uniform && (has_block || has_buffer_block)) ||
(storage_buffer && has_block)) {
auto entry_points = vstate.EntryPointReferences(var_id);
if (!entry_points.empty() &&
!hasDecoration(var_id, SpvDecorationBinding, vstate)) {
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id))
<< (uniform ? "Uniform" : "Storage Buffer") << " id '"
<< var_id << "' is missing Binding decoration.\n"
<< "From ARB_gl_spirv extension:\n"
<< "Uniform and shader storage block variables must "
<< "also be decorated with a *Binding*.";
}
}
}
const bool phys_storage_buffer =
storageClass == SpvStorageClassPhysicalStorageBufferEXT;
if (uniform || push_constant || storage_buffer || phys_storage_buffer) {
@ -1440,6 +1459,11 @@ spv_result_t CheckComponentDecoration(ValidationState_t& vstate,
}
if (spvIsVulkanEnv(vstate.context()->target_env)) {
// Strip the array, if present.
if (vstate.GetIdOpcode(type_id) == SpvOpTypeArray) {
type_id = vstate.FindDef(type_id)->word(2u);
}
if (!vstate.IsIntScalarOrVectorType(type_id) &&
!vstate.IsFloatScalarOrVectorType(type_id)) {
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)

View File

@ -45,52 +45,40 @@ TEST(TransformationAddConstantBooleanTest, NeitherPresentInitiallyAddBoth) {
FactManager fact_manager;
// True and false can both be added as neither is present.
ASSERT_TRUE(transformation::IsApplicable(
transformation::MakeTransformationAddConstantBoolean(7, true),
ASSERT_TRUE(TransformationAddConstantBoolean(7, true).IsApplicable(
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(
transformation::MakeTransformationAddConstantBoolean(7, false),
ASSERT_TRUE(TransformationAddConstantBoolean(7, false).IsApplicable(
context.get(), fact_manager));
// Id 5 is already taken.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationAddConstantBoolean(5, true),
ASSERT_FALSE(TransformationAddConstantBoolean(5, true).IsApplicable(
context.get(), fact_manager));
auto add_true = transformation::MakeTransformationAddConstantBoolean(7, true);
auto add_false =
transformation::MakeTransformationAddConstantBoolean(8, false);
auto add_true = TransformationAddConstantBoolean(7, true);
auto add_false = TransformationAddConstantBoolean(8, false);
ASSERT_TRUE(
transformation::IsApplicable(add_true, context.get(), fact_manager));
transformation::Apply(add_true, context.get(), &fact_manager);
ASSERT_TRUE(add_true.IsApplicable(context.get(), fact_manager));
add_true.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Having added true, we cannot add it again with the same id.
ASSERT_FALSE(
transformation::IsApplicable(add_true, context.get(), fact_manager));
ASSERT_FALSE(add_true.IsApplicable(context.get(), fact_manager));
// But we can add it with a different id.
auto add_true_again =
transformation::MakeTransformationAddConstantBoolean(100, true);
ASSERT_TRUE(transformation::IsApplicable(add_true_again, context.get(),
fact_manager));
transformation::Apply(add_true_again, context.get(), &fact_manager);
auto add_true_again = TransformationAddConstantBoolean(100, true);
ASSERT_TRUE(add_true_again.IsApplicable(context.get(), fact_manager));
add_true_again.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(
transformation::IsApplicable(add_false, context.get(), fact_manager));
transformation::Apply(add_false, context.get(), &fact_manager);
ASSERT_TRUE(add_false.IsApplicable(context.get(), fact_manager));
add_false.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Having added false, we cannot add it again with the same id.
ASSERT_FALSE(
transformation::IsApplicable(add_false, context.get(), fact_manager));
ASSERT_FALSE(add_false.IsApplicable(context.get(), fact_manager));
// But we can add it with a different id.
auto add_false_again =
transformation::MakeTransformationAddConstantBoolean(101, false);
ASSERT_TRUE(transformation::IsApplicable(add_false_again, context.get(),
fact_manager));
transformation::Apply(add_false_again, context.get(), &fact_manager);
auto add_false_again = TransformationAddConstantBoolean(101, false);
ASSERT_TRUE(add_false_again.IsApplicable(context.get(), fact_manager));
add_false_again.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_transformation = R"(
@ -142,11 +130,9 @@ TEST(TransformationAddConstantBooleanTest, NoOpTypeBoolPresent) {
FactManager fact_manager;
// Neither true nor false can be added as OpTypeBool is not present.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationAddConstantBoolean(6, true),
ASSERT_FALSE(TransformationAddConstantBoolean(6, true).IsApplicable(
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationAddConstantBoolean(6, false),
ASSERT_FALSE(TransformationAddConstantBoolean(6, false).IsApplicable(
context.get(), fact_manager));
}

View File

@ -67,97 +67,75 @@ TEST(TransformationAddConstantScalarTest, BasicTest) {
uint32_t uint_for_float[2];
memcpy(uint_for_float, float_values, sizeof(float_values));
auto add_signed_int_1 =
transformation::MakeTransformationAddConstantScalar(100, 6, {1});
auto add_signed_int_10 =
transformation::MakeTransformationAddConstantScalar(101, 6, {10});
auto add_unsigned_int_2 =
transformation::MakeTransformationAddConstantScalar(102, 10, {2});
auto add_unsigned_int_20 =
transformation::MakeTransformationAddConstantScalar(103, 10, {20});
auto add_float_3 = transformation::MakeTransformationAddConstantScalar(
104, 14, {uint_for_float[0]});
auto add_float_30 = transformation::MakeTransformationAddConstantScalar(
105, 14, {uint_for_float[1]});
auto add_signed_int_1 = TransformationAddConstantScalar(100, 6, {1});
auto add_signed_int_10 = TransformationAddConstantScalar(101, 6, {10});
auto add_unsigned_int_2 = TransformationAddConstantScalar(102, 10, {2});
auto add_unsigned_int_20 = TransformationAddConstantScalar(103, 10, {20});
auto add_float_3 =
TransformationAddConstantScalar(104, 14, {uint_for_float[0]});
auto add_float_30 =
TransformationAddConstantScalar(105, 14, {uint_for_float[1]});
auto bad_add_float_30_id_already_used =
transformation::MakeTransformationAddConstantScalar(104, 14,
{uint_for_float[1]});
auto bad_id_already_used =
transformation::MakeTransformationAddConstantScalar(1, 6, {1});
auto bad_no_data =
transformation::MakeTransformationAddConstantScalar(100, 6, {});
auto bad_too_much_data =
transformation::MakeTransformationAddConstantScalar(100, 6, {1, 2});
TransformationAddConstantScalar(104, 14, {uint_for_float[1]});
auto bad_id_already_used = TransformationAddConstantScalar(1, 6, {1});
auto bad_no_data = TransformationAddConstantScalar(100, 6, {});
auto bad_too_much_data = TransformationAddConstantScalar(100, 6, {1, 2});
auto bad_type_id_does_not_exist =
transformation::MakeTransformationAddConstantScalar(108, 2020,
{uint_for_float[0]});
auto bad_type_id_is_not_a_type =
transformation::MakeTransformationAddConstantScalar(109, 9, {0});
auto bad_type_id_is_void =
transformation::MakeTransformationAddConstantScalar(110, 2, {0});
auto bad_type_id_is_pointer =
transformation::MakeTransformationAddConstantScalar(111, 11, {0});
TransformationAddConstantScalar(108, 2020, {uint_for_float[0]});
auto bad_type_id_is_not_a_type = TransformationAddConstantScalar(109, 9, {0});
auto bad_type_id_is_void = TransformationAddConstantScalar(110, 2, {0});
auto bad_type_id_is_pointer = TransformationAddConstantScalar(111, 11, {0});
// Id is already in use.
ASSERT_FALSE(transformation::IsApplicable(bad_id_already_used, context.get(),
fact_manager));
ASSERT_FALSE(bad_id_already_used.IsApplicable(context.get(), fact_manager));
// At least one word of data must be provided.
ASSERT_FALSE(
transformation::IsApplicable(bad_no_data, context.get(), fact_manager));
ASSERT_FALSE(bad_no_data.IsApplicable(context.get(), fact_manager));
// Cannot give two data words for a 32-bit type.
ASSERT_FALSE(transformation::IsApplicable(bad_too_much_data, context.get(),
fact_manager));
ASSERT_FALSE(bad_too_much_data.IsApplicable(context.get(), fact_manager));
// Type id does not exist
ASSERT_FALSE(transformation::IsApplicable(bad_type_id_does_not_exist,
context.get(), fact_manager));
ASSERT_FALSE(
bad_type_id_does_not_exist.IsApplicable(context.get(), fact_manager));
// Type id is not a type
ASSERT_FALSE(transformation::IsApplicable(bad_type_id_is_not_a_type,
context.get(), fact_manager));
ASSERT_FALSE(
bad_type_id_is_not_a_type.IsApplicable(context.get(), fact_manager));
// Type id is void
ASSERT_FALSE(transformation::IsApplicable(bad_type_id_is_void, context.get(),
fact_manager));
ASSERT_FALSE(bad_type_id_is_void.IsApplicable(context.get(), fact_manager));
// Type id is pointer
ASSERT_FALSE(transformation::IsApplicable(bad_type_id_is_pointer,
context.get(), fact_manager));
ASSERT_FALSE(
bad_type_id_is_pointer.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(add_signed_int_1, context.get(),
fact_manager));
transformation::Apply(add_signed_int_1, context.get(), &fact_manager);
ASSERT_TRUE(add_signed_int_1.IsApplicable(context.get(), fact_manager));
add_signed_int_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(transformation::IsApplicable(add_signed_int_10, context.get(),
fact_manager));
transformation::Apply(add_signed_int_10, context.get(), &fact_manager);
ASSERT_TRUE(add_signed_int_10.IsApplicable(context.get(), fact_manager));
add_signed_int_10.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(transformation::IsApplicable(add_unsigned_int_2, context.get(),
fact_manager));
transformation::Apply(add_unsigned_int_2, context.get(), &fact_manager);
ASSERT_TRUE(add_unsigned_int_2.IsApplicable(context.get(), fact_manager));
add_unsigned_int_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(transformation::IsApplicable(add_unsigned_int_20, context.get(),
fact_manager));
transformation::Apply(add_unsigned_int_20, context.get(), &fact_manager);
ASSERT_TRUE(add_unsigned_int_20.IsApplicable(context.get(), fact_manager));
add_unsigned_int_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(
transformation::IsApplicable(add_float_3, context.get(), fact_manager));
transformation::Apply(add_float_3, context.get(), &fact_manager);
ASSERT_TRUE(add_float_3.IsApplicable(context.get(), fact_manager));
add_float_3.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(
transformation::IsApplicable(add_float_30, context.get(), fact_manager));
transformation::Apply(add_float_30, context.get(), &fact_manager);
ASSERT_TRUE(add_float_30.IsApplicable(context.get(), fact_manager));
add_float_30.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_FALSE(transformation::IsApplicable(bad_add_float_30_id_already_used,
context.get(), fact_manager));
ASSERT_FALSE(bad_add_float_30_id_already_used.IsApplicable(context.get(),
fact_manager));
std::string after_transformation = R"(
OpCapability Shader

File diff suppressed because it is too large Load Diff

View File

@ -44,20 +44,17 @@ TEST(TransformationAddTypeBooleanTest, BasicTest) {
FactManager fact_manager;
// Not applicable because id 1 is already in use.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationAddTypeBoolean(1), context.get(),
fact_manager));
ASSERT_FALSE(TransformationAddTypeBoolean(1).IsApplicable(context.get(),
fact_manager));
auto add_type_bool = transformation::MakeTransformationAddTypeBoolean(100);
ASSERT_TRUE(
transformation::IsApplicable(add_type_bool, context.get(), fact_manager));
transformation::Apply(add_type_bool, context.get(), &fact_manager);
auto add_type_bool = TransformationAddTypeBoolean(100);
ASSERT_TRUE(add_type_bool.IsApplicable(context.get(), fact_manager));
add_type_bool.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Not applicable as we already have this type now.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationAddTypeBoolean(101), context.get(),
fact_manager));
ASSERT_FALSE(TransformationAddTypeBoolean(101).IsApplicable(context.get(),
fact_manager));
std::string after_transformation = R"(
OpCapability Shader

View File

@ -44,21 +44,17 @@ TEST(TransformationAddTypeFloatTest, BasicTest) {
FactManager fact_manager;
// Not applicable because id 1 is already in use.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationAddTypeFloat(1, 32), context.get(),
fact_manager));
ASSERT_FALSE(TransformationAddTypeFloat(1, 32).IsApplicable(context.get(),
fact_manager));
auto add_type_float_32 =
transformation::MakeTransformationAddTypeFloat(100, 32);
ASSERT_TRUE(transformation::IsApplicable(add_type_float_32, context.get(),
fact_manager));
transformation::Apply(add_type_float_32, context.get(), &fact_manager);
auto add_type_float_32 = TransformationAddTypeFloat(100, 32);
ASSERT_TRUE(add_type_float_32.IsApplicable(context.get(), fact_manager));
add_type_float_32.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Not applicable as we already have this type now.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationAddTypeFloat(101, 32), context.get(),
fact_manager));
ASSERT_FALSE(TransformationAddTypeFloat(101, 32).IsApplicable(context.get(),
fact_manager));
std::string after_transformation = R"(
OpCapability Shader

View File

@ -44,34 +44,29 @@ TEST(TransformationAddTypeIntTest, BasicTest) {
FactManager fact_manager;
// Not applicable because id 1 is already in use.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationAddTypeInt(1, 32, false), context.get(),
fact_manager));
ASSERT_FALSE(TransformationAddTypeInt(1, 32, false)
.IsApplicable(context.get(), fact_manager));
auto add_type_signed_int_32 =
transformation::MakeTransformationAddTypeInt(100, 32, true);
auto add_type_unsigned_int_32 =
transformation::MakeTransformationAddTypeInt(101, 32, false);
auto add_type_signed_int_32_again =
transformation::MakeTransformationAddTypeInt(102, 32, true);
auto add_type_signed_int_32 = TransformationAddTypeInt(100, 32, true);
auto add_type_unsigned_int_32 = TransformationAddTypeInt(101, 32, false);
auto add_type_signed_int_32_again = TransformationAddTypeInt(102, 32, true);
auto add_type_unsigned_int_32_again =
transformation::MakeTransformationAddTypeInt(103, 32, false);
TransformationAddTypeInt(103, 32, false);
ASSERT_TRUE(transformation::IsApplicable(add_type_signed_int_32,
context.get(), fact_manager));
transformation::Apply(add_type_signed_int_32, context.get(), &fact_manager);
ASSERT_TRUE(add_type_signed_int_32.IsApplicable(context.get(), fact_manager));
add_type_signed_int_32.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(transformation::IsApplicable(add_type_unsigned_int_32,
context.get(), fact_manager));
transformation::Apply(add_type_unsigned_int_32, context.get(), &fact_manager);
ASSERT_TRUE(
add_type_unsigned_int_32.IsApplicable(context.get(), fact_manager));
add_type_unsigned_int_32.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Not applicable as we already have these types now.
ASSERT_FALSE(transformation::IsApplicable(add_type_signed_int_32_again,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(add_type_unsigned_int_32_again,
context.get(), fact_manager));
ASSERT_FALSE(
add_type_signed_int_32_again.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
add_type_unsigned_int_32_again.IsApplicable(context.get(), fact_manager));
std::string after_transformation = R"(
OpCapability Shader

View File

@ -99,46 +99,35 @@ TEST(TransformationAddTypePointerTest, BasicTest) {
FactManager fact_manager;
auto bad_type_id_does_not_exist =
transformation::MakeTransformationAddTypePointer(
100, SpvStorageClassFunction, 101);
TransformationAddTypePointer(100, SpvStorageClassFunction, 101);
auto bad_type_id_is_not_type =
transformation::MakeTransformationAddTypePointer(
100, SpvStorageClassFunction, 23);
TransformationAddTypePointer(100, SpvStorageClassFunction, 23);
auto bad_result_id_is_not_fresh =
transformation::MakeTransformationAddTypePointer(
17, SpvStorageClassFunction, 21);
TransformationAddTypePointer(17, SpvStorageClassFunction, 21);
auto good_new_private_pointer_to_t =
transformation::MakeTransformationAddTypePointer(
101, SpvStorageClassPrivate, 7);
TransformationAddTypePointer(101, SpvStorageClassPrivate, 7);
auto good_new_uniform_pointer_to_t =
transformation::MakeTransformationAddTypePointer(
102, SpvStorageClassUniform, 7);
TransformationAddTypePointer(102, SpvStorageClassUniform, 7);
auto good_another_function_pointer_to_s =
transformation::MakeTransformationAddTypePointer(
103, SpvStorageClassFunction, 8);
TransformationAddTypePointer(103, SpvStorageClassFunction, 8);
auto good_new_uniform_pointer_to_s =
transformation::MakeTransformationAddTypePointer(
104, SpvStorageClassUniform, 8);
TransformationAddTypePointer(104, SpvStorageClassUniform, 8);
auto good_another_private_pointer_to_float =
transformation::MakeTransformationAddTypePointer(
105, SpvStorageClassPrivate, 21);
TransformationAddTypePointer(105, SpvStorageClassPrivate, 21);
auto good_new_private_pointer_to_private_pointer_to_float =
transformation::MakeTransformationAddTypePointer(
106, SpvStorageClassPrivate, 105);
TransformationAddTypePointer(106, SpvStorageClassPrivate, 105);
auto good_new_uniform_pointer_to_vec2 =
transformation::MakeTransformationAddTypePointer(
107, SpvStorageClassUniform, 24);
TransformationAddTypePointer(107, SpvStorageClassUniform, 24);
auto good_new_private_pointer_to_uniform_pointer_to_vec2 =
transformation::MakeTransformationAddTypePointer(
108, SpvStorageClassPrivate, 107);
TransformationAddTypePointer(108, SpvStorageClassPrivate, 107);
ASSERT_FALSE(transformation::IsApplicable(bad_type_id_does_not_exist,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(bad_type_id_is_not_type,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(bad_result_id_is_not_fresh,
context.get(), fact_manager));
ASSERT_FALSE(
bad_type_id_does_not_exist.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
bad_type_id_is_not_type.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(
bad_result_id_is_not_fresh.IsApplicable(context.get(), fact_manager));
for (auto& transformation :
{good_new_private_pointer_to_t, good_new_uniform_pointer_to_t,
@ -147,9 +136,8 @@ TEST(TransformationAddTypePointerTest, BasicTest) {
good_new_private_pointer_to_private_pointer_to_float,
good_new_uniform_pointer_to_vec2,
good_new_private_pointer_to_uniform_pointer_to_vec2}) {
ASSERT_TRUE(transformation::IsApplicable(transformation, context.get(),
fact_manager));
transformation::Apply(transformation, context.get(), &fact_manager);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
}

View File

@ -54,9 +54,8 @@ TEST(TransformationMoveBlockDownTest, NoMovePossible1) {
FactManager fact_manager;
auto transformation = transformation::MakeTransformationMoveBlockDown(11);
ASSERT_FALSE(transformation::IsApplicable(transformation, context.get(),
fact_manager));
auto transformation = TransformationMoveBlockDown(11);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, NoMovePossible2) {
@ -92,9 +91,8 @@ TEST(TransformationMoveBlockDownTest, NoMovePossible2) {
FactManager fact_manager;
auto transformation = transformation::MakeTransformationMoveBlockDown(5);
ASSERT_FALSE(transformation::IsApplicable(transformation, context.get(),
fact_manager));
auto transformation = TransformationMoveBlockDown(5);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, NoMovePossible3) {
@ -132,9 +130,8 @@ TEST(TransformationMoveBlockDownTest, NoMovePossible3) {
FactManager fact_manager;
auto transformation = transformation::MakeTransformationMoveBlockDown(100);
ASSERT_FALSE(transformation::IsApplicable(transformation, context.get(),
fact_manager));
auto transformation = TransformationMoveBlockDown(100);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, NoMovePossible4) {
@ -176,81 +173,8 @@ TEST(TransformationMoveBlockDownTest, NoMovePossible4) {
FactManager fact_manager;
auto transformation = transformation::MakeTransformationMoveBlockDown(12);
ASSERT_FALSE(transformation::IsApplicable(transformation, context.get(),
fact_manager));
}
TEST(TransformationMoveBlockDownTest, MovePossible) {
// Block 11 can be moved down as it does not dominate block 12 (both are
// unreachable).
std::string before_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpDecorate %8 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%10 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpStore %8 %10
OpReturn
%11 = OpLabel
OpReturn
%12 = OpLabel
OpReturn
OpFunctionEnd
)";
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
OpDecorate %8 RelaxedPrecision
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypePointer Function %6
%9 = OpConstant %6 1
%10 = OpConstant %6 2
%4 = OpFunction %2 None %3
%5 = OpLabel
%8 = OpVariable %7 Function
OpStore %8 %9
OpStore %8 %10
OpReturn
%12 = OpLabel
OpReturn
%11 = OpLabel
OpReturn
OpFunctionEnd
)";
const auto env = SPV_ENV_UNIVERSAL_1_3;
const auto consumer = nullptr;
const auto context =
BuildModule(env, consumer, before_transformation, kFuzzAssembleOption);
FactManager fact_manager;
auto transformation = transformation::MakeTransformationMoveBlockDown(11);
ASSERT_TRUE(transformation::IsApplicable(transformation, context.get(),
fact_manager));
transformation::Apply(transformation, context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
auto transformation = TransformationMoveBlockDown(12);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, ManyMovesPossible) {
@ -357,17 +281,17 @@ TEST(TransformationMoveBlockDownTest, ManyMovesPossible) {
// The block ids are: 5 14 20 23 21 25 29 32 30 15
// We make a transformation to move each of them down, plus a transformation
// to move a non-block, 27, down.
auto move_down_5 = transformation::MakeTransformationMoveBlockDown(5);
auto move_down_14 = transformation::MakeTransformationMoveBlockDown(14);
auto move_down_20 = transformation::MakeTransformationMoveBlockDown(20);
auto move_down_23 = transformation::MakeTransformationMoveBlockDown(23);
auto move_down_21 = transformation::MakeTransformationMoveBlockDown(21);
auto move_down_25 = transformation::MakeTransformationMoveBlockDown(25);
auto move_down_29 = transformation::MakeTransformationMoveBlockDown(29);
auto move_down_32 = transformation::MakeTransformationMoveBlockDown(32);
auto move_down_30 = transformation::MakeTransformationMoveBlockDown(30);
auto move_down_15 = transformation::MakeTransformationMoveBlockDown(15);
auto move_down_27 = transformation::MakeTransformationMoveBlockDown(27);
auto move_down_5 = TransformationMoveBlockDown(5);
auto move_down_14 = TransformationMoveBlockDown(14);
auto move_down_20 = TransformationMoveBlockDown(20);
auto move_down_23 = TransformationMoveBlockDown(23);
auto move_down_21 = TransformationMoveBlockDown(21);
auto move_down_25 = TransformationMoveBlockDown(25);
auto move_down_29 = TransformationMoveBlockDown(29);
auto move_down_32 = TransformationMoveBlockDown(32);
auto move_down_30 = TransformationMoveBlockDown(30);
auto move_down_15 = TransformationMoveBlockDown(15);
auto move_down_27 = TransformationMoveBlockDown(27);
// Dominance is as follows:
// 5 dominates everything else
@ -382,180 +306,110 @@ TEST(TransformationMoveBlockDownTest, ManyMovesPossible) {
// 15 dominates nothing
// Current ordering: 5 14 20 23 21 25 29 32 30 15
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
// Let's bubble 20 all the way down.
transformation::Apply(move_down_20, context.get(), &fact_manager);
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 20 21 25 29 32 30 15
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_20, context.get(), &fact_manager);
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 20 25 29 32 30 15
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_20, context.get(), &fact_manager);
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 25 20 29 32 30 15
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_20, context.get(), &fact_manager);
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 25 29 20 32 30 15
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_20, context.get(), &fact_manager);
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 25 29 32 20 30 15
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_20, context.get(), &fact_manager);
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 23 21 25 29 32 30 20 15
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_20.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_15.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_20, context.get(), &fact_manager);
move_down_20.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_bubbling_20_down = R"(
@ -631,103 +485,63 @@ TEST(TransformationMoveBlockDownTest, ManyMovesPossible) {
ASSERT_TRUE(IsEqual(env, after_bubbling_20_down, context.get()));
// Current ordering: 5 14 23 21 25 29 32 30 15 20
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_23, context.get(), &fact_manager);
move_down_23.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 21 23 25 29 32 30 15 20
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_23, context.get(), &fact_manager);
move_down_23.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 21 25 23 29 32 30 15 20
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_21, context.get(), &fact_manager);
move_down_21.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Current ordering: 5 14 25 21 23 29 32 30 15 20
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
transformation::Apply(move_down_14, context.get(), &fact_manager);
move_down_14.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_more_shuffling = R"(
@ -803,26 +617,52 @@ TEST(TransformationMoveBlockDownTest, ManyMovesPossible) {
ASSERT_TRUE(IsEqual(env, after_more_shuffling, context.get()));
// Final ordering: 5 25 14 21 23 29 32 30 15 20
ASSERT_FALSE(
transformation::IsApplicable(move_down_5, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_25, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_14, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_21, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_23, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_29, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_32, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_30, context.get(), fact_manager));
ASSERT_TRUE(
transformation::IsApplicable(move_down_15, context.get(), fact_manager));
ASSERT_FALSE(
transformation::IsApplicable(move_down_20, context.get(), fact_manager));
ASSERT_FALSE(move_down_5.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_25.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_14.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_21.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_23.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_29.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_32.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_30.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(move_down_15.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(move_down_20.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationMoveBlockDownTest, DoNotMoveUnreachable) {
// Block 6 is unreachable, so cannot be moved down.
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"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%10 = OpTypeInt 32 1
%4 = OpFunction %2 None %3
%5 = OpLabel
OpReturn
%6 = OpLabel
%7 = OpUndef %10
OpBranch %8
%8 = OpLabel
%9 = OpCopyObject %10 %7
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;
auto transformation = TransformationMoveBlockDown(6);
ASSERT_FALSE(transformation.IsApplicable(context.get(), fact_manager));
}
} // namespace

View File

@ -192,16 +192,12 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
std::vector<SpvOp> uint_lt_opcodes = {SpvOpULessThan, SpvOpULessThanEqual};
#define CHECK_OPERATOR(USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
ASSERT_TRUE(transformation::IsApplicable( \
transformation:: \
MakeTransformationReplaceBooleanConstantWithConstantBinary( \
USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID), \
context.get(), fact_manager)); \
ASSERT_FALSE(transformation::IsApplicable( \
transformation:: \
MakeTransformationReplaceBooleanConstantWithConstantBinary( \
USE_DESCRIPTOR, RHS_ID, LHS_ID, OPCODE, FRESH_ID), \
context.get(), fact_manager));
ASSERT_TRUE(TransformationReplaceBooleanConstantWithConstantBinary( \
USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \
.IsApplicable(context.get(), fact_manager)); \
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( \
USE_DESCRIPTOR, RHS_ID, LHS_ID, OPCODE, FRESH_ID) \
.IsApplicable(context.get(), fact_manager));
#define CHECK_TRANSFORMATION_APPLICABILITY(GT_OPCODES, LT_OPCODES, SMALL_ID, \
LARGE_ID) \
@ -251,72 +247,58 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
}
// Target id is not fresh
ASSERT_FALSE(transformation::IsApplicable(
transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 17, SpvOpFOrdLessThan, 15),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 17, SpvOpFOrdLessThan, 15)
.IsApplicable(context.get(), fact_manager));
// LHS id does not exist
ASSERT_FALSE(transformation::IsApplicable(
transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 300, 17, SpvOpFOrdLessThan, 200),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 300, 17, SpvOpFOrdLessThan, 200)
.IsApplicable(context.get(), fact_manager));
// RHS id does not exist
ASSERT_FALSE(transformation::IsApplicable(
transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 300, SpvOpFOrdLessThan, 200),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 300, SpvOpFOrdLessThan, 200)
.IsApplicable(context.get(), fact_manager));
// LHS and RHS ids do not match type
ASSERT_FALSE(transformation::IsApplicable(
transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 17, SpvOpFOrdLessThan, 200),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 17, SpvOpFOrdLessThan, 200)
.IsApplicable(context.get(), fact_manager));
// Opcode not appropriate
ASSERT_FALSE(transformation::IsApplicable(
transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 17, SpvOpFDiv, 200),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 15, 17, SpvOpFDiv, 200)
.IsApplicable(context.get(), fact_manager));
auto replace_true_with_double_comparison = transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
auto replace_true_with_double_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 9, SpvOpFUnordGreaterThan, 100);
auto replace_true_with_uint32_comparison = transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
auto replace_true_with_uint32_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[1], 27, 29, SpvOpULessThanEqual, 101);
auto replace_false_with_float_comparison = transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
auto replace_false_with_float_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_false[0], 17, 15, SpvOpFOrdLessThan, 102);
auto replace_false_with_sint64_comparison = transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
auto replace_false_with_sint64_comparison =
TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_false[1], 33, 31, SpvOpSLessThan, 103);
ASSERT_TRUE(transformation::IsApplicable(replace_true_with_double_comparison,
context.get(), fact_manager));
transformation::Apply(replace_true_with_double_comparison, context.get(),
&fact_manager);
ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable(context.get(),
fact_manager));
replace_true_with_double_comparison.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(transformation::IsApplicable(replace_true_with_uint32_comparison,
context.get(), fact_manager));
transformation::Apply(replace_true_with_uint32_comparison, context.get(),
&fact_manager);
ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable(context.get(),
fact_manager));
replace_true_with_uint32_comparison.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(transformation::IsApplicable(replace_false_with_float_comparison,
context.get(), fact_manager));
transformation::Apply(replace_false_with_float_comparison, context.get(),
&fact_manager);
ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable(context.get(),
fact_manager));
replace_false_with_float_comparison.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(transformation::IsApplicable(replace_false_with_sint64_comparison,
context.get(), fact_manager));
transformation::Apply(replace_false_with_sint64_comparison, context.get(),
&fact_manager);
ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable(context.get(),
fact_manager));
replace_false_with_sint64_comparison.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after = R"(
@ -432,11 +414,9 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
fuzzerutil::UpdateModuleIdBound(context.get(), 200);
ASSERT_TRUE(IsValid(env, context.get()));
// The transformation is not applicable because %200 is NaN.
ASSERT_FALSE(transformation::IsApplicable(
transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 200, SpvOpFOrdLessThan, 300),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 200, SpvOpFOrdLessThan, 300)
.IsApplicable(context.get(), fact_manager));
}
if (std::numeric_limits<double>::has_infinity) {
double positive_infinity_double = std::numeric_limits<double>::infinity();
@ -451,11 +431,9 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
ASSERT_TRUE(IsValid(env, context.get()));
// Even though the double constant %11 is less than the infinity %201, the
// transformation is restricted to only apply to finite values.
ASSERT_FALSE(transformation::IsApplicable(
transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 201, SpvOpFOrdLessThan, 300),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 11, 201, SpvOpFOrdLessThan, 300)
.IsApplicable(context.get(), fact_manager));
}
if (std::numeric_limits<float>::has_infinity) {
float positive_infinity_float = std::numeric_limits<float>::infinity();
@ -478,11 +456,9 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
// Even though the negative infinity at %203 is less than the positive
// infinity %202, the transformation is restricted to only apply to finite
// values.
ASSERT_FALSE(transformation::IsApplicable(
transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 203, 202, SpvOpFOrdLessThan, 300),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary(
uses_of_true[0], 203, 202, SpvOpFOrdLessThan, 300)
.IsApplicable(context.get(), fact_manager));
}
}
@ -558,21 +534,17 @@ TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest,
auto use_of_false_in_while =
transformation::MakeIdUseDescriptor(21, SpvOpBranchConditional, 0, 16, 0);
auto replacement_1 = transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
auto replacement_2 = transformation::
MakeTransformationReplaceBooleanConstantWithConstantBinary(
use_of_false_in_while, 9, 11, SpvOpSGreaterThanEqual, 101);
auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
auto replacement_2 = TransformationReplaceBooleanConstantWithConstantBinary(
use_of_false_in_while, 9, 11, SpvOpSGreaterThanEqual, 101);
ASSERT_TRUE(
transformation::IsApplicable(replacement_1, context.get(), fact_manager));
transformation::Apply(replacement_1, context.get(), &fact_manager);
ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
replacement_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_TRUE(
transformation::IsApplicable(replacement_2, context.get(), fact_manager));
transformation::Apply(replacement_2, context.get(), &fact_manager);
ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
replacement_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after = R"(

View File

@ -124,35 +124,32 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
// These transformations work: they match the facts.
auto transformation_use_of_9_in_store =
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, blockname_a, 100, 101);
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_9_in_store,
context.get(), fact_manager));
TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_a,
100, 101);
ASSERT_TRUE(transformation_use_of_9_in_store.IsApplicable(context.get(),
fact_manager));
auto transformation_use_of_11_in_add =
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_11_in_add, blockname_b, 102, 103);
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_11_in_add,
context.get(), fact_manager));
TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b,
102, 103);
ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable(context.get(),
fact_manager));
auto transformation_use_of_14_in_add =
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_14_in_add, blockname_c, 104, 105);
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_14_in_add,
context.get(), fact_manager));
TransformationReplaceConstantWithUniform(use_of_14_in_add, blockname_c,
104, 105);
ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable(context.get(),
fact_manager));
// The transformations are not applicable if we change which uniforms are
// applied to which constants.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, blockname_b, 101, 102),
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_11_in_add, blockname_c, 101, 102),
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_14_in_add, blockname_a, 101, 102),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
blockname_b, 101, 102)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add,
blockname_c, 101, 102)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_14_in_add,
blockname_a, 101, 102)
.IsApplicable(context.get(), fact_manager));
// The following transformations do not apply because the uniform descriptors
// are not sensible.
@ -160,37 +157,31 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
MakeUniformBufferElementDescriptor(1, 2, {0});
protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor2 =
MakeUniformBufferElementDescriptor(0, 0, {5});
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, nonsense_uniform_descriptor1, 101, 102),
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, nonsense_uniform_descriptor2, 101, 102),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(
use_of_9_in_store, nonsense_uniform_descriptor1, 101, 102)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(
use_of_9_in_store, nonsense_uniform_descriptor2, 101, 102)
.IsApplicable(context.get(), fact_manager));
// The following transformation does not apply because the id descriptor is
// not sensible.
protobufs::IdUseDescriptor nonsense_id_use_descriptor =
transformation::MakeIdUseDescriptor(9, SpvOpIAdd, 0, 15, 0);
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
nonsense_id_use_descriptor, blockname_a, 101, 102),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(
nonsense_id_use_descriptor, blockname_a, 101, 102)
.IsApplicable(context.get(), fact_manager));
// The following transformations do not apply because the ids are not fresh.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_11_in_add, blockname_b, 15, 103),
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_11_in_add, blockname_b, 102, 15),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add,
blockname_b, 15, 103)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add,
blockname_b, 102, 15)
.IsApplicable(context.get(), fact_manager));
// Apply the use of 9 in a store.
transformation::Apply(transformation_use_of_9_in_store, context.get(),
&fact_manager);
transformation_use_of_9_in_store.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_replacing_use_of_9_in_store = R"(
OpCapability Shader
@ -241,11 +232,10 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
)";
ASSERT_TRUE(IsEqual(env, after_replacing_use_of_9_in_store, context.get()));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_11_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable(context.get(),
fact_manager));
// Apply the use of 11 in an add.
transformation::Apply(transformation_use_of_11_in_add, context.get(),
&fact_manager);
transformation_use_of_11_in_add.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_replacing_use_of_11_in_add = R"(
OpCapability Shader
@ -298,11 +288,10 @@ TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) {
)";
ASSERT_TRUE(IsEqual(env, after_replacing_use_of_11_in_add, context.get()));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_14_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable(context.get(),
fact_manager));
// Apply the use of 15 in an add.
transformation::Apply(transformation_use_of_14_in_add, context.get(),
&fact_manager);
transformation_use_of_14_in_add.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_replacing_use_of_14_in_add = R"(
OpCapability Shader
@ -498,82 +487,78 @@ TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) {
// These transformations work: they match the facts.
auto transformation_use_of_13_in_store =
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_13_in_store, blockname_1, 100, 101);
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_13_in_store,
context.get(), fact_manager));
TransformationReplaceConstantWithUniform(use_of_13_in_store, blockname_1,
100, 101);
ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable(context.get(),
fact_manager));
auto transformation_use_of_15_in_add =
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_15_in_add, blockname_2, 102, 103);
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add,
context.get(), fact_manager));
TransformationReplaceConstantWithUniform(use_of_15_in_add, blockname_2,
102, 103);
ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(),
fact_manager));
auto transformation_use_of_17_in_add =
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_17_in_add, blockname_3, 104, 105);
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add,
context.get(), fact_manager));
TransformationReplaceConstantWithUniform(use_of_17_in_add, blockname_3,
104, 105);
ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(),
fact_manager));
auto transformation_use_of_20_in_store =
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_20_in_store, blockname_4, 106, 107);
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
context.get(), fact_manager));
TransformationReplaceConstantWithUniform(use_of_20_in_store, blockname_4,
106, 107);
ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_13_in_store,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
context.get(), fact_manager));
ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
fact_manager));
transformation::Apply(transformation_use_of_13_in_store, context.get(),
&fact_manager);
transformation_use_of_13_in_store.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_15_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
context.get(), fact_manager));
ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
fact_manager));
transformation::Apply(transformation_use_of_15_in_add, context.get(),
&fact_manager);
transformation_use_of_15_in_add.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_17_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
context.get(), fact_manager));
ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
fact_manager));
transformation::Apply(transformation_use_of_17_in_add, context.get(),
&fact_manager);
transformation_use_of_17_in_add.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_17_in_add,
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(transformation_use_of_20_in_store,
context.get(), fact_manager));
ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable(context.get(),
fact_manager));
transformation::Apply(transformation_use_of_20_in_store, context.get(),
&fact_manager);
transformation_use_of_20_in_store.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_13_in_store,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_15_in_add,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_17_in_add,
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(transformation_use_of_20_in_store,
context.get(), fact_manager));
ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(transformation_use_of_20_in_store.IsApplicable(context.get(),
fact_manager));
std::string after = R"(
OpCapability Shader
@ -722,10 +707,9 @@ TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) {
// This transformation is not available because no uniform pointer to integer
// type is present:
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, blockname_0, 100, 101),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
blockname_0, 100, 101)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) {
@ -798,10 +782,9 @@ TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) {
// This transformation is not available because no constant is present for the
// index 1 required to index into the uniform buffer:
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, blockname_9, 100, 101),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
blockname_9, 100, 101)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationReplaceConstantWithUniformTest,
@ -873,10 +856,9 @@ TEST(TransformationReplaceConstantWithUniformTest,
// This transformation is not available because no integer type is present to
// allow a constant index to be expressed:
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, blockname_3, 100, 101),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
blockname_3, 100, 101)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationReplaceConstantWithUniformTest,
@ -960,25 +942,21 @@ TEST(TransformationReplaceConstantWithUniformTest,
transformation::MakeIdUseDescriptor(11, SpvOpStore, 1, 10, 1);
// These are right:
ASSERT_TRUE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, blockname_9, 100, 101),
context.get(), fact_manager));
ASSERT_TRUE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_11_in_store, blockname_10, 102, 103),
context.get(), fact_manager));
ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
blockname_9, 100, 101)
.IsApplicable(context.get(), fact_manager));
ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_11_in_store,
blockname_10, 102, 103)
.IsApplicable(context.get(), fact_manager));
// These are wrong because the constants do not match the facts about
// uniforms.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_11_in_store, blockname_9, 100, 101),
context.get(), fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationReplaceConstantWithUniform(
use_of_9_in_store, blockname_10, 102, 103),
context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_store,
blockname_9, 100, 101)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
blockname_10, 102, 103)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) {
@ -1239,83 +1217,65 @@ TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) {
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 100, uniform_h_x));
ASSERT_TRUE(AddFactHelper(&fact_manager, context.get(), 200, uniform_h_y));
std::vector<protobufs::TransformationReplaceConstantWithUniform>
transformations;
std::vector<TransformationReplaceConstantWithUniform> transformations;
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0),
uniform_f_a_4, 200, 201));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0),
uniform_f_a_3, 202, 203));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0),
uniform_f_a_2, 204, 205));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0),
uniform_f_a_1, 206, 207));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0),
uniform_f_a_0, 208, 209));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0),
uniform_f_a_4, 200, 201));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0),
uniform_f_a_3, 202, 203));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0),
uniform_f_a_2, 204, 205));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0),
uniform_f_a_1, 206, 207));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0),
uniform_f_a_0, 208, 209));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0),
uniform_f_b_w, 210, 211));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0),
uniform_f_b_z, 212, 213));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0),
uniform_f_b_y, 214, 215));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0),
uniform_f_b_x, 216, 217));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0),
uniform_f_b_w, 210, 211));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0),
uniform_f_b_z, 212, 213));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0),
uniform_f_b_y, 214, 215));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0),
uniform_f_b_x, 216, 217));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0),
uniform_f_c_z, 220, 221));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0),
uniform_f_c_y, 222, 223));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0),
uniform_f_c_x, 224, 225));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0),
uniform_f_c_z, 220, 221));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0),
uniform_f_c_y, 222, 223));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0),
uniform_f_c_x, 224, 225));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0),
uniform_f_d, 226, 227));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0),
uniform_f_d, 226, 227));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0),
uniform_h_x, 228, 229));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0),
uniform_h_y, 230, 231));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0),
uniform_h_x, 228, 229));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0),
uniform_h_y, 230, 231));
transformations.emplace_back(
transformation::MakeTransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0),
uniform_g, 218, 219));
transformations.emplace_back(TransformationReplaceConstantWithUniform(
transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0), uniform_g,
218, 219));
for (auto& transformation : transformations) {
ASSERT_TRUE(transformation::IsApplicable(transformation, context.get(),
fact_manager));
transformation::Apply(transformation, context.get(), &fact_manager);
ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
transformation.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
}

View File

@ -90,57 +90,44 @@ TEST(TransformationSplitBlockTest, NotApplicable) {
FactManager fact_manager;
// No split before OpVariable
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(8, 0, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(8, 1, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(8, 0, 100).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(8, 1, 100).IsApplicable(context.get(),
fact_manager));
// No split before OpLabel
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(14, 0, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(14, 0, 100)
.IsApplicable(context.get(), fact_manager));
// No split if base instruction is outside a function
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(1, 0, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(1, 4, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(1, 35, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(1, 0, 100).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(1, 4, 100).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(1, 35, 100)
.IsApplicable(context.get(), fact_manager));
// No split if block is loop header
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(27, 0, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(27, 1, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(27, 0, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(27, 1, 100)
.IsApplicable(context.get(), fact_manager));
// No split if base instruction does not exist
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(88, 0, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(88, 22, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(88, 0, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(88, 22, 100)
.IsApplicable(context.get(), fact_manager));
// No split if offset is too large (goes into another block)
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(18, 3, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(18, 3, 100)
.IsApplicable(context.get(), fact_manager));
// No split if id in use
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(18, 0, 27), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(18, 0, 14), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(18, 0, 27).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(18, 0, 14).IsApplicable(context.get(),
fact_manager));
}
TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
@ -201,10 +188,9 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
FactManager fact_manager;
auto split_1 = transformation::MakeTransformationSplitBlock(5, 3, 100);
ASSERT_TRUE(
transformation::IsApplicable(split_1, context.get(), fact_manager));
transformation::Apply(split_1, context.get(), &fact_manager);
auto split_1 = TransformationSplitBlock(5, 3, 100);
ASSERT_TRUE(split_1.IsApplicable(context.get(), fact_manager));
split_1.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_1 = R"(
@ -249,10 +235,9 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
)";
ASSERT_TRUE(IsEqual(env, after_split_1, context.get()));
auto split_2 = transformation::MakeTransformationSplitBlock(11, 1, 101);
ASSERT_TRUE(
transformation::IsApplicable(split_2, context.get(), fact_manager));
transformation::Apply(split_2, context.get(), &fact_manager);
auto split_2 = TransformationSplitBlock(11, 1, 101);
ASSERT_TRUE(split_2.IsApplicable(context.get(), fact_manager));
split_2.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_2 = R"(
@ -299,10 +284,9 @@ TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) {
)";
ASSERT_TRUE(IsEqual(env, after_split_2, context.get()));
auto split_3 = transformation::MakeTransformationSplitBlock(14, 0, 102);
ASSERT_TRUE(
transformation::IsApplicable(split_3, context.get(), fact_manager));
transformation::Apply(split_3, context.get(), &fact_manager);
auto split_3 = TransformationSplitBlock(14, 0, 102);
ASSERT_TRUE(split_3.IsApplicable(context.get(), fact_manager));
split_3.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split_3 = R"(
@ -415,16 +399,14 @@ TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) {
FactManager fact_manager;
// Illegal to split between the merge and the conditional branch.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(14, 2, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(12, 3, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(14, 2, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(12, 3, 100)
.IsApplicable(context.get(), fact_manager));
auto split = transformation::MakeTransformationSplitBlock(14, 1, 100);
ASSERT_TRUE(transformation::IsApplicable(split, context.get(), fact_manager));
transformation::Apply(split, context.get(), &fact_manager);
auto split = TransformationSplitBlock(14, 1, 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = R"(
@ -541,16 +523,14 @@ TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) {
FactManager fact_manager;
// Illegal to split between the merge and the conditional branch.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(9, 2, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(15, 3, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(9, 2, 100).IsApplicable(context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(15, 3, 100)
.IsApplicable(context.get(), fact_manager));
auto split = transformation::MakeTransformationSplitBlock(9, 1, 100);
ASSERT_TRUE(transformation::IsApplicable(split, context.get(), fact_manager));
transformation::Apply(split, context.get(), &fact_manager);
auto split = TransformationSplitBlock(9, 1, 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = R"(
@ -674,15 +654,12 @@ TEST(TransformationSplitBlockTest, NoSplitDuringOpPhis) {
// We cannot split before OpPhi instructions, since the number of incoming
// blocks may not appropriately match after splitting.
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(26, 0, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(27, 0, 100), context.get(),
fact_manager));
ASSERT_FALSE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(27, 1, 100), context.get(),
fact_manager));
ASSERT_FALSE(TransformationSplitBlock(26, 0, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(27, 0, 100)
.IsApplicable(context.get(), fact_manager));
ASSERT_FALSE(TransformationSplitBlock(27, 1, 100)
.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
@ -724,12 +701,11 @@ TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) {
FactManager fact_manager;
ASSERT_TRUE(transformation::IsApplicable(
transformation::MakeTransformationSplitBlock(21, 0, 100), context.get(),
fact_manager));
auto split = transformation::MakeTransformationSplitBlock(20, 1, 100);
ASSERT_TRUE(transformation::IsApplicable(split, context.get(), fact_manager));
transformation::Apply(split, context.get(), &fact_manager);
ASSERT_TRUE(TransformationSplitBlock(21, 0, 100)
.IsApplicable(context.get(), fact_manager));
auto split = TransformationSplitBlock(20, 1, 100);
ASSERT_TRUE(split.IsApplicable(context.get(), fact_manager));
split.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
std::string after_split = R"(

View File

@ -8276,6 +8276,328 @@ OpFunctionEnd
true, 7u, 23u, true, true, 2u);
}
TEST_F(InstBindlessTest,
InstBoundsComputeShaderInitLoadVariableSizedSampledImagesArray) {
// #version 450
// #extension GL_EXT_nonuniform_qualifier : enable
//
// layout (local_size_x = 1, local_size_y = 1) in;
//
// layout(set = 0, binding = 0, std140) buffer Input {
// uint index;
// float red;
// } sbo;
//
// layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[];
//
// void main()
// {
// sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r;
// }
const std::string defs_before =
R"(OpCapability Shader
OpCapability RuntimeDescriptorArrayEXT
OpExtension "SPV_EXT_descriptor_indexing"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpName %main "main"
OpName %Input "Input"
OpMemberName %Input 0 "index"
OpMemberName %Input 1 "red"
OpName %sbo "sbo"
OpName %images "images"
OpMemberDecorate %Input 0 Offset 0
OpMemberDecorate %Input 1 Offset 4
OpDecorate %Input BufferBlock
OpDecorate %sbo DescriptorSet 0
OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
%void = OpTypeVoid
%3 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%Input = OpTypeStruct %uint %float
%_ptr_Uniform_Input = OpTypePointer Uniform %Input
%sbo = OpVariable %_ptr_Uniform_Input Uniform
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
%_runtimearr_13 = OpTypeRuntimeArray %13
%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
%v2int = OpTypeVector %int 2
%25 = OpConstantComposite %v2int %int_0 %int_0
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
)";
const std::string defs_after =
R"(OpCapability Shader
OpCapability RuntimeDescriptorArrayEXT
OpExtension "SPV_EXT_descriptor_indexing"
OpExtension "SPV_KHR_storage_buffer_storage_class"
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
OpExecutionMode %main LocalSize 1 1 1
OpSource GLSL 450
OpSourceExtension "GL_EXT_nonuniform_qualifier"
OpName %main "main"
OpName %Input "Input"
OpMemberName %Input 0 "index"
OpMemberName %Input 1 "red"
OpName %sbo "sbo"
OpName %images "images"
OpMemberDecorate %Input 0 Offset 0
OpMemberDecorate %Input 1 Offset 4
OpDecorate %Input BufferBlock
OpDecorate %sbo DescriptorSet 0
OpDecorate %sbo Binding 0
OpDecorate %images DescriptorSet 0
OpDecorate %images Binding 1
OpDecorate %images NonWritable
OpDecorate %_runtimearr_uint ArrayStride 4
OpDecorate %_struct_39 Block
OpMemberDecorate %_struct_39 0 Offset 0
OpDecorate %41 DescriptorSet 7
OpDecorate %41 Binding 1
OpDecorate %_struct_63 Block
OpMemberDecorate %_struct_63 0 Offset 0
OpMemberDecorate %_struct_63 1 Offset 4
OpDecorate %65 DescriptorSet 7
OpDecorate %65 Binding 0
OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
%void = OpTypeVoid
%7 = OpTypeFunction %void
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%Input = OpTypeStruct %uint %float
%_ptr_Uniform_Input = OpTypePointer Uniform %Input
%sbo = OpVariable %_ptr_Uniform_Input Uniform
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f
%_runtimearr_13 = OpTypeRuntimeArray %13
%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13
%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13
%v2int = OpTypeVector %int 2
%20 = OpConstantComposite %v2int %int_0 %int_0
%v4float = OpTypeVector %float 4
%uint_0 = OpConstant %uint 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%uint_1 = OpConstant %uint 1
%34 = OpTypeFunction %uint %uint %uint
%_runtimearr_uint = OpTypeRuntimeArray %uint
%_struct_39 = OpTypeStruct %_runtimearr_uint
%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39
%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
%bool = OpTypeBool
%57 = OpTypeFunction %void %uint %uint %uint %uint
%_struct_63 = OpTypeStruct %uint %_runtimearr_uint
%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63
%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer
%uint_10 = OpConstant %uint 10
%uint_4 = OpConstant %uint 4
%uint_23 = OpConstant %uint 23
%uint_2 = OpConstant %uint 2
%uint_5 = OpConstant %uint 5
%uint_3 = OpConstant %uint 3
%v3uint = OpTypeVector %uint 3
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
%uint_6 = OpConstant %uint 6
%uint_7 = OpConstant %uint 7
%uint_8 = OpConstant %uint 8
%uint_9 = OpConstant %uint 9
%uint_50 = OpConstant %uint 50
%112 = OpConstantNull %v4float
%115 = OpTypeFunction %uint %uint %uint %uint %uint
%uint_47 = OpConstant %uint 47
%140 = OpConstantNull %uint
%uint_53 = OpConstant %uint 53
)";
const std::string func_before =
R"(%main = OpFunction %void None %3
%5 = OpLabel
%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
%20 = OpLoad %uint %19
%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20
%23 = OpLoad %13 %22
%27 = OpImageRead %v4float %23 %25
%29 = OpCompositeExtract %float %27 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
OpStore %31 %29
OpReturn
OpFunctionEnd
)";
const std::string func_after =
R"(%main = OpFunction %void None %7
%24 = OpLabel
%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0
%132 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_0 %uint_0
%133 = OpINotEqual %bool %132 %uint_0
OpSelectionMerge %134 None
OpBranchConditional %133 %135 %136
%135 = OpLabel
%137 = OpLoad %uint %25
OpBranch %134
%136 = OpLabel
%139 = OpFunctionCall %void %56 %uint_47 %uint_1 %uint_0 %uint_0
OpBranch %134
%134 = OpLabel
%141 = OpPhi %uint %137 %135 %140 %136
%27 = OpAccessChain %_ptr_UniformConstant_13 %images %141
%28 = OpLoad %13 %27
%48 = OpFunctionCall %uint %33 %uint_1 %uint_1
%50 = OpULessThan %bool %141 %48
OpSelectionMerge %51 None
OpBranchConditional %50 %52 %53
%52 = OpLabel
%54 = OpLoad %13 %27
%142 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_1 %141
%143 = OpINotEqual %bool %142 %uint_0
OpSelectionMerge %144 None
OpBranchConditional %143 %145 %146
%145 = OpLabel
%147 = OpLoad %13 %27
%148 = OpImageRead %v4float %147 %20
OpBranch %144
%146 = OpLabel
%149 = OpFunctionCall %void %56 %uint_50 %uint_1 %141 %uint_0
OpBranch %144
%144 = OpLabel
%150 = OpPhi %v4float %148 %145 %112 %146
OpBranch %51
%53 = OpLabel
%111 = OpFunctionCall %void %56 %uint_50 %uint_0 %141 %48
OpBranch %51
%51 = OpLabel
%113 = OpPhi %v4float %150 %144 %112 %53
%30 = OpCompositeExtract %float %113 0
%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1
%151 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_0 %uint_0
%152 = OpINotEqual %bool %151 %uint_0
OpSelectionMerge %153 None
OpBranchConditional %152 %154 %155
%154 = OpLabel
OpStore %31 %30
OpBranch %153
%155 = OpLabel
%157 = OpFunctionCall %void %56 %uint_53 %uint_1 %uint_0 %uint_0
OpBranch %153
%153 = OpLabel
OpReturn
OpFunctionEnd
)";
const std::string new_funcs =
R"(%33 = OpFunction %uint None %34
%35 = OpFunctionParameter %uint
%36 = OpFunctionParameter %uint
%37 = OpLabel
%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35
%44 = OpLoad %uint %43
%45 = OpIAdd %uint %44 %36
%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45
%47 = OpLoad %uint %46
OpReturnValue %47
OpFunctionEnd
%56 = OpFunction %void None %57
%58 = OpFunctionParameter %uint
%59 = OpFunctionParameter %uint
%60 = OpFunctionParameter %uint
%61 = OpFunctionParameter %uint
%62 = OpLabel
%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0
%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10
%70 = OpIAdd %uint %69 %uint_10
%71 = OpArrayLength %uint %65 1
%72 = OpULessThanEqual %bool %70 %71
OpSelectionMerge %73 None
OpBranchConditional %72 %74 %73
%74 = OpLabel
%75 = OpIAdd %uint %69 %uint_0
%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75
OpStore %76 %uint_10
%78 = OpIAdd %uint %69 %uint_1
%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78
OpStore %79 %uint_23
%81 = OpIAdd %uint %69 %uint_2
%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81
OpStore %82 %58
%85 = OpIAdd %uint %69 %uint_3
%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85
OpStore %86 %uint_5
%90 = OpLoad %v3uint %gl_GlobalInvocationID
%91 = OpCompositeExtract %uint %90 0
%92 = OpCompositeExtract %uint %90 1
%93 = OpCompositeExtract %uint %90 2
%94 = OpIAdd %uint %69 %uint_4
%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94
OpStore %95 %91
%96 = OpIAdd %uint %69 %uint_5
%97 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %96
OpStore %97 %92
%99 = OpIAdd %uint %69 %uint_6
%100 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %99
OpStore %100 %93
%102 = OpIAdd %uint %69 %uint_7
%103 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %102
OpStore %103 %59
%105 = OpIAdd %uint %69 %uint_8
%106 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %105
OpStore %106 %60
%108 = OpIAdd %uint %69 %uint_9
%109 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %108
OpStore %109 %61
OpBranch %73
%73 = OpLabel
OpReturn
OpFunctionEnd
%114 = OpFunction %uint None %115
%116 = OpFunctionParameter %uint
%117 = OpFunctionParameter %uint
%118 = OpFunctionParameter %uint
%119 = OpFunctionParameter %uint
%120 = OpLabel
%121 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %116
%122 = OpLoad %uint %121
%123 = OpIAdd %uint %122 %117
%124 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %123
%125 = OpLoad %uint %124
%126 = OpIAdd %uint %125 %118
%127 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %126
%128 = OpLoad %uint %127
%129 = OpIAdd %uint %128 %119
%130 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %129
%131 = OpLoad %uint %130
OpReturnValue %131
OpFunctionEnd
)";
// SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
SinglePassRunAndCheck<InstBindlessCheckPass>(
defs_before + func_before, defs_after + func_after + new_funcs, true,
true, 7u, 23u, true, true, 2u);
}
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Compute shader

View File

@ -6491,10 +6491,14 @@ OpDecorate %entryPointOutput )" +
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%vtype = )" + type + R"(
%v3float = OpTypeVector %float 3
%v4float = OpTypeVector %float 4
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%arr_v3float_uint_2 = OpTypeArray %v3float %uint_2
%float_0 = OpConstant %float 0
%_ptr_Output_vtype = OpTypePointer Output %vtype
%entryPointOutput = OpVariable %_ptr_Output_vtype Output
%_ptr_Output_type = OpTypePointer Output %)" + type + R"(
%entryPointOutput = OpVariable %_ptr_Output_type Output
%main = OpFunction %void None %3
%5 = OpLabel
OpReturn
@ -6504,8 +6508,7 @@ OpFunctionEnd
TEST_F(ValidateDecorations, ComponentDecorationIntGood0Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 0");
std::string spirv = ShaderWithComponentDecoration("uint", "Component 0");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
@ -6514,8 +6517,7 @@ TEST_F(ValidateDecorations, ComponentDecorationIntGood0Vulkan) {
TEST_F(ValidateDecorations, ComponentDecorationIntGood1Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 1");
std::string spirv = ShaderWithComponentDecoration("uint", "Component 1");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
@ -6524,8 +6526,7 @@ TEST_F(ValidateDecorations, ComponentDecorationIntGood1Vulkan) {
TEST_F(ValidateDecorations, ComponentDecorationIntGood2Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 2");
std::string spirv = ShaderWithComponentDecoration("uint", "Component 2");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
@ -6534,8 +6535,7 @@ TEST_F(ValidateDecorations, ComponentDecorationIntGood2Vulkan) {
TEST_F(ValidateDecorations, ComponentDecorationIntGood3Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 3");
std::string spirv = ShaderWithComponentDecoration("uint", "Component 3");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
@ -6544,8 +6544,7 @@ TEST_F(ValidateDecorations, ComponentDecorationIntGood3Vulkan) {
TEST_F(ValidateDecorations, ComponentDecorationIntBad4Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeInt 32 0", "Component 4");
std::string spirv = ShaderWithComponentDecoration("uint", "Component 4");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
@ -6556,8 +6555,7 @@ TEST_F(ValidateDecorations, ComponentDecorationIntBad4Vulkan) {
TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeVector %float 3", "Component 1");
std::string spirv = ShaderWithComponentDecoration("v3float", "Component 1");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
@ -6566,8 +6564,7 @@ TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) {
TEST_F(ValidateDecorations, ComponentDecorationVector4GoodVulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 0");
std::string spirv = ShaderWithComponentDecoration("v4float", "Component 0");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
@ -6576,8 +6573,7 @@ TEST_F(ValidateDecorations, ComponentDecorationVector4GoodVulkan) {
TEST_F(ValidateDecorations, ComponentDecorationVector4Bad1Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 1");
std::string spirv = ShaderWithComponentDecoration("v4float", "Component 1");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
@ -6588,8 +6584,7 @@ TEST_F(ValidateDecorations, ComponentDecorationVector4Bad1Vulkan) {
TEST_F(ValidateDecorations, ComponentDecorationVector4Bad3Vulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("OpTypeVector %float 4", "Component 3");
std::string spirv = ShaderWithComponentDecoration("v4float", "Component 3");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
@ -6598,6 +6593,28 @@ TEST_F(ValidateDecorations, ComponentDecorationVector4Bad3Vulkan) {
"and ending with 6 gets larger than 3"));
}
TEST_F(ValidateDecorations, ComponentDecorationArrayGoodVulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("arr_v3float_uint_2", "Component 1");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(), Eq(""));
}
TEST_F(ValidateDecorations, ComponentDecorationArrayBadVulkan) {
const spv_target_env env = SPV_ENV_VULKAN_1_0;
std::string spirv =
ShaderWithComponentDecoration("arr_v3float_uint_2", "Component 2");
CompileSuccessfully(spirv, env);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env));
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Sequence of components starting with 2 "
"and ending with 4 gets larger than 3"));
}
TEST_F(ValidateDecorations, ComponentDecorationBlockGood) {
std::string spirv = R"(
OpCapability Shader

View File

@ -257,7 +257,7 @@ int main(int argc, const char** argv) {
const std::string dot_spv(".spv");
std::string in_facts_file =
in_binary_file.substr(0, in_binary_file.length() - dot_spv.length()) +
".json";
".facts";
std::ifstream facts_input(in_facts_file);
if (facts_input) {
std::string facts_json_string((std::istreambuf_iterator<char>(facts_input)),

View File

@ -28,8 +28,6 @@
namespace {
using ErrorOrInt = std::pair<std::string, int>;
// Check that the std::system function can actually be used.
bool CheckExecuteCommand() {
int res = std::system(nullptr);