Resolver: Defer building of semantic::Struct until end

This will allow us to collect up usage information of the structures in a single pass.

Bug: tint:320
Bug: tint:643
Change-Id: Iaa700dc1e287f6df2717c422e66ec453b23b22dc
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/45123
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index fcb9164..53a69f9 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -1242,6 +1242,15 @@
     sem.Add(expr, builder_->create<semantic::Expression>(expr, info.type,
                                                          info.statement));
   }
+
+  // Create semantic nodes for all structs
+  for (auto it : struct_info_) {
+    auto* str = it.first;
+    auto* info = it.second;
+    builder_->Sem().Add(str, builder_->create<semantic::Struct>(
+                                 str, std::move(info->members), info->align,
+                                 info->size, info->size_no_padding));
+  }
 }
 
 bool Resolver::DefaultAlignAndSize(type::Type* ty,
@@ -1288,9 +1297,9 @@
     size = vector_align[mat->rows()] * mat->columns();
     return true;
   } else if (auto* s = ty->As<type::Struct>()) {
-    if (auto* sem = Structure(s)) {
-      align = sem->Align();
-      size = sem->Size();
+    if (auto* si = Structure(s)) {
+      align = si->align;
+      size = si->size;
       return true;
     }
     return false;
@@ -1354,10 +1363,11 @@
   return create_semantic(utils::RoundUp(el_align, el_size));
 }
 
-const semantic::Struct* Resolver::Structure(type::Struct* str) {
-  if (auto* sem = builder_->Sem().Get(str)) {
-    // Semantic info already constructed for this structure type
-    return sem;
+Resolver::StructInfo* Resolver::Structure(type::Struct* str) {
+  auto info_it = struct_info_.find(str);
+  if (info_it != struct_info_.end()) {
+    // StructInfo already resolved for this structure type
+    return info_it->second;
   }
 
   semantic::StructMemberList sem_members;
@@ -1451,10 +1461,13 @@
   auto size_no_padding = struct_size;
   struct_size = utils::RoundUp(struct_align, struct_size);
 
-  auto* sem = builder_->create<semantic::Struct>(
-      str, std::move(sem_members), struct_align, struct_size, size_no_padding);
-  builder_->Sem().Add(str, sem);
-  return sem;
+  auto* info = struct_infos_.Create();
+  info->members = std::move(sem_members);
+  info->align = struct_align;
+  info->size = struct_size;
+  info->size_no_padding = size_no_padding;
+  struct_info_.emplace(str, info);
+  return info;
 }
 
 template <typename F>
@@ -1470,8 +1483,10 @@
 Resolver::VariableInfo::~VariableInfo() = default;
 
 Resolver::FunctionInfo::FunctionInfo(ast::Function* decl) : declaration(decl) {}
-
 Resolver::FunctionInfo::~FunctionInfo() = default;
 
+Resolver::StructInfo::StructInfo() = default;
+Resolver::StructInfo::~StructInfo() = default;
+
 }  // namespace resolver
 }  // namespace tint
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index 8fd6900..4bbe504 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -114,6 +114,18 @@
     semantic::Statement* statement;
   };
 
+  /// Structure holding semantic information about a struct.
+  /// Used to build the semantic::Struct nodes at the end of resolving.
+  struct StructInfo {
+    StructInfo();
+    ~StructInfo();
+
+    std::vector<semantic::StructMember*> members;
+    uint32_t align = 0;
+    uint32_t size = 0;
+    uint32_t size_no_padding = 0;
+  };
+
   /// Structure holding semantic information about a block (i.e. scope), such as
   /// parent block and variables declared in the block.
   /// Used to validate variable scoping rules.
@@ -201,10 +213,9 @@
   /// returned.
   const semantic::Array* Array(type::Array*);
 
-  /// @returns the semantic information for the structure `str`, building it if
-  /// it hasn't been constructed already. If an error is raised, nullptr is
-  /// returned.
-  const semantic::Struct* Structure(type::Struct* str);
+  /// @returns the StructInfo for the structure `str`, building it if it hasn't
+  /// been constructed already. If an error is raised, nullptr is returned.
+  StructInfo* Structure(type::Struct* str);
 
   /// @param align the output default alignment in bytes for the type `ty`
   /// @param size the output default size in bytes for the type `ty`
@@ -239,10 +250,12 @@
   std::unordered_map<ast::Variable*, VariableInfo*> variable_to_info_;
   std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_;
   std::unordered_map<ast::Expression*, ExpressionInfo> expr_info_;
+  std::unordered_map<type::Struct*, StructInfo*> struct_info_;
   FunctionInfo* current_function_ = nullptr;
   semantic::Statement* current_statement_ = nullptr;
   BlockAllocator<VariableInfo> variable_infos_;
   BlockAllocator<FunctionInfo> function_infos_;
+  BlockAllocator<StructInfo> struct_infos_;
 };
 
 }  // namespace resolver