[glsl][ir] Emit struct types.

This CL adds emission of struct types to the GLSL IR backend.

Bug: 42251044
Change-Id: I7238aa554d3799646383af63d1bf9dd01348e94f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/204274
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/glsl/writer/printer/printer.cc b/src/tint/lang/glsl/writer/printer/printer.cc
index a499f72..ec54a22 100644
--- a/src/tint/lang/glsl/writer/printer/printer.cc
+++ b/src/tint/lang/glsl/writer/printer/printer.cc
@@ -28,6 +28,8 @@
 #include "src/tint/lang/glsl/writer/printer/printer.h"
 
 #include <string>
+#include <unordered_map>
+#include <unordered_set>
 #include <utility>
 
 #include "src/tint/lang/core/constant/splat.h"
@@ -61,9 +63,11 @@
 #include "src/tint/lang/core/type/void.h"
 #include "src/tint/lang/glsl/writer/common/printer_support.h"
 #include "src/tint/lang/glsl/writer/common/version.h"
+#include "src/tint/utils/containers/map.h"
 #include "src/tint/utils/generator/text_generator.h"
 #include "src/tint/utils/macros/scoped_assignment.h"
 #include "src/tint/utils/rtti/switch.h"
+#include "src/tint/utils/text/string.h"
 
 using namespace tint::core::fluent_types;  // NOLINT
 
@@ -126,6 +130,12 @@
     /// A hashmap of value to name
     Hashmap<const core::ir::Value*, std::string, 32> names_;
 
+    /// Map of builtin structure to unique generated name
+    std::unordered_map<const core::type::Struct*, std::string> builtin_struct_names_;
+
+    // The set of emitted structs
+    std::unordered_set<const core::type::Struct*> emitted_structs_;
+
     /// @returns the name of the given value, creating a new unique name if the value is unnamed in
     /// the module.
     std::string NameOf(const core::ir::Value* value) {
@@ -142,6 +152,19 @@
         return ir_.symbols.New(prefix).Name();
     }
 
+    /// @param s the structure
+    /// @returns the name of the structure, taking special care of builtin structures that start
+    /// with double underscores. If the structure is a builtin, then the returned name will be a
+    /// unique name without the leading underscores.
+    std::string StructName(const core::type::Struct* s) {
+        auto name = s->Name().Name();
+        if (HasPrefix(name, "__")) {
+            name = tint::GetOrAdd(builtin_struct_names_, s,
+                                  [&] { return UniqueIdentifier(name.substr(2)); });
+        }
+        return name;
+    }
+
     /// Emit the function
     /// @param func the function to emit
     void EmitFunction(const core::ir::Function* func) {
@@ -307,11 +330,42 @@
             },
             [&](const core::type::Vector* v) { EmitVectorType(out, v); },
             [&](const core::type::Matrix* m) { EmitMatrixType(out, m); },
+            [&](const core::type::Struct* s) {
+                EmitStructType(s);
+                out << StructName(s);
+            },
 
             // TODO(dsinclair): Handle remaining types
             TINT_ICE_ON_NO_MATCH);
     }
 
+    void EmitStructType(const core::type::Struct* str) {
+        auto it = emitted_structs_.emplace(str);
+        if (!it.second) {
+            return;
+        }
+
+        // This does not append directly to the preamble because a struct may require other
+        // structs to get emitted before it. So, the struct emits into a temporary text buffer, then
+        // anything it depends on will emit to the preamble first, and then it copies the text
+        // buffer into the preamble.
+        TextBuffer str_buf;
+        Line(&str_buf) << "\n" << "struct " << StructName(str) << " {";
+
+        str_buf.IncrementIndent();
+
+        for (auto* mem : str->Members()) {
+            auto out = Line(&str_buf);
+            EmitTypeAndName(out, mem->Type(), mem->Name().Name());
+            out << ";";
+        }
+
+        str_buf.DecrementIndent();
+        Line(&str_buf) << "};";
+
+        preamble_buffer_.Append(str_buf);
+    }
+
     void EmitVectorType(StringStream& out, const core::type::Vector* v) {
         tint::Switch(
             v->Type(),                       //
@@ -450,11 +504,26 @@
             [&](const core::type::F16*) { PrintF16(out, c->ValueAs<f16>()); },
             [&](const core::type::Vector* v) { EmitConstantVector(out, v, c); },
             [&](const core::type::Matrix* m) { EmitConstantMatrix(out, m, c); },
+            [&](const core::type::Struct* s) { EmitConstantStruct(out, s, c); },
 
             // TODO(dsinclair): Emit remaining constant types
             TINT_ICE_ON_NO_MATCH);
     }
 
+    void EmitConstantStruct(StringStream& out,
+                            const core::type::Struct* s,
+                            const core::constant::Value* c) {
+        EmitType(out, s);
+        ScopedParen sp(out);
+
+        for (size_t i = 0; i < s->Members().Length(); ++i) {
+            if (i > 0) {
+                out << ", ";
+            }
+            EmitConstant(out, c->Index(i));
+        }
+    }
+
     void EmitConstantVector(StringStream& out,
                             const core::type::Vector* v,
                             const core::constant::Value* c) {
diff --git a/src/tint/lang/glsl/writer/type_test.cc b/src/tint/lang/glsl/writer/type_test.cc
index 14a21a7..e717b37 100644
--- a/src/tint/lang/glsl/writer/type_test.cc
+++ b/src/tint/lang/glsl/writer/type_test.cc
@@ -366,8 +366,7 @@
 )");
 }
 
-// TODO(dsinclair): Add struct support
-TEST_F(GlslWriterTest, DISABLED_EmitType_Struct) {
+TEST_F(GlslWriterTest, EmitType_Struct) {
     auto* s = ty.Struct(mod.symbols.New("S"), {
                                                   {mod.symbols.Register("a"), ty.i32()},
                                                   {mod.symbols.Register("b"), ty.f32()},
@@ -388,40 +387,12 @@
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void foo() {
-  S a = {};
+  S a = S(0, 0.0f);
 }
 )");
 }
 
-// TODO(dsinclair): Add struct support
-TEST_F(GlslWriterTest, DISABLED_EmitType_Struct_NameCollision) {
-    auto* s = ty.Struct(mod.symbols.New("S"), {
-                                                  {mod.symbols.Register("double"), ty.i32()},
-                                                  {mod.symbols.Register("float"), ty.f32()},
-                                              });
-    auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kCompute);
-    func->SetWorkgroupSize(1, 1, 1);
-    b.Append(func->Block(), [&] {
-        b.Var("a", ty.ptr(core::AddressSpace::kPrivate, s));
-        b.Return(func);
-    });
-
-    ASSERT_TRUE(Generate()) << err_ << output_.glsl;
-    EXPECT_EQ(output_.glsl, GlslHeader() + R"(
-struct S {
-  int tint_symbol_1;
-  float tint_symbol_2;
-};
-
-layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
-void foo() {
-  S a = {};
-}
-)");
-}
-
-// TODO(dsinclair): Add struct support
-TEST_F(GlslWriterTest, DISABLED_EmitType_Struct_Dedup) {
+TEST_F(GlslWriterTest, EmitType_Struct_Dedup) {
     auto* s = ty.Struct(mod.symbols.New("S"), {
                                                   {mod.symbols.Register("a"), ty.i32()},
                                                   {mod.symbols.Register("b"), ty.f32()},
@@ -443,8 +414,45 @@
 
 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 void foo() {
-  S a = {};
-  S b = {};
+  S a = S(0, 0.0f);
+  S b = S(0, 0.0f);
+}
+)");
+}
+
+TEST_F(GlslWriterTest, EmitType_Struct_Nested) {
+    auto* inner =
+        ty.Struct(mod.symbols.New("Inner"), {
+                                                {mod.symbols.Register("x"), ty.u32()},
+                                                {mod.symbols.Register("y"), ty.vec4<f32>()},
+                                            });
+
+    auto* s = ty.Struct(mod.symbols.New("S"), {
+                                                  {mod.symbols.Register("a"), ty.i32()},
+                                                  {mod.symbols.Register("b"), inner},
+                                              });
+    auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kCompute);
+    func->SetWorkgroupSize(1, 1, 1);
+    b.Append(func->Block(), [&] {
+        b.Var("a", ty.ptr(core::AddressSpace::kPrivate, s));
+        b.Return(func);
+    });
+
+    ASSERT_TRUE(Generate()) << err_ << output_.glsl;
+    EXPECT_EQ(output_.glsl, GlslHeader() + R"(
+struct Inner {
+  uint x;
+  vec4 y;
+};
+
+struct S {
+  int a;
+  Inner b;
+};
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void foo() {
+  S a = S(0, Inner(0u, vec4(0.0f)));
 }
 )");
 }