M117: Metal: Fix dropped out arguments from functions with many args.

RewriteOutArgs has an early-exit if it spots a potentially aliased
arg. It's also responsible for marking out args as references,
which caused an issue in Google Earth.
Removing the early-exit fixes both issues.

Bug: chromium:1474736
Change-Id: Ib68dd3f3e2e0a1e773e4e09edcdfa3a4bdfc1ef2
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4823006
Commit-Queue: Jonah Ryan-Davis <jonahr@google.com>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
(cherry picked from commit 1ab5d01d9b0ff56522ddbfd6cbfe7cbea51e0c8e)
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4851051
Commit-Queue: Prudhvikumar Bommana <pbommana@google.com>
diff --git a/src/compiler/translator/tree_ops/msl/RewriteOutArgs.cpp b/src/compiler/translator/tree_ops/msl/RewriteOutArgs.cpp
index dda3d1f..6534457 100644
--- a/src/compiler/translator/tree_ops/msl/RewriteOutArgs.cpp
+++ b/src/compiler/translator/tree_ops/msl/RewriteOutArgs.cpp
@@ -125,23 +125,11 @@
             const TVariable &param     = *func->getParam(i);
             const TType &paramType     = param.getType();
             const TQualifier paramQual = paramType.getQualifier();
-            switch (paramQual)
-            {
-                case TQualifier::EvqParamOut:
-                case TQualifier::EvqParamInOut:
-                    if (!mSymbolEnv.isReference(param))
-                    {
-                        mSymbolEnv.markAsReference(param, AddressSpace::Thread);
-                    }
-                    break;
-                default:
-                    break;
-            }
             return paramQual;
         };
 
+        // Check which params might be aliased, and mark all out params as references.
         bool mightAlias = false;
-
         for (size_t i = 0; i < argCount; ++i)
         {
             const TQualifier paramQual = getParamQualifier(i);
@@ -151,11 +139,17 @@
                 case TQualifier::EvqParamOut:
                 case TQualifier::EvqParamInOut:
                 {
+                    const TVariable &param = *func->getParam(i);
+                    if (!mSymbolEnv.isReference(param))
+                    {
+                        mSymbolEnv.markAsReference(param, AddressSpace::Thread);
+                    }
+                    // Note: not the same as param above, this refers to the variable in the
+                    // argument list in the callsite.
                     const TVariable *var = GetVariable(*args[i]);
                     if (mVarBuffer.insert(var).count > 1)
                     {
                         mightAlias = true;
-                        i          = argCount;
                     }
                 }
                 break;
diff --git a/src/tests/compiler_tests/MSLOutput_test.cpp b/src/tests/compiler_tests/MSLOutput_test.cpp
index 1355dde..79f1df9 100644
--- a/src/tests/compiler_tests/MSLOutput_test.cpp
+++ b/src/tests/compiler_tests/MSLOutput_test.cpp
@@ -635,3 +635,215 @@
     // When WebKit build is able to run the tests, this should be changed to something else.
     //    ASSERT_TRUE(foundInCode(SH_MSL_METAL_OUTPUT, "__unnamed"));
 }
+
+TEST_F(MSLOutputTest, GlobalRescopingSimple)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        // Should rescope uf into main
+
+        float uf;
+        out vec4 my_FragColor;
+
+        void main()
+        {
+            uf += 1.0f;
+            my_FragColor = vec4(uf, 0.0, 0.0, 1.0);
+        })";
+    compile(shaderString);
+}
+
+TEST_F(MSLOutputTest, GlobalRescopingNoRescope)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        // Should not rescope any variable
+
+        float uf;
+        out vec4 my_FragColor;
+        void modifyGlobal()
+        {
+            uf = 1.0f;
+        }
+        void main()
+        {
+            modifyGlobal();
+            my_FragColor = vec4(uf, 0.0, 0.0, 1.0);
+        })";
+    compile(shaderString);
+}
+
+TEST_F(MSLOutputTest, GlobalRescopingInitializer)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        // Should rescope uf into main
+
+        float uf = 1.0f;
+        out vec4 my_FragColor;
+
+        void main()
+        {
+            uf += 1.0;
+            my_FragColor = vec4(uf, 0.0, 0.0, 1.0);
+        })";
+    compile(shaderString);
+}
+
+TEST_F(MSLOutputTest, GlobalRescopingInitializerNoRescope)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        // Should not rescope any variable
+
+        float uf = 1.0f;
+        out vec4 my_FragColor;
+
+        void modifyGlobal()
+        {
+            uf =+ 1.0f;
+        }
+        void main()
+        {
+            modifyGlobal();
+            my_FragColor = vec4(uf, 0.0, 0.0, 1.0);
+        })";
+    compile(shaderString);
+}
+
+TEST_F(MSLOutputTest, GlobalRescopingNestedFunction)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        // Should rescope a info modifyGlobal
+
+        float a = 1.0f;
+        float uf = 1.0f;
+        out vec4 my_FragColor;
+
+        void modifyGlobal()
+        {
+            uf =+ a;
+        }
+        void main()
+        {
+            modifyGlobal();
+            my_FragColor = vec4(uf, 0.0, 0.0, 1.0);
+        })";
+    compile(shaderString);
+}
+
+TEST_F(MSLOutputTest, GlobalRescopingMultipleUses)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        // Should rescope uf into main
+
+        float uf = 1.0f;
+        out vec4 my_FragColor;
+
+        void main()
+        {
+            uf =+ 1.0;
+            if (uf > 0.0)
+            {
+                uf =- 0.5;
+            }
+            my_FragColor = vec4(uf, 0.0, 0.0, 1.0);
+        })";
+    compile(shaderString);
+}
+
+TEST_F(MSLOutputTest, GlobalRescopingGloballyReferencedVar)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        // Should rescope uf into main
+
+        const float a = 1.0f;
+        float uf = a;
+        out vec4 my_FragColor;
+
+        void main()
+        {
+            my_FragColor = vec4(uf, 0.0, a, 0.0);
+        })";
+    compile(shaderString);
+}
+
+TEST_F(MSLOutputTest, GlobalRescopingDeclarationAfterFunction)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        // Should rescope c and b into main
+
+        float a = 1.0f;
+        float c = 1.0f;
+        out vec4 my_FragColor;
+
+        void modifyGlobal()
+        {
+            a =+ 1.0f;
+        }
+
+        float b = 1.0f;
+
+        void main()
+        {
+            modifyGlobal();
+            my_FragColor = vec4(a, b, c, 0.0);
+        }
+
+        )";
+    compile(shaderString);
+}
+
+TEST_F(MSLOutputTest, ReusedOutVarName)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        precision mediump float;
+
+        out vec4 my_FragColor;
+
+        void funcWith1Out(
+        out float outC) {
+            outC = 1.0;
+        }
+
+        void funcWith4Outs(
+        out float outA,
+        out float outB,
+        out float outC,
+        out float outD) {
+            outA = 1.0;
+            outB = 1.0;
+            outD = 1.0;
+        }
+
+
+        void main()
+        {
+            funcWith1Out(my_FragColor.g);
+            funcWith4Outs(my_FragColor.r, my_FragColor.g, my_FragColor.b, my_FragColor.a);
+        }
+
+        )";
+    compile(shaderString);
+}
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index c11f6f8..c35d1ee 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -16188,6 +16188,34 @@
     ASSERT_GL_NO_ERROR();
 }
 
+// Test that aliasing function out parameters work even when multiple params are aliased.
+TEST_P(GLSLTest, AliasingFunctionOutParamsMultiple)
+{
+    constexpr char kFS[] = R"(precision highp float;
+
+const vec4 colorGreen = vec4(0.,1.,0.,1.);
+const vec4 colorRed   = vec4(1.,0.,0.,1.);
+
+bool outParametersAreDistinct(out float x, out float y, out float z, out float a) {
+    x = 1.0;
+    y = 2.0;
+    z = 3.0;
+    a = 4.0;
+    return x == 1.0 && y == 2.0 && z == 3.0 && a == 4.0;
+}
+void main() {
+    float x = 0.0;
+    float y = 0.0;
+    gl_FragColor = outParametersAreDistinct(x, x, y, y) ? colorGreen : colorRed;
+}
+)";
+
+    ANGLE_GL_PROGRAM(testProgram, essl1_shaders::vs::Simple(), kFS);
+    drawQuad(testProgram, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    ASSERT_GL_NO_ERROR();
+}
+
 // Test that aliasing function inout parameters work.
 TEST_P(GLSLTest, AliasingFunctionInOutParams)
 {
@@ -16213,6 +16241,34 @@
     ASSERT_GL_NO_ERROR();
 }
 
+// Test that aliasing function inout parameters work when more than one param is aliased.
+TEST_P(GLSLTest, AliasingFunctionInOutParamsMultiple)
+{
+    constexpr char kFS[] = R"(precision highp float;
+
+const vec4 colorGreen = vec4(0.,1.,0.,1.);
+const vec4 colorRed   = vec4(1.,0.,0.,1.);
+
+bool inoutParametersAreDistinct(inout float x, inout float y, inout float z, inout float a) {
+    x = 1.0;
+    y = 2.0;
+    z = 3.0;
+    a = 4.0;
+    return x == 1.0 && y == 2.0 && z == 3.0 && a == 4.0;
+}
+void main() {
+    float x = 0.0;
+    float y = 0.0;
+    gl_FragColor = inoutParametersAreDistinct(x, x, y, y) ? colorGreen : colorRed;
+}
+)";
+
+    ANGLE_GL_PROGRAM(testProgram, essl1_shaders::vs::Simple(), kFS);
+    drawQuad(testProgram, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+    ASSERT_GL_NO_ERROR();
+}
+
 // Test that aliasing function out parameter with a global works.
 TEST_P(GLSLTest, AliasingFunctionOutParamAndGlobal)
 {