94ee620d创建于 2025年6月16日历史提交
//
// Copyright 2025 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// EnsureLoopForwardProgress_test.cpp:
//   Tests that loops make forward progress.
//

#include "GLSLANG/ShaderLang.h"
#include "angle_gl.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "tests/test_utils/compiler_test.h"

namespace
{
using namespace sh;
using namespace testing;

class EnsureLoopForwardProgressTest : public MatchOutputCodeTest
{
  public:
    EnsureLoopForwardProgressTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_ESSL_OUTPUT)
    {
        ShCompileOptions defaultCompileOptions          = {};
        defaultCompileOptions.ensureLoopForwardProgress = true;
        defaultCompileOptions.validateAST               = true;
        setDefaultCompileOptions(defaultCompileOptions);
    }
};

TEST_F(EnsureLoopForwardProgressTest, FiniteForInitLessThanConstantPlusPlus)
{
    const char kShader[]   = R"(#version 300 es
void main() {
        for (highp int i = 0; i < 100; ++i) { }
})";
    const char kExpected[] = R"(#version 300 es
void main(){
  for (highp int _ui = 0; (_ui < 100); (++_ui))
  {
  }
}
)";
    compile(kShader);
    EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}

TEST_F(EnsureLoopForwardProgressTest, InfiniteForExample)
{
    const char kShader[]   = R"(#version 300 es
void main() {
  for (highp int i = 0; i < 100; i++) { i = 0; }
})";
    const char kExpected[] = R"(#version 300 es
void main(){
  for (highp int _ui = 0; (_ui < 100); (_ui++))
  {
    loopForwardProgress();
    {
      (_ui = 0);
    }
  }
}
)";
    compile(kShader);
    EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}

TEST_F(EnsureLoopForwardProgressTest, InfiniteNestedForExample)
{
    const char kShader[]   = R"(#version 300 es
void main() {
  for (highp int i = 0; i < 100; i++) { for (highp int j = 0; j < 100; j++) { j = 0; } i = 0; }
})";
    const char kExpected[] = R"(#version 300 es
void main(){
  for (highp int _ui = 0; (_ui < 100); (_ui++))
  {
    loopForwardProgress();
    {
      for (highp int _uj = 0; (_uj < 100); (_uj++))
      {
        loopForwardProgress();
        {
          (_uj = 0);
        }
      }
      (_ui = 0);
    }
  }
}
)";
    compile(kShader);
    EXPECT_EQ(kExpected, outputCode(SH_ESSL_OUTPUT));
}

TEST_F(EnsureLoopForwardProgressTest, FiniteFors)
{
    const char kShaderPrefix[] = R"(#version 300 es
precision highp int;
uniform int a;
uniform uint b;
void main() {

)";
    const char kShaderSuffix[] = "}\n";
    const char *tests[]{"int i = 101; for (; i < 10; i++) { }",
                        "int i = 101; for (; i < 10; i+=1) { }",
                        "int i = 101; for (; i < 10; i-=1) { }",
                        "for (int i = 0; i < 10; i++) { }",
                        "for (int i = 0; i < a; i++) { }",
                        "for (int i = 0; i < 100000/2; ++i) { }",
                        "for (uint i = 0u; i < 10u; i++) { }",
                        "for (uint i = 0u; i < b; i++) { }",
                        "for (uint i = 0u; i < 100000u/2u; ++i) { }",
                        "for (uint i = 0u; i < 4294967295u; ++i) { }",
                        "for (uint i = 10u; i > 1u+3u ; --i) { }",
                        "const int z = 7; for (int i = 0; i < z; i++) { }",
                        "for (int i = 0; i < 10; i++) { for (int j = 0; j < 1000; ++j) { }}"};
    for (auto &test : tests)
    {
        std::string shader = (std::stringstream() << kShaderPrefix << test << kShaderSuffix).str();
        compile(shader.c_str());
        std::string output = outputCode(SH_ESSL_OUTPUT);
        EXPECT_THAT(output, HasSubstr("void main(){"));
        EXPECT_THAT(output, Not(HasSubstr("loopForwardProgress();")))
            << "input: " << test << "output: " << output;
    }
}

TEST_F(EnsureLoopForwardProgressTest, InfiniteFors)
{
    const char kShaderPrefix[] = R"(#version 300 es
precision highp int;
uniform int a;
uniform uint b;
void main() {

)";
    const char kShaderSuffix[] = "}\n";
    const char *tests[]{"for (;;) { }",
                        "for (bool b = true; b; b = false) { }",
                        "for (int i = 0; i < 10;) { }",
                        "int i = 101; for (; i < 10; i+=2) { }",
                        "int i = 101; for (; i < 10; i-=2) { }",
                        "int z = 7; for (int i = 0; i < z; i++) { }",
                        "for (int i = 0; i < 10; i++) { i++; }",
                        "for (int i = 0; i < 10;) { i++; }",
                        "for (int i = 0; i < a/2; i++) { }",
                        "for (int i = 0; float(i) < 10e10; ++i) { }",
                        "for (int i = 0; i < 10; i++) { for (int j = 0; j < 1000; ++i) { }}",
                        "for (int i = 0; i != 1; i+=2) { }"};
    for (auto &test : tests)
    {
        std::string shader = (std::stringstream() << kShaderPrefix << test << kShaderSuffix).str();
        compile(shader.c_str());
        std::string output = outputCode(SH_ESSL_OUTPUT);
        EXPECT_THAT(output, HasSubstr("void main(){"));
        EXPECT_THAT(output, HasSubstr("loopForwardProgress();"))
            << "input: " << test << "output: " << output;
    }
}

}  // namespace