Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions reference/shaders-hlsl-no-opt/asm/comp/phi-undef-loop.asm.comp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
static uint _16;

RWByteAddressBuffer _4 : register(u0);

void comp_main()
{
uint _19 = 0u;
for (uint _22 = 0u; _22 < 10u; _19++, _22++)
{
_4.Store(0, _19);
}
}

[numthreads(1, 1, 1)]
void main()
{
comp_main();
}
55 changes: 55 additions & 0 deletions shaders-hlsl-no-opt/asm/comp/phi-undef-loop.asm.comp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
; Tests an OpPhi at a loop header where one incoming value is OpUndef
; (the other is the loop back-edge). This pattern is produced by
; --merge-return + --inline-entry-points-exhaustive when an inlined function
; with an early `return` inside a loop is folded back. Reading the variable
; on the first iteration would be undefined; we materialize the OpUndef
; incoming as a zero so HLSL/FXC do not reject the generated code with X4555/X4000.
;
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 1
; Bound: 30
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint GLCompute %main "main"
OpExecutionMode %main LocalSize 1 1 1
OpMemberDecorate %SSBO 0 Offset 0
OpDecorate %SSBO BufferBlock
OpDecorate %ssbo DescriptorSet 0
OpDecorate %ssbo Binding 0
%void = OpTypeVoid
%fn_type = OpTypeFunction %void
%bool = OpTypeBool
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%uint_1 = OpConstant %uint 1
%uint_10 = OpConstant %uint 10
%SSBO = OpTypeStruct %uint
%ptr_SSBO = OpTypePointer Uniform %SSBO
%ptr_uint = OpTypePointer Uniform %uint
%ssbo = OpVariable %ptr_SSBO Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%undef_uint = OpUndef %uint
%main = OpFunction %void None %fn_type
%entry = OpLabel
OpBranch %header
%header = OpLabel
%phi = OpPhi %uint %undef_uint %entry %back %continue
%i = OpPhi %uint %uint_0 %entry %inext %continue
%cond = OpULessThan %bool %i %uint_10
OpLoopMerge %merge %continue None
OpBranchConditional %cond %body %merge
%body = OpLabel
%dst = OpAccessChain %ptr_uint %ssbo %int_0
OpStore %dst %phi
OpBranch %continue
%continue = OpLabel
%back = OpIAdd %uint %phi %uint_1
%inext = OpIAdd %uint %i %uint_1
OpBranch %header
%merge = OpLabel
OpReturn
OpFunctionEnd
29 changes: 25 additions & 4 deletions spirv_glsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17943,13 +17943,30 @@ string CompilerGLSL::emit_continue_block(uint32_t continue_block, bool follow_tr
return merge(statements);
}

// Loop variable with OpUndef init: zero-init instead of leaving uninitialized (FXC X4555/X4000).
std::string CompilerGLSL::undef_loop_variable_initializer_suffix(const SPIRVariable &var)
{
if (!backend.requires_phi_undef_zero_init)
return "";

uint32_t expr = var.static_expression;
if (expr == 0 || ir.ids[expr].get_type() != TypeUndef)
return "";

auto &type = get<SPIRType>(var.basetype);
if (!type_can_zero_initialize(type))
return "";

return join(" = ", to_zero_initialized_expression(var.basetype));
}

void CompilerGLSL::emit_while_loop_initializers(const SPIRBlock &block)
{
// While loops do not take initializers, so declare all of them outside.
for (auto &loop_var : block.loop_variables)
{
auto &var = get<SPIRVariable>(loop_var);
statement(variable_decl(var), ";");
statement(variable_decl(var), undef_loop_variable_initializer_suffix(var), ";");
}
}

Expand Down Expand Up @@ -17981,7 +17998,10 @@ string CompilerGLSL::emit_for_loop_initializers(const SPIRBlock &block)
else if (!same_types || missing_initializers == uint32_t(block.loop_variables.size()))
{
for (auto &loop_var : block.loop_variables)
statement(variable_decl(get<SPIRVariable>(loop_var)), ";");
{
auto &var = get<SPIRVariable>(loop_var);
statement(variable_decl(var), undef_loop_variable_initializer_suffix(var), ";");
}
return "";
}
else
Expand All @@ -17992,10 +18012,11 @@ string CompilerGLSL::emit_for_loop_initializers(const SPIRBlock &block)

for (auto &loop_var : block.loop_variables)
{
uint32_t static_expr = get<SPIRVariable>(loop_var).static_expression;
auto &var_for_undef = get<SPIRVariable>(loop_var);
uint32_t static_expr = var_for_undef.static_expression;
if (static_expr == 0 || ir.ids[static_expr].get_type() == TypeUndef)
{
statement(variable_decl(get<SPIRVariable>(loop_var)), ";");
statement(variable_decl(var_for_undef), undef_loop_variable_initializer_suffix(var_for_undef), ";");
}
else
{
Expand Down
2 changes: 2 additions & 0 deletions spirv_glsl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ class CompilerGLSL : public Compiler
bool requires_relaxed_precision_analysis = false;
bool implicit_c_integer_promotion_rules = false;
bool supports_spec_constant_array_size = true;
bool requires_phi_undef_zero_init = false;
} backend;

void emit_struct(SPIRType &type);
Expand Down Expand Up @@ -1023,6 +1024,7 @@ class CompilerGLSL : public Compiler

std::string emit_for_loop_initializers(const SPIRBlock &block);
void emit_while_loop_initializers(const SPIRBlock &block);
std::string undef_loop_variable_initializer_suffix(const SPIRVariable &var);
bool for_loop_initializers_are_same_type(const SPIRBlock &block);
bool optimize_read_modify_write(const SPIRType &type, const std::string &lhs, const std::string &rhs);
void fixup_image_load_store_access();
Expand Down
1 change: 1 addition & 0 deletions spirv_hlsl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7101,6 +7101,7 @@ string CompilerHLSL::compile()
backend.can_return_array = false;
backend.nonuniform_qualifier = "NonUniformResourceIndex";
backend.support_case_fallthrough = false;
backend.requires_phi_undef_zero_init = true;
backend.force_merged_mesh_block = get_execution_model() == ExecutionModelMeshEXT;
backend.force_gl_in_out_block = backend.force_merged_mesh_block;
backend.supports_empty_struct = hlsl_options.shader_model <= 30;
Expand Down
Loading