blob: ca2f3396c9d5d8036db0cc0cbf05cb37d17a8fb0 [file] [log] [blame]
// 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/wgsl/writer/writer.h"
#include <ostream>
#include <string>
#include <string_view>
#include "gtest/gtest.h"
#include "src/tint/lang/core/ir/disassembler.h"
#include "src/tint/lang/core/ir/ir_helper_test.h"
#include "src/tint/lang/wgsl/writer/ir_to_program/ir_to_program.h"
#include "src/tint/lang/wgsl/writer/ir_to_program/program_options.h"
#include "src/tint/lang/wgsl/writer/raise/raise.h"
#include "src/tint/utils/result/result.h"
#include "src/tint/utils/text/string.h"
namespace tint::wgsl::writer {
namespace {
/// Class used for IR to Program tests
class WgslIRWriterTest : public core::ir::IRTestHelper {
public:
struct Result {
/// The resulting WGSL
std::string wgsl;
/// The resulting AST
std::string ast;
/// The resulting IR before raising
std::string ir_pre_raise;
/// The resulting IR after raising
std::string ir_post_raise;
/// The resulting error
std::string err;
/// The expected WGSL
std::string expected;
};
/// @returns the WGSL generated from the IR
Result Run(std::string_view expected_wgsl) {
Result result;
result.ir_pre_raise = core::ir::Disassemble(mod).Plain();
if (auto res = tint::wgsl::writer::Raise(mod); res != Success) {
result.err = res.Failure().reason.Str();
return result;
}
result.ir_post_raise = core::ir::Disassemble(mod).Plain();
writer::ProgramOptions program_options;
program_options.allowed_features = AllowedFeatures::Everything();
auto output_program = wgsl::writer::IRToProgram(mod, program_options);
if (!output_program.IsValid()) {
result.err = output_program.Diagnostics().Str();
result.ast = Program::printer(output_program);
return result;
}
auto output = wgsl::writer::Generate(output_program, {});
if (output != Success) {
std::stringstream ss;
ss << "wgsl::Generate() errored: " << output.Failure();
result.err = ss.str();
result.ast = Program::printer(output_program);
return result;
}
result.expected = tint::TrimSpace(expected_wgsl);
if (!result.expected.empty()) {
result.expected = "\n" + result.expected + "\n";
}
result.wgsl = std::string(tint::TrimSpace(output->wgsl));
if (!result.wgsl.empty()) {
result.wgsl = "\n" + result.wgsl + "\n";
}
return result;
}
};
std::ostream& operator<<(std::ostream& o, const WgslIRWriterTest::Result& res) {
if (!res.err.empty()) {
o << "============================" << std::endl
<< "== Error ==" << std::endl
<< "============================" << std::endl
<< res.err << std::endl
<< std::endl;
}
if (!res.ir_pre_raise.empty()) {
o << "============================" << std::endl
<< "== IR (pre-raise) ==" << std::endl
<< "============================" << std::endl
<< res.ir_pre_raise << std::endl
<< std::endl;
}
if (!res.ir_post_raise.empty()) {
o << "============================" << std::endl
<< "== IR (post-raise) ==" << std::endl
<< "============================" << std::endl
<< res.ir_post_raise << std::endl
<< std::endl;
}
if (!res.ast.empty()) {
o << "============================" << std::endl
<< "== AST ==" << std::endl
<< "============================" << std::endl
<< res.ast << std::endl
<< std::endl;
}
return o;
}
#define RUN_TEST(EXPECTED) \
do { \
if (auto res = Run(EXPECTED); res.err.empty()) { \
EXPECT_EQ(res.expected, res.wgsl) << res; \
} else { \
FAIL() << res; \
} \
} while (false)
////////////////////////////////////////////////////////////////////////////////
// Short-circuiting binary ops
////////////////////////////////////////////////////////////////////////////////
TEST_F(WgslIRWriterTest, ShortCircuit_And_Param_2) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
fn->SetParams({pa, pb});
b.Append(fn->Block(), [&] {
auto* if_ = b.If(pa);
if_->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if_->True(), [&] { b.ExitIf(if_, pb); });
b.Append(if_->False(), [&] { b.ExitIf(if_, false); });
b.Return(fn, if_);
});
RUN_TEST(R"(
fn f(a : bool, b : bool) -> bool {
return (a && b);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Param_3_ab_c) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, pb); });
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
auto* if2 = b.If(if1);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, pc); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
return ((a && b) && c);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Param_3_a_bc) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] {
auto* if2 = b.If(pb);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, pc); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.ExitIf(if1, if2);
});
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
b.Return(fn, if1);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
return (a && (b && c));
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Let_2) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
fn->SetParams({pa, pb});
b.Append(fn->Block(), [&] {
auto* if_ = b.If(pa);
if_->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if_->True(), [&] { b.ExitIf(if_, pb); });
b.Append(if_->False(), [&] { b.ExitIf(if_, false); });
mod.SetName(if_, "l");
b.Return(fn, if_);
});
RUN_TEST(R"(
fn f(a : bool, b : bool) -> bool {
let l = (a && b);
return l;
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Let_3_ab_c) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, pb); });
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
auto* if2 = b.If(if1);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, pc); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
mod.SetName(if2, "l");
b.Return(fn, if2);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
let l = ((a && b) && c);
return l;
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Let_3_a_bc) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] {
auto* if2 = b.If(pb);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, pc); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.ExitIf(if1, if2);
});
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
mod.SetName(if1, "l");
b.Return(fn, if1);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
let l = (a && (b && c));
return l;
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Call_2) {
auto* fn_a = b.Function("a", ty.bool_());
b.Append(fn_a->Block(), [&] { b.Return(fn_a, true); });
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn = b.Function("f", ty.bool_());
b.Append(fn->Block(), [&] {
auto* if_ = b.If(b.Call(ty.bool_(), fn_a));
if_->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if_->True(), [&] { b.ExitIf(if_, b.Call(ty.bool_(), fn_b)); });
b.Append(if_->False(), [&] { b.ExitIf(if_, false); });
b.Return(fn, if_);
});
RUN_TEST(R"(
fn a() -> bool {
return true;
}
fn b() -> bool {
return true;
}
fn f() -> bool {
return (a() && b());
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Call_3_ab_c) {
auto* fn_a = b.Function("a", ty.bool_());
b.Append(fn_a->Block(), [&] { b.Return(fn_a, true); });
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn_c = b.Function("c", ty.bool_());
b.Append(fn_c->Block(), [&] { b.Return(fn_c, true); });
auto* fn = b.Function("f", ty.bool_());
b.Append(fn->Block(), [&] {
auto* if1 = b.If(b.Call(ty.bool_(), fn_a));
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, b.Call(ty.bool_(), fn_b)); });
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
auto* if2 = b.If(if1);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, b.Call(ty.bool_(), fn_c)); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn a() -> bool {
return true;
}
fn b() -> bool {
return true;
}
fn c() -> bool {
return true;
}
fn f() -> bool {
return ((a() && b()) && c());
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Call_3_a_bc) {
auto* fn_a = b.Function("a", ty.bool_());
b.Append(fn_a->Block(), [&] { b.Return(fn_a, true); });
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn_c = b.Function("c", ty.bool_());
b.Append(fn_c->Block(), [&] { b.Return(fn_c, true); });
auto* fn = b.Function("f", ty.bool_());
b.Append(fn->Block(), [&] {
auto* if1 = b.If(b.Call(ty.bool_(), fn_a));
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] {
auto* if2 = b.If(b.Call(ty.bool_(), fn_b));
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, b.Call(ty.bool_(), fn_c)); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.ExitIf(if1, if2);
});
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
b.Return(fn, if1);
});
RUN_TEST(R"(
fn a() -> bool {
return true;
}
fn b() -> bool {
return true;
}
fn c() -> bool {
return true;
}
fn f() -> bool {
return (a() && (b() && c()));
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Param_2) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
fn->SetParams({pa, pb});
b.Append(fn->Block(), [&] {
auto* if_ = b.If(pa);
if_->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if_->True(), [&] { b.ExitIf(if_, true); });
b.Append(if_->False(), [&] { b.ExitIf(if_, pb); });
b.Return(fn, if_);
});
RUN_TEST(R"(
fn f(a : bool, b : bool) -> bool {
return (a || b);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Param_3_ab_c) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] { b.ExitIf(if1, pb); });
auto* if2 = b.If(if1);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, pc); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
return ((a || b) || c);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Param_3_a_bc) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] {
auto* if2 = b.If(pb);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, pc); });
b.ExitIf(if1, if2);
});
b.Return(fn, if1);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
return (a || (b || c));
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Let_2) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
fn->SetParams({pa, pb});
b.Append(fn->Block(), [&] {
auto* if_ = b.If(pa);
if_->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if_->True(), [&] { b.ExitIf(if_, true); });
b.Append(if_->False(), [&] { b.ExitIf(if_, pb); });
mod.SetName(if_, "l");
b.Return(fn, if_);
});
RUN_TEST(R"(
fn f(a : bool, b : bool) -> bool {
let l = (a || b);
return l;
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Let_3_ab_c) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] { b.ExitIf(if1, pb); });
auto* if2 = b.If(if1);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, pc); });
mod.SetName(if2, "l");
b.Return(fn, if2);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
let l = ((a || b) || c);
return l;
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Let_3_a_bc) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pb = b.FunctionParam("b", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] {
auto* if2 = b.If(pb);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, pc); });
b.ExitIf(if1, if2);
});
mod.SetName(if1, "l");
b.Return(fn, if1);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
let l = (a || (b || c));
return l;
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Call_2) {
auto* fn_a = b.Function("a", ty.bool_());
b.Append(fn_a->Block(), [&] { b.Return(fn_a, true); });
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn = b.Function("f", ty.bool_());
b.Append(fn->Block(), [&] {
auto* if_ = b.If(b.Call(ty.bool_(), fn_a));
if_->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if_->True(), [&] { b.ExitIf(if_, true); });
b.Append(if_->False(), [&] { b.ExitIf(if_, b.Call(ty.bool_(), fn_b)); });
b.Return(fn, if_);
});
RUN_TEST(R"(
fn a() -> bool {
return true;
}
fn b() -> bool {
return true;
}
fn f() -> bool {
return (a() || b());
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Call_3_ab_c) {
auto* fn_a = b.Function("a", ty.bool_());
b.Append(fn_a->Block(), [&] { b.Return(fn_a, true); });
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn_c = b.Function("c", ty.bool_());
b.Append(fn_c->Block(), [&] { b.Return(fn_c, true); });
auto* fn = b.Function("f", ty.bool_());
b.Append(fn->Block(), [&] {
auto* if1 = b.If(b.Call(ty.bool_(), fn_a));
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] { b.ExitIf(if1, b.Call(ty.bool_(), fn_b)); });
auto* if2 = b.If(if1);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, b.Call(ty.bool_(), fn_c)); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn a() -> bool {
return true;
}
fn b() -> bool {
return true;
}
fn c() -> bool {
return true;
}
fn f() -> bool {
return ((a() || b()) || c());
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Call_3_a_bc) {
auto* fn_a = b.Function("a", ty.bool_());
b.Append(fn_a->Block(), [&] { b.Return(fn_a, true); });
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn_c = b.Function("c", ty.bool_());
b.Append(fn_c->Block(), [&] { b.Return(fn_c, true); });
auto* fn = b.Function("f", ty.bool_());
b.Append(fn->Block(), [&] {
auto* if1 = b.If(b.Call(ty.bool_(), fn_a));
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] {
auto* if2 = b.If(b.Call(ty.bool_(), fn_b));
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, b.Call(ty.bool_(), fn_c)); });
b.ExitIf(if1, if2);
});
b.Return(fn, if1);
});
RUN_TEST(R"(
fn a() -> bool {
return true;
}
fn b() -> bool {
return true;
}
fn c() -> bool {
return true;
}
fn f() -> bool {
return (a() || (b() || c()));
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Mixed) {
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn_d = b.Function("d", ty.bool_());
b.Append(fn_d->Block(), [&] { b.Return(fn_d, true); });
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam("a", ty.bool_());
auto* pc = b.FunctionParam("c", ty.bool_());
fn->SetParams({pa, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pa);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] { b.ExitIf(if1, b.Call(ty.bool_(), fn_b)); });
auto* if2 = b.If(if1);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] {
auto* if3 = b.If(pc);
if3->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if3->True(), [&] { b.ExitIf(if3, true); });
b.Append(if3->False(), [&] { b.ExitIf(if3, b.Call(ty.bool_(), fn_d)); });
b.ExitIf(if2, if3);
});
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn b() -> bool {
return true;
}
fn d() -> bool {
return true;
}
fn f(a : bool, c : bool) -> bool {
return ((a || b()) && (c || d()));
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_ParamCallParam_a_bc_EarlyEval) {
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam(ty.bool_());
auto* pc = b.FunctionParam(ty.bool_());
mod.SetName(pa, "a");
mod.SetName(pc, "c");
fn->SetParams({pa, pc});
b.Append(fn->Block(), [&] {
// 'b() && c' is evaluated before 'a'.
auto* if1 = b.If(b.Call(ty.bool_(), fn_b));
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, pc); });
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
auto* if2 = b.If(pa);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, if1); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn b() -> bool {
return true;
}
fn f(a : bool, c : bool) -> bool {
let v = (b() && c);
return (a && v);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Call_3_a_bc_EarlyEval) {
auto* fn_a = b.Function("a", ty.bool_());
b.Append(fn_a->Block(), [&] { b.Return(fn_a, true); });
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn_c = b.Function("c", ty.bool_());
b.Append(fn_c->Block(), [&] { b.Return(fn_c, true); });
auto* fn = b.Function("f", ty.bool_());
b.Append(fn->Block(), [&] {
// 'b() && c()' is evaluated before 'a()'.
auto* if1 = b.If(b.Call(ty.bool_(), fn_b));
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, b.Call(ty.bool_(), fn_c)); });
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
auto* if2 = b.If(b.Call(ty.bool_(), fn_a));
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, if1); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn a() -> bool {
return true;
}
fn b() -> bool {
return true;
}
fn c() -> bool {
return true;
}
fn f() -> bool {
let v = (b() && c());
return (a() && v);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_And_Param_3_a_bc_EarlyEval) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam(ty.bool_());
auto* pb = b.FunctionParam(ty.bool_());
auto* pc = b.FunctionParam(ty.bool_());
mod.SetName(pa, "a");
mod.SetName(pb, "b");
mod.SetName(pc, "c");
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pb);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, pc); });
b.Append(if1->False(), [&] { b.ExitIf(if1, false); });
auto* if2 = b.If(pa);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, if1); });
b.Append(if2->False(), [&] { b.ExitIf(if2, false); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
let v = (b && c);
return (a && v);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_ParamCallParam_a_bc_EarlyEval) {
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam(ty.bool_());
auto* pc = b.FunctionParam(ty.bool_());
mod.SetName(pa, "a");
mod.SetName(pc, "c");
fn->SetParams({pa, pc});
b.Append(fn->Block(), [&] {
// 'b() && c' is evaluated before 'a'.
auto* if1 = b.If(b.Call(ty.bool_(), fn_b));
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] { b.ExitIf(if1, pc); });
auto* v = b.Let("v", if1);
auto* if2 = b.If(pa);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, v); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn b() -> bool {
return true;
}
fn f(a : bool, c : bool) -> bool {
let v = (b() || c);
return (a || v);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Call_3_a_bc_EarlyEval) {
auto* fn_a = b.Function("a", ty.bool_());
b.Append(fn_a->Block(), [&] { b.Return(fn_a, true); });
auto* fn_b = b.Function("b", ty.bool_());
b.Append(fn_b->Block(), [&] { b.Return(fn_b, true); });
auto* fn_c = b.Function("c", ty.bool_());
b.Append(fn_c->Block(), [&] { b.Return(fn_c, true); });
auto* fn = b.Function("f", ty.bool_());
b.Append(fn->Block(), [&] {
auto* if1 = b.If(b.Call(ty.bool_(), fn_b));
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] { b.ExitIf(if1, b.Call(ty.bool_(), fn_c)); });
auto* v = b.Let("v", if1);
auto* if2 = b.If(b.Call(ty.bool_(), fn_a));
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, v); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn a() -> bool {
return true;
}
fn b() -> bool {
return true;
}
fn c() -> bool {
return true;
}
fn f() -> bool {
let v = (b() || c());
return (a() || v);
}
)");
}
TEST_F(WgslIRWriterTest, ShortCircuit_Or_Param_3_a_bc_EarlyEval) {
auto* fn = b.Function("f", ty.bool_());
auto* pa = b.FunctionParam(ty.bool_());
auto* pb = b.FunctionParam(ty.bool_());
auto* pc = b.FunctionParam(ty.bool_());
mod.SetName(pa, "a");
mod.SetName(pb, "b");
mod.SetName(pc, "c");
fn->SetParams({pa, pb, pc});
b.Append(fn->Block(), [&] {
auto* if1 = b.If(pb);
if1->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if1->True(), [&] { b.ExitIf(if1, true); });
b.Append(if1->False(), [&] { b.ExitIf(if1, pc); });
auto* if2 = b.If(pa);
if2->SetResults(b.InstructionResult(ty.bool_()));
b.Append(if2->True(), [&] { b.ExitIf(if2, true); });
b.Append(if2->False(), [&] { b.ExitIf(if2, if1); });
b.Return(fn, if2);
});
RUN_TEST(R"(
fn f(a : bool, b : bool, c : bool) -> bool {
let v = (b || c);
return (a || v);
}
)");
}
} // namespace
} // namespace tint::wgsl::writer