diff options
author | Sebastián Aedo <saedo@codeweavers.com> | 2021-11-18 22:08:59 +0300 |
---|---|---|
committer | Sebastián Aedo <saedo@codeweavers.com> | 2021-11-26 21:54:56 +0300 |
commit | 6d8302ef14d603f5e6171ea17048ad04e18c3a80 (patch) | |
tree | 750e74f45782da3187b498b2d3e7909b659e65ae | |
parent | 37dfb3f45f4fc47c841f81e618c602f6f3de0f17 (diff) |
MSL: Add 64 bit switch support
Add 64 bit switch support for MSL version 2.2.
* Also fixes a wrong endianness conversion.
Signed-off-by: Sebastián Aedo <saedo@codeweavers.com>
16 files changed, 474 insertions, 20 deletions
diff --git a/reference/opt/shaders-msl/asm/frag/switch-different-sizes.asm.frag b/reference/opt/shaders-msl/asm/frag/switch-different-sizes.asm.frag new file mode 100644 index 00000000..92ac1d9f --- /dev/null +++ b/reference/opt/shaders-msl/asm/frag/switch-different-sizes.asm.frag @@ -0,0 +1,9 @@ +#include <metal_stdlib> +#include <simd/simd.h> + +using namespace metal; + +fragment void main0() +{ +} + diff --git a/reference/opt/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag b/reference/opt/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag new file mode 100644 index 00000000..92ac1d9f --- /dev/null +++ b/reference/opt/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag @@ -0,0 +1,9 @@ +#include <metal_stdlib> +#include <simd/simd.h> + +using namespace metal; + +fragment void main0() +{ +} + diff --git a/reference/opt/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag b/reference/opt/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag new file mode 100644 index 00000000..92ac1d9f --- /dev/null +++ b/reference/opt/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag @@ -0,0 +1,9 @@ +#include <metal_stdlib> +#include <simd/simd.h> + +using namespace metal; + +fragment void main0() +{ +} + diff --git a/reference/opt/shaders/asm/frag/switch-preserve-sign-extension.asm.frag b/reference/opt/shaders/asm/frag/switch-preserve-sign-extension.asm.frag new file mode 100644 index 00000000..41b98085 --- /dev/null +++ b/reference/opt/shaders/asm/frag/switch-preserve-sign-extension.asm.frag @@ -0,0 +1,9 @@ +#version 330 +#ifdef GL_ARB_shading_language_420pack +#extension GL_ARB_shading_language_420pack : require +#endif + +void main() +{ +} + diff --git a/reference/shaders-msl/asm/frag/switch-different-sizes.asm.frag b/reference/shaders-msl/asm/frag/switch-different-sizes.asm.frag new file mode 100644 index 00000000..1ee9eebf --- /dev/null +++ b/reference/shaders-msl/asm/frag/switch-different-sizes.asm.frag @@ -0,0 +1,78 @@ +#include <metal_stdlib> +#include <simd/simd.h> + +using namespace metal; + +fragment void main0() +{ + int sw0 = 42; + int result = 0; + switch (sw0) + { + case -42: + { + result = 42; + } + case 420: + { + result = 420; + } + case -1234: + { + result = 420; + break; + } + } + char sw1 = char(10); + switch (sw1) + { + case -42: + { + result = 42; + } + case 42: + { + result = 420; + } + case -123: + { + result = 512; + break; + } + } + short sw2 = short(10); + switch (sw2) + { + case -42: + { + result = 42; + } + case 42: + { + result = 420; + } + case -1234: + { + result = 512; + break; + } + } + short sw3 = short(10); + switch (sw3) + { + case -42: + { + result = 42; + } + case 42: + { + result = 420; + } + case -1234: + { + result = 512; + break; + } + } +} + diff --git a/reference/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag b/reference/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag new file mode 100644 index 00000000..5ec002e7 --- /dev/null +++ b/reference/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag @@ -0,0 +1,27 @@ +#include <metal_stdlib> +#include <simd/simd.h> + +using namespace metal; + +fragment void main0() +{ + long sw = 42l; + int result = 0; + switch (sw) + { + case -42l: + { + result = 42; + } + case 420l: + { + result = 420; + } + case -34359738368l: + { + result = 420; + break; + } + } +} + diff --git a/reference/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag b/reference/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag new file mode 100644 index 00000000..2bf44c20 --- /dev/null +++ b/reference/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag @@ -0,0 +1,27 @@ +#include <metal_stdlib> +#include <simd/simd.h> + +using namespace metal; + +fragment void main0() +{ + ulong sw = 42ul; + int result = 0; + switch (sw) + { + case 42ul: + { + result = 42; + } + case 420ul: + { + result = 420; + } + case 343597383680ul: + { + result = 420; + break; + } + } +} + diff --git a/reference/shaders/asm/frag/switch-preserve-sign-extension.asm.frag b/reference/shaders/asm/frag/switch-preserve-sign-extension.asm.frag new file mode 100644 index 00000000..08921e1e --- /dev/null +++ b/reference/shaders/asm/frag/switch-preserve-sign-extension.asm.frag @@ -0,0 +1,27 @@ +#version 330 +#ifdef GL_ARB_shading_language_420pack +#extension GL_ARB_shading_language_420pack : require +#endif + +void main() +{ + int sw = 42; + int result = 0; + switch (sw) + { + case -42: + { + result = 42; + } + case 420: + { + result = 420; + } + case -1234: + { + result = 420; + break; + } + } +} + diff --git a/shaders-msl/asm/frag/switch-different-sizes.asm.frag b/shaders-msl/asm/frag/switch-different-sizes.asm.frag new file mode 100644 index 00000000..ee6daa3d --- /dev/null +++ b/shaders-msl/asm/frag/switch-different-sizes.asm.frag @@ -0,0 +1,106 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 10 +; Bound: 42 +; Schema: 0 + OpCapability Shader + OpCapability Int8 + OpCapability Int16 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %sw0 "sw0" + OpName %result "result" + OpName %sw1 "sw1" + OpName %sw2 "sw2" + OpName %sw3 "sw3" + OpDecorate %sw1 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %sw2 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %lowp_int = OpTypeInt 8 1 + %highp_int = OpTypeInt 16 1 + %_ptr_Function_int = OpTypePointer Function %int + %_ptr_Function_lowp_int = OpTypePointer Function %lowp_int + %_ptr_Function_highp_int = OpTypePointer Function %highp_int + %int_42 = OpConstant %int 42 + %int_0 = OpConstant %int 0 + %int_420 = OpConstant %int 420 + %int_10 = OpConstant %int 10 + %int_512 = OpConstant %int 512 + %lowp_int_10 = OpConstant %lowp_int 10 + %highp_int_10 = OpConstant %highp_int 10 + %main = OpFunction %void None %3 + %5 = OpLabel + %sw0 = OpVariable %_ptr_Function_int Function + %result = OpVariable %_ptr_Function_int Function + %sw1 = OpVariable %_ptr_Function_lowp_int Function + %sw2 = OpVariable %_ptr_Function_highp_int Function + %sw3 = OpVariable %_ptr_Function_highp_int Function + OpStore %sw0 %int_42 + OpStore %result %int_0 + %12 = OpLoad %int %sw0 + OpSelectionMerge %16 None + OpSwitch %12 %16 -42 %13 420 %14 -1234 %15 + %13 = OpLabel + OpStore %result %int_42 + OpBranch %14 + %14 = OpLabel + OpStore %result %int_420 + OpBranch %15 + %15 = OpLabel + OpStore %result %int_420 + OpBranch %16 + %16 = OpLabel + OpStore %sw1 %lowp_int_10 + %21 = OpLoad %lowp_int %sw1 + OpSelectionMerge %25 None + OpSwitch %21 %25 -42 %22 42 %23 -123 %24 + %22 = OpLabel + OpStore %result %int_42 + OpBranch %23 + %23 = OpLabel + OpStore %result %int_420 + OpBranch %24 + %24 = OpLabel + OpStore %result %int_512 + OpBranch %25 + %25 = OpLabel + OpStore %sw2 %highp_int_10 + %29 = OpLoad %highp_int %sw2 + OpSelectionMerge %33 None + OpSwitch %29 %33 -42 %30 42 %31 -1234 %32 + %30 = OpLabel + OpStore %result %int_42 + OpBranch %31 + %31 = OpLabel + OpStore %result %int_420 + OpBranch %32 + %32 = OpLabel + OpStore %result %int_512 + OpBranch %33 + %33 = OpLabel + OpStore %sw3 %highp_int_10 + %36 = OpLoad %highp_int %sw3 + OpSelectionMerge %40 None + OpSwitch %36 %40 -42 %37 42 %38 -1234 %39 + %37 = OpLabel + OpStore %result %int_42 + OpBranch %38 + %38 = OpLabel + OpStore %result %int_420 + OpBranch %39 + %39 = OpLabel + OpStore %result %int_512 + OpBranch %40 + %40 = OpLabel + OpReturn + OpFunctionEnd diff --git a/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag b/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag new file mode 100644 index 00000000..62f2dc68 --- /dev/null +++ b/shaders-msl/asm/frag/switch-long-case.asm.msl22.frag @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 21 +; Schema: 0 + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %sw "sw" + OpName %result "result" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %long = OpTypeInt 64 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Function_long = OpTypePointer Function %long + %int_42 = OpConstant %int 42 + %int_0 = OpConstant %int 0 + %int_420 = OpConstant %int 420 + %long_42 = OpConstant %long 42 + %main = OpFunction %void None %6 + %15 = OpLabel + %sw = OpVariable %_ptr_Function_long Function + %result = OpVariable %_ptr_Function_int Function + OpStore %sw %long_42 + OpStore %result %int_0 + %16 = OpLoad %long %sw + OpSelectionMerge %17 None + OpSwitch %16 %17 -42 %18 420 %19 -34359738368 %20 + %18 = OpLabel + OpStore %result %int_42 + OpBranch %19 + %19 = OpLabel + OpStore %result %int_420 + OpBranch %20 + %20 = OpLabel + OpStore %result %int_420 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd diff --git a/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag b/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag new file mode 100644 index 00000000..cea32b42 --- /dev/null +++ b/shaders-msl/asm/frag/switch-unsigned-long-case.asm.msl22.frag @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 21 +; Schema: 0 + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %sw "sw" + OpName %result "result" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %long = OpTypeInt 64 0 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Function_long = OpTypePointer Function %long + %int_42 = OpConstant %int 42 + %int_0 = OpConstant %int 0 + %int_420 = OpConstant %int 420 + %long_42 = OpConstant %long 42 + %main = OpFunction %void None %6 + %15 = OpLabel + %sw = OpVariable %_ptr_Function_long Function + %result = OpVariable %_ptr_Function_int Function + OpStore %sw %long_42 + OpStore %result %int_0 + %16 = OpLoad %long %sw + OpSelectionMerge %17 None + OpSwitch %16 %17 42 %18 420 %19 343597383680 %20 + %18 = OpLabel + OpStore %result %int_42 + OpBranch %19 + %19 = OpLabel + OpStore %result %int_420 + OpBranch %20 + %20 = OpLabel + OpStore %result %int_420 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd diff --git a/shaders/asm/frag/switch-preserve-sign-extension.asm.frag b/shaders/asm/frag/switch-preserve-sign-extension.asm.frag new file mode 100644 index 00000000..97140ee5 --- /dev/null +++ b/shaders/asm/frag/switch-preserve-sign-extension.asm.frag @@ -0,0 +1,44 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 10 +; Bound: 19 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %sw "sw" + OpName %result "result" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_42 = OpConstant %int 42 + %int_0 = OpConstant %int 0 + %int_420 = OpConstant %int 420 + %main = OpFunction %void None %3 + %5 = OpLabel + %sw = OpVariable %_ptr_Function_int Function + %result = OpVariable %_ptr_Function_int Function + OpStore %sw %int_42 + OpStore %result %int_0 + %12 = OpLoad %int %sw + OpSelectionMerge %16 None + OpSwitch %12 %16 -42 %13 420 %14 -1234 %15 + %13 = OpLabel + OpStore %result %int_42 + OpBranch %14 + %14 = OpLabel + OpStore %result %int_420 + OpBranch %15 + %15 = OpLabel + OpStore %result %int_420 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 5c21518f..9ae5d966 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -14768,13 +14768,13 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) case SPIRBlock::MultiSelect: { auto &type = expression_type(block.condition); - bool unsigned_case = - type.basetype == SPIRType::UInt || type.basetype == SPIRType::UShort || type.basetype == SPIRType::UByte; + bool unsigned_case = type.basetype == SPIRType::UInt || type.basetype == SPIRType::UShort || + type.basetype == SPIRType::UByte || type.basetype == SPIRType::UInt64; if (block.merge == SPIRBlock::MergeNone) SPIRV_CROSS_THROW("Switch statement is not structured"); - if (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64) + if (!backend.support_64bit_switch && (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64)) { // SPIR-V spec suggests this is allowed, but we cannot support it in higher level languages. SPIRV_CROSS_THROW("Cannot use 64-bit switch selectors."); @@ -14783,6 +14783,10 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) const char *label_suffix = ""; if (type.basetype == SPIRType::UInt && backend.uint32_t_literal_suffix) label_suffix = "u"; + else if (type.basetype == SPIRType::Int64 && backend.support_64bit_switch) + label_suffix = "l"; + else if (type.basetype == SPIRType::UInt64 && backend.support_64bit_switch) + label_suffix = "ul"; else if (type.basetype == SPIRType::UShort) label_suffix = backend.uint16_t_literal_suffix; else if (type.basetype == SPIRType::Short) @@ -14795,9 +14799,9 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) statement("bool _", block.self, "_ladder_break = false;"); // Find all unique case constructs. - unordered_map<uint32_t, SmallVector<uint32_t>> case_constructs; + unordered_map<uint32_t, SmallVector<uint64_t>> case_constructs; SmallVector<uint32_t> block_declaration_order; - SmallVector<uint32_t> literals_to_merge; + SmallVector<uint64_t> literals_to_merge; // If a switch case branches to the default block for some reason, we can just remove that literal from consideration // and let the default: block handle it. @@ -14806,21 +14810,17 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) auto &cases = get_case_list(block); for (auto &c : cases) { - // It's safe to cast to uint32_t since we actually do a check - // previously that we're not using uint64_t as the switch selector. - auto case_value = static_cast<uint32_t>(c.value); - if (c.block != block.next_block && c.block != block.default_block) { if (!case_constructs.count(c.block)) block_declaration_order.push_back(c.block); - case_constructs[c.block].push_back(case_value); + case_constructs[c.block].push_back(c.value); } else if (c.block == block.next_block && block.default_block != block.next_block) { // We might have to flush phi inside specific case labels. // If we can piggyback on default:, do so instead. - literals_to_merge.push_back(case_value); + literals_to_merge.push_back(c.value); } } @@ -14865,11 +14865,22 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) size_t num_blocks = block_declaration_order.size(); - const auto to_case_label = [](uint32_t literal, bool is_unsigned_case) -> string { - return is_unsigned_case ? convert_to_string(literal) : convert_to_string(int32_t(literal)); + const auto to_case_label = [](uint64_t literal, uint32_t width, bool is_unsigned_case) -> string + { + if (is_unsigned_case) + return convert_to_string(literal); + + // For smaller cases, the literals are compiled as 32 bit wide + // literals so we don't need to care for all sizes specifically. + if (width <= 32) + { + return convert_to_string(int64_t(int32_t(literal))); + } + + return convert_to_string(int64_t(literal)); }; - const auto to_legacy_case_label = [&](uint32_t condition, const SmallVector<uint32_t> &labels, + const auto to_legacy_case_label = [&](uint32_t condition, const SmallVector<uint64_t> &labels, const char *suffix) -> string { string ret; size_t count = labels.size(); @@ -14911,7 +14922,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) auto &negative_literals = case_constructs[block_declaration_order[j]]; for (auto &case_label : negative_literals) conditions.push_back(join(to_enclosed_expression(block.condition), - " != ", to_case_label(case_label, unsigned_case))); + " != ", to_case_label(case_label, type.width, unsigned_case))); } statement("if (", merge(conditions, " && "), ")"); @@ -14925,7 +14936,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) conditions.reserve(literals.size()); for (auto &case_label : literals) conditions.push_back(join(to_enclosed_expression(block.condition), - " == ", to_case_label(case_label, unsigned_case))); + " == ", to_case_label(case_label, type.width, unsigned_case))); statement("if (", merge(conditions, " || "), ")"); begin_scope(); flush_phi(block.self, target_block); @@ -14940,7 +14951,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) // If there is only one default block, and no cases, this is a case where SPIRV-opt decided to emulate // non-structured exits with the help of a switch block. // This is buggy on FXC, so just emit the logical equivalent of a do { } while(false), which is more idiomatic. - bool degenerate_switch = block.default_block != block.merge_block && block.cases_32bit.empty(); + bool degenerate_switch = block.default_block != block.merge_block && cases.empty(); if (degenerate_switch || is_legacy_es()) { @@ -14989,7 +15000,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) for (auto &case_literal : literals) { // The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here. - statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":"); + statement("case ", to_case_label(case_literal, type.width, unsigned_case), label_suffix, ":"); } } } @@ -15027,7 +15038,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block) if ((header_merge_requires_phi && need_fallthrough_block) || !literals_to_merge.empty()) { for (auto &case_literal : literals_to_merge) - statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":"); + statement("case ", to_case_label(case_literal, type.width, unsigned_case), label_suffix, ":"); if (block.default_block == block.next_block) { diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index 52362442..b34ed993 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -586,6 +586,7 @@ protected: bool needs_row_major_load_workaround = false; bool support_pointer_to_pointer = false; bool support_precise_qualifier = false; + bool support_64bit_switch = false; } backend; void emit_struct(SPIRType &type); diff --git a/spirv_msl.cpp b/spirv_msl.cpp index fabf2e9c..ffa69bfe 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -1355,6 +1355,7 @@ string CompilerMSL::compile() backend.nonuniform_qualifier = ""; backend.support_small_type_sampling_result = true; backend.supports_empty_struct = true; + backend.support_64bit_switch = true; // Allow Metal to use the array<T> template unless we force it off. backend.can_return_array = !msl_options.force_native_arrays; diff --git a/spirv_parser.cpp b/spirv_parser.cpp index da39367d..4faf3ca0 100644 --- a/spirv_parser.cpp +++ b/spirv_parser.cpp @@ -1029,7 +1029,7 @@ void Parser::parse(const Instruction &instruction) { for (uint32_t i = 2; i + 3 <= length; i += 3) { - uint64_t value = (static_cast<uint64_t>(ops[i]) << 32) | ops[i + 1]; + uint64_t value = (static_cast<uint64_t>(ops[i + 1]) << 32) | ops[i]; current_block->cases_64bit.push_back({ value, ops[i + 2] }); } } |