[hlsl] Add start of FXC polyfill for HLSL IR backend.
This CL adds the start of the FXC polyfill and implements the switch
with only a default case transformation.
Bug: 42251045
Change-Id: I4fb874296afad6438a5292ab0924a9170a6e7aa3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/194621
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Auto-Submit: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/cmd/test/BUILD.bazel b/src/tint/cmd/test/BUILD.bazel
index de049c2..cc3866d 100644
--- a/src/tint/cmd/test/BUILD.bazel
+++ b/src/tint/cmd/test/BUILD.bazel
@@ -52,6 +52,7 @@
"//src/tint/lang/core/type:test",
"//src/tint/lang/core:test",
"//src/tint/lang/hlsl/writer/common:test",
+ "//src/tint/lang/hlsl/writer/raise:test",
"//src/tint/lang/msl/ir:test",
"//src/tint/lang/msl/type:test",
"//src/tint/lang/spirv/ir:test",
diff --git a/src/tint/cmd/test/BUILD.cmake b/src/tint/cmd/test/BUILD.cmake
index cb94ba9..341bf6e 100644
--- a/src/tint/cmd/test/BUILD.cmake
+++ b/src/tint/cmd/test/BUILD.cmake
@@ -53,6 +53,7 @@
tint_lang_core_type_test
tint_lang_core_test
tint_lang_hlsl_writer_common_test
+ tint_lang_hlsl_writer_raise_test
tint_lang_msl_ir_test
tint_lang_msl_type_test
tint_lang_spirv_ir_test
diff --git a/src/tint/cmd/test/BUILD.gn b/src/tint/cmd/test/BUILD.gn
index 7d0c39c..ea2de9c 100644
--- a/src/tint/cmd/test/BUILD.gn
+++ b/src/tint/cmd/test/BUILD.gn
@@ -58,6 +58,7 @@
"${tint_src_dir}/lang/core/ir/transform/common:unittests",
"${tint_src_dir}/lang/core/type:unittests",
"${tint_src_dir}/lang/hlsl/writer/common:unittests",
+ "${tint_src_dir}/lang/hlsl/writer/raise:unittests",
"${tint_src_dir}/lang/msl/ir:unittests",
"${tint_src_dir}/lang/msl/type:unittests",
"${tint_src_dir}/lang/spirv/ir:unittests",
diff --git a/src/tint/lang/hlsl/writer/raise/BUILD.bazel b/src/tint/lang/hlsl/writer/raise/BUILD.bazel
index 4285f0c..f989717 100644
--- a/src/tint/lang/hlsl/writer/raise/BUILD.bazel
+++ b/src/tint/lang/hlsl/writer/raise/BUILD.bazel
@@ -39,29 +39,70 @@
cc_library(
name = "raise",
srcs = [
+ "fxc_polyfill.cc",
"raise.cc",
],
hdrs = [
+ "fxc_polyfill.h",
"raise.h",
],
deps = [
"//src/tint/api/common",
"//src/tint/lang/core",
+ "//src/tint/lang/core/constant",
+ "//src/tint/lang/core/intrinsic",
+ "//src/tint/lang/core/ir",
"//src/tint/lang/core/ir/transform",
+ "//src/tint/lang/core/type",
"//src/tint/lang/hlsl/writer/common",
"//src/tint/utils/containers",
"//src/tint/utils/diagnostic",
"//src/tint/utils/ice",
+ "//src/tint/utils/id",
"//src/tint/utils/macros",
"//src/tint/utils/math",
"//src/tint/utils/memory",
"//src/tint/utils/reflection",
"//src/tint/utils/result",
"//src/tint/utils/rtti",
+ "//src/tint/utils/symbol",
"//src/tint/utils/text",
"//src/tint/utils/traits",
],
copts = COPTS,
visibility = ["//visibility:public"],
)
+cc_library(
+ name = "test",
+ alwayslink = True,
+ srcs = [
+ "fxc_polyfill_test.cc",
+ ],
+ deps = [
+ "//src/tint/api/common",
+ "//src/tint/lang/core",
+ "//src/tint/lang/core/constant",
+ "//src/tint/lang/core/intrinsic",
+ "//src/tint/lang/core/ir",
+ "//src/tint/lang/core/ir/transform:test",
+ "//src/tint/lang/core/type",
+ "//src/tint/lang/hlsl/writer/raise",
+ "//src/tint/utils/containers",
+ "//src/tint/utils/diagnostic",
+ "//src/tint/utils/ice",
+ "//src/tint/utils/id",
+ "//src/tint/utils/macros",
+ "//src/tint/utils/math",
+ "//src/tint/utils/memory",
+ "//src/tint/utils/reflection",
+ "//src/tint/utils/result",
+ "//src/tint/utils/rtti",
+ "//src/tint/utils/symbol",
+ "//src/tint/utils/text",
+ "//src/tint/utils/traits",
+ "@gtest",
+ ],
+ copts = COPTS,
+ visibility = ["//visibility:public"],
+)
diff --git a/src/tint/lang/hlsl/writer/raise/BUILD.cmake b/src/tint/lang/hlsl/writer/raise/BUILD.cmake
index 7934f8d..47e9ce4 100644
--- a/src/tint/lang/hlsl/writer/raise/BUILD.cmake
+++ b/src/tint/lang/hlsl/writer/raise/BUILD.cmake
@@ -39,6 +39,8 @@
# Kind: lib
################################################################################
tint_add_target(tint_lang_hlsl_writer_raise lib
+ lang/hlsl/writer/raise/fxc_polyfill.cc
+ lang/hlsl/writer/raise/fxc_polyfill.h
lang/hlsl/writer/raise/raise.cc
lang/hlsl/writer/raise/raise.h
)
@@ -46,17 +48,59 @@
tint_target_add_dependencies(tint_lang_hlsl_writer_raise lib
tint_api_common
tint_lang_core
+ tint_lang_core_constant
+ tint_lang_core_intrinsic
+ tint_lang_core_ir
tint_lang_core_ir_transform
+ tint_lang_core_type
tint_lang_hlsl_writer_common
tint_utils_containers
tint_utils_diagnostic
tint_utils_ice
+ tint_utils_id
tint_utils_macros
tint_utils_math
tint_utils_memory
tint_utils_reflection
tint_utils_result
tint_utils_rtti
+ tint_utils_symbol
tint_utils_text
tint_utils_traits
)
+
+################################################################################
+# Target: tint_lang_hlsl_writer_raise_test
+# Kind: test
+################################################################################
+tint_add_target(tint_lang_hlsl_writer_raise_test test
+ lang/hlsl/writer/raise/fxc_polyfill_test.cc
+)
+
+tint_target_add_dependencies(tint_lang_hlsl_writer_raise_test test
+ tint_api_common
+ tint_lang_core
+ tint_lang_core_constant
+ tint_lang_core_intrinsic
+ tint_lang_core_ir
+ tint_lang_core_ir_transform_test
+ tint_lang_core_type
+ tint_lang_hlsl_writer_raise
+ tint_utils_containers
+ tint_utils_diagnostic
+ tint_utils_ice
+ tint_utils_id
+ tint_utils_macros
+ tint_utils_math
+ tint_utils_memory
+ tint_utils_reflection
+ tint_utils_result
+ tint_utils_rtti
+ tint_utils_symbol
+ tint_utils_text
+ tint_utils_traits
+)
+
+tint_target_add_external_dependencies(tint_lang_hlsl_writer_raise_test test
+ "gtest"
+)
diff --git a/src/tint/lang/hlsl/writer/raise/BUILD.gn b/src/tint/lang/hlsl/writer/raise/BUILD.gn
index c287af3..0d755f2 100644
--- a/src/tint/lang/hlsl/writer/raise/BUILD.gn
+++ b/src/tint/lang/hlsl/writer/raise/BUILD.gn
@@ -38,26 +38,67 @@
import("${tint_src_dir}/tint.gni")
+if (tint_build_unittests || tint_build_benchmarks) {
+ import("//testing/test.gni")
+}
+
libtint_source_set("raise") {
sources = [
+ "fxc_polyfill.cc",
+ "fxc_polyfill.h",
"raise.cc",
"raise.h",
]
deps = [
"${tint_src_dir}/api/common",
"${tint_src_dir}/lang/core",
+ "${tint_src_dir}/lang/core/constant",
+ "${tint_src_dir}/lang/core/intrinsic",
+ "${tint_src_dir}/lang/core/ir",
"${tint_src_dir}/lang/core/ir/transform",
+ "${tint_src_dir}/lang/core/type",
"${tint_src_dir}/lang/hlsl/writer/common",
"${tint_src_dir}/utils/containers",
"${tint_src_dir}/utils/diagnostic",
"${tint_src_dir}/utils/ice",
+ "${tint_src_dir}/utils/id",
"${tint_src_dir}/utils/macros",
"${tint_src_dir}/utils/math",
"${tint_src_dir}/utils/memory",
"${tint_src_dir}/utils/reflection",
"${tint_src_dir}/utils/result",
"${tint_src_dir}/utils/rtti",
+ "${tint_src_dir}/utils/symbol",
"${tint_src_dir}/utils/text",
"${tint_src_dir}/utils/traits",
]
}
+if (tint_build_unittests) {
+ tint_unittests_source_set("unittests") {
+ sources = [ "fxc_polyfill_test.cc" ]
+ deps = [
+ "${tint_src_dir}:gmock_and_gtest",
+ "${tint_src_dir}/api/common",
+ "${tint_src_dir}/lang/core",
+ "${tint_src_dir}/lang/core/constant",
+ "${tint_src_dir}/lang/core/intrinsic",
+ "${tint_src_dir}/lang/core/ir",
+ "${tint_src_dir}/lang/core/ir/transform:unittests",
+ "${tint_src_dir}/lang/core/type",
+ "${tint_src_dir}/lang/hlsl/writer/raise",
+ "${tint_src_dir}/utils/containers",
+ "${tint_src_dir}/utils/diagnostic",
+ "${tint_src_dir}/utils/ice",
+ "${tint_src_dir}/utils/id",
+ "${tint_src_dir}/utils/macros",
+ "${tint_src_dir}/utils/math",
+ "${tint_src_dir}/utils/memory",
+ "${tint_src_dir}/utils/reflection",
+ "${tint_src_dir}/utils/result",
+ "${tint_src_dir}/utils/rtti",
+ "${tint_src_dir}/utils/symbol",
+ "${tint_src_dir}/utils/text",
+ "${tint_src_dir}/utils/traits",
+ ]
+ }
+}
diff --git a/src/tint/lang/hlsl/writer/raise/fxc_polyfill.cc b/src/tint/lang/hlsl/writer/raise/fxc_polyfill.cc
new file mode 100644
index 0000000..7369b40
--- /dev/null
+++ b/src/tint/lang/hlsl/writer/raise/fxc_polyfill.cc
@@ -0,0 +1,88 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/hlsl/writer/raise/fxc_polyfill.h"
+
+#include "src/tint/lang/core/ir/block.h"
+#include "src/tint/lang/core/ir/builder.h"
+#include "src/tint/lang/core/ir/exit.h"
+#include "src/tint/lang/core/ir/loop.h"
+#include "src/tint/lang/core/ir/switch.h"
+#include "src/tint/lang/core/ir/validator.h"
+#include "src/tint/lang/core/type/manager.h"
+#include "src/tint/utils/containers/vector.h"
+#include "src/tint/utils/ice/ice.h"
+#include "src/tint/utils/result/result.h"
+
+namespace tint::hlsl::writer::raise {
+namespace {
+
+using namespace tint::core::fluent_types; // NOLINT
+
+/// PIMPL state for the transform.
+struct State {
+ /// The IR module.
+ core::ir::Module& ir;
+
+ /// The IR builder.
+ core::ir::Builder b{ir};
+
+ /// The type manager.
+ core::type::Manager& ty{ir.Types()};
+
+ /// Process the module.
+ void Process() {
+ for (auto* inst : ir.Instructions()) {
+ if (auto* swtch = inst->As<core::ir::Switch>()) {
+ // BUG(crbug.com/tint/1188): work around default-only switches
+ //
+ // FXC fails to compile a switch with just a default case, ignoring the
+ // default case body. We work around this here by emitting the zero case as a
+ // fallthrough to the default case
+ if (swtch->Cases().Length() == 1 && swtch->Cases()[0].selectors.Length() == 1 &&
+ swtch->Cases()[0].selectors[0].IsDefault()) {
+ swtch->Cases()[0].selectors.Push({b.Zero(swtch->Condition()->Type())});
+ }
+ }
+ }
+ }
+};
+
+} // namespace
+
+Result<SuccessType> FxcPolyfill(core::ir::Module& ir) {
+ auto result = ValidateAndDumpIfNeeded(ir, "FxcPolyfill transform");
+ if (result != Success) {
+ return result.Failure();
+ }
+
+ State{ir}.Process();
+
+ return Success;
+}
+
+} // namespace tint::hlsl::writer::raise
diff --git a/src/tint/lang/hlsl/writer/raise/fxc_polyfill.h b/src/tint/lang/hlsl/writer/raise/fxc_polyfill.h
new file mode 100644
index 0000000..1cedaac
--- /dev/null
+++ b/src/tint/lang/hlsl/writer/raise/fxc_polyfill.h
@@ -0,0 +1,50 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_LANG_HLSL_WRITER_RAISE_FXC_POLYFILL_H_
+#define SRC_TINT_LANG_HLSL_WRITER_RAISE_FXC_POLYFILL_H_
+
+#include <string>
+
+#include "src/tint/utils/result/result.h"
+
+// Forward declarations.
+namespace tint::core::ir {
+class Module;
+} // namespace tint::core::ir
+
+namespace tint::hlsl::writer::raise {
+
+/// FxcPollyfill is a transform that replaces code constructs which cause FXC mis-compiles with
+/// safer constructs.
+/// @param module the module to transform
+/// @returns success or failure
+Result<SuccessType> FxcPolyfill(core::ir::Module& module);
+
+} // namespace tint::hlsl::writer::raise
+
+#endif // SRC_TINT_LANG_HLSL_WRITER_RAISE_FXC_POLYFILL_H_
diff --git a/src/tint/lang/hlsl/writer/raise/fxc_polyfill_test.cc b/src/tint/lang/hlsl/writer/raise/fxc_polyfill_test.cc
new file mode 100644
index 0000000..45302a4
--- /dev/null
+++ b/src/tint/lang/hlsl/writer/raise/fxc_polyfill_test.cc
@@ -0,0 +1,376 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/hlsl/writer/raise/fxc_polyfill.h"
+
+#include <gtest/gtest.h>
+
+#include "src/tint/lang/core/fluent_types.h"
+#include "src/tint/lang/core/ir/function.h"
+#include "src/tint/lang/core/ir/transform/helper_test.h"
+#include "src/tint/lang/core/number.h"
+
+using namespace tint::core::fluent_types; // NOLINT
+using namespace tint::core::number_suffixes; // NOLINT
+
+namespace tint::hlsl::writer::raise {
+namespace {
+
+using HlslWriterFxcPolyfillTest = core::ir::transform::TransformTest;
+
+// No change, no switch
+TEST_F(HlslWriterFxcPolyfillTest, NoSwitch) {
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] { b.Return(func); });
+
+ auto* src = R"(
+%foo = @fragment func():void {
+ $B1: {
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+ Run(FxcPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+// No change, switch with case and default
+TEST_F(HlslWriterFxcPolyfillTest, SwitchCaseAndDefault) {
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* s = b.Switch(1_i);
+ b.Append(b.Case(s, {b.Constant(0_i)}), [&] { b.ExitSwitch(s); });
+ b.Append(b.DefaultCase(s), [&] { b.ExitSwitch(s); });
+
+ b.Return(func);
+ });
+
+ auto* src = R"(
+%foo = @fragment func():void {
+ $B1: {
+ switch 1i [c: (0i, $B2), c: (default, $B3)] { # switch_1
+ $B2: { # case
+ exit_switch # switch_1
+ }
+ $B3: { # case
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+ Run(FxcPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+// No change, switch with multi-selector default case
+TEST_F(HlslWriterFxcPolyfillTest, SwitchMultiSelectorDefault) {
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* s = b.Switch(1_i);
+ b.Append(b.Case(s, {b.Constant(0_i), nullptr}), [&] { b.ExitSwitch(s); });
+ b.Return(func);
+ });
+
+ auto* src = R"(
+%foo = @fragment func():void {
+ $B1: {
+ switch 1i [c: (0i default, $B2)] { # switch_1
+ $B2: { # case
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+ EXPECT_EQ(src, str());
+
+ auto* expect = src;
+ Run(FxcPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+// Switch body just has a ExitSwitch
+TEST_F(HlslWriterFxcPolyfillTest, Switch) {
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+ b.Append(func->Block(), [&] {
+ auto* s = b.Switch(1_i);
+ b.Append(b.DefaultCase(s), [&] { b.ExitSwitch(s); });
+ b.Return(func);
+ });
+
+ auto* src = R"(
+%foo = @fragment func():void {
+ $B1: {
+ switch 1i [c: (default, $B2)] { # switch_1
+ $B2: { # case
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+%foo = @fragment func():void {
+ $B1: {
+ switch 1i [c: (default 0i, $B2)] { # switch_1
+ $B2: { # case
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ Run(FxcPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+// Switch body with assignment
+TEST_F(HlslWriterFxcPolyfillTest, SwitchWithAssignment) {
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+
+ auto* a = b.Var<private_>("a", b.Zero<i32>());
+ b.ir.root_block->Append(a);
+
+ b.Append(func->Block(), [&] {
+ auto* s = b.Switch(1_i);
+ b.Append(b.DefaultCase(s), [&] {
+ b.Store(a, 1_i);
+ b.ExitSwitch(s);
+ });
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<private, i32, read_write> = var, 0i
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ switch 1i [c: (default, $B3)] { # switch_1
+ $B3: { # case
+ store %a, 1i
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %a:ptr<private, i32, read_write> = var, 0i
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ switch 1i [c: (default 0i, $B3)] { # switch_1
+ $B3: { # case
+ store %a, 1i
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ Run(FxcPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+// Switch with if with break
+TEST_F(HlslWriterFxcPolyfillTest, SwitchWithIfBreak) {
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+
+ auto* a = b.Var<private_>("a", b.Zero<i32>());
+ b.ir.root_block->Append(a);
+
+ b.Append(func->Block(), [&] {
+ auto* s = b.Switch(1_i);
+ b.Append(b.DefaultCase(s), [&] {
+ auto* i = b.If(true);
+ b.Append(i->True(), [&] {
+ b.Store(a, 1_i);
+ b.ExitSwitch(s);
+ });
+ b.ExitSwitch(s);
+ });
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<private, i32, read_write> = var, 0i
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ switch 1i [c: (default, $B3)] { # switch_1
+ $B3: { # case
+ if true [t: $B4] { # if_1
+ $B4: { # true
+ store %a, 1i
+ exit_switch # switch_1
+ }
+ }
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %a:ptr<private, i32, read_write> = var, 0i
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ switch 1i [c: (default 0i, $B3)] { # switch_1
+ $B3: { # case
+ if true [t: $B4] { # if_1
+ $B4: { # true
+ store %a, 1i
+ exit_switch # switch_1
+ }
+ }
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ Run(FxcPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+// Switch with loop with break
+TEST_F(HlslWriterFxcPolyfillTest, SwitchWithLoopBreak) {
+ auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+
+ auto* a = b.Var<private_>("a", b.Zero<i32>());
+ b.ir.root_block->Append(a);
+
+ b.Append(func->Block(), [&] {
+ auto* s = b.Switch(1_i);
+ b.Append(b.DefaultCase(s), [&] {
+ auto* l = b.Loop();
+ b.Append(l->Body(), [&] {
+ b.Store(a, 1_i);
+ b.ExitLoop(l);
+ });
+ b.ExitSwitch(s);
+ });
+ b.Return(func);
+ });
+
+ auto* src = R"(
+$B1: { # root
+ %a:ptr<private, i32, read_write> = var, 0i
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ switch 1i [c: (default, $B3)] { # switch_1
+ $B3: { # case
+ loop [b: $B4] { # loop_1
+ $B4: { # body
+ store %a, 1i
+ exit_loop # loop_1
+ }
+ }
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ EXPECT_EQ(src, str());
+
+ auto* expect = R"(
+$B1: { # root
+ %a:ptr<private, i32, read_write> = var, 0i
+}
+
+%foo = @fragment func():void {
+ $B2: {
+ switch 1i [c: (default 0i, $B3)] { # switch_1
+ $B3: { # case
+ loop [b: $B4] { # loop_1
+ $B4: { # body
+ store %a, 1i
+ exit_loop # loop_1
+ }
+ }
+ exit_switch # switch_1
+ }
+ }
+ ret
+ }
+}
+)";
+
+ Run(FxcPolyfill);
+
+ EXPECT_EQ(expect, str());
+}
+
+} // namespace
+} // namespace tint::hlsl::writer::raise
diff --git a/src/tint/lang/hlsl/writer/raise/raise.cc b/src/tint/lang/hlsl/writer/raise/raise.cc
index ddb6442..da40afa 100644
--- a/src/tint/lang/hlsl/writer/raise/raise.cc
+++ b/src/tint/lang/hlsl/writer/raise/raise.cc
@@ -30,11 +30,12 @@
#include "src/tint/lang/core/ir/transform/add_empty_entry_point.h"
#include "src/tint/lang/core/ir/transform/remove_terminator_args.h"
#include "src/tint/lang/hlsl/writer/common/options.h"
+#include "src/tint/lang/hlsl/writer/raise/fxc_polyfill.h"
#include "src/tint/utils/result/result.h"
namespace tint::hlsl::writer {
-Result<SuccessType> Raise(core::ir::Module& module, const Options&) {
+Result<SuccessType> Raise(core::ir::Module& module, const Options& options) {
#define RUN_TRANSFORM(name, ...) \
do { \
auto result = name(module, ##__VA_ARGS__); \
@@ -45,6 +46,10 @@
RUN_TRANSFORM(core::ir::transform::AddEmptyEntryPoint);
+ if (options.compiler == Options::Compiler::kFXC) {
+ RUN_TRANSFORM(raise::FxcPolyfill);
+ }
+
// These transforms need to be run last as various transforms introduce terminator arguments,
// naming conflicts, and expressions that need to be explicitly not inlined.
RUN_TRANSFORM(core::ir::transform::RemoveTerminatorArgs);
diff --git a/src/tint/lang/hlsl/writer/switch_test.cc b/src/tint/lang/hlsl/writer/switch_test.cc
index 01f1152..f6458cd 100644
--- a/src/tint/lang/hlsl/writer/switch_test.cc
+++ b/src/tint/lang/hlsl/writer/switch_test.cc
@@ -39,7 +39,7 @@
b.Append(f->Block(), [&] {
auto* a = b.Var("a", b.Zero<i32>());
- auto* s = b.Switch(a);
+ auto* s = b.Switch(b.Load(a));
b.Append(b.Case(s, {b.Constant(5_i)}), [&] { b.ExitSwitch(s); });
b.Append(b.DefaultCase(s), [&] { b.ExitSwitch(s); });
b.Return(f);
@@ -71,7 +71,7 @@
b.Append(f->Block(), [&] {
auto* a = b.Var("a", b.Zero<i32>());
- auto* s = b.Switch(a);
+ auto* s = b.Switch(b.Load(a));
auto* c = b.Case(s, {b.Constant(5_i), nullptr});
b.Append(c, [&] { b.ExitSwitch(s); });
b.Return(f);
@@ -111,7 +111,7 @@
b.Append(f->Block(), [&] {
auto* cond = b.Var("cond", b.Zero<i32>());
auto* a = b.Var("a", b.Zero<i32>());
- auto* s = b.Switch(cond);
+ auto* s = b.Switch(b.Load(cond));
b.Append(b.DefaultCase(s), [&] {
b.Store(a, 42_i);
b.ExitSwitch(s);
@@ -202,8 +202,7 @@
)");
}
-// TODO(dsinclair): Needs transfrom to convert single default switch to while loop
-TEST_F(HlslWriterTest, DISABLED_SwitchOnlyDefaultCaseNoSideEffectsConditionFXC) {
+TEST_F(HlslWriterTest, SwitchOnlyDefaultCaseNoSideEffectsConditionFXC) {
// var<private> cond : i32;
// var<private> a : i32;
// fn test() {
@@ -220,7 +219,7 @@
b.Append(f->Block(), [&] {
auto* cond = b.Var("cond", b.Zero<i32>());
auto* a = b.Var("a", b.Zero<i32>());
- auto* s = b.Switch(cond);
+ auto* s = b.Switch(b.Load(cond));
b.Append(b.DefaultCase(s), [&] {
b.Store(a, 42_i);
b.ExitSwitch(s);
@@ -235,17 +234,22 @@
EXPECT_EQ(output_.hlsl, R"(
[numthreads(1, 1, 1)]
void foo() {
- while(true) {
- a = 42;
- break;
+ int cond = 0;
+ int a = 0;
+ switch(cond) {
+ default:
+ case 0:
+ {
+ a = 42;
+ break;
+ }
}
}
)");
}
-// TODO(dsinclair): Needs transfrom to convert single default switch to while loop
-TEST_F(HlslWriterTest, DISABLED_SwitchOnlyDefaultCaseSideEffectsConditionFXC) {
+TEST_F(HlslWriterTest, SwitchOnlyDefaultCaseSideEffectsConditionFXC) {
// var<private> global : i32;
// fn bar() -> i32 {
// global = 84;
@@ -290,9 +294,10 @@
ASSERT_TRUE(Generate(options)) << err_ << output_.hlsl;
EXPECT_EQ(output_.hlsl, R"(
-static int global = 0;
-static int a = 0;
-
+static
+int global = 0;
+static
+int a = 0;
int bar() {
global = 84;
return global;
@@ -300,10 +305,13 @@
[numthreads(1, 1, 1)]
void foo() {
- bar();
- while(true) {
- a = 42;
- break;
+ switch(bar()) {
+ default:
+ case 0:
+ {
+ a = 42;
+ break;
+ }
}
}