blob: c7bb6b28a1c81664a8a93a3d598e4953ab824e67 [file] [log] [blame]
// Copyright (c) 2026 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 <string>
#include "test/opt/assembly_builder.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"
namespace spvtools {
namespace opt {
namespace {
using LegalizeMultidimArrayTest = PassTest<::testing::Test>;
TEST_F(LegalizeMultidimArrayTest, Flatten2DResourceArray) {
// HLSL:
// Texture2D g_Textures[2][3];
// SamplerState g_Sampler;
// float4 main(float2 uv : TEXCOORD) : SV_Target {
// return g_Textures[0][1].Sample(g_Sampler, uv);
// }
const std::string text = R"(
; CHECK: %uint_6 = OpConstant %uint 6
; CHECK: %_arr_type_2d_image_uint_6 = OpTypeArray %type_2d_image %uint_6
; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_6 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_6
; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant
; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3
; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx]]
; CHECK: OpLoad %type_2d_image [[ptr]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %in_var_TEXCOORD %out_var_SV_Target
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 600
OpName %type_2d_image "type.2d.image"
OpName %g_Textures "g_Textures"
OpName %type_sampler "type.sampler"
OpName %g_Sampler "g_Sampler"
OpName %main "main"
OpName %src_main "src.main"
OpDecorate %in_var_TEXCOORD Location 0
OpDecorate %out_var_SV_Target Location 0
OpDecorate %g_Textures DescriptorSet 0
OpDecorate %g_Textures Binding 0
OpDecorate %g_Sampler DescriptorSet 0
OpDecorate %g_Sampler Binding 1
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
%type_sampler = OpTypeSampler
%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
%v2float = OpTypeVector %float 2
%_ptr_Input_v2float = OpTypePointer Input %v2float
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%void = OpTypeVoid
%24 = OpTypeFunction %void
%_ptr_Function_v2float = OpTypePointer Function %v2float
%31 = OpTypeFunction %v4float %_ptr_Function_v2float
%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
%type_sampled_image = OpTypeSampledImage %type_2d_image
%g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
%g_Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant
%in_var_TEXCOORD = OpVariable %_ptr_Input_v2float Input
%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %24
%25 = OpLabel
%param_var_uv = OpVariable %_ptr_Function_v2float Function
%28 = OpLoad %v2float %in_var_TEXCOORD
OpStore %param_var_uv %28
%29 = OpFunctionCall %v4float %src_main %param_var_uv
OpStore %out_var_SV_Target %29
OpReturn
OpFunctionEnd
%src_main = OpFunction %v4float None %31
%uv = OpFunctionParameter %_ptr_Function_v2float
%bb_entry = OpLabel
%37 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1
%38 = OpLoad %type_2d_image %37
%39 = OpLoad %type_sampler %g_Sampler
%40 = OpLoad %v2float %uv
%42 = OpSampledImage %type_sampled_image %38 %39
%43 = OpImageSampleImplicitLod %v4float %42 %40 None
OpReturnValue %43
OpFunctionEnd
)";
const std::string expected = R"(
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text,
/*do_validation=*/true);
}
// Test that the pass fails when the access chain is split into multiple access
// chains. We expect CombineAccessChains to be run before this pass to avoid
// this.
TEST_F(LegalizeMultidimArrayTest, IndirectUseViaPartialAccessChain) {
// HLSL source approximation:
// Texture2D g_Textures[2][3];
// ...
// Texture2D row[3] = g_Textures[0];
// return row[1].Sample(...);
//
// In SPIR-V, this often looks like:
// %ptr_row = OpAccessChain %_ptr_UniformConstant_arr_type_2d_image_uint_3
// %g_Textures %int_0 %ptr_tex = OpAccessChain
// %_ptr_UniformConstant_type_2d_image %ptr_row %int_1 OpLoad %type_2d_image
// %ptr_tex
const std::string text = R"(
; CHECK: Unable to legalize multidimensional array
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %out_var_SV_Target
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 600
OpName %type_2d_image "type.2d.image"
OpName %g_Textures "g_Textures"
OpName %main "main"
OpDecorate %out_var_SV_Target Location 0
OpDecorate %g_Textures DescriptorSet 0
OpDecorate %g_Textures Binding 0
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
%_ptr_UniformConstant__arr_type_2d_image_uint_3 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_3
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%void = OpTypeVoid
%24 = OpTypeFunction %void
%g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
%main = OpFunction %void None %24
%25 = OpLabel
%37 = OpAccessChain %_ptr_UniformConstant__arr_type_2d_image_uint_3 %g_Textures %int_0
%38 = OpAccessChain %_ptr_UniformConstant_type_2d_image %37 %int_1
%39 = OpLoad %type_2d_image %38
OpReturn
OpFunctionEnd
)";
SinglePassRunAndFail<LegalizeMultidimArrayPass>(text);
}
TEST_F(LegalizeMultidimArrayTest, Flatten3DResourceArray) {
// Texture2D g_Textures[2][3][4];
// Access: g_Textures[0][1][2]
const std::string text = R"(
; CHECK: %uint_24 = OpConstant %uint 24
; CHECK: %_arr_type_2d_image_uint_24 = OpTypeArray %type_2d_image %uint_24
; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_24 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_24
; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_24 UniformConstant
; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_12
; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_4
; CHECK: [[add1:%\w+]] = OpIAdd %uint [[mul1]] [[mul2]]
; CHECK: [[final_idx:%\w+]] = OpIAdd %uint [[add1]] %int_2
; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[final_idx]]
; CHECK: OpLoad %type_2d_image [[ptr]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %type_2d_image "type.2d.image"
OpName %g_Textures "g_Textures"
OpDecorate %g_Textures DescriptorSet 0
OpDecorate %g_Textures Binding 0
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%uint_4 = OpConstant %uint 4
%uint_12 = OpConstant %uint 12
%float = OpTypeFloat 32
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
%_arr_type_2d_image_uint_4 = OpTypeArray %type_2d_image %uint_4
%_arr_arr_type_2d_image_uint_4_uint_3 = OpTypeArray %_arr_type_2d_image_uint_4 %uint_3
%_arr_arr_arr_type_2d_image_uint_4_uint_3_uint_2 = OpTypeArray %_arr_arr_type_2d_image_uint_4_uint_3 %uint_2
%_ptr_UniformConstant_arr_3d = OpTypePointer UniformConstant %_arr_arr_arr_type_2d_image_uint_4_uint_3_uint_2
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%g_Textures = OpVariable %_ptr_UniformConstant_arr_3d UniformConstant
%main = OpFunction %void None %main_func
%label = OpLabel
%ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1 %int_2
%val = OpLoad %type_2d_image %ptr
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, FlattenSamplerArray) {
// SamplerState g_Samplers[2][2];
const std::string text = R"(
; CHECK: %uint_4 = OpConstant %uint 4
; CHECK: %_arr_type_sampler_uint_4 = OpTypeArray %type_sampler %uint_4
; CHECK: %_ptr_UniformConstant__arr_type_sampler_uint_4 = OpTypePointer UniformConstant %_arr_type_sampler_uint_4
; CHECK: %g_Samplers = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_4 UniformConstant
; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_2
; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers [[idx]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %type_sampler "type.sampler"
OpName %g_Samplers "g_Samplers"
OpDecorate %g_Samplers DescriptorSet 0
OpDecorate %g_Samplers Binding 0
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%type_sampler = OpTypeSampler
%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2
%_arr_arr_type_sampler_uint_2_uint_2 = OpTypeArray %_arr_type_sampler_uint_2 %uint_2
%_ptr_UniformConstant_arr_2d = OpTypePointer UniformConstant %_arr_arr_type_sampler_uint_2_uint_2
%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%g_Samplers = OpVariable %_ptr_UniformConstant_arr_2d UniformConstant
%main = OpFunction %void None %main_func
%label = OpLabel
%ptr = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers %int_0 %int_1
%val = OpLoad %type_sampler %ptr
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, FlattenStorageBufferArray) {
// struct S { float f; };
// S buffers[2][3];
// buffers[0][1].f
const std::string text = R"(
; CHECK: %uint_6 = OpConstant %uint 6
; CHECK: %_arr_S_uint_6 = OpTypeArray %S %uint_6
; CHECK: %_ptr_StorageBuffer__arr_S_uint_6 = OpTypePointer StorageBuffer %_arr_S_uint_6
; CHECK: %g_Buffers = OpVariable %_ptr_StorageBuffer__arr_S_uint_6 StorageBuffer
; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3
; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_StorageBuffer_float %g_Buffers [[idx]] %int_0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %S "S"
OpName %g_Buffers "g_Buffers"
OpDecorate %g_Buffers DescriptorSet 0
OpDecorate %g_Buffers Binding 0
OpMemberDecorate %S 0 Offset 0
OpDecorate %S Block
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%S = OpTypeStruct %float
%_arr_S_uint_3 = OpTypeArray %S %uint_3
%_arr__arr_S_uint_3_uint_2 = OpTypeArray %_arr_S_uint_3 %uint_2
%_ptr_StorageBuffer_arr_2d = OpTypePointer StorageBuffer %_arr__arr_S_uint_3_uint_2
%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%g_Buffers = OpVariable %_ptr_StorageBuffer_arr_2d StorageBuffer
%main = OpFunction %void None %main_func
%label = OpLabel
%ptr = OpAccessChain %_ptr_StorageBuffer_float %g_Buffers %int_0 %int_1 %int_0
%val = OpLoad %float %ptr
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, FlattenUniformArray) {
// Uniform buffer array: MyBlock buffers[2][3];
// Access: buffers[0][1].member
const std::string text = R"(
; CHECK: %uint_6 = OpConstant %uint 6
; CHECK: %_arr_MyBlock_uint_6 = OpTypeArray %MyBlock %uint_6
; CHECK: %_ptr_Uniform__arr_MyBlock_uint_6 = OpTypePointer Uniform %_arr_MyBlock_uint_6
; CHECK: %g_Uniforms = OpVariable %_ptr_Uniform__arr_MyBlock_uint_6 Uniform
; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3
; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_Uniform_float %g_Uniforms [[idx]] %int_0
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %MyBlock "MyBlock"
OpName %g_Uniforms "g_Uniforms"
OpDecorate %g_Uniforms DescriptorSet 0
OpDecorate %g_Uniforms Binding 0
OpMemberDecorate %MyBlock 0 Offset 0
OpDecorate %MyBlock Block
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%MyBlock = OpTypeStruct %float
%_arr_MyBlock_uint_3 = OpTypeArray %MyBlock %uint_3
%_arr__arr_MyBlock_uint_3_uint_2 = OpTypeArray %_arr_MyBlock_uint_3 %uint_2
%_ptr_Uniform__arr__arr_MyBlock_uint_3_uint_2 = OpTypePointer Uniform %_arr__arr_MyBlock_uint_3_uint_2
%_ptr_Uniform_float = OpTypePointer Uniform %float
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%g_Uniforms = OpVariable %_ptr_Uniform__arr__arr_MyBlock_uint_3_uint_2 Uniform
%main = OpFunction %void None %main_func
%label = OpLabel
%ptr = OpAccessChain %_ptr_Uniform_float %g_Uniforms %int_0 %int_1 %int_0
%val = OpLoad %float %ptr
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, AccessChainThroughCopyObject) {
// Texture2D g_Textures[2][3];
// %copy = OpCopyObject %ptr_type %g_Textures
// %ptr = OpAccessChain %... %copy %int_0 %int_1
const std::string text = R"(
; CHECK: %uint_6 = OpConstant %uint 6
; CHECK: %_arr_type_2d_image_uint_6 = OpTypeArray %type_2d_image %uint_6
; CHECK: %_ptr_UniformConstant__arr_type_2d_image_uint_6 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_6
; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant
; CHECK: [[copy:%\w+]] = OpCopyObject %_ptr_UniformConstant__arr_type_2d_image_uint_6 %g_Textures
; CHECK: [[mul:%\w+]] = OpIMul %uint %int_0 %uint_3
; CHECK: [[idx:%\w+]] = OpIAdd %uint [[mul]] %int_1
; CHECK: [[ptr:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image [[copy]] [[idx]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %type_2d_image "type.2d.image"
OpName %g_Textures "g_Textures"
OpDecorate %g_Textures DescriptorSet 0
OpDecorate %g_Textures Binding 0
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
%main = OpFunction %void None %main_func
%label = OpLabel
%copy = OpCopyObject %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 %g_Textures
%ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %copy %int_0 %int_1
%val = OpLoad %type_2d_image %ptr
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, DynamicIndices) {
// Access with non-constant indices.
// g_Textures[var_i][var_j]
const std::string text = R"(
; CHECK: [[idx1:%\w+]] = OpLoad %int %idx_var_1
; CHECK: [[idx2:%\w+]] = OpLoad %int %idx_var_2
; CHECK: [[mul:%\w+]] = OpIMul %uint [[idx1]] %uint_3
; CHECK: [[add:%\w+]] = OpIAdd %uint [[mul]] [[idx2]]
; CHECK: OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[add]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %type_2d_image "type.2d.image"
OpName %g_Textures "g_Textures"
OpName %idx_var_1 "idx_var_1"
OpName %idx_var_2 "idx_var_2"
OpDecorate %g_Textures DescriptorSet 0
OpDecorate %g_Textures Binding 0
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
%_ptr_Function_int = OpTypePointer Function %int
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
%main = OpFunction %void None %main_func
%label = OpLabel
%idx_var_1 = OpVariable %_ptr_Function_int Function
%idx_var_2 = OpVariable %_ptr_Function_int Function
%i = OpLoad %int %idx_var_1
%j = OpLoad %int %idx_var_2
%ptr = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %i %j
%val = OpLoad %type_2d_image %ptr
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, IgnoreFunctionScopeArray) {
// Function scope array [2][3] should NOT be legalized.
const std::string text = R"(
; CHECK: %_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2
; CHECK: %_ptr_Function__arr__arr_float_uint_3_uint_2 = OpTypePointer Function %_arr__arr_float_uint_3_uint_2
; CHECK: %var = OpVariable %_ptr_Function__arr__arr_float_uint_3_uint_2 Function
; CHECK: OpAccessChain %_ptr_Function_float %var %int_0 %int_1
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %var "var"
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%_arr_float_uint_3 = OpTypeArray %float %uint_3
%_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2
%_ptr_Function__arr__arr_float_uint_3_uint_2 = OpTypePointer Function %_arr__arr_float_uint_3_uint_2
%_ptr_Function_float = OpTypePointer Function %float
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%main = OpFunction %void None %main_func
%label = OpLabel
%var = OpVariable %_ptr_Function__arr__arr_float_uint_3_uint_2 Function
%ptr = OpAccessChain %_ptr_Function_float %var %int_0 %int_1
%val = OpLoad %float %ptr
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, IgnoreWorkgroupScopeArray) {
// Workgroup scope array [2][3] should NOT be legalized.
const std::string text = R"(
; CHECK: %_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2
; CHECK: %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 = OpTypePointer Workgroup %_arr__arr_float_uint_3_uint_2
; CHECK: %var = OpVariable %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 Workgroup
; CHECK: OpAccessChain %_ptr_Workgroup_float %var %int_0 %int_1
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %var "var"
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%_arr_float_uint_3 = OpTypeArray %float %uint_3
%_arr__arr_float_uint_3_uint_2 = OpTypeArray %_arr_float_uint_3 %uint_2
%_ptr_Workgroup__arr__arr_float_uint_3_uint_2 = OpTypePointer Workgroup %_arr__arr_float_uint_3_uint_2
%_ptr_Workgroup_float = OpTypePointer Workgroup %float
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%var = OpVariable %_ptr_Workgroup__arr__arr_float_uint_3_uint_2 Workgroup
%main = OpFunction %void None %main_func
%label = OpLabel
%ptr = OpAccessChain %_ptr_Workgroup_float %var %int_0 %int_1
%val = OpLoad %float %ptr
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, MultipleAccessChains) {
// Access g_Textures[0][1] and g_Textures[1][2] in the same function.
const std::string text = R"(
; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_3
; CHECK: [[idx1:%\w+]] = OpIAdd %uint [[mul1]] %int_1
; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx1]]
; CHECK: OpLoad %type_2d_image [[ptr1]]
; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_3
; CHECK: [[idx2:%\w+]] = OpIAdd %uint [[mul2]] %int_2
; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx2]]
; CHECK: OpLoad %type_2d_image [[ptr2]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %type_2d_image "type.2d.image"
OpName %g_Textures "g_Textures"
OpDecorate %g_Textures DescriptorSet 0
OpDecorate %g_Textures Binding 0
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
%main = OpFunction %void None %main_func
%label = OpLabel
%ptr1 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1
%val1 = OpLoad %type_2d_image %ptr1
%ptr2 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_1 %int_2
%val2 = OpLoad %type_2d_image %ptr2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
TEST_F(LegalizeMultidimArrayTest, MultipleResources) {
// Two different resource arrays:
// Texture2D g_Textures[2][3];
// SamplerState g_Samplers[2][2];
const std::string text = R"(
; CHECK: %g_Textures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_6 UniformConstant
; CHECK: %g_Samplers = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_4 UniformConstant
; CHECK: [[mul1:%\w+]] = OpIMul %uint %int_0 %uint_3
; CHECK: [[idx1:%\w+]] = OpIAdd %uint [[mul1]] %int_1
; CHECK: [[ptr1:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures [[idx1]]
; CHECK: OpLoad %type_2d_image [[ptr1]]
; CHECK: [[mul2:%\w+]] = OpIMul %uint %int_1 %uint_2
; CHECK: [[idx2:%\w+]] = OpIAdd %uint [[mul2]] %int_1
; CHECK: [[ptr2:%\w+]] = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers [[idx2]]
; CHECK: OpLoad %type_sampler [[ptr2]]
OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpName %type_2d_image "type.2d.image"
OpName %g_Textures "g_Textures"
OpName %type_sampler "type.sampler"
OpName %g_Samplers "g_Samplers"
OpDecorate %g_Textures DescriptorSet 0
OpDecorate %g_Textures Binding 0
OpDecorate %g_Samplers DescriptorSet 0
OpDecorate %g_Samplers Binding 1
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
%uint_3 = OpConstant %uint 3
%float = OpTypeFloat 32
%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown
%_arr_type_2d_image_uint_3 = OpTypeArray %type_2d_image %uint_3
%_arr__arr_type_2d_image_uint_3_uint_2 = OpTypeArray %_arr_type_2d_image_uint_3 %uint_2
%_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 = OpTypePointer UniformConstant %_arr__arr_type_2d_image_uint_3_uint_2
%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image
%type_sampler = OpTypeSampler
%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2
%_arr_arr_type_sampler_uint_2_uint_2 = OpTypeArray %_arr_type_sampler_uint_2 %uint_2
%_ptr_UniformConstant_arr_2d_sampler = OpTypePointer UniformConstant %_arr_arr_type_sampler_uint_2_uint_2
%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler
%void = OpTypeVoid
%main_func = OpTypeFunction %void
%g_Textures = OpVariable %_ptr_UniformConstant__arr__arr_type_2d_image_uint_3_uint_2 UniformConstant
%g_Samplers = OpVariable %_ptr_UniformConstant_arr_2d_sampler UniformConstant
%main = OpFunction %void None %main_func
%label = OpLabel
%ptr1 = OpAccessChain %_ptr_UniformConstant_type_2d_image %g_Textures %int_0 %int_1
%val1 = OpLoad %type_2d_image %ptr1
%ptr2 = OpAccessChain %_ptr_UniformConstant_type_sampler %g_Samplers %int_1 %int_1
%val2 = OpLoad %type_sampler %ptr2
OpReturn
OpFunctionEnd
)";
SinglePassRunAndMatch<LegalizeMultidimArrayPass>(text, true);
}
} // namespace
} // namespace opt
} // namespace spvtools