spirv-fuzz: do not replace struct indices with synonyms (#2915)

This change introduces a robust check for whether an index in an
access chain is indexing into a struct, in which case the index needs
to be an OpConstant and cannot be replaced with a synonym.

Fixes #2906.
This commit is contained in:
Alastair Donaldson 2019-09-25 16:52:35 +01:00 committed by GitHub
parent c1e03834e3
commit 70097c7761
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 581 additions and 4 deletions

View File

@ -19,6 +19,7 @@
#include "source/fuzz/data_descriptor.h"
#include "source/fuzz/fuzzer_util.h"
#include "source/fuzz/id_use_descriptor.h"
#include "source/opt/types.h"
namespace spvtools {
namespace fuzz {
@ -116,15 +117,52 @@ bool TransformationReplaceIdWithSynonym::ReplacingUseWithSynonymIsOk(
if (use_instruction->opcode() == SpvOpAccessChain &&
use_in_operand_index > 0) {
// This is an access chain index. If the object being accessed has
// pointer-to-struct type then we cannot replace the use with a synonym, as
// the use needs to be an OpConstant.
// This is an access chain index. If the (sub-)object being accessed by the
// given index has struct type then we cannot replace the use with a
// synonym, as the use needs to be an OpConstant.
// Get the top-level composite type that is being accessed.
auto object_being_accessed = context->get_def_use_mgr()->GetDef(
use_instruction->GetSingleWordInOperand(0));
auto pointer_type =
context->get_type_mgr()->GetType(object_being_accessed->type_id());
assert(pointer_type->AsPointer());
if (pointer_type->AsPointer()->pointee_type()->AsStruct()) {
auto composite_type_being_accessed =
pointer_type->AsPointer()->pointee_type();
// Now walk the access chain, tracking the type of each sub-object of the
// composite that is traversed, until the index of interest is reached.
for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index;
index_in_operand++) {
// For vectors, matrices and arrays, getting the type of the sub-object is
// trivial. For the struct case, the sub-object type is field-sensitive,
// and depends on the constant index that is used.
if (composite_type_being_accessed->AsVector()) {
composite_type_being_accessed =
composite_type_being_accessed->AsVector()->element_type();
} else if (composite_type_being_accessed->AsMatrix()) {
composite_type_being_accessed =
composite_type_being_accessed->AsMatrix()->element_type();
} else if (composite_type_being_accessed->AsArray()) {
composite_type_being_accessed =
composite_type_being_accessed->AsArray()->element_type();
} else {
assert(composite_type_being_accessed->AsStruct());
auto constant_index_instruction = context->get_def_use_mgr()->GetDef(
use_instruction->GetSingleWordInOperand(index_in_operand));
assert(constant_index_instruction->opcode() == SpvOpConstant);
uint32_t member_index =
constant_index_instruction->GetSingleWordInOperand(0);
composite_type_being_accessed =
composite_type_being_accessed->AsStruct()
->element_types()[member_index];
}
}
// We have found the composite type being accessed by the index we are
// considering replacing. If it is a struct, then we cannot do the
// replacement as struct indices must be constants.
if (composite_type_being_accessed->AsStruct()) {
return false;
}
}

View File

@ -632,6 +632,545 @@ TEST(TransformationReplaceIdWithSynonymTest,
ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
}
TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) {
// The following SPIR-V comes from this GLSL, with object copies added:
//
// #version 310 es
//
// precision highp float;
// precision highp int;
//
// struct S {
// int[3] a;
// vec4 b;
// bool c;
// } d;
//
// float[20] e;
//
// struct T {
// float f;
// S g;
// } h;
//
// T[4] i;
//
// void main() {
// d.a[2] = 10;
// d.b[3] = 11.0;
// d.c = false;
// e[17] = 12.0;
// h.f = 13.0;
// h.g.a[1] = 14;
// h.g.b[0] = 15.0;
// h.g.c = true;
// i[0].f = 16.0;
// i[1].g.a[0] = 17;
// i[2].g.b[1] = 18.0;
// i[3].g.c = true;
// }
const std::string shader = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %13 "S"
OpMemberName %13 0 "a"
OpMemberName %13 1 "b"
OpMemberName %13 2 "c"
OpName %15 "d"
OpName %31 "e"
OpName %35 "T"
OpMemberName %35 0 "f"
OpMemberName %35 1 "g"
OpName %37 "h"
OpName %50 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeInt 32 0
%8 = OpConstant %7 3
%9 = OpTypeArray %6 %8
%10 = OpTypeFloat 32
%11 = OpTypeVector %10 4
%12 = OpTypeBool
%13 = OpTypeStruct %9 %11 %12
%14 = OpTypePointer Private %13
%15 = OpVariable %14 Private
%16 = OpConstant %6 0
%17 = OpConstant %6 2
%18 = OpConstant %6 10
%19 = OpTypePointer Private %6
%21 = OpConstant %6 1
%22 = OpConstant %10 11
%23 = OpTypePointer Private %10
%25 = OpConstantFalse %12
%26 = OpTypePointer Private %12
%28 = OpConstant %7 20
%29 = OpTypeArray %10 %28
%30 = OpTypePointer Private %29
%31 = OpVariable %30 Private
%32 = OpConstant %6 17
%33 = OpConstant %10 12
%35 = OpTypeStruct %10 %13
%36 = OpTypePointer Private %35
%37 = OpVariable %36 Private
%38 = OpConstant %10 13
%40 = OpConstant %6 14
%42 = OpConstant %10 15
%43 = OpConstant %7 0
%45 = OpConstantTrue %12
%47 = OpConstant %7 4
%48 = OpTypeArray %35 %47
%49 = OpTypePointer Private %48
%50 = OpVariable %49 Private
%51 = OpConstant %10 16
%54 = OpConstant %10 18
%55 = OpConstant %7 1
%57 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%100 = OpCopyObject %6 %16 ; 0
%101 = OpCopyObject %6 %21 ; 1
%102 = OpCopyObject %6 %17 ; 2
%103 = OpCopyObject %6 %57 ; 3
%104 = OpCopyObject %6 %18 ; 10
%105 = OpCopyObject %6 %40 ; 14
%106 = OpCopyObject %6 %32 ; 17
%107 = OpCopyObject %7 %43 ; 0
%108 = OpCopyObject %7 %55 ; 1
%109 = OpCopyObject %7 %8 ; 3
%110 = OpCopyObject %7 %47 ; 4
%111 = OpCopyObject %7 %28 ; 20
%112 = OpCopyObject %12 %45 ; true
%20 = OpAccessChain %19 %15 %16 %17
OpStore %20 %18
%24 = OpAccessChain %23 %15 %21 %8
OpStore %24 %22
%27 = OpAccessChain %26 %15 %17
OpStore %27 %25
%34 = OpAccessChain %23 %31 %32
OpStore %34 %33
%39 = OpAccessChain %23 %37 %16
OpStore %39 %38
%41 = OpAccessChain %19 %37 %21 %16 %21
OpStore %41 %40
%44 = OpAccessChain %23 %37 %21 %21 %43
OpStore %44 %42
%46 = OpAccessChain %26 %37 %21 %17
OpStore %46 %45
%52 = OpAccessChain %23 %50 %16 %16
OpStore %52 %51
%53 = OpAccessChain %19 %50 %21 %21 %16 %16
OpStore %53 %32
%56 = OpAccessChain %23 %50 %17 %21 %21 %55
OpStore %56 %54
%58 = OpAccessChain %26 %50 %57 %21 %17
OpStore %58 %45
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;
// Add synonym facts corresponding to the OpCopyObject operations that have
// been applied to all constants in the module.
fact_manager.AddFact(MakeFact(16, 100), context.get());
fact_manager.AddFact(MakeFact(21, 101), context.get());
fact_manager.AddFact(MakeFact(17, 102), context.get());
fact_manager.AddFact(MakeFact(57, 103), context.get());
fact_manager.AddFact(MakeFact(18, 104), context.get());
fact_manager.AddFact(MakeFact(40, 105), context.get());
fact_manager.AddFact(MakeFact(32, 106), context.get());
fact_manager.AddFact(MakeFact(43, 107), context.get());
fact_manager.AddFact(MakeFact(55, 108), context.get());
fact_manager.AddFact(MakeFact(8, 109), context.get());
fact_manager.AddFact(MakeFact(47, 110), context.get());
fact_manager.AddFact(MakeFact(28, 111), context.get());
fact_manager.AddFact(MakeFact(45, 112), context.get());
// Replacements of the form %16 -> %100
// %20 = OpAccessChain %19 %15 *%16* %17
// Corresponds to d.*a*[2]
// The index %16 used for a cannot be replaced
auto replacement1 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 20, 0),
MakeDataDescriptor(100, {}), 0);
ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager));
// %39 = OpAccessChain %23 %37 *%16*
// Corresponds to h.*f*
// The index %16 used for f cannot be replaced
auto replacement2 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 39, 0),
MakeDataDescriptor(100, {}), 0);
ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager));
// %41 = OpAccessChain %19 %37 %21 *%16* %21
// Corresponds to h.g.*a*[1]
// The index %16 used for a cannot be replaced
auto replacement3 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 41, 0),
MakeDataDescriptor(100, {}), 0);
ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager));
// %52 = OpAccessChain %23 %50 *%16* %16
// Corresponds to i[*0*].f
// The index %16 used for 0 *can* be replaced
auto replacement4 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 52, 0),
MakeDataDescriptor(100, {}), 0);
ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
replacement4.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// %52 = OpAccessChain %23 %50 %16 *%16*
// Corresponds to i[0].*f*
// The index %16 used for f cannot be replaced
auto replacement5 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 52, 0),
MakeDataDescriptor(100, {}), 0);
ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager));
// %53 = OpAccessChain %19 %50 %21 %21 *%16* %16
// Corresponds to i[1].g.*a*[0]
// The index %16 used for a cannot be replaced
auto replacement6 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 3, 53, 0),
MakeDataDescriptor(100, {}), 0);
ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager));
// %53 = OpAccessChain %19 %50 %21 %21 %16 *%16*
// Corresponds to i[1].g.a[*0*]
// The index %16 used for 0 *can* be replaced
auto replacement7 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 4, 53, 0),
MakeDataDescriptor(100, {}), 0);
ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager));
replacement7.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Replacements of the form %21 -> %101
// %24 = OpAccessChain %23 %15 *%21* %8
// Corresponds to d.*b*[3]
// The index %24 used for b cannot be replaced
auto replacement8 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 24, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager));
// %41 = OpAccessChain %19 %37 *%21* %16 %21
// Corresponds to h.*g*.a[1]
// The index %24 used for g cannot be replaced
auto replacement9 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 41, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager));
// %41 = OpAccessChain %19 %37 %21 %16 *%21*
// Corresponds to h.g.a[*1*]
// The index %24 used for 1 *can* be replaced
auto replacement10 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 41, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager));
replacement10.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// %44 = OpAccessChain %23 %37 *%21* %21 %43
// Corresponds to h.*g*.b[0]
// The index %24 used for g cannot be replaced
auto replacement11 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 44, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager));
// %44 = OpAccessChain %23 %37 %21 *%21* %43
// Corresponds to h.g.*b*[0]
// The index %24 used for b cannot be replaced
auto replacement12 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 44, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager));
// %46 = OpAccessChain %26 %37 *%21* %17
// Corresponds to h.*g*.c
// The index %24 used for g cannot be replaced
auto replacement13 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 46, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager));
// %53 = OpAccessChain %19 %50 *%21* %21 %16 %16
// Corresponds to i[*1*].g.a[0]
// The index %24 used for 1 *can* be replaced
auto replacement14 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 53, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager));
replacement14.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// %53 = OpAccessChain %19 %50 %21 *%21* %16 %16
// Corresponds to i[1].*g*.a[0]
// The index %24 used for g cannot be replaced
auto replacement15 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 53, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager));
// %56 = OpAccessChain %23 %50 %17 *%21* %21 %55
// Corresponds to i[2].*g*.b[1]
// The index %24 used for g cannot be replaced
auto replacement16 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 56, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager));
// %56 = OpAccessChain %23 %50 %17 %21 *%21* %55
// Corresponds to i[2].g.*b*[1]
// The index %24 used for b cannot be replaced
auto replacement17 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 56, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager));
// %58 = OpAccessChain %26 %50 %57 *%21* %17
// Corresponds to i[3].*g*.c
// The index %24 used for g cannot be replaced
auto replacement18 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 58, 0),
MakeDataDescriptor(101, {}), 0);
ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager));
// Replacements of the form %17 -> %102
// %20 = OpAccessChain %19 %15 %16 %17
// Corresponds to d.a[*2*]
// The index %17 used for 2 *can* be replaced
auto replacement19 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 20, 0),
MakeDataDescriptor(102, {}), 0);
ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager));
replacement19.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// %27 = OpAccessChain %26 %15 %17
// Corresponds to d.c
// The index %17 used for c cannot be replaced
auto replacement20 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 27, 0),
MakeDataDescriptor(102, {}), 0);
ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager));
// %46 = OpAccessChain %26 %37 %21 %17
// Corresponds to h.g.*c*
// The index %17 used for c cannot be replaced
auto replacement21 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 46, 0),
MakeDataDescriptor(102, {}), 0);
ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager));
// %56 = OpAccessChain %23 %50 %17 %21 %21 %55
// Corresponds to i[*2*].g.b[1]
// The index %17 used for 2 *can* be replaced
auto replacement22 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 56, 0),
MakeDataDescriptor(102, {}), 0);
ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager));
replacement22.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// %58 = OpAccessChain %26 %50 %57 %21 %17
// Corresponds to i[3].g.*c*
// The index %17 used for c cannot be replaced
auto replacement23 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 3, 58, 0),
MakeDataDescriptor(102, {}), 0);
ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager));
// Replacements of the form %57 -> %103
// %58 = OpAccessChain %26 %50 *%57* %21 %17
// Corresponds to i[*3*].g.c
// The index %57 used for 3 *can* be replaced
auto replacement24 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(57, SpvOpAccessChain, 1, 58, 0),
MakeDataDescriptor(103, {}), 0);
ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager));
replacement24.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Replacements of the form %32 -> %106
// %34 = OpAccessChain %23 %31 *%32*
// Corresponds to e[*17*]
// The index %32 used for 17 *can* be replaced
auto replacement25 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(32, SpvOpAccessChain, 1, 34, 0),
MakeDataDescriptor(106, {}), 0);
ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager));
replacement25.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Replacements of the form %43 -> %107
// %44 = OpAccessChain %23 %37 %21 %21 *%43*
// Corresponds to h.g.b[*0*]
// The index %43 used for 0 *can* be replaced
auto replacement26 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(43, SpvOpAccessChain, 3, 44, 0),
MakeDataDescriptor(107, {}), 0);
ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager));
replacement26.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Replacements of the form %55 -> %108
// %56 = OpAccessChain %23 %50 %17 %21 %21 *%55*
// Corresponds to i[2].g.b[*1*]
// The index %55 used for 1 *can* be replaced
auto replacement27 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(55, SpvOpAccessChain, 4, 56, 0),
MakeDataDescriptor(108, {}), 0);
ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager));
replacement27.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
// Replacements of the form %8 -> %109
// %24 = OpAccessChain %23 %15 %21 *%8*
// Corresponds to d.b[*3*]
// The index %8 used for 3 *can* be replaced
auto replacement28 = TransformationReplaceIdWithSynonym(
transformation::MakeIdUseDescriptor(8, SpvOpAccessChain, 2, 24, 0),
MakeDataDescriptor(109, {}), 0);
ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager));
replacement28.Apply(context.get(), &fact_manager);
ASSERT_TRUE(IsValid(env, context.get()));
const std::string after_transformation = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 310
OpName %4 "main"
OpName %13 "S"
OpMemberName %13 0 "a"
OpMemberName %13 1 "b"
OpMemberName %13 2 "c"
OpName %15 "d"
OpName %31 "e"
OpName %35 "T"
OpMemberName %35 0 "f"
OpMemberName %35 1 "g"
OpName %37 "h"
OpName %50 "i"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeInt 32 0
%8 = OpConstant %7 3
%9 = OpTypeArray %6 %8
%10 = OpTypeFloat 32
%11 = OpTypeVector %10 4
%12 = OpTypeBool
%13 = OpTypeStruct %9 %11 %12
%14 = OpTypePointer Private %13
%15 = OpVariable %14 Private
%16 = OpConstant %6 0
%17 = OpConstant %6 2
%18 = OpConstant %6 10
%19 = OpTypePointer Private %6
%21 = OpConstant %6 1
%22 = OpConstant %10 11
%23 = OpTypePointer Private %10
%25 = OpConstantFalse %12
%26 = OpTypePointer Private %12
%28 = OpConstant %7 20
%29 = OpTypeArray %10 %28
%30 = OpTypePointer Private %29
%31 = OpVariable %30 Private
%32 = OpConstant %6 17
%33 = OpConstant %10 12
%35 = OpTypeStruct %10 %13
%36 = OpTypePointer Private %35
%37 = OpVariable %36 Private
%38 = OpConstant %10 13
%40 = OpConstant %6 14
%42 = OpConstant %10 15
%43 = OpConstant %7 0
%45 = OpConstantTrue %12
%47 = OpConstant %7 4
%48 = OpTypeArray %35 %47
%49 = OpTypePointer Private %48
%50 = OpVariable %49 Private
%51 = OpConstant %10 16
%54 = OpConstant %10 18
%55 = OpConstant %7 1
%57 = OpConstant %6 3
%4 = OpFunction %2 None %3
%5 = OpLabel
%100 = OpCopyObject %6 %16 ; 0
%101 = OpCopyObject %6 %21 ; 1
%102 = OpCopyObject %6 %17 ; 2
%103 = OpCopyObject %6 %57 ; 3
%104 = OpCopyObject %6 %18 ; 10
%105 = OpCopyObject %6 %40 ; 14
%106 = OpCopyObject %6 %32 ; 17
%107 = OpCopyObject %7 %43 ; 0
%108 = OpCopyObject %7 %55 ; 1
%109 = OpCopyObject %7 %8 ; 3
%110 = OpCopyObject %7 %47 ; 4
%111 = OpCopyObject %7 %28 ; 20
%112 = OpCopyObject %12 %45 ; true
%20 = OpAccessChain %19 %15 %16 %102
OpStore %20 %18
%24 = OpAccessChain %23 %15 %21 %109
OpStore %24 %22
%27 = OpAccessChain %26 %15 %17
OpStore %27 %25
%34 = OpAccessChain %23 %31 %106
OpStore %34 %33
%39 = OpAccessChain %23 %37 %16
OpStore %39 %38
%41 = OpAccessChain %19 %37 %21 %16 %101
OpStore %41 %40
%44 = OpAccessChain %23 %37 %21 %21 %107
OpStore %44 %42
%46 = OpAccessChain %26 %37 %21 %17
OpStore %46 %45
%52 = OpAccessChain %23 %50 %100 %16
OpStore %52 %51
%53 = OpAccessChain %19 %50 %101 %21 %16 %100
OpStore %53 %32
%56 = OpAccessChain %23 %50 %102 %21 %21 %108
OpStore %56 %54
%58 = OpAccessChain %26 %50 %103 %21 %17
OpStore %58 %45
OpReturn
OpFunctionEnd
)";
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
} // namespace
} // namespace fuzz
} // namespace spvtools