transform: Update MultiplanarExternalTexture to support OOO-decls

MultiplanarExternalTexture cannot deal with out-of-order declarations.
Re-work things so that it can.

Bug: tint:1266
Change-Id: Ie2c8237be4f6ddb91120cbeb25f3c186b572ba59
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/79768
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/transform/multiplanar_external_texture.cc b/src/transform/multiplanar_external_texture.cc
index 18ded92..360a18d 100644
--- a/src/transform/multiplanar_external_texture.cc
+++ b/src/transform/multiplanar_external_texture.cc
@@ -48,8 +48,8 @@
   /// ProgramBuilder for the context
   ProgramBuilder& b;
 
-  /// Desination binding locations for the expanded texture_external provided as
-  /// input into the transform.
+  /// Destination binding locations for the expanded texture_external provided
+  /// as input into the transform.
   const NewBindingPoints* new_binding_points;
 
   /// Symbol for the ExternalTextureParams struct
@@ -63,7 +63,8 @@
 
   /// Storage for new bindings that have been created corresponding to an
   /// original texture_external binding.
-  std::unordered_map<Symbol, NewBindingSymbols> new_binding_symbols;
+  std::unordered_map<const sem::Variable*, NewBindingSymbols>
+      new_binding_symbols;
 
   /// Constructor
   /// @param context the clone
@@ -80,16 +81,17 @@
     // binding and create two additional bindings (one texture_2d<f32> to
     // represent the secondary plane and one uniform buffer for the
     // ExternalTextureParams struct).
-    ctx.ReplaceAll([&](const ast::Variable* var) -> const ast::Variable* {
-      if (!sem.Get<sem::ExternalTexture>(var->type)) {
-        return nullptr;
+    for (auto* var : ctx.src->AST().GlobalVariables()) {
+      auto* sem_var = sem.Get(var);
+      if (!sem_var->Type()->UnwrapRef()->Is<sem::ExternalTexture>()) {
+        continue;
       }
 
       // If the attributes are empty, then this must be a texture_external
       // passed as a function parameter. These variables are transformed
       // elsewhere.
       if (var->attributes.empty()) {
-        return nullptr;
+        continue;
       }
 
       // If we find a texture_external binding, we know we must emit the
@@ -113,7 +115,7 @@
             "missing new binding points for texture_external at binding {" +
                 std::to_string(bp.group) + "," + std::to_string(bp.binding) +
                 "}");
-        return nullptr;
+        continue;
       }
 
       BindingPoints bps = it->second;
@@ -123,7 +125,7 @@
       // the source symbol associated with the texture_external binding that
       // corresponds with the new destination bindings.
       // NewBindingSymbols new_binding_syms;
-      auto& syms = new_binding_symbols[var->symbol];
+      auto& syms = new_binding_symbols[sem_var];
       syms.plane_0 = ctx.Clone(var->symbol);
       syms.plane_1 = b.Symbols().New("ext_tex_plane_1");
       b.Global(syms.plane_1,
@@ -139,69 +141,21 @@
       ast::AttributeList cloned_attributes = ctx.Clone(var->attributes);
       const ast::Expression* cloned_constructor = ctx.Clone(var->constructor);
 
-      return b.Var(syms.plane_0,
-                   b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
-                   cloned_constructor, cloned_attributes);
-    });
-
-    // Transform the original textureLoad and textureSampleLevel calls into
-    // textureLoadExternal and textureSampleExternal calls.
-    ctx.ReplaceAll(
-        [&](const ast::CallExpression* expr) -> const ast::CallExpression* {
-          auto* builtin = sem.Get(expr)->Target()->As<sem::Builtin>();
-
-          if (builtin && !builtin->Parameters().empty() &&
-              builtin->Parameters()[0]->Type()->Is<sem::ExternalTexture>() &&
-              builtin->Type() != sem::BuiltinType::kTextureDimensions) {
-            auto it = new_binding_symbols.find(
-                expr->args[0]->As<ast::IdentifierExpression>()->symbol);
-            if (it == new_binding_symbols.end()) {
-              // If valid new binding locations were not provided earlier, we
-              // would have been unable to create these symbols. An error
-              // message was emitted earlier, so just return early to avoid
-              // internal compiler errors and retain a clean error message.
-              return nullptr;
-            }
-            auto& syms = it->second;
-
-            if (builtin->Type() == sem::BuiltinType::kTextureLoad) {
-              return createTexLdExt(expr, syms);
-            }
-
-            if (builtin->Type() == sem::BuiltinType::kTextureSampleLevel) {
-              return createTexSmpExt(expr, syms);
-            }
-
-          } else if (sem.Get(expr)->Target()->Is<sem::Function>()) {
-            // The call expression may be to a user-defined function that
-            // contains a texture_external parameter. These need to be expanded
-            // out to multiple plane textures and the texture parameters
-            // structure.
-            for (const ast::Expression* arg : expr->args) {
-              if (auto* id_expr = arg->As<ast::IdentifierExpression>()) {
-                // Check if a parameter is a texture_external by trying to find
-                // it in the transform state.
-                auto it = new_binding_symbols.find(id_expr->symbol);
-                if (it != new_binding_symbols.end()) {
-                  auto& syms = it->second;
-                  // When we find a texture_external, we must unpack it into its
-                  // components.
-                  ctx.Replace(id_expr, b.Expr(syms.plane_0));
-                  ctx.InsertAfter(expr->args, id_expr, b.Expr(syms.plane_1));
-                  ctx.InsertAfter(expr->args, id_expr, b.Expr(syms.params));
-                }
-              }
-            }
-          }
-
-          return nullptr;
-        });
+      auto* replacement =
+          b.Var(syms.plane_0,
+                b.ty.sampled_texture(ast::TextureDimension::k2d, b.ty.f32()),
+                cloned_constructor, cloned_attributes);
+      ctx.Replace(var, replacement);
+    }
 
     // We must update all the texture_external parameters for user declared
     // functions.
-    ctx.ReplaceAll([&](const ast::Function* fn) -> const ast::Function* {
+    for (auto* fn : ctx.src->AST().Functions()) {
       for (const ast::Variable* param : fn->params) {
-        if (sem.Get<sem::ExternalTexture>(param->type)) {
+        if (auto* sem_var = sem.Get(param)) {
+          if (!sem_var->Type()->UnwrapRef()->Is<sem::ExternalTexture>()) {
+            continue;
+          }
           // If we find a texture_external, we must ensure the
           // ExternalTextureParams struct exists.
           if (!params_struct_sym.IsValid()) {
@@ -211,7 +165,7 @@
           // the texture_external into the parameter list. We must also place
           // the new symbols into the transform state so they can be used when
           // transforming function calls.
-          auto& syms = new_binding_symbols[param->symbol];
+          auto& syms = new_binding_symbols[sem_var];
           syms.plane_0 = ctx.Clone(param->symbol);
           syms.plane_1 = b.Symbols().New("ext_tex_plane_1");
           syms.params = b.Symbols().New("ext_tex_params");
@@ -226,10 +180,61 @@
               b.Param(syms.params, b.ty.type_name(params_struct_sym)));
         }
       }
-      // Clone the function. This will use the Replace() and InsertAfter() calls
-      // above.
-      return nullptr;
-    });
+    }
+
+    // Transform the original textureLoad and textureSampleLevel calls into
+    // textureLoadExternal and textureSampleExternal calls.
+    ctx.ReplaceAll(
+        [&](const ast::CallExpression* expr) -> const ast::CallExpression* {
+          auto* builtin = sem.Get(expr)->Target()->As<sem::Builtin>();
+
+          if (builtin && !builtin->Parameters().empty() &&
+              builtin->Parameters()[0]->Type()->Is<sem::ExternalTexture>() &&
+              builtin->Type() != sem::BuiltinType::kTextureDimensions) {
+            if (auto* var_user = sem.Get<sem::VariableUser>(expr->args[0])) {
+              auto it = new_binding_symbols.find(var_user->Variable());
+              if (it == new_binding_symbols.end()) {
+                // If valid new binding locations were not provided earlier, we
+                // would have been unable to create these symbols. An error
+                // message was emitted earlier, so just return early to avoid
+                // internal compiler errors and retain a clean error message.
+                return nullptr;
+              }
+              auto& syms = it->second;
+
+              if (builtin->Type() == sem::BuiltinType::kTextureLoad) {
+                return createTexLdExt(expr, syms);
+              }
+
+              if (builtin->Type() == sem::BuiltinType::kTextureSampleLevel) {
+                return createTexSmpExt(expr, syms);
+              }
+            }
+
+          } else if (sem.Get(expr)->Target()->Is<sem::Function>()) {
+            // The call expression may be to a user-defined function that
+            // contains a texture_external parameter. These need to be expanded
+            // out to multiple plane textures and the texture parameters
+            // structure.
+            for (auto* arg : expr->args) {
+              if (auto* var_user = sem.Get<sem::VariableUser>(arg)) {
+                // Check if a parameter is a texture_external by trying to find
+                // it in the transform state.
+                auto it = new_binding_symbols.find(var_user->Variable());
+                if (it != new_binding_symbols.end()) {
+                  auto& syms = it->second;
+                  // When we find a texture_external, we must unpack it into its
+                  // components.
+                  ctx.Replace(arg, b.Expr(syms.plane_0));
+                  ctx.InsertAfter(expr->args, arg, b.Expr(syms.plane_1));
+                  ctx.InsertAfter(expr->args, arg, b.Expr(syms.params));
+                }
+              }
+            }
+          }
+
+          return nullptr;
+        });
   }
 
   /// Creates the ExternalTextureParams struct.
diff --git a/src/transform/multiplanar_external_texture_test.cc b/src/transform/multiplanar_external_texture_test.cc
index 0d8df30..a0a42b8 100644
--- a/src/transform/multiplanar_external_texture_test.cc
+++ b/src/transform/multiplanar_external_texture_test.cc
@@ -149,8 +149,6 @@
 )";
 
   auto* expect = R"(
-@group(0) @binding(0) var s : sampler;
-
 struct ExternalTextureParams {
   numPlanes : u32;
   vr : f32;
@@ -163,6 +161,8 @@
 
 @group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
 
+@group(0) @binding(0) var s : sampler;
+
 @group(0) @binding(1) var ext_tex : texture_2d<f32>;
 
 fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
@@ -259,8 +259,6 @@
 )";
 
   auto* expect = R"(
-@group(0) @binding(0) var s : sampler;
-
 struct ExternalTextureParams {
   numPlanes : u32;
   vr : f32;
@@ -273,6 +271,8 @@
 
 @group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
 
+@group(0) @binding(0) var s : sampler;
+
 @group(0) @binding(1) var ext_tex : texture_2d<f32>;
 
 fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
@@ -332,8 +332,6 @@
 )";
 
   auto* expect = R"(
-@group(0) @binding(0) var s : sampler;
-
 struct ExternalTextureParams {
   numPlanes : u32;
   vr : f32;
@@ -346,24 +344,26 @@
 
 @group(0) @binding(5) var<uniform> ext_tex_params : ExternalTextureParams;
 
-@group(0) @binding(1) var ext_tex : texture_2d<f32>;
-
 @group(0) @binding(6) var ext_tex_plane_1_1 : texture_2d<f32>;
 
 @group(0) @binding(7) var<uniform> ext_tex_params_1 : ExternalTextureParams;
 
-@group(0) @binding(2) var ext_tex_1 : texture_2d<f32>;
-
 @group(0) @binding(8) var ext_tex_plane_1_2 : texture_2d<f32>;
 
 @group(0) @binding(9) var<uniform> ext_tex_params_2 : ExternalTextureParams;
 
-@group(0) @binding(3) var ext_tex_2 : texture_2d<f32>;
-
 @group(1) @binding(1) var ext_tex_plane_1_3 : texture_2d<f32>;
 
 @group(1) @binding(2) var<uniform> ext_tex_params_3 : ExternalTextureParams;
 
+@group(0) @binding(0) var s : sampler;
+
+@group(0) @binding(1) var ext_tex : texture_2d<f32>;
+
+@group(0) @binding(2) var ext_tex_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var ext_tex_2 : texture_2d<f32>;
+
 @group(1) @binding(0) var ext_tex_3 : texture_2d<f32>;
 
 fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
@@ -424,6 +424,10 @@
   ub : f32;
 }
 
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
 fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
   if ((params.numPlanes == 1u)) {
     return textureSampleLevel(plane0, smp, coord, 0.0);
@@ -438,21 +442,17 @@
   return vec4<f32>(r, g, b, 1.0);
 }
 
-fn f(t : texture_2d<f32>, ext_tex_plane_1 : texture_2d<f32>, ext_tex_params : ExternalTextureParams, s : sampler) {
-  textureSampleExternal(t, ext_tex_plane_1, s, vec2<f32>(1.0, 2.0), ext_tex_params);
+fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
+  textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
 }
 
-@group(0) @binding(2) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
 @group(0) @binding(0) var ext_tex : texture_2d<f32>;
 
 @group(0) @binding(1) var smp : sampler;
 
 @stage(fragment)
 fn main() {
-  f(ext_tex, ext_tex_plane_1_1, ext_tex_params_1, smp);
+  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
 }
 )";
   DataMap data;
@@ -490,6 +490,10 @@
   ub : f32;
 }
 
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
 fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
   if ((params.numPlanes == 1u)) {
     return textureSampleLevel(plane0, smp, coord, 0.0);
@@ -504,21 +508,17 @@
   return vec4<f32>(r, g, b, 1.0);
 }
 
-fn f(s : sampler, t : texture_2d<f32>, ext_tex_plane_1 : texture_2d<f32>, ext_tex_params : ExternalTextureParams) {
-  textureSampleExternal(t, ext_tex_plane_1, s, vec2<f32>(1.0, 2.0), ext_tex_params);
+fn f(s : sampler, t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams) {
+  textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
 }
 
-@group(0) @binding(2) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
 @group(0) @binding(0) var ext_tex : texture_2d<f32>;
 
 @group(0) @binding(1) var smp : sampler;
 
 @stage(fragment)
 fn main() {
-  f(smp, ext_tex, ext_tex_plane_1_1, ext_tex_params_1);
+  f(smp, ext_tex, ext_tex_plane_1, ext_tex_params);
 }
 )";
   DataMap data;
@@ -558,6 +558,14 @@
   ub : f32;
 }
 
+@group(0) @binding(3) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(4) var<uniform> ext_tex_params : ExternalTextureParams;
+
+@group(0) @binding(5) var ext_tex_plane_1_1 : texture_2d<f32>;
+
+@group(0) @binding(6) var<uniform> ext_tex_params_1 : ExternalTextureParams;
+
 fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
   if ((params.numPlanes == 1u)) {
     return textureSampleLevel(plane0, smp, coord, 0.0);
@@ -572,28 +580,20 @@
   return vec4<f32>(r, g, b, 1.0);
 }
 
-fn f(t : texture_2d<f32>, ext_tex_plane_1 : texture_2d<f32>, ext_tex_params : ExternalTextureParams, s : sampler, t2 : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams) {
-  textureSampleExternal(t, ext_tex_plane_1, s, vec2<f32>(1.0, 2.0), ext_tex_params);
-  textureSampleExternal(t2, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
+fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler, t2 : texture_2d<f32>, ext_tex_plane_1_3 : texture_2d<f32>, ext_tex_params_3 : ExternalTextureParams) {
+  textureSampleExternal(t, ext_tex_plane_1_2, s, vec2<f32>(1.0, 2.0), ext_tex_params_2);
+  textureSampleExternal(t2, ext_tex_plane_1_3, s, vec2<f32>(1.0, 2.0), ext_tex_params_3);
 }
 
-@group(0) @binding(3) var ext_tex_plane_1_2 : texture_2d<f32>;
-
-@group(0) @binding(4) var<uniform> ext_tex_params_2 : ExternalTextureParams;
-
 @group(0) @binding(0) var ext_tex : texture_2d<f32>;
 
 @group(0) @binding(1) var smp : sampler;
 
-@group(0) @binding(5) var ext_tex_plane_1_3 : texture_2d<f32>;
-
-@group(0) @binding(6) var<uniform> ext_tex_params_3 : ExternalTextureParams;
-
 @group(0) @binding(2) var ext_tex2 : texture_2d<f32>;
 
 @stage(fragment)
 fn main() {
-  f(ext_tex, ext_tex_plane_1_2, ext_tex_params_2, smp, ext_tex2, ext_tex_plane_1_3, ext_tex_params_3);
+  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp, ext_tex2, ext_tex_plane_1_1, ext_tex_params_1);
 }
 )";
   DataMap data;
@@ -636,6 +636,10 @@
   ub : f32;
 }
 
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
 fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
   if ((params.numPlanes == 1u)) {
     return textureSampleLevel(plane0, smp, coord, 0.0);
@@ -650,25 +654,21 @@
   return vec4<f32>(r, g, b, 1.0);
 }
 
-fn nested(t : texture_2d<f32>, ext_tex_plane_1 : texture_2d<f32>, ext_tex_params : ExternalTextureParams, s : sampler) {
-  textureSampleExternal(t, ext_tex_plane_1, s, vec2<f32>(1.0, 2.0), ext_tex_params);
+fn nested(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
+  textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
 }
 
-fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
-  nested(t, ext_tex_plane_1_1, ext_tex_params_1, s);
+fn f(t : texture_2d<f32>, ext_tex_plane_1_2 : texture_2d<f32>, ext_tex_params_2 : ExternalTextureParams, s : sampler) {
+  nested(t, ext_tex_plane_1_2, ext_tex_params_2, s);
 }
 
-@group(0) @binding(2) var ext_tex_plane_1_2 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params_2 : ExternalTextureParams;
-
 @group(0) @binding(0) var ext_tex : texture_2d<f32>;
 
 @group(0) @binding(1) var smp : sampler;
 
 @stage(fragment)
 fn main() {
-  f(ext_tex, ext_tex_plane_1_2, ext_tex_params_2, smp);
+  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
 }
 )";
   DataMap data;
@@ -730,8 +730,6 @@
 )";
 
   auto* expect = R"(
-type ET = texture_external;
-
 struct ExternalTextureParams {
   numPlanes : u32;
   vr : f32;
@@ -740,6 +738,12 @@
   ub : f32;
 }
 
+@group(0) @binding(2) var ext_tex_plane_1 : texture_2d<f32>;
+
+@group(0) @binding(3) var<uniform> ext_tex_params : ExternalTextureParams;
+
+type ET = texture_external;
+
 fn textureSampleExternal(plane0 : texture_2d<f32>, plane1 : texture_2d<f32>, smp : sampler, coord : vec2<f32>, params : ExternalTextureParams) -> vec4<f32> {
   if ((params.numPlanes == 1u)) {
     return textureSampleLevel(plane0, smp, coord, 0.0);
@@ -754,21 +758,17 @@
   return vec4<f32>(r, g, b, 1.0);
 }
 
-fn f(t : texture_2d<f32>, ext_tex_plane_1 : texture_2d<f32>, ext_tex_params : ExternalTextureParams, s : sampler) {
-  textureSampleExternal(t, ext_tex_plane_1, s, vec2<f32>(1.0, 2.0), ext_tex_params);
+fn f(t : texture_2d<f32>, ext_tex_plane_1_1 : texture_2d<f32>, ext_tex_params_1 : ExternalTextureParams, s : sampler) {
+  textureSampleExternal(t, ext_tex_plane_1_1, s, vec2<f32>(1.0, 2.0), ext_tex_params_1);
 }
 
-@group(0) @binding(2) var ext_tex_plane_1_1 : texture_2d<f32>;
-
-@group(0) @binding(3) var<uniform> ext_tex_params_1 : ExternalTextureParams;
-
 @group(0) @binding(0) var ext_tex : texture_2d<f32>;
 
 @group(0) @binding(1) var smp : sampler;
 
 @stage(fragment)
 fn main() {
-  f(ext_tex, ext_tex_plane_1_1, ext_tex_params_1, smp);
+  f(ext_tex, ext_tex_plane_1, ext_tex_params, smp);
 }
 )";
   DataMap data;