blob: 02a565c4e853ecb57c71ffd7327a8a75323ec6e5 [file] [log] [blame]
// Copyright 2022 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/transform/merge_return.h"
#include <utility>
#include "src/tint/transform/test_helper.h"
namespace tint::transform {
namespace {
using MergeReturnTest = TransformTest;
TEST_F(MergeReturnTest, ShouldRunEmptyModule) {
auto* src = R"()";
EXPECT_FALSE(ShouldRun<MergeReturn>(src));
}
TEST_F(MergeReturnTest, ShouldRunOnlyEntryPoints) {
auto* src = R"(
@fragment
fn foo() {}
@compute @workgroup_size(1)
fn bar() {}
)";
EXPECT_FALSE(ShouldRun<MergeReturn>(src));
}
TEST_F(MergeReturnTest, ShouldRunUserFuncNoReturn) {
auto* src = R"(
fn foo() {}
@compute @workgroup_size(1)
fn bar() {
foo();
}
)";
EXPECT_FALSE(ShouldRun<MergeReturn>(src));
}
TEST_F(MergeReturnTest, ShouldRunUserFuncSingleReturn) {
auto* src = R"(
fn foo() -> i32 {
return 42;
}
@compute @workgroup_size(1)
fn bar() {
foo();
}
)";
EXPECT_FALSE(ShouldRun<MergeReturn>(src));
}
TEST_F(MergeReturnTest, ShouldRunUserFuncMultipleReturn) {
auto* src = R"(
fn foo(x : i32) -> i32 {
if (x == 0) {
return 42;
}
return 0;
}
@compute @workgroup_size(1)
fn bar() {
foo(7);
}
)";
EXPECT_TRUE(ShouldRun<MergeReturn>(src));
}
TEST_F(MergeReturnTest, EmptyModule) {
auto* src = R"()";
auto* expect = src;
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, SingleExitPoint) {
auto* src = R"(
fn foo(x : i32) -> i32 {
var retval = 0;
if ((x == 0)) {
retval = 42;
} else {
retval = 7;
}
return retval;
}
)";
auto* expect = src;
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, Basic) {
auto* src = R"(
var<private> non_uniform_global : i32;
fn foo() {
if (non_uniform_global == 0) {
return;
} else {
return;
}
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
fn foo() {
var tint_return_flag : bool;
if ((non_uniform_global == 0)) {
{
tint_return_flag = true;
}
} else {
{
tint_return_flag = true;
}
}
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, BasicRetval) {
auto* src = R"(
var<private> non_uniform_global : i32;
fn foo() -> i32 {
if (non_uniform_global == 0) {
return 1;
} else {
return 2;
}
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
if ((non_uniform_global == 0)) {
{
tint_return_flag = true;
tint_return_value = 1;
}
} else {
{
tint_return_flag = true;
tint_return_value = 2;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, CodeAfterIf) {
auto* src = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
if (non_uniform_global == 0) {
return 1;
}
bar = 42;
return 2;
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
if ((non_uniform_global == 0)) {
{
tint_return_flag = true;
tint_return_value = 1;
}
}
if (!(tint_return_flag)) {
bar = 42;
{
tint_return_flag = true;
tint_return_value = 2;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, SequenceOfConditionalReturns) {
auto* src = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
if (non_uniform_global == 0) {
return 1;
}
bar = 42;
if (non_uniform_global == 1) {
return 2;
}
bar = 43;
if (non_uniform_global == 2) {
return 3;
}
bar = 44;
if (non_uniform_global == 3) {
return 4;
}
bar = 45;
return 0;
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
if ((non_uniform_global == 0)) {
{
tint_return_flag = true;
tint_return_value = 1;
}
}
if (!(tint_return_flag)) {
bar = 42;
if ((non_uniform_global == 1)) {
{
tint_return_flag = true;
tint_return_value = 2;
}
}
if (!(tint_return_flag)) {
bar = 43;
if ((non_uniform_global == 2)) {
{
tint_return_flag = true;
tint_return_value = 3;
}
}
if (!(tint_return_flag)) {
bar = 44;
if ((non_uniform_global == 3)) {
{
tint_return_flag = true;
tint_return_value = 4;
}
}
if (!(tint_return_flag)) {
bar = 45;
{
tint_return_flag = true;
tint_return_value = 0;
}
}
}
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, NestedConditionals) {
auto* src = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
if (non_uniform_global == 0) {
if (true) {
return 1;
} else {
return 2;
}
} else {
if (true) {
bar = 42;
} else if (false) {
return 3;
} else if (true) {
return 4;
}
return 5;
}
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
if ((non_uniform_global == 0)) {
if (true) {
{
tint_return_flag = true;
tint_return_value = 1;
}
} else {
{
tint_return_flag = true;
tint_return_value = 2;
}
}
} else {
if (true) {
bar = 42;
} else if (false) {
{
tint_return_flag = true;
tint_return_value = 3;
}
} else if (true) {
{
tint_return_flag = true;
tint_return_value = 4;
}
}
if (!(tint_return_flag)) {
{
tint_return_flag = true;
tint_return_value = 5;
}
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, Loop) {
auto* src = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var i = 0;
loop {
bar = i;
if (non_uniform_global == 0) {
return non_uniform_global;
}
if (i >= 10) {
break;
}
}
return 0;
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
var i = 0;
loop {
bar = i;
if ((non_uniform_global == 0)) {
{
tint_return_flag = true;
tint_return_value = non_uniform_global;
break;
}
}
if ((i >= 10)) {
break;
}
}
if (!(tint_return_flag)) {
{
tint_return_flag = true;
tint_return_value = 0;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, NestedLoop) {
auto* src = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var i = 0;
loop {
loop {
bar = i;
if (non_uniform_global == 0) {
return non_uniform_global;
}
if (i >= 10) {
break;
}
}
bar = 10;
}
return 0;
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
var i = 0;
loop {
loop {
bar = i;
if ((non_uniform_global == 0)) {
{
tint_return_flag = true;
tint_return_value = non_uniform_global;
break;
}
}
if ((i >= 10)) {
break;
}
}
if (tint_return_flag) {
break;
}
bar = 10;
}
if (!(tint_return_flag)) {
{
tint_return_flag = true;
tint_return_value = 0;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, NestedLoopAlwaysReturns) {
auto* src = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var i = 0;
loop {
bar = i;
loop {
return non_uniform_global;
}
if (i == 10) {
break;
}
}
return 0;
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
var i = 0;
loop {
bar = i;
loop {
{
tint_return_flag = true;
tint_return_value = non_uniform_global;
break;
}
}
break;
if ((i == 10)) {
break;
}
}
if (!(tint_return_flag)) {
{
tint_return_flag = true;
tint_return_value = 0;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, ForLoop) {
auto* src = R"(
var<private> non_uniform_global : array<i32, 10>;
fn foo() -> i32 {
for (var i = 0; i < 10; i++) {
if (non_uniform_global[i] == 0) {
return i;
}
}
return 0;
}
)";
auto* expect = R"(
var<private> non_uniform_global : array<i32, 10>;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
for(var i = 0; (i < 10); i++) {
if ((non_uniform_global[i] == 0)) {
{
tint_return_flag = true;
tint_return_value = i;
break;
}
}
}
if (!(tint_return_flag)) {
{
tint_return_flag = true;
tint_return_value = 0;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, WhileLoop) {
auto* src = R"(
var<private> non_uniform_global : array<i32, 10>;
fn foo() -> i32 {
var i = 0;
while (i < 10) {
if (non_uniform_global[i] == 0) {
return i;
}
i++;
}
return 0;
}
)";
auto* expect = R"(
var<private> non_uniform_global : array<i32, 10>;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
var i = 0;
while((i < 10)) {
if ((non_uniform_global[i] == 0)) {
{
tint_return_flag = true;
tint_return_value = i;
break;
}
}
i++;
}
if (!(tint_return_flag)) {
{
tint_return_flag = true;
tint_return_value = 0;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, Switch) {
auto* src = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
switch (non_uniform_global) {
case 0 {
return 0;
}
case 1 {
if (false) {
return 1;
}
bar = 6;
}
case 2 {
bar = 5;
}
default {
return 42;
}
}
return 0;
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
switch(non_uniform_global) {
case 0: {
{
tint_return_flag = true;
tint_return_value = 0;
break;
}
}
case 1: {
if (false) {
{
tint_return_flag = true;
tint_return_value = 1;
break;
}
}
bar = 6;
}
case 2: {
bar = 5;
}
default: {
{
tint_return_flag = true;
tint_return_value = 42;
break;
}
}
}
if (!(tint_return_flag)) {
{
tint_return_flag = true;
tint_return_value = 0;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(MergeReturnTest, ComplexNesting) {
auto* src = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
for (var i = 0; i < 10; i++) {
bar = i;
switch (non_uniform_global) {
case 0 {
return 0;
}
case 1 {
if (false) {
loop {
if (i == non_uniform_global) {
return 1;
}
continuing {
bar++;
break if (i == 7);
}
}
}
bar = 0;
}
default {
return 42;
}
}
}
return 0;
}
)";
auto* expect = R"(
var<private> non_uniform_global : i32;
var<private> bar : i32;
fn foo() -> i32 {
var tint_return_flag : bool;
var tint_return_value : i32;
for(var i = 0; (i < 10); i++) {
bar = i;
switch(non_uniform_global) {
case 0: {
{
tint_return_flag = true;
tint_return_value = 0;
break;
}
}
case 1: {
if (false) {
loop {
if ((i == non_uniform_global)) {
{
tint_return_flag = true;
tint_return_value = 1;
break;
}
}
continuing {
bar++;
break if (i == 7);
}
}
if (tint_return_flag) {
break;
}
}
bar = 0;
}
default: {
{
tint_return_flag = true;
tint_return_value = 42;
break;
}
}
}
if (tint_return_flag) {
break;
}
}
if (!(tint_return_flag)) {
{
tint_return_flag = true;
tint_return_value = 0;
}
}
return tint_return_value;
}
)";
auto got = Run<MergeReturn>(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace tint::transform