Fix Windows Clang SPIR-V serialization with typed operands

This commit is contained in:
nmzik
2026-09-12 13:08:23 +02:00
parent 3a27f8a76a
commit 1208d22547
17 changed files with 1204 additions and 1084 deletions
+41 -43
View File
@@ -1603,30 +1603,29 @@ std::vector<u32> MakePassthroughVertexSpirv(bool layered, float clip_w = 1.0f) {
Builder b;
const auto void_type = b.Type(spv::OpTypeVoid);
const auto uint_type = b.Type(spv::OpTypeInt, {32, 0});
const auto int_type = b.Type(spv::OpTypeInt, {32, 1});
const auto float_type = b.Type(spv::OpTypeFloat, {32});
const auto vec2_type = b.Type(spv::OpTypeVector, {float_type, 2});
const auto vec4_type = b.Type(spv::OpTypeVector, {float_type, 4});
const auto uint_type = b.Type(spv::OpTypeInt, 32, 0);
const auto int_type = b.Type(spv::OpTypeInt, 32, 1);
const auto float_type = b.Type(spv::OpTypeFloat, 32);
const auto vec2_type = b.Type(spv::OpTypeVector, float_type, 2);
const auto vec4_type = b.Type(spv::OpTypeVector, float_type, 4);
const auto per_vertex_type =
b.DecoratedType(spv::OpTypeStruct, {vec4_type},
{{spv::OpDecorate, {spv::DecorationBlock}},
b.DecoratedType(spv::OpTypeStruct, {{spv::OpDecorate, {spv::DecorationBlock}},
{spv::OpMemberDecorate,
{0, spv::DecorationBuiltIn, spv::BuiltInPosition}}});
{0, spv::DecorationBuiltIn, spv::BuiltInPosition}}}, vec4_type);
const auto ptr_input_vec2 =
b.Type(spv::OpTypePointer, {spv::StorageClassInput, vec2_type});
b.Type(spv::OpTypePointer, spv::StorageClassInput, vec2_type);
const auto ptr_input_vec4 =
b.Type(spv::OpTypePointer, {spv::StorageClassInput, vec4_type});
b.Type(spv::OpTypePointer, spv::StorageClassInput, vec4_type);
const auto ptr_output_vec4 =
b.Type(spv::OpTypePointer, {spv::StorageClassOutput, vec4_type});
b.Type(spv::OpTypePointer, spv::StorageClassOutput, vec4_type);
const auto ptr_output_per_vertex =
b.Type(spv::OpTypePointer, {spv::StorageClassOutput, per_vertex_type});
const auto func_type = b.Type(spv::OpTypeFunction, {void_type});
const auto const_u32_0 = b.Constant(spv::OpConstant, uint_type, {0});
b.Type(spv::OpTypePointer, spv::StorageClassOutput, per_vertex_type);
const auto func_type = b.Type(spv::OpTypeFunction, void_type);
const auto const_u32_0 = b.Constant(spv::OpConstant, uint_type, 0);
const auto const_f32_0 =
b.Constant(spv::OpConstant, float_type, {0x00000000u});
b.Constant(spv::OpConstant, float_type, 0x00000000u);
const auto const_f32_w =
b.Constant(spv::OpConstant, float_type, {std::bit_cast<u32>(clip_w)});
b.Constant(spv::OpConstant, float_type, std::bit_cast<u32>(clip_w));
const auto in_pos =
b.DefineGlobalVariable(ptr_input_vec2, spv::StorageClassInput);
const auto in_color =
@@ -1640,15 +1639,14 @@ std::vector<u32> MakePassthroughVertexSpirv(bool layered, float clip_w = 1.0f) {
std::vector<u32> interfaces = {in_pos, in_color, per_vertex, out_color};
if (layered) {
instance = b.DefineGlobalVariable(
b.Type(spv::OpTypePointer, {spv::StorageClassInput, int_type}),
b.Type(spv::OpTypePointer, spv::StorageClassInput, int_type),
spv::StorageClassInput);
layer = b.DefineGlobalVariable(
b.Type(spv::OpTypePointer, {spv::StorageClassOutput, int_type}),
b.Type(spv::OpTypePointer, spv::StorageClassOutput, int_type),
spv::StorageClassOutput);
b.AddAnnotation({spv::OpDecorate, instance, spv::DecorationBuiltIn,
spv::BuiltInInstanceIndex});
b.AddAnnotation(
{spv::OpDecorate, layer, spv::DecorationBuiltIn, spv::BuiltInLayer});
b.AddAnnotation(spv::OpDecorate, instance, spv::DecorationBuiltIn,
spv::BuiltInInstanceIndex);
b.AddAnnotation(spv::OpDecorate, layer, spv::DecorationBuiltIn, spv::BuiltInLayer);
b.RequireVersion(0x00010500u);
b.RequireCapability(spv::CapabilityShaderLayer);
interfaces.insert(interfaces.end(), {instance, layer});
@@ -1663,32 +1661,32 @@ std::vector<u32> MakePassthroughVertexSpirv(bool layered, float clip_w = 1.0f) {
const auto position_ptr = b.AllocateId();
b.RequireCapability(spv::CapabilityShader);
b.AddMemoryModel({spv::AddressingModelLogical, spv::MemoryModelGLSL450});
b.AddMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
b.AddEntryPoint(spv::ExecutionModelVertex, main, "main", interfaces);
b.AddAnnotation({spv::OpDecorate, in_pos, spv::DecorationLocation, 0});
b.AddAnnotation({spv::OpDecorate, in_color, spv::DecorationLocation, 1});
b.AddAnnotation({spv::OpDecorate, out_color, spv::DecorationLocation, 0});
b.AddAnnotation(spv::OpDecorate, in_pos, spv::DecorationLocation, 0);
b.AddAnnotation(spv::OpDecorate, in_color, spv::DecorationLocation, 1);
b.AddAnnotation(spv::OpDecorate, out_color, spv::DecorationLocation, 0);
b.AddFunction({spv::OpFunction, void_type, main, spv::FunctionControlMaskNone,
func_type});
b.AddFunction({spv::OpLabel, label});
b.AddFunction({spv::OpLoad, vec2_type, pos2, in_pos});
b.AddFunction({spv::OpLoad, vec4_type, color4, in_color});
b.AddFunction({spv::OpCompositeExtract, float_type, pos_x, pos2, 0});
b.AddFunction({spv::OpCompositeExtract, float_type, pos_y, pos2, 1});
b.AddFunction({spv::OpCompositeConstruct, vec4_type, position, pos_x, pos_y,
const_f32_0, const_f32_w});
b.AddFunction({spv::OpAccessChain, ptr_output_vec4, position_ptr, per_vertex,
const_u32_0});
b.AddFunction({spv::OpStore, position_ptr, position});
b.AddFunction({spv::OpStore, out_color, color4});
b.AddFunction(spv::OpFunction, void_type, main, spv::FunctionControlMaskNone,
func_type);
b.AddFunction(spv::OpLabel, label);
b.AddFunction(spv::OpLoad, vec2_type, pos2, in_pos);
b.AddFunction(spv::OpLoad, vec4_type, color4, in_color);
b.AddFunction(spv::OpCompositeExtract, float_type, pos_x, pos2, 0);
b.AddFunction(spv::OpCompositeExtract, float_type, pos_y, pos2, 1);
b.AddFunction(spv::OpCompositeConstruct, vec4_type, position, pos_x, pos_y,
const_f32_0, const_f32_w);
b.AddFunction(spv::OpAccessChain, ptr_output_vec4, position_ptr, per_vertex,
const_u32_0);
b.AddFunction(spv::OpStore, position_ptr, position);
b.AddFunction(spv::OpStore, out_color, color4);
if (layered) {
const auto index = b.AllocateId();
b.AddFunction({spv::OpLoad, int_type, index, instance});
b.AddFunction({spv::OpStore, layer, index});
b.AddFunction(spv::OpLoad, int_type, index, instance);
b.AddFunction(spv::OpStore, layer, index);
}
b.AddFunction({spv::OpReturn});
b.AddFunction({spv::OpFunctionEnd});
b.AddFunction(spv::OpReturn);
b.AddFunction(spv::OpFunctionEnd);
return b.Build();
}
+126 -15
View File
@@ -10025,11 +10025,120 @@ void TestNewShaderRecompilerAuxPositionExports() {
"PA_CL_VS_OUT_CNTL is absent from the vertex shader cache key");
}
void TestTypedSpirvSerialization() {
// Parameters exercise runtime enums, which cannot narrow implicitly to a
// uint32_t initializer list when the enum uses the Windows signed ABI.
const auto check_serialization = [](spv::StorageClass storage_class,
spv::FunctionControlMask control,
spv::Decoration decoration,
GLSLstd450 glsl_opcode,
int32_t signed_literal,
uint32_t high_bit_literal) {
ShaderRecompiler::Spirv::Builder builder;
builder.RequireCapability(spv::CapabilityShader);
const auto glsl = builder.Import("GLSL.std.450");
builder.AddMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450);
const auto void_type = builder.Type(spv::OpTypeVoid);
const std::vector<uint32_t> uint_operands{32u, 0u};
const auto uint_type = builder.Type(spv::OpTypeInt, uint_operands);
Check(uint_type == builder.Type(spv::OpTypeInt, 32u, 0u) &&
uint_type == builder.Type(spv::OpTypeInt,
std::span<const uint32_t>{uint_operands}),
"typed, vector and span type operands produced different declarations");
const auto float_type = builder.Type(spv::OpTypeFloat, 32u);
const auto vector_type = builder.Type(spv::OpTypeVector, uint_type, 2u);
const auto pointer_type =
builder.Type(spv::OpTypePointer, storage_class, uint_type);
const auto function_type = builder.Type(spv::OpTypeFunction, void_type);
const auto all_bits =
builder.Constant(spv::OpConstant, uint_type, signed_literal);
Check(all_bits == builder.Constant(spv::OpConstant, uint_type, UINT32_MAX),
"signed and unsigned constant operands produced different declarations");
const auto high_bit =
builder.Constant(spv::OpConstant, uint_type, high_bit_literal);
const auto one = builder.Constant(spv::OpConstant, float_type, 0x3f800000u);
const std::vector<uint32_t> components{all_bits, high_bit};
const auto constant_vector = builder.Constant(
spv::OpConstantComposite, vector_type,
std::span<const uint32_t>{components});
Check(constant_vector == builder.Constant(spv::OpConstantComposite,
vector_type, components),
"vector and span constant operands produced different declarations");
const auto variable =
builder.DefineGlobalVariable(pointer_type, storage_class);
const auto function = builder.AllocateId();
const auto label = builder.AllocateId();
const auto absolute = builder.AllocateId();
const auto constructed = builder.AllocateId();
const auto shuffled = builder.AllocateId();
const auto loaded = builder.AllocateId();
builder.AddEntryPoint(spv::ExecutionModelGLCompute, function, "main", {});
builder.AddExecutionMode(function, spv::ExecutionModeLocalSize, 1u, 2u, 3u);
builder.AddAnnotation(spv::OpDecorate, one, decoration);
builder.AddFunction(spv::OpFunction, void_type, function, control,
function_type);
const std::vector<uint32_t> label_words{248u, label};
builder.AddFunction(label_words);
builder.AddFunction(spv::OpExtInst, float_type, absolute, glsl, glsl_opcode,
one);
builder.AddFunction(spv::OpCompositeConstruct, vector_type, constructed,
components);
const std::array<uint32_t, 2> selectors{1u, 2u};
builder.AddFunction(spv::OpVectorShuffle, vector_type, shuffled,
constant_vector, constructed,
std::span<const uint32_t>{selectors});
builder.AddFunction(spv::OpLoad, uint_type, loaded, variable,
spv::MemoryAccessAlignedMask, 4u);
builder.AddFunction(std::vector<uint32_t>{});
const std::array<uint32_t, 1> return_words{253u};
builder.AddFunction(std::span<const uint32_t>{return_words});
builder.AddFunction(spv::OpFunctionEnd);
// Literal headers and enum values keep this independent of the builder's
// instruction word counts, operand conversion and range flattening.
const std::vector<uint32_t> expected{
0x07230203u, 0x00010300u, 0u, loaded + 1u, 0u,
0x00020011u, 1u, // OpCapability Shader
0x0006000bu, glsl, 0x4c534c47u, 0x6474732eu, 0x3035342eu, 0u,
0x0003000eu, 0u, 1u, // OpMemoryModel Logical GLSL450
0x0005000fu, 5u, function, 0x6e69616du, 0u,
0x00060010u, function, 17u, 1u, 2u, 3u,
0x00030047u, one, 0u, // OpDecorate RelaxedPrecision
0x00020013u, void_type,
0x00040015u, uint_type, 32u, 0u,
0x00030016u, float_type, 32u,
0x00040017u, vector_type, uint_type, 2u,
0x00040020u, pointer_type, 6u, uint_type,
0x00030021u, function_type, void_type,
0x0004002bu, uint_type, all_bits, 0xffffffffu,
0x0004002bu, uint_type, high_bit, 0x80000000u,
0x0004002bu, float_type, one, 0x3f800000u,
0x0005002cu, vector_type, constant_vector, all_bits, high_bit,
0x0004003bu, pointer_type, variable, 6u,
0x00050036u, void_type, function, 0u, function_type,
0x000200f8u, label,
0x0006000cu, float_type, absolute, glsl, 4u, one,
0x00050050u, vector_type, constructed, all_bits, high_bit,
0x0007004fu, vector_type, shuffled, constant_vector, constructed, 1u, 2u,
0x0006003du, uint_type, loaded, variable, 2u, 4u,
0x000100fdu,
0x00010038u,
};
const auto binary = builder.Build();
Check(binary == expected,
"typed SPIR-V serialization changed instruction words or operand order");
CheckSpirvBinaryValidates(binary);
};
check_serialization(spv::StorageClassPrivate, spv::FunctionControlMaskNone,
spv::DecorationRelaxedPrecision, GLSLstd450FAbs, -1,
0x80000000u);
}
void TestDeferredSpirvPhiPatching() {
ShaderRecompiler::Spirv::Builder builder;
const auto type = builder.Type(spv::OpTypeInt, {32u, 0u});
const auto zero = builder.Constant(spv::OpConstant, type, {0u});
const auto one = builder.Constant(spv::OpConstant, type, {1u});
const auto type = builder.Type(spv::OpTypeInt, 32u, 0u);
const auto zero = builder.Constant(spv::OpConstant, type, 0u);
const auto one = builder.Constant(spv::OpConstant, type, 1u);
const auto left = builder.AllocateId();
const auto right = builder.AllocateId();
const auto result = builder.AllocateId();
@@ -10080,35 +10189,36 @@ void TestDemandDrivenSpirvDeclarations() {
using ShaderRecompiler::Spirv::Builder;
Builder declarations;
const auto uint_type = declarations.Type(spv::OpTypeInt, {32u, 0u});
Check(uint_type == declarations.Type(spv::OpTypeInt, {32u, 0u}),
const auto uint_type = declarations.Type(spv::OpTypeInt, 32u, 0u);
Check(uint_type == declarations.Type(spv::OpTypeInt, 32u, 0u),
"structural type interning returned different IDs");
const auto zero = declarations.Constant(spv::OpConstant, uint_type, {0u});
Check(zero == declarations.Constant(spv::OpConstant, uint_type, {0u}),
const auto zero = declarations.Constant(spv::OpConstant, uint_type, 0u);
Check(zero == declarations.Constant(spv::OpConstant, uint_type, 0u),
"constant interning returned different IDs");
Check(declarations.Import("GLSL.std.450") ==
declarations.Import("GLSL.std.450"),
"extended-instruction import was duplicated");
declarations.RequireCapability(spv::CapabilityShader);
declarations.RequireCapability(spv::CapabilityShader);
const auto plain_array =
declarations.Type(spv::OpTypeArray, {uint_type, zero});
const auto plain_array = declarations.Type(spv::OpTypeArray, uint_type, zero);
const auto decorated_array = declarations.DecoratedType(
spv::OpTypeArray, {uint_type, zero},
{{spv::OpDecorate, {spv::DecorationArrayStride, sizeof(uint32_t)}}});
spv::OpTypeArray,
{{spv::OpDecorate, {spv::DecorationArrayStride, sizeof(uint32_t)}}},
uint_type, zero);
Check(decorated_array ==
declarations.DecoratedType(
spv::OpTypeArray, {uint_type, zero},
spv::OpTypeArray,
{{spv::OpDecorate,
{spv::DecorationArrayStride, sizeof(uint32_t)}}}),
{spv::DecorationArrayStride, sizeof(uint32_t)}}},
uint_type, zero),
"decorated type interning returned different IDs");
Check(plain_array != decorated_array,
"decorated and undecorated aggregate types were aliased");
Check(declarations.DecoratedType(spv::OpTypeArray, {uint_type, zero}, {}) ==
Check(declarations.DecoratedType(spv::OpTypeArray, {}, uint_type, zero) ==
plain_array,
"empty decorated type request bypassed structural interning");
const auto ptr = declarations.Type(spv::OpTypePointer,
{spv::StorageClassFunction, uint_type});
spv::StorageClassFunction, uint_type);
Check(declarations.DefineGlobalVariable(ptr, spv::StorageClassFunction) !=
declarations.DefineGlobalVariable(ptr, spv::StorageClassFunction),
"distinct variables were structurally interned");
@@ -13020,6 +13130,7 @@ int main() {
TestNativeShaderResourceDependencies();
TestNormalizedImageContracts();
TestSpirvRequirementsAnalysis();
TestTypedSpirvSerialization();
TestDeferredSpirvPhiPatching();
TestCompilerStageInputOwnership();
TestSpirvEmissionOwnsRequirements();