Remove EntryPoint.

This CL removes the EntryPoint node and transitions everything to the
stage decoration.

Change-Id: Ib2840155905c8fa60ff35870f0c4b6705efb73ff
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/28705
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
Reviewed-by: Sarah Mashayekhi <sarahmashay@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 37366c1..084ee6f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -255,8 +255,6 @@
     "src/ast/discard_statement.h",
     "src/ast/else_statement.cc",
     "src/ast/else_statement.h",
-    "src/ast/entry_point.cc",
-    "src/ast/entry_point.h",
     "src/ast/expression.cc",
     "src/ast/expression.h",
     "src/ast/fallthrough_statement.cc",
@@ -706,7 +704,6 @@
     "src/ast/decorated_variable_test.cc",
     "src/ast/discard_statement_test.cc",
     "src/ast/else_statement_test.cc",
-    "src/ast/entry_point_test.cc",
     "src/ast/expression_test.cc",
     "src/ast/fallthrough_statement_test.cc",
     "src/ast/float_literal_test.cc",
@@ -840,7 +837,6 @@
     "src/writer/spirv/builder_cast_expression_test.cc",
     "src/writer/spirv/builder_constructor_expression_test.cc",
     "src/writer/spirv/builder_discard_test.cc",
-    "src/writer/spirv/builder_entry_point_test.cc",
     "src/writer/spirv/builder_format_conversion_test.cc",
     "src/writer/spirv/builder_function_decoration_test.cc",
     "src/writer/spirv/builder_function_test.cc",
@@ -899,7 +895,6 @@
     "src/reader/wgsl/parser_impl_depth_texture_type_test.cc",
     "src/reader/wgsl/parser_impl_else_stmt_test.cc",
     "src/reader/wgsl/parser_impl_elseif_stmt_test.cc",
-    "src/reader/wgsl/parser_impl_entry_point_decl_test.cc",
     "src/reader/wgsl/parser_impl_equality_expression_test.cc",
     "src/reader/wgsl/parser_impl_exclusive_or_expression_test.cc",
     "src/reader/wgsl/parser_impl_for_stmt_test.cc",
@@ -992,7 +987,6 @@
     "src/writer/wgsl/generator_impl_constructor_test.cc",
     "src/writer/wgsl/generator_impl_continue_test.cc",
     "src/writer/wgsl/generator_impl_discard_test.cc",
-    "src/writer/wgsl/generator_impl_entry_point_test.cc",
     "src/writer/wgsl/generator_impl_fallthrough_test.cc",
     "src/writer/wgsl/generator_impl_function_test.cc",
     "src/writer/wgsl/generator_impl_identifier_test.cc",
@@ -1043,7 +1037,6 @@
     "src/writer/msl/generator_impl_constructor_test.cc",
     "src/writer/msl/generator_impl_continue_test.cc",
     "src/writer/msl/generator_impl_discard_test.cc",
-    "src/writer/msl/generator_impl_entry_point_test.cc",
     "src/writer/msl/generator_impl_function_entry_point_data_test.cc",
     "src/writer/msl/generator_impl_function_test.cc",
     "src/writer/msl/generator_impl_identifier_test.cc",
@@ -1096,7 +1089,6 @@
     "src/writer/hlsl/generator_impl_constructor_test.cc",
     "src/writer/hlsl/generator_impl_continue_test.cc",
     "src/writer/hlsl/generator_impl_discard_test.cc",
-    "src/writer/hlsl/generator_impl_entry_point_test.cc",
     "src/writer/hlsl/generator_impl_function_entry_point_data_test.cc",
     "src/writer/hlsl/generator_impl_function_test.cc",
     "src/writer/hlsl/generator_impl_identifier_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 213cc99..15c84cd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -76,8 +76,6 @@
   ast/discard_statement.h
   ast/else_statement.cc
   ast/else_statement.h
-  ast/entry_point.cc
-  ast/entry_point.h
   ast/expression.cc
   ast/expression.h
   ast/fallthrough_statement.cc
@@ -315,7 +313,6 @@
   ast/discard_statement_test.cc
   ast/decorated_variable_test.cc
   ast/else_statement_test.cc
-  ast/entry_point_test.cc
   ast/expression_test.cc
   ast/fallthrough_statement_test.cc
   ast/float_literal_test.cc
@@ -429,7 +426,6 @@
     reader/wgsl/parser_impl_depth_texture_type_test.cc
     reader/wgsl/parser_impl_else_stmt_test.cc
     reader/wgsl/parser_impl_elseif_stmt_test.cc
-    reader/wgsl/parser_impl_entry_point_decl_test.cc
     reader/wgsl/parser_impl_equality_expression_test.cc
     reader/wgsl/parser_impl_exclusive_or_expression_test.cc
     reader/wgsl/parser_impl_for_stmt_test.cc
@@ -500,7 +496,6 @@
     writer/spirv/builder_cast_expression_test.cc
     writer/spirv/builder_constructor_expression_test.cc
     writer/spirv/builder_discard_test.cc
-    writer/spirv/builder_entry_point_test.cc
     writer/spirv/builder_format_conversion_test.cc
     writer/spirv/builder_function_decoration_test.cc
     writer/spirv/builder_function_test.cc
@@ -539,7 +534,6 @@
     writer/wgsl/generator_impl_constructor_test.cc
     writer/wgsl/generator_impl_continue_test.cc
     writer/wgsl/generator_impl_discard_test.cc
-    writer/wgsl/generator_impl_entry_point_test.cc
     writer/wgsl/generator_impl_fallthrough_test.cc
     writer/wgsl/generator_impl_function_test.cc
     writer/wgsl/generator_impl_identifier_test.cc
@@ -571,7 +565,6 @@
     writer/msl/generator_impl_constructor_test.cc
     writer/msl/generator_impl_continue_test.cc
     writer/msl/generator_impl_discard_test.cc
-    writer/msl/generator_impl_entry_point_test.cc
     writer/msl/generator_impl_function_entry_point_data_test.cc
     writer/msl/generator_impl_function_test.cc
     writer/msl/generator_impl_identifier_test.cc
@@ -606,7 +599,6 @@
     writer/hlsl/generator_impl_constructor_test.cc
     writer/hlsl/generator_impl_continue_test.cc
     writer/hlsl/generator_impl_discard_test.cc
-    writer/hlsl/generator_impl_entry_point_test.cc
     writer/hlsl/generator_impl_function_entry_point_data_test.cc
     writer/hlsl/generator_impl_function_test.cc
     writer/hlsl/generator_impl_identifier_test.cc
diff --git a/src/ast/entry_point.cc b/src/ast/entry_point.cc
deleted file mode 100644
index 69a4493..0000000
--- a/src/ast/entry_point.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/entry_point.h"
-
-namespace tint {
-namespace ast {
-
-EntryPoint::EntryPoint(PipelineStage stage,
-                       const std::string& name,
-                       const std::string& fn_name)
-    : Node(), stage_(stage), name_(name), fn_name_(fn_name) {}
-
-EntryPoint::EntryPoint(const Source& source,
-                       PipelineStage stage,
-                       const std::string& name,
-                       const std::string& fn_name)
-    : Node(source), stage_(stage), name_(name), fn_name_(fn_name) {}
-
-EntryPoint::~EntryPoint() = default;
-
-bool EntryPoint::IsValid() const {
-  if (stage_ == PipelineStage::kNone) {
-    return false;
-  }
-  if (fn_name_.length() == 0) {
-    return false;
-  }
-  return true;
-}
-
-void EntryPoint::to_str(std::ostream& out, size_t indent) const {
-  make_indent(out, indent);
-  out << "EntryPoint{" << stage_;
-  if (name_.length() > 0)
-    out << " as " << name_;
-
-  out << " = " << fn_name_ << "}" << std::endl;
-}
-
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/entry_point.h b/src/ast/entry_point.h
deleted file mode 100644
index 3b4086e..0000000
--- a/src/ast/entry_point.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#ifndef SRC_AST_ENTRY_POINT_H_
-#define SRC_AST_ENTRY_POINT_H_
-
-#include <memory>
-#include <ostream>
-#include <string>
-#include <vector>
-
-#include "src/ast/node.h"
-#include "src/ast/pipeline_stage.h"
-
-namespace tint {
-namespace ast {
-
-/// An entry point statement.
-class EntryPoint : public Node {
- public:
-  /// Constructor
-  EntryPoint() = default;
-  /// Constructor
-  /// @param stage the entry point stage
-  /// @param name the entry point name
-  /// @param fn_name the function name
-  EntryPoint(PipelineStage stage,
-             const std::string& name,
-             const std::string& fn_name);
-  /// Constructor
-  /// @param source the source of the entry point
-  /// @param stage the entry point stage
-  /// @param name the entry point name
-  /// @param fn_name the function name
-  EntryPoint(const Source& source,
-             PipelineStage stage,
-             const std::string& name,
-             const std::string& fn_name);
-  /// Move constructor
-  EntryPoint(EntryPoint&&) = default;
-
-  ~EntryPoint() override;
-
-  /// Sets the entry point name
-  /// @param name the name to set
-  void set_name(const std::string& name) { name_ = name; }
-  /// @returns the entry points name
-  const std::string& name() const { return name_; }
-  /// Sets the entry point function name
-  /// @param name the function name
-  void set_function_name(const std::string& name) { fn_name_ = name; }
-  /// @returns the function name for the entry point
-  const std::string& function_name() const { return fn_name_; }
-  /// Sets the piepline stage
-  /// @param stage the stage to set
-  void set_pipeline_stage(PipelineStage stage) { stage_ = stage; }
-  /// @returns the pipeline stage for the entry point
-  PipelineStage stage() const { return stage_; }
-
-  /// @returns true if the entry point is valid
-  bool IsValid() const override;
-
-  /// Writes a representation of the entry point to the output stream
-  /// @param out the stream to write too
-  /// @param indent number of spaces to ident the node when writing
-  void to_str(std::ostream& out, size_t indent) const override;
-
- private:
-  EntryPoint(const EntryPoint&) = delete;
-
-  Source source_;
-  PipelineStage stage_;
-  std::string name_;
-  std::string fn_name_;
-};
-
-/// A list of unique entry points.
-using EntryPointList = std::vector<std::unique_ptr<EntryPoint>>;
-
-}  // namespace ast
-}  // namespace tint
-
-#endif  // SRC_AST_ENTRY_POINT_H_
diff --git a/src/ast/entry_point_test.cc b/src/ast/entry_point_test.cc
deleted file mode 100644
index 0f34fb9..0000000
--- a/src/ast/entry_point_test.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/entry_point.h"
-
-#include <sstream>
-
-#include "gtest/gtest.h"
-
-namespace tint {
-namespace ast {
-namespace {
-
-using EntryPointTest = testing::Test;
-
-TEST_F(EntryPointTest, Creation) {
-  EntryPoint e(PipelineStage::kVertex, "main", "vtx_main");
-
-  EXPECT_EQ(e.name(), "main");
-  EXPECT_EQ(e.function_name(), "vtx_main");
-  EXPECT_EQ(e.stage(), PipelineStage::kVertex);
-  EXPECT_EQ(e.line(), 0u);
-  EXPECT_EQ(e.column(), 0u);
-}
-
-TEST_F(EntryPointTest, CreationWithSource) {
-  Source s{27, 4};
-  EntryPoint e(s, PipelineStage::kVertex, "main", "vtx_main");
-
-  EXPECT_EQ(e.name(), "main");
-  EXPECT_EQ(e.function_name(), "vtx_main");
-  EXPECT_EQ(e.stage(), PipelineStage::kVertex);
-  EXPECT_EQ(e.line(), 27u);
-  EXPECT_EQ(e.column(), 4u);
-}
-
-TEST_F(EntryPointTest, CreationEmpty) {
-  Source s{27, 4};
-  EntryPoint e;
-  e.set_source(s);
-  e.set_pipeline_stage(PipelineStage::kFragment);
-  e.set_function_name("my_func");
-  e.set_name("a_name");
-
-  EXPECT_EQ(e.function_name(), "my_func");
-  EXPECT_EQ(e.name(), "a_name");
-  EXPECT_EQ(e.stage(), PipelineStage::kFragment);
-  EXPECT_EQ(e.line(), 27u);
-  EXPECT_EQ(e.column(), 4u);
-}
-
-TEST_F(EntryPointTest, IsValid) {
-  EntryPoint e(PipelineStage::kVertex, "", "vtx_main");
-  EXPECT_TRUE(e.IsValid());
-}
-
-TEST_F(EntryPointTest, IsValid_MissingFunctionName) {
-  EntryPoint e(PipelineStage::kVertex, "main", "");
-  EXPECT_FALSE(e.IsValid());
-}
-
-TEST_F(EntryPointTest, IsValid_MissingStage) {
-  EntryPoint e(PipelineStage::kNone, "main", "fn");
-  EXPECT_FALSE(e.IsValid());
-}
-
-TEST_F(EntryPointTest, IsValid_MissingBoth) {
-  EntryPoint e;
-  EXPECT_FALSE(e.IsValid());
-}
-
-TEST_F(EntryPointTest, ToStr) {
-  EntryPoint e(PipelineStage::kVertex, "text", "vtx_main");
-  std::ostringstream out;
-  e.to_str(out, 2);
-  EXPECT_EQ(out.str(), R"(  EntryPoint{vertex as text = vtx_main}
-)");
-}
-
-TEST_F(EntryPointTest, ToStr_NoName) {
-  EntryPoint e(PipelineStage::kVertex, "", "vtx_main");
-  std::ostringstream out;
-  e.to_str(out, 2);
-  EXPECT_EQ(out.str(), R"(  EntryPoint{vertex = vtx_main}
-)");
-}
-
-}  // namespace
-}  // namespace ast
-}  // namespace tint
diff --git a/src/ast/module.cc b/src/ast/module.cc
index ffb4132..b650a78 100644
--- a/src/ast/module.cc
+++ b/src/ast/module.cc
@@ -45,12 +45,14 @@
   return nullptr;
 }
 
-bool Module::IsFunctionEntryPoint(const std::string& name) const {
-  for (const auto& ep : entry_points_) {
-    if (ep->function_name() == name)
-      return true;
+Function* Module::FindFunctionByNameAndStage(const std::string& name,
+                                             ast::PipelineStage stage) const {
+  for (const auto& func : functions_) {
+    if (func->name() == name && func->pipeline_stage() == stage) {
+      return func.get();
+    }
   }
-  return false;
+  return nullptr;
 }
 
 bool Module::IsValid() const {
@@ -64,11 +66,6 @@
       return false;
     }
   }
-  for (const auto& ep : entry_points_) {
-    if (ep == nullptr || !ep->IsValid()) {
-      return false;
-    }
-  }
   for (auto* const alias : alias_types_) {
     if (alias == nullptr) {
       return false;
@@ -93,9 +90,6 @@
   for (const auto& var : global_variables_) {
     var->to_str(out, indent);
   }
-  for (const auto& ep : entry_points_) {
-    ep->to_str(out, indent);
-  }
   for (auto* const alias : alias_types_) {
     for (size_t i = 0; i < indent; ++i) {
       out << " ";
diff --git a/src/ast/module.h b/src/ast/module.h
index 7734026..cb8f13a 100644
--- a/src/ast/module.h
+++ b/src/ast/module.h
@@ -20,7 +20,6 @@
 #include <utility>
 #include <vector>
 
-#include "src/ast/entry_point.h"
 #include "src/ast/function.h"
 #include "src/ast/import.h"
 #include "src/ast/type/alias_type.h"
@@ -60,19 +59,6 @@
   /// @returns the global variables for the module
   VariableList& global_variables() { return global_variables_; }
 
-  /// Adds an entry point to the module
-  /// @param ep the entry point to add
-  void AddEntryPoint(std::unique_ptr<EntryPoint> ep) {
-    entry_points_.push_back(std::move(ep));
-  }
-  /// @returns the entry points in the module
-  const EntryPointList& entry_points() const { return entry_points_; }
-
-  /// Checks if the given function name is an entry point function
-  /// @param name the function name
-  /// @returns true if name is an entry point function
-  bool IsFunctionEntryPoint(const std::string& name) const;
-
   /// Adds a type alias to the module
   /// @param type the alias to add
   void AddAliasType(type::AliasType* type) { alias_types_.push_back(type); }
@@ -92,6 +78,12 @@
   /// @param name the name to search for
   /// @returns the associated function or nullptr if none exists
   Function* FindFunctionByName(const std::string& name) const;
+  /// Returns the function with the given name
+  /// @param name the name to search for
+  /// @param stage the pipeline stage
+  /// @returns the associated function or nullptr if none exists
+  Function* FindFunctionByNameAndStage(const std::string& name,
+                                       ast::PipelineStage stage) const;
 
   /// @returns true if all required fields in the AST are present.
   bool IsValid() const;
@@ -104,7 +96,6 @@
 
   ImportList imports_;
   VariableList global_variables_;
-  EntryPointList entry_points_;
   // The alias types are owned by the type manager
   std::vector<type::AliasType*> alias_types_;
   FunctionList functions_;
diff --git a/src/ast/module_test.cc b/src/ast/module_test.cc
index 4cd0ebb..e1cec96 100644
--- a/src/ast/module_test.cc
+++ b/src/ast/module_test.cc
@@ -18,7 +18,6 @@
 #include <utility>
 
 #include "gmock/gmock.h"
-#include "src/ast/entry_point.h"
 #include "src/ast/function.h"
 #include "src/ast/import.h"
 #include "src/ast/type/f32_type.h"
@@ -91,19 +90,6 @@
   EXPECT_EQ(func_ptr, m.FindFunctionByName("main"));
 }
 
-TEST_F(ModuleTest, IsEntryPoint) {
-  type::F32Type f32;
-  Module m;
-
-  auto func = std::make_unique<Function>("other_func", VariableList{}, &f32);
-  m.AddFunction(std::move(func));
-
-  m.AddEntryPoint(
-      std::make_unique<EntryPoint>(PipelineStage::kVertex, "main", "vtx_main"));
-  EXPECT_TRUE(m.IsFunctionEntryPoint("vtx_main"));
-  EXPECT_FALSE(m.IsFunctionEntryPoint("other_func"));
-}
-
 TEST_F(ModuleTest, LookupFunctionMissing) {
   Module m;
   EXPECT_EQ(nullptr, m.FindFunctionByName("Missing"));
@@ -155,25 +141,6 @@
   EXPECT_FALSE(m.IsValid());
 }
 
-TEST_F(ModuleTest, IsValid_EntryPoint) {
-  Module m;
-  m.AddEntryPoint(
-      std::make_unique<EntryPoint>(PipelineStage::kVertex, "main", "vtx_main"));
-  EXPECT_TRUE(m.IsValid());
-}
-
-TEST_F(ModuleTest, IsValid_Null_EntryPoint) {
-  Module m;
-  m.AddEntryPoint(nullptr);
-  EXPECT_FALSE(m.IsValid());
-}
-
-TEST_F(ModuleTest, IsValid_Invalid_EntryPoint) {
-  Module m;
-  m.AddEntryPoint(std::make_unique<EntryPoint>());
-  EXPECT_FALSE(m.IsValid());
-}
-
 TEST_F(ModuleTest, IsValid_Alias) {
   type::F32Type f32;
   type::AliasType alias("alias", &f32);
diff --git a/src/ast/transform/vertex_pulling_transform.cc b/src/ast/transform/vertex_pulling_transform.cc
index 9f2fe3d..69b9f43 100644
--- a/src/ast/transform/vertex_pulling_transform.cc
+++ b/src/ast/transform/vertex_pulling_transform.cc
@@ -77,29 +77,15 @@
   }
 
   // Find entry point
-  EntryPoint* entry_point = nullptr;
-  for (const auto& entry : mod_->entry_points()) {
-    if (entry->name() == entry_point_name_ ||
-        (entry->name().empty() &&
-         entry->function_name() == entry_point_name_)) {
-      entry_point = entry.get();
-      break;
-    }
-  }
-
-  if (entry_point == nullptr) {
+  auto* func = mod_->FindFunctionByNameAndStage(entry_point_name_,
+                                                PipelineStage::kVertex);
+  if (func == nullptr) {
     SetError("Vertex stage entry point not found");
     return false;
   }
 
-  // Check entry point is the right stage
-  if (entry_point->stage() != PipelineStage::kVertex) {
-    SetError("Entry point is not for vertex stage");
-    return false;
-  }
-
   // Save the vertex function
-  auto* vertex_func = mod_->FindFunctionByName(entry_point->function_name());
+  auto* vertex_func = mod_->FindFunctionByName(func->name());
 
   // TODO(idanr): Need to check shader locations in descriptor cover all
   // attributes
diff --git a/src/ast/transform/vertex_pulling_transform_test.cc b/src/ast/transform/vertex_pulling_transform_test.cc
index 070a4c4..caf4e4a 100644
--- a/src/ast/transform/vertex_pulling_transform_test.cc
+++ b/src/ast/transform/vertex_pulling_transform_test.cc
@@ -17,6 +17,8 @@
 #include "gtest/gtest.h"
 #include "src/ast/decorated_variable.h"
 #include "src/ast/function.h"
+#include "src/ast/pipeline_stage.h"
+#include "src/ast/stage_decoration.h"
 #include "src/ast/type/array_type.h"
 #include "src/ast/type/f32_type.h"
 #include "src/ast/type/i32_type.h"
@@ -38,11 +40,12 @@
 
   // Create basic module with an entry point and vertex function
   void InitBasicModule() {
-    mod()->AddEntryPoint(std::make_unique<EntryPoint>(PipelineStage::kVertex,
-                                                      "main", "vtx_main"));
-    mod()->AddFunction(std::make_unique<Function>(
-        "vtx_main", VariableList{},
-        ctx_.type_mgr().Get(std::make_unique<type::VoidType>())));
+    auto func = std::make_unique<Function>(
+        "main", VariableList{},
+        ctx_.type_mgr().Get(std::make_unique<type::VoidType>()));
+    func->add_decoration(
+        std::make_unique<ast::StageDecoration>(ast::PipelineStage ::kVertex));
+    mod()->AddFunction(std::move(func));
   }
 
   // Set up the transformation, after building the module
@@ -71,6 +74,7 @@
     mod_->AddGlobalVariable(std::move(var));
   }
 
+  Context* ctx() { return &ctx_; }
   ast::Module* mod() { return mod_.get(); }
   VertexPullingTransform* transform() { return transform_.get(); }
 
@@ -104,12 +108,16 @@
 }
 
 TEST_F(VertexPullingTransformTest, Error_EntryPointWrongStage) {
-  InitBasicModule();
-  mod()->entry_points()[0]->set_pipeline_stage(PipelineStage::kFragment);
+  auto func = std::make_unique<Function>(
+      "main", VariableList{},
+      ctx()->type_mgr().Get(std::make_unique<type::VoidType>()));
+  func->add_decoration(
+      std::make_unique<ast::StageDecoration>(ast::PipelineStage::kFragment));
+  mod()->AddFunction(std::move(func));
 
   InitTransform({});
   EXPECT_FALSE(transform()->Run());
-  EXPECT_EQ(transform()->GetError(), "Entry point is not for vertex stage");
+  EXPECT_EQ(transform()->GetError(), "Vertex stage entry point not found");
 }
 
 TEST_F(VertexPullingTransformTest, BasicModule) {
@@ -118,14 +126,6 @@
   EXPECT_TRUE(transform()->Run());
 }
 
-TEST_F(VertexPullingTransformTest, EntryPointUsingFunctionName) {
-  InitBasicModule();
-  mod()->entry_points()[0]->set_name("");
-  InitTransform({});
-  transform()->SetEntryPoint("vtx_main");
-  EXPECT_TRUE(transform()->Run());
-}
-
 TEST_F(VertexPullingTransformTest, OneAttribute) {
   InitBasicModule();
 
@@ -159,8 +159,8 @@
     storage_buffer
     __struct_
   }
-  EntryPoint{vertex as main = vtx_main}
-  Function vtx_main -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Block{
@@ -240,8 +240,8 @@
     storage_buffer
     __struct_
   }
-  EntryPoint{vertex as main = vtx_main}
-  Function vtx_main -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Block{
@@ -321,8 +321,8 @@
     storage_buffer
     __struct_
   }
-  EntryPoint{vertex as main = vtx_main}
-  Function vtx_main -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Block{
@@ -454,8 +454,8 @@
     storage_buffer
     __struct_
   }
-  EntryPoint{vertex as main = vtx_main}
-  Function vtx_main -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Block{
@@ -573,8 +573,8 @@
     storage_buffer
     __struct_
   }
-  EntryPoint{vertex as main = vtx_main}
-  Function vtx_main -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Block{
@@ -777,8 +777,8 @@
     storage_buffer
     __struct_
   }
-  EntryPoint{vertex as main = vtx_main}
-  Function vtx_main -> __void
+  Function main -> __void
+  StageDecoration{vertex}
   ()
   {
     Block{
diff --git a/src/reader/wgsl/lexer.cc b/src/reader/wgsl/lexer.cc
index 9a825b2..76d71e5 100644
--- a/src/reader/wgsl/lexer.cc
+++ b/src/reader/wgsl/lexer.cc
@@ -503,8 +503,6 @@
     return {Token::Type::kElse, source, "else"};
   if (str == "elseif")
     return {Token::Type::kElseIf, source, "elseif"};
-  if (str == "entry_point")
-    return {Token::Type::kEntryPoint, source, "entry_point"};
   if (str == "f32")
     return {Token::Type::kF32, source, "f32"};
   if (str == "fallthrough")
diff --git a/src/reader/wgsl/lexer_test.cc b/src/reader/wgsl/lexer_test.cc
index 0254824..1e90cdb 100644
--- a/src/reader/wgsl/lexer_test.cc
+++ b/src/reader/wgsl/lexer_test.cc
@@ -429,7 +429,6 @@
         TokenData{"discard", Token::Type::kDiscard},
         TokenData{"else", Token::Type::kElse},
         TokenData{"elseif", Token::Type::kElseIf},
-        TokenData{"entry_point", Token::Type::kEntryPoint},
         TokenData{"f32", Token::Type::kF32},
         TokenData{"fallthrough", Token::Type::kFallthrough},
         TokenData{"false", Token::Type::kFalse},
diff --git a/src/reader/wgsl/parser_impl.cc b/src/reader/wgsl/parser_impl.cc
index cff1b50..6a41627 100644
--- a/src/reader/wgsl/parser_impl.cc
+++ b/src/reader/wgsl/parser_impl.cc
@@ -206,7 +206,6 @@
 //  | import_decl SEMICOLON
 //  | global_variable_decl SEMICLON
 //  | global_constant_decl SEMICOLON
-//  | entry_point_decl SEMICOLON
 //  | type_alias SEMICOLON
 //  | function_decl
 void ParserImpl::global_decl() {
@@ -258,19 +257,6 @@
     return;
   }
 
-  auto ep = entry_point_decl();
-  if (has_error())
-    return;
-  if (ep != nullptr) {
-    t = next();
-    if (!t.IsSemicolon()) {
-      set_error(t, "missing ';' for entry point");
-      return;
-    }
-    module_.AddEntryPoint(std::move(ep));
-    return;
-  }
-
   auto* ta = type_alias();
   if (has_error())
     return;
@@ -2036,60 +2022,6 @@
   return ret;
 }
 
-// entry_point_decl
-//   : ENTRY_POINT pipeline_stage EQUAL IDENT
-//   | ENTRY_POINT pipeline_stage AS STRING_LITERAL EQUAL IDENT
-//   | ENTRY_POINT pipeline_stage AS IDENT EQUAL IDENT
-std::unique_ptr<ast::EntryPoint> ParserImpl::entry_point_decl() {
-  auto t = peek();
-  auto source = t.source();
-  if (!t.IsEntryPoint())
-    return nullptr;
-
-  next();  // Consume the peek
-
-  auto stage = pipeline_stage();
-  if (has_error())
-    return nullptr;
-  if (stage == ast::PipelineStage::kNone) {
-    set_error(peek(), "missing pipeline stage for entry point");
-    return nullptr;
-  }
-
-  t = next();
-  std::string name;
-  if (t.IsAs()) {
-    t = next();
-    if (t.IsStringLiteral()) {
-      name = t.to_str();
-    } else if (t.IsIdentifier()) {
-      name = t.to_str();
-    } else {
-      set_error(t, "invalid name for entry point");
-      return nullptr;
-    }
-    t = next();
-  }
-
-  if (!t.IsEqual()) {
-    set_error(t, "missing = for entry point");
-    return nullptr;
-  }
-
-  t = next();
-  if (!t.IsIdentifier()) {
-    set_error(t, "invalid function name for entry point");
-    return nullptr;
-  }
-  auto fn_name = t.to_str();
-
-  // Set the name to the function name if it isn't provided
-  if (name.length() == 0)
-    name = fn_name;
-
-  return std::make_unique<ast::EntryPoint>(source, stage, name, fn_name);
-}
-
 // pipeline_stage
 //   : VERTEX
 //   | FRAGMENT
diff --git a/src/reader/wgsl/parser_impl.h b/src/reader/wgsl/parser_impl.h
index 578b4fd..2574958 100644
--- a/src/reader/wgsl/parser_impl.h
+++ b/src/reader/wgsl/parser_impl.h
@@ -27,7 +27,6 @@
 #include "src/ast/case_statement.h"
 #include "src/ast/constructor_expression.h"
 #include "src/ast/else_statement.h"
-#include "src/ast/entry_point.h"
 #include "src/ast/function.h"
 #include "src/ast/import.h"
 #include "src/ast/literal.h"
@@ -222,9 +221,6 @@
   /// Parses a `param_list` grammar element
   /// @returns the parsed variables
   ast::VariableList param_list();
-  /// Parses a `entry_point_decl` grammar element
-  /// @returns the EntryPoint or nullptr on error
-  std::unique_ptr<ast::EntryPoint> entry_point_decl();
   /// Parses a `pipeline_stage` grammar element
   /// @returns the pipeline stage or PipelineStage::kNone if none matched
   ast::PipelineStage pipeline_stage();
diff --git a/src/reader/wgsl/parser_impl_entry_point_decl_test.cc b/src/reader/wgsl/parser_impl_entry_point_decl_test.cc
deleted file mode 100644
index ede75b3..0000000
--- a/src/reader/wgsl/parser_impl_entry_point_decl_test.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "gtest/gtest.h"
-#include "src/ast/variable.h"
-#include "src/reader/wgsl/parser_impl.h"
-#include "src/reader/wgsl/parser_impl_test_helper.h"
-
-namespace tint {
-namespace reader {
-namespace wgsl {
-namespace {
-
-TEST_F(ParserImplTest, EntryPoint_Parses) {
-  auto* p = parser("entry_point fragment = main");
-  auto e = p->entry_point_decl();
-  ASSERT_NE(e, nullptr);
-  ASSERT_FALSE(p->has_error());
-  EXPECT_EQ(e->stage(), ast::PipelineStage::kFragment);
-  EXPECT_EQ(e->name(), "main");
-  EXPECT_EQ(e->function_name(), "main");
-}
-
-TEST_F(ParserImplTest, EntryPoint_ParsesWithStringName) {
-  auto* p = parser(R"(entry_point vertex as "main" = vtx_main)");
-  auto e = p->entry_point_decl();
-  ASSERT_NE(e, nullptr);
-  ASSERT_FALSE(p->has_error());
-  EXPECT_EQ(e->stage(), ast::PipelineStage::kVertex);
-  EXPECT_EQ(e->name(), "main");
-  EXPECT_EQ(e->function_name(), "vtx_main");
-}
-
-TEST_F(ParserImplTest, EntryPoint_ParsesWithIdentName) {
-  auto* p = parser(R"(entry_point vertex as main = vtx_main)");
-  auto e = p->entry_point_decl();
-  ASSERT_NE(e, nullptr);
-  ASSERT_FALSE(p->has_error());
-  EXPECT_EQ(e->stage(), ast::PipelineStage::kVertex);
-  EXPECT_EQ(e->name(), "main");
-  EXPECT_EQ(e->function_name(), "vtx_main");
-}
-
-TEST_F(ParserImplTest, EntryPoint_MissingFnName) {
-  auto* p = parser(R"(entry_point vertex as main =)");
-  auto e = p->entry_point_decl();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:29: invalid function name for entry point");
-}
-
-TEST_F(ParserImplTest, EntryPoint_InvalidFnName) {
-  auto* p = parser(R"(entry_point vertex as main = 123)");
-  auto e = p->entry_point_decl();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:30: invalid function name for entry point");
-}
-
-TEST_F(ParserImplTest, EntryPoint_MissingEqual) {
-  auto* p = parser(R"(entry_point vertex as main vtx_main)");
-  auto e = p->entry_point_decl();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:28: missing = for entry point");
-}
-
-TEST_F(ParserImplTest, EntryPoint_MissingName) {
-  auto* p = parser(R"(entry_point vertex as = vtx_main)");
-  auto e = p->entry_point_decl();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:23: invalid name for entry point");
-}
-
-TEST_F(ParserImplTest, EntryPoint_InvalidName) {
-  auto* p = parser(R"(entry_point vertex as 123 = vtx_main)");
-  auto e = p->entry_point_decl();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:23: invalid name for entry point");
-}
-
-TEST_F(ParserImplTest, EntryPoint_MissingStageWithIdent) {
-  auto* p = parser(R"(entry_point as 123 = vtx_main)");
-  auto e = p->entry_point_decl();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:13: missing pipeline stage for entry point");
-}
-
-TEST_F(ParserImplTest, EntryPoint_MissingStage) {
-  auto* p = parser(R"(entry_point = vtx_main)");
-  auto e = p->entry_point_decl();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:13: missing pipeline stage for entry point");
-}
-
-TEST_F(ParserImplTest, EntryPoint_InvalidStage) {
-  auto* p = parser(R"(entry_point invalid = vtx_main)");
-  auto e = p->entry_point_decl();
-  ASSERT_TRUE(p->has_error());
-  ASSERT_EQ(e, nullptr);
-  EXPECT_EQ(p->error(), "1:13: missing pipeline stage for entry point");
-}
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace reader
-}  // namespace tint
diff --git a/src/reader/wgsl/parser_impl_global_decl_test.cc b/src/reader/wgsl/parser_impl_global_decl_test.cc
index c04735b..432f2e1 100644
--- a/src/reader/wgsl/parser_impl_global_decl_test.cc
+++ b/src/reader/wgsl/parser_impl_global_decl_test.cc
@@ -108,30 +108,6 @@
   EXPECT_EQ(p->error(), "1:38: missing ';' for constant declaration");
 }
 
-TEST_F(ParserImplTest, GlobalDecl_EntryPoint) {
-  auto* p = parser("entry_point vertex = main;");
-  p->global_decl();
-  ASSERT_FALSE(p->has_error()) << p->error();
-
-  auto m = p->module();
-  ASSERT_EQ(m.entry_points().size(), 1u);
-  EXPECT_EQ(m.entry_points()[0]->name(), "main");
-}
-
-TEST_F(ParserImplTest, GlobalDecl_EntryPoint_Invalid) {
-  auto* p = parser("entry_point main;");
-  p->global_decl();
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:13: missing pipeline stage for entry point");
-}
-
-TEST_F(ParserImplTest, GlobalDecl_EntryPoint_MissingSemicolon) {
-  auto* p = parser("entry_point vertex = main");
-  p->global_decl();
-  ASSERT_TRUE(p->has_error());
-  EXPECT_EQ(p->error(), "1:26: missing ';' for entry point");
-}
-
 TEST_F(ParserImplTest, GlobalDecl_TypeAlias) {
   auto* p = parser("type A = i32;");
   p->global_decl();
diff --git a/src/reader/wgsl/parser_impl_test.cc b/src/reader/wgsl/parser_impl_test.cc
index 154a33a..7b6a5a0 100644
--- a/src/reader/wgsl/parser_impl_test.cc
+++ b/src/reader/wgsl/parser_impl_test.cc
@@ -34,7 +34,7 @@
 
 [[location(0)]] var<out> gl_FragColor : vec4<f32>;
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   gl_FragColor = vec4<f32>(.4, .2, .3, 1);
 }
@@ -43,7 +43,6 @@
 
   auto m = p->module();
   ASSERT_EQ(1u, m.imports().size());
-  ASSERT_EQ(1u, m.entry_points().size());
   ASSERT_EQ(1u, m.functions().size());
   ASSERT_EQ(1u, m.global_variables().size());
 }
diff --git a/src/reader/wgsl/parser_test.cc b/src/reader/wgsl/parser_test.cc
index 767c236..a67751e 100644
--- a/src/reader/wgsl/parser_test.cc
+++ b/src/reader/wgsl/parser_test.cc
@@ -38,7 +38,7 @@
 
 [[location(0)]] var<out> gl_FragColor : vec4<f32>;
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   gl_FragColor = vec4<f32>(.4, .2, .3, 1);
 }
@@ -47,7 +47,6 @@
 
   auto m = p.module();
   ASSERT_EQ(1u, m.imports().size());
-  ASSERT_EQ(1u, m.entry_points().size());
   ASSERT_EQ(1u, m.functions().size());
   ASSERT_EQ(1u, m.global_variables().size());
 }
diff --git a/src/reader/wgsl/token.cc b/src/reader/wgsl/token.cc
index a5dbea5..7afd80c 100644
--- a/src/reader/wgsl/token.cc
+++ b/src/reader/wgsl/token.cc
@@ -139,8 +139,6 @@
       return "else";
     case Token::Type::kElseIf:
       return "elseif";
-    case Token::Type::kEntryPoint:
-      return "entry_point";
     case Token::Type::kF32:
       return "f32";
     case Token::Type::kFallthrough:
diff --git a/src/reader/wgsl/token.h b/src/reader/wgsl/token.h
index 0f5f531..c67ef34 100644
--- a/src/reader/wgsl/token.h
+++ b/src/reader/wgsl/token.h
@@ -150,8 +150,6 @@
     kElse,
     /// A 'elseif'
     kElseIf,
-    /// A 'entry_point'
-    kEntryPoint,
     /// A 'f32'
     kF32,
     /// A 'fallthrough'
@@ -531,8 +529,6 @@
   bool IsElse() const { return type_ == Type::kElse; }
   /// @returns true if token is a 'elseif'
   bool IsElseIf() const { return type_ == Type::kElseIf; }
-  /// @returns true if token is a 'entry_point'
-  bool IsEntryPoint() const { return type_ == Type::kEntryPoint; }
   /// @returns true if token is a 'f32'
   bool IsF32() const { return type_ == Type::kF32; }
   /// @returns true if token is a 'fallthrough'
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 7e62fcf..7dbfaf1 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -211,14 +211,6 @@
 
   // Walk over the caller to callee information and update functions with which
   // entry points call those functions.
-  for (const auto& ep : mod_->entry_points()) {
-    for (const auto& callee : caller_to_callee_[ep->function_name()]) {
-      set_entry_points(callee, ep->name());
-    }
-  }
-
-  // Walk over the caller to callee information and update functions with which
-  // entry points call those functions.
   for (const auto& func : mod_->functions()) {
     if (!func->IsEntryPoint()) {
       continue;
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 67821ad..f2ce9d6 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -4373,114 +4373,6 @@
                          testing::Values(GLSLData{"sclamp", GLSLstd450SClamp},
                                          GLSLData{"uclamp", GLSLstd450UClamp}));
 
-TEST_F(TypeDeterminerTest, Function_EntryPoints) {
-  ast::type::F32Type f32;
-
-  // fn b() {}
-  // fn c() { b(); }
-  // fn a() { c(); }
-  // fn ep_1() { a(); b(); }
-  // fn ep_2() { c();}
-  //
-  // c -> {ep_1, ep_2}
-  // a -> {ep_1}
-  // b -> {ep_1, ep_2}
-  // ep_1 -> {}
-  // ep_2 -> {}
-
-  ast::VariableList params;
-  auto func_b = std::make_unique<ast::Function>("b", std::move(params), &f32);
-  auto* func_b_ptr = func_b.get();
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  func_b->set_body(std::move(body));
-
-  auto func_c = std::make_unique<ast::Function>("c", std::move(params), &f32);
-  auto* func_c_ptr = func_c.get();
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("second"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("b"),
-          ast::ExpressionList{})));
-  func_c->set_body(std::move(body));
-
-  auto func_a = std::make_unique<ast::Function>("a", std::move(params), &f32);
-  auto* func_a_ptr = func_a.get();
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("first"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("c"),
-          ast::ExpressionList{})));
-  func_a->set_body(std::move(body));
-
-  auto ep_1_func =
-      std::make_unique<ast::Function>("ep_1_func", std::move(params), &f32);
-  auto* ep_1_func_ptr = ep_1_func.get();
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("call_a"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("a"),
-          ast::ExpressionList{})));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("call_b"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("b"),
-          ast::ExpressionList{})));
-  ep_1_func->set_body(std::move(body));
-
-  auto ep_2_func =
-      std::make_unique<ast::Function>("ep_2_func", std::move(params), &f32);
-  auto* ep_2_func_ptr = ep_2_func.get();
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("call_c"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("c"),
-          ast::ExpressionList{})));
-  ep_2_func->set_body(std::move(body));
-
-  auto ep_1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex,
-                                                "ep_1", "ep_1_func");
-  auto ep_2 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex,
-                                                "ep_2", "ep_2_func");
-
-  mod()->AddFunction(std::move(func_b));
-  mod()->AddFunction(std::move(func_c));
-  mod()->AddFunction(std::move(func_a));
-  mod()->AddFunction(std::move(ep_1_func));
-  mod()->AddFunction(std::move(ep_2_func));
-
-  mod()->AddEntryPoint(std::move(ep_1));
-  mod()->AddEntryPoint(std::move(ep_2));
-
-  // Register the functions and calculate the callers
-  ASSERT_TRUE(td()->Determine()) << td()->error();
-
-  const auto& b_eps = func_b_ptr->ancestor_entry_points();
-  ASSERT_EQ(2u, b_eps.size());
-  EXPECT_EQ("ep_1", b_eps[0]);
-  EXPECT_EQ("ep_2", b_eps[1]);
-
-  const auto& a_eps = func_a_ptr->ancestor_entry_points();
-  ASSERT_EQ(1u, a_eps.size());
-  EXPECT_EQ("ep_1", a_eps[0]);
-
-  const auto& c_eps = func_c_ptr->ancestor_entry_points();
-  ASSERT_EQ(2u, c_eps.size());
-  EXPECT_EQ("ep_1", c_eps[0]);
-  EXPECT_EQ("ep_2", c_eps[1]);
-
-  EXPECT_TRUE(ep_1_func_ptr->ancestor_entry_points().empty());
-  EXPECT_TRUE(ep_2_func_ptr->ancestor_entry_points().empty());
-}
-
 TEST_F(TypeDeterminerTest, Function_EntryPoints_StageDecoration) {
   ast::type::F32Type f32;
 
diff --git a/src/validator_function_test.cc b/src/validator_function_test.cc
index ab827f2..145b345 100644
--- a/src/validator_function_test.cc
+++ b/src/validator_function_test.cc
@@ -17,7 +17,6 @@
 #include "gtest/gtest.h"
 #include "spirv/unified1/GLSL.std.450.h"
 #include "src/ast/call_statement.h"
-#include "src/ast/entry_point.h"
 #include "src/ast/pipeline_stage.h"
 #include "src/ast/return_statement.h"
 #include "src/ast/scalar_constructor_expression.h"
@@ -91,8 +90,7 @@
   mod()->AddFunction(std::move(func));
 
   EXPECT_TRUE(td()->DetermineFunctions(mod()->functions())) << td()->error();
-  EXPECT_TRUE(v()->ValidateFunctions(mod(), mod()->functions()))
-      << v()->error();
+  EXPECT_TRUE(v()->ValidateFunctions(mod()->functions())) << v()->error();
 }
 
 TEST_F(ValidateFunctionTest, FunctionTypeMustMatchReturnStatementType_fail) {
diff --git a/src/validator_impl.cc b/src/validator_impl.cc
index e64d9f1..2a8ce16 100644
--- a/src/validator_impl.cc
+++ b/src/validator_impl.cc
@@ -49,7 +49,7 @@
   if (!CheckImports(module)) {
     return false;
   }
-  if (!ValidateFunctions(module, module->functions())) {
+  if (!ValidateFunctions(module->functions())) {
     return false;
   }
 
@@ -82,8 +82,7 @@
   return true;
 }
 
-bool ValidatorImpl::ValidateFunctions(const ast::Module* mod,
-                                      const ast::FunctionList& funcs) {
+bool ValidatorImpl::ValidateFunctions(const ast::FunctionList& funcs) {
   ScopeStack<ast::PipelineStage> entry_point_map;
   entry_point_map.push_scope();
 
@@ -135,7 +134,7 @@
     current_function_ = nullptr;
   }
 
-  if (pipeline_count == 0 && mod->entry_points().empty()) {
+  if (pipeline_count == 0) {
     set_error(Source{0, 0},
               "v-0003: At least one of vertex, fragment or compute shader must "
               "be present");
diff --git a/src/validator_impl.h b/src/validator_impl.h
index 81e46f1..6481292 100644
--- a/src/validator_impl.h
+++ b/src/validator_impl.h
@@ -20,7 +20,6 @@
 
 #include "src/ast/assignment_statement.h"
 #include "src/ast/call_expression.h"
-#include "src/ast/entry_point.h"
 #include "src/ast/expression.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/module.h"
@@ -58,11 +57,9 @@
   /// @returns true if the validation was successful
   bool ValidateGlobalVariables(const ast::VariableList& global_vars);
   /// Validates Functions
-  /// @param mod the module
   /// @param funcs the functions to check
   /// @returns true if the validation was successful
-  bool ValidateFunctions(const ast::Module* mod,
-                         const ast::FunctionList& funcs);
+  bool ValidateFunctions(const ast::FunctionList& funcs);
   /// Validates a function
   /// @param func the function to check
   /// @returns true if the validation was successful
@@ -112,11 +109,6 @@
   /// @param expr the call to validate
   /// @returns true if successful
   bool ValidateCallExpr(const ast::CallExpression* expr);
-  /// Validates entry points
-  /// this funtion must be called after populating function_stack_
-  /// @param eps the vector of entry points to check
-  /// @return true if the validation was successful
-  bool ValidateEntryPoints(const ast::EntryPointList& eps);
   /// Validates switch statements
   /// @param s the switch statement to check
   /// @returns true if the valdiation was successful
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index b219595..7fa9229 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -134,12 +134,6 @@
     }
   }
 
-  for (const auto& ep : module_->entry_points()) {
-    if (!EmitEntryPointData(out, ep.get())) {
-      return false;
-    }
-  }
-
   // Make sure all entry point data is emitted before the entry point functions
   for (const auto& func : module_->functions()) {
     if (!func->IsEntryPoint()) {
@@ -156,12 +150,6 @@
       return false;
     }
   }
-  for (const auto& ep : module_->entry_points()) {
-    if (!EmitEntryPointFunction(out, ep.get())) {
-      return false;
-    }
-    out << std::endl;
-  }
 
   for (const auto& func : module_->functions()) {
     if (!func->IsEntryPoint()) {
@@ -1106,7 +1094,7 @@
   make_indent(out);
 
   // Entry points will be emitted later, skip for now.
-  if (func->IsEntryPoint() || module_->IsFunctionEntryPoint(func->name())) {
+  if (func->IsEntryPoint()) {
     return true;
   }
 
@@ -1208,200 +1196,6 @@
   return true;
 }
 
-bool GeneratorImpl::EmitEntryPointData(std::ostream& out, ast::EntryPoint* ep) {
-  auto* func = module_->FindFunctionByName(ep->function_name());
-  if (func == nullptr) {
-    error_ = "Unable to find entry point function: " + ep->function_name();
-    return false;
-  }
-
-  std::vector<std::pair<ast::Variable*, ast::VariableDecoration*>> in_variables;
-  std::vector<std::pair<ast::Variable*, ast::VariableDecoration*>> outvariables;
-  for (auto data : func->referenced_location_variables()) {
-    auto* var = data.first;
-    auto* deco = data.second;
-
-    if (var->storage_class() == ast::StorageClass::kInput) {
-      in_variables.push_back({var, deco});
-    } else if (var->storage_class() == ast::StorageClass::kOutput) {
-      outvariables.push_back({var, deco});
-    }
-  }
-
-  for (auto data : func->referenced_builtin_variables()) {
-    auto* var = data.first;
-    auto* deco = data.second;
-
-    if (var->storage_class() == ast::StorageClass::kInput) {
-      in_variables.push_back({var, deco});
-    } else if (var->storage_class() == ast::StorageClass::kOutput) {
-      outvariables.push_back({var, deco});
-    }
-  }
-
-  bool emitted_uniform = false;
-  for (auto data : func->referenced_uniform_variables()) {
-    auto* var = data.first;
-    // TODO(dsinclair): We're using the binding to make up the buffer number but
-    // we should instead be using a provided mapping that uses both buffer and
-    // set. https://bugs.chromium.org/p/tint/issues/detail?id=104
-    auto* binding = data.second.binding;
-    if (binding == nullptr) {
-      error_ = "unable to find binding information for uniform: " + var->name();
-      return false;
-    }
-    // auto* set = data.second.set;
-
-    auto* type = var->type()->UnwrapAliasesIfNeeded();
-    if (type->IsStruct()) {
-      auto* strct = type->AsStruct();
-
-      out << "ConstantBuffer<" << strct->name() << "> " << var->name()
-          << " : register(b" << binding->value() << ");" << std::endl;
-    } else {
-      // TODO(dsinclair): There is outstanding spec work to require all uniform
-      // buffers to be [[block]] decorated, which means structs. This is
-      // currently not the case, so this code handles the cases where the data
-      // is not a block.
-      // Relevant: https://github.com/gpuweb/gpuweb/issues/1004
-      //           https://github.com/gpuweb/gpuweb/issues/1008
-      out << "cbuffer : register(b" << binding->value() << ") {" << std::endl;
-
-      increment_indent();
-      make_indent(out);
-      if (!EmitType(out, type, "")) {
-        return false;
-      }
-      out << " " << var->name() << ";" << std::endl;
-      decrement_indent();
-      out << "};" << std::endl;
-    }
-
-    emitted_uniform = true;
-  }
-  if (emitted_uniform) {
-    out << std::endl;
-  }
-
-  bool emitted_storagebuffer = false;
-  for (auto data : func->referenced_storagebuffer_variables()) {
-    auto* var = data.first;
-    auto* binding = data.second.binding;
-
-    out << "RWByteAddressBuffer " << var->name() << " : register(u"
-        << binding->value() << ");" << std::endl;
-    emitted_storagebuffer = true;
-  }
-  if (emitted_storagebuffer) {
-    out << std::endl;
-  }
-
-  auto ep_name = ep->name();
-  if (ep_name.empty()) {
-    ep_name = ep->function_name();
-  }
-
-  // TODO(dsinclair): There is a potential bug here. Entry points can have the
-  // same name in WGSL if they have different pipeline stages. This does not
-  // take that into account and will emit duplicate struct names. I'm ignoring
-  // this until https://github.com/gpuweb/gpuweb/issues/662 is resolved as it
-  // may remove this issue and entry point names will need to be unique.
-  if (!in_variables.empty()) {
-    auto in_struct_name = generate_name(ep_name + "_" + kInStructNameSuffix);
-    auto in_var_name = generate_name(kTintStructInVarPrefix);
-    ep_name_to_in_data_[ep_name] = {in_struct_name, in_var_name};
-
-    make_indent(out);
-    out << "struct " << in_struct_name << " {" << std::endl;
-
-    increment_indent();
-
-    for (auto& data : in_variables) {
-      auto* var = data.first;
-      auto* deco = data.second;
-
-      make_indent(out);
-      if (!EmitType(out, var->type(), var->name())) {
-        return false;
-      }
-
-      out << " " << var->name() << " : ";
-      if (deco->IsLocation()) {
-        if (ep->stage() == ast::PipelineStage::kCompute) {
-          error_ = "invalid location variable for pipeline stage";
-          return false;
-        }
-        out << "TEXCOORD" << deco->AsLocation()->value();
-      } else if (deco->IsBuiltin()) {
-        auto attr = builtin_to_attribute(deco->AsBuiltin()->value());
-        if (attr.empty()) {
-          error_ = "unsupported builtin";
-          return false;
-        }
-        out << attr;
-      } else {
-        error_ = "unsupported variable decoration for entry point output";
-        return false;
-      }
-      out << ";" << std::endl;
-    }
-    decrement_indent();
-    make_indent(out);
-
-    out << "};" << std::endl << std::endl;
-  }
-
-  if (!outvariables.empty()) {
-    auto outstruct_name = generate_name(ep_name + "_" + kOutStructNameSuffix);
-    auto outvar_name = generate_name(kTintStructOutVarPrefix);
-    ep_name_to_out_data_[ep_name] = {outstruct_name, outvar_name};
-
-    make_indent(out);
-    out << "struct " << outstruct_name << " {" << std::endl;
-
-    increment_indent();
-    for (auto& data : outvariables) {
-      auto* var = data.first;
-      auto* deco = data.second;
-
-      make_indent(out);
-      if (!EmitType(out, var->type(), var->name())) {
-        return false;
-      }
-
-      out << " " << var->name() << " : ";
-
-      if (deco->IsLocation()) {
-        auto loc = deco->AsLocation()->value();
-        if (ep->stage() == ast::PipelineStage::kVertex) {
-          out << "TEXCOORD" << loc;
-        } else if (ep->stage() == ast::PipelineStage::kFragment) {
-          out << "SV_Target" << loc << "";
-        } else {
-          error_ = "invalid location variable for pipeline stage";
-          return false;
-        }
-      } else if (deco->IsBuiltin()) {
-        auto attr = builtin_to_attribute(deco->AsBuiltin()->value());
-        if (attr.empty()) {
-          error_ = "unsupported builtin";
-          return false;
-        }
-        out << attr;
-      } else {
-        error_ = "unsupported variable decoration for entry point output";
-        return false;
-      }
-      out << ";" << std::endl;
-    }
-    decrement_indent();
-    make_indent(out);
-    out << "};" << std::endl << std::endl;
-  }
-
-  return true;
-}
-
 bool GeneratorImpl::EmitEntryPointData(std::ostream& out, ast::Function* func) {
   std::vector<std::pair<ast::Variable*, ast::VariableDecoration*>> in_variables;
   std::vector<std::pair<ast::Variable*, ast::VariableDecoration*>> outvariables;
@@ -1609,71 +1403,6 @@
 }
 
 bool GeneratorImpl::EmitEntryPointFunction(std::ostream& out,
-                                           ast::EntryPoint* ep) {
-  make_indent(out);
-
-  current_ep_name_ = ep->name();
-  if (current_ep_name_.empty()) {
-    current_ep_name_ = ep->function_name();
-  }
-
-  auto* func = module_->FindFunctionByName(ep->function_name());
-  if (func == nullptr) {
-    error_ = "unable to find function for entry point: " + ep->function_name();
-    return false;
-  }
-
-  if (ep->stage() == ast::PipelineStage::kCompute) {
-    uint32_t x = 0;
-    uint32_t y = 0;
-    uint32_t z = 0;
-    std::tie(x, y, z) = func->workgroup_size();
-    out << "[numthreads(" << std::to_string(x) << ", " << std::to_string(y)
-        << ", " << std::to_string(z) << ")]" << std::endl;
-    make_indent(out);
-  }
-
-  auto outdata = ep_name_to_out_data_.find(current_ep_name_);
-  bool has_outdata = outdata != ep_name_to_out_data_.end();
-  if (has_outdata) {
-    out << outdata->second.struct_name;
-  } else {
-    out << "void";
-  }
-  out << " " << namer_.NameFor(current_ep_name_) << "(";
-
-  auto in_data = ep_name_to_in_data_.find(current_ep_name_);
-  if (in_data != ep_name_to_in_data_.end()) {
-    out << in_data->second.struct_name << " " << in_data->second.var_name;
-  }
-  out << ") {" << std::endl;
-
-  increment_indent();
-
-  if (has_outdata) {
-    make_indent(out);
-    out << outdata->second.struct_name << " " << outdata->second.var_name << ";"
-        << std::endl;
-  }
-
-  generating_entry_point_ = true;
-  for (const auto& s : *(func->body())) {
-    if (!EmitStatement(out, s.get())) {
-      return false;
-    }
-  }
-  generating_entry_point_ = false;
-
-  decrement_indent();
-  make_indent(out);
-  out << "}" << std::endl;
-
-  current_ep_name_ = "";
-
-  return true;
-}
-
-bool GeneratorImpl::EmitEntryPointFunction(std::ostream& out,
                                            ast::Function* func) {
   make_indent(out);
 
diff --git a/src/writer/hlsl/generator_impl.h b/src/writer/hlsl/generator_impl.h
index 264c119..798bf87 100644
--- a/src/writer/hlsl/generator_impl.h
+++ b/src/writer/hlsl/generator_impl.h
@@ -193,21 +193,11 @@
                             const std::string& ep_name);
   /// Handles emitting information for an entry point
   /// @param out the output stream
-  /// @param ep the entry point
-  /// @returns true if the entry point data was emitted
-  bool EmitEntryPointData(std::ostream& out, ast::EntryPoint* ep);
-  /// Handles emitting information for an entry point
-  /// @param out the output stream
   /// @param func the entry point
   /// @returns true if the entry point data was emitted
   bool EmitEntryPointData(std::ostream& out, ast::Function* func);
   /// Handles emitting the entry point function
   /// @param out the output stream
-  /// @param ep the entry point
-  /// @returns true if the entry point function was emitted
-  bool EmitEntryPointFunction(std::ostream& out, ast::EntryPoint* ep);
-  /// Handles emitting the entry point function
-  /// @param out the output stream
   /// @param func the entry point
   /// @returns true if the entry point function was emitted
   bool EmitEntryPointFunction(std::ostream& out, ast::Function* func);
diff --git a/src/writer/hlsl/generator_impl_entry_point_test.cc b/src/writer/hlsl/generator_impl_entry_point_test.cc
deleted file mode 100644
index 184e725..0000000
--- a/src/writer/hlsl/generator_impl_entry_point_test.cc
+++ /dev/null
@@ -1,463 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "src/ast/assignment_statement.h"
-#include "src/ast/decorated_variable.h"
-#include "src/ast/entry_point.h"
-#include "src/ast/identifier_expression.h"
-#include "src/ast/location_decoration.h"
-#include "src/ast/member_accessor_expression.h"
-#include "src/ast/module.h"
-#include "src/ast/return_statement.h"
-#include "src/ast/type/f32_type.h"
-#include "src/ast/type/i32_type.h"
-#include "src/ast/type/vector_type.h"
-#include "src/ast/type/void_type.h"
-#include "src/ast/variable.h"
-#include "src/context.h"
-#include "src/type_determiner.h"
-#include "src/writer/hlsl/test_helper.h"
-
-namespace tint {
-namespace writer {
-namespace hlsl {
-namespace {
-
-using HlslGeneratorImplTest_EntryPoint = TestHelper;
-
-TEST_F(HlslGeneratorImplTest_EntryPoint, EmitEntryPointData_Vertex_Input) {
-  // [[location(0)]] var<in> foo : f32;
-  // [[location(1)]] var<in> bar : i32;
-  //
-  // struct vtx_main_in {
-  //   float foo : TEXCOORD0;
-  //   int bar : TEXCOORD1;
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kInput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("vtx_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex, "",
-                                              "vtx_main");
-  auto* ep_ptr = ep.get();
-
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().EmitEntryPointData(out(), ep_ptr)) << gen().error();
-  EXPECT_EQ(result(), R"(struct vtx_main_in {
-  float foo : TEXCOORD0;
-  int bar : TEXCOORD1;
-};
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_EntryPoint, EmitEntryPointData_Vertex_Output) {
-  // [[location(0)]] var<out> foo : f32;
-  // [[location(1)]] var<out> bar : i32;
-  //
-  // struct vtx_main_out {
-  //   float foo : TEXCOORD0;
-  //   int bar : TEXCOORD1;
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kOutput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("vtx_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex, "",
-                                              "vtx_main");
-  auto* ep_ptr = ep.get();
-
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().EmitEntryPointData(out(), ep_ptr)) << gen().error();
-  EXPECT_EQ(result(), R"(struct vtx_main_out {
-  float foo : TEXCOORD0;
-  int bar : TEXCOORD1;
-};
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_EntryPoint, EmitEntryPointData_Fragment_Input) {
-  // [[location(0)]] var<in> foo : f32;
-  // [[location(1)]] var<in> bar : i32;
-  //
-  // struct frag_main_in {
-  //   float foo : TEXCOORD0;
-  //   int bar : TEXCOORD1;
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kInput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("frag_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                              "main", "frag_main");
-  auto* ep_ptr = ep.get();
-
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().EmitEntryPointData(out(), ep_ptr)) << gen().error();
-  EXPECT_EQ(result(), R"(struct main_in {
-  float foo : TEXCOORD0;
-  int bar : TEXCOORD1;
-};
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_EntryPoint, EmitEntryPointData_Fragment_Output) {
-  // [[location(0)]] var<out> foo : f32;
-  // [[location(1)]] var<out> bar : i32;
-  //
-  // struct frag_main_out {
-  //   float foo : SV_Target0;
-  //   int bar : SV_Target1;
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kOutput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("frag_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                              "main", "frag_main");
-  auto* ep_ptr = ep.get();
-
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().EmitEntryPointData(out(), ep_ptr)) << gen().error();
-  EXPECT_EQ(result(), R"(struct main_out {
-  float foo : SV_Target0;
-  int bar : SV_Target1;
-};
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_EntryPoint, EmitEntryPointData_Compute_Input) {
-  // [[location(0)]] var<in> foo : f32;
-  // [[location(1)]] var<in> bar : i32;
-  //
-  // -> Error, not allowed
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kInput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("comp_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
-                                              "main", "comp_main");
-  auto* ep_ptr = ep.get();
-
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_FALSE(gen().EmitEntryPointData(out(), ep_ptr)) << gen().error();
-  EXPECT_EQ(gen().error(), R"(invalid location variable for pipeline stage)");
-}
-
-TEST_F(HlslGeneratorImplTest_EntryPoint, EmitEntryPointData_Compute_Output) {
-  // [[location(0)]] var<out> foo : f32;
-  // [[location(1)]] var<out> bar : i32;
-  //
-  // -> Error not allowed
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kOutput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("comp_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
-                                              "main", "comp_main");
-  auto* ep_ptr = ep.get();
-
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_FALSE(gen().EmitEntryPointData(out(), ep_ptr)) << gen().error();
-  EXPECT_EQ(gen().error(), R"(invalid location variable for pipeline stage)");
-}
-
-TEST_F(HlslGeneratorImplTest_EntryPoint, EmitEntryPointData_Builtins) {
-  // [[builtin(frag_coord)]] var<in> coord : vec4<f32>;
-  // [[builtin(frag_depth)]] var<out> depth : f32;
-  //
-  // struct main_in {
-  //   vector<float, 4> coord : SV_Position;
-  // };
-  //
-  // struct main_out {
-  //   float depth : SV_Depth;
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::VoidType void_type;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kInput, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragCoord));
-  coord_var->set_decorations(std::move(decos));
-
-  auto depth_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "depth", ast::StorageClass::kOutput, &f32));
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth));
-  depth_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-  td().RegisterVariableForTesting(depth_var.get());
-
-  mod()->AddGlobalVariable(std::move(coord_var));
-  mod()->AddGlobalVariable(std::move(depth_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                              "main", "frag_main");
-  auto* ep_ptr = ep.get();
-
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().EmitEntryPointData(out(), ep_ptr)) << gen().error();
-  EXPECT_EQ(result(), R"(struct main_in {
-  vector<float, 4> coord : SV_Position;
-};
-
-struct main_out {
-  float depth : SV_Depth;
-};
-
-)");
-}
-
-}  // namespace
-}  // namespace hlsl
-}  // namespace writer
-}  // namespace tint
diff --git a/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc b/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
index 4a65bef..04eca18 100644
--- a/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
+++ b/src/writer/hlsl/generator_impl_function_entry_point_data_test.cc
@@ -14,7 +14,6 @@
 
 #include "src/ast/assignment_statement.h"
 #include "src/ast/decorated_variable.h"
-#include "src/ast/entry_point.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/location_decoration.h"
 #include "src/ast/member_accessor_expression.h"
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc
index 4c25dd7..86e392f 100644
--- a/src/writer/hlsl/generator_impl_function_test.cc
+++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -124,82 +124,6 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Function, Emit_Function_EntryPoint_NoName) {
-  ast::type::VoidType void_type;
-
-  auto func = std::make_unique<ast::Function>("frag_main", ast::VariableList{},
-                                              &void_type);
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-
-  mod()->AddFunction(std::move(func));
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(void frag_main() {
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function, Emit_Function_EntryPoint_WithInOutVars) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(struct frag_main_in {
-  float foo : TEXCOORD0;
-};
-
-struct frag_main_out {
-  float bar : SV_Target1;
-};
-
-frag_main_out frag_main(frag_main_in tint_in) {
-  frag_main_out tint_out;
-  tint_out.bar = tint_in.foo;
-  return tint_out;
-}
-
-)");
-}
-
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_WithInOutVars) {
   ast::type::VoidType void_type;
@@ -258,72 +182,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_EntryPoint_WithInOut_Builtins) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kInput, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragCoord));
-  coord_var->set_decorations(std::move(decos));
-
-  auto depth_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "depth", ast::StorageClass::kOutput, &f32));
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth));
-  depth_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-  td().RegisterVariableForTesting(depth_var.get());
-
-  mod()->AddGlobalVariable(std::move(coord_var));
-  mod()->AddGlobalVariable(std::move(depth_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(struct frag_main_in {
-  vector<float, 4> coord : SV_Position;
-};
-
-struct frag_main_out {
-  float depth : SV_Depth;
-};
-
-frag_main_out frag_main(frag_main_in tint_in) {
-  frag_main_out tint_out;
-  tint_out.depth = tint_in.coord.x;
-  return tint_out;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_WithInOut_Builtins) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -387,58 +245,6 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Function, Emit_Function_EntryPoint_With_Uniform) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kUniform, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-  mod()->AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::MemberAccessorExpression>(
-      std::make_unique<ast::IdentifierExpression>("coord"),
-      std::make_unique<ast::IdentifierExpression>("x")));
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(cbuffer : register(b0) {
-  vector<float, 4> coord;
-};
-
-void frag_main() {
-  float v = coord.x;
-  return;
-}
-
-)");
-}
-
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_Uniform) {
   ast::type::VoidType void_type;
@@ -491,76 +297,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_EntryPoint_With_UniformStruct) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  ast::StructMemberList members;
-  members.push_back(std::make_unique<ast::StructMember>(
-      "coord", &vec4, ast::StructMemberDecorationList{}));
-
-  auto str = std::make_unique<ast::Struct>();
-  str->set_members(std::move(members));
-
-  ast::type::StructType s(std::move(str));
-  s.set_name("Uniforms");
-  auto alias = std::make_unique<ast::type::AliasType>("Uniforms", &s);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "uniforms", ast::StorageClass::kUniform, alias.get()));
-
-  mod()->AddAliasType(alias.get());
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-  mod()->AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::MemberAccessorExpression>(
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("uniforms"),
-          std::make_unique<ast::IdentifierExpression>("coord")),
-      std::make_unique<ast::IdentifierExpression>("x")));
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(struct Uniforms {
-  vector<float, 4> coord;
-};
-
-ConstantBuffer<Uniforms> uniforms : register(b0);
-
-void frag_main() {
-  float v = uniforms.coord.x;
-  return;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_UniformStruct) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -629,74 +365,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_EntryPoint_With_StorageBuffer_Read) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      std::make_unique<ast::StructMember>("a", &i32, std::move(a_deco)));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      std::make_unique<ast::StructMember>("b", &f32, std::move(b_deco)));
-
-  auto str = std::make_unique<ast::Struct>();
-  str->set_members(std::move(members));
-
-  ast::type::StructType s(std::move(str));
-  s.set_name("Data");
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kStorageBuffer, &s));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-  mod()->AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::MemberAccessorExpression>(
-      std::make_unique<ast::IdentifierExpression>("coord"),
-      std::make_unique<ast::IdentifierExpression>("b")));
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
-
-void frag_main() {
-  float v = asfloat(coord.Load(4));
-  return;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_StorageBuffer_Read) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -763,76 +431,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_EntryPoint_With_StorageBuffer_Store) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  ast::StructMemberList members;
-  ast::StructMemberDecorationList a_deco;
-  a_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(0));
-  members.push_back(
-      std::make_unique<ast::StructMember>("a", &i32, std::move(a_deco)));
-
-  ast::StructMemberDecorationList b_deco;
-  b_deco.push_back(std::make_unique<ast::StructMemberOffsetDecoration>(4));
-  members.push_back(
-      std::make_unique<ast::StructMember>("b", &f32, std::move(b_deco)));
-
-  auto str = std::make_unique<ast::Struct>();
-  str->set_members(std::move(members));
-
-  ast::type::StructType s(std::move(str));
-  s.set_name("Data");
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kStorageBuffer, &s));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-
-  mod()->AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto assign = std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("b")),
-      std::make_unique<ast::ScalarConstructorExpression>(
-          std::make_unique<ast::FloatLiteral>(&f32, 2.0f)));
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::move(assign));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
-
-void frag_main() {
-  coord.Store(4, asuint(2.00000000f));
-  return;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_With_StorageBuffer_Store) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -900,103 +498,6 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_Called_By_EntryPoints_WithLocationGlobals_And_Params) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  auto val_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("val", ast::StorageClass::kOutput, &f32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  val_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-  td().RegisterVariableForTesting(val_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-  mod()->AddGlobalVariable(std::move(val_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("val"),
-      std::make_unique<ast::IdentifierExpression>("param")));
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  sub_func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("sub_func"),
-          std::move(expr))));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  mod()->AddEntryPoint(std::move(ep1));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(struct ep_1_in {
-  float foo : TEXCOORD0;
-};
-
-struct ep_1_out {
-  float bar : SV_Target1;
-  float val : SV_Target0;
-};
-
-float sub_func_ep_1(in ep_1_in tint_in, out ep_1_out tint_out, float param) {
-  tint_out.bar = tint_in.foo;
-  tint_out.val = param;
-  return tint_in.foo;
-}
-
-ep_1_out ep_1(ep_1_in tint_in) {
-  ep_1_out tint_out;
-  tint_out.bar = sub_func_ep_1(tint_in, tint_out, 1.00000000f);
-  return tint_out;
-}
-
-)");
-}
-
 TEST_F(
     HlslGeneratorImplTest_Function,
     Emit_FunctionDecoration_Called_By_EntryPoints_WithLocationGlobals_And_Params) {
@@ -1094,79 +595,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_Called_By_EntryPoints_NoUsedGlobals) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto depth_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "depth", ast::StorageClass::kOutput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth));
-  depth_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(depth_var.get());
-
-  mod()->AddGlobalVariable(std::move(depth_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::IdentifierExpression>("param")));
-  sub_func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("sub_func"),
-          std::move(expr))));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  mod()->AddEntryPoint(std::move(ep1));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(struct ep_1_out {
-  float depth : SV_Depth;
-};
-
-float sub_func(float param) {
-  return param;
-}
-
-ep_1_out ep_1() {
-  ep_1_out tint_out;
-  tint_out.depth = sub_func(1.00000000f);
-  return tint_out;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_Called_By_EntryPoints_NoUsedGlobals) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -1237,98 +665,6 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kInput, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragCoord));
-  coord_var->set_decorations(std::move(decos));
-
-  auto depth_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "depth", ast::StorageClass::kOutput, &f32));
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth));
-  depth_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-  td().RegisterVariableForTesting(depth_var.get());
-
-  mod()->AddGlobalVariable(std::move(coord_var));
-  mod()->AddGlobalVariable(std::move(depth_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::IdentifierExpression>("param")));
-  sub_func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("sub_func"),
-          std::move(expr))));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  mod()->AddEntryPoint(std::move(ep1));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(struct ep_1_in {
-  vector<float, 4> coord : SV_Position;
-};
-
-struct ep_1_out {
-  float depth : SV_Depth;
-};
-
-float sub_func_ep_1(in ep_1_in tint_in, out ep_1_out tint_out, float param) {
-  tint_out.depth = tint_in.coord.x;
-  return param;
-}
-
-ep_1_out ep_1(ep_1_in tint_in) {
-  ep_1_out tint_out;
-  tint_out.depth = sub_func_ep_1(tint_in, tint_out, 1.00000000f);
-  return tint_out;
-}
-
-)");
-}
-
 TEST_F(
     HlslGeneratorImplTest_Function,
     Emit_FunctionDecoration_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) {
@@ -1421,82 +757,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_Called_By_EntryPoint_With_Uniform) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kUniform, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-
-  mod()->AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  sub_func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(sub_func));
-
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::CallExpression>(
-      std::make_unique<ast::IdentifierExpression>("sub_func"),
-      std::move(expr)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(cbuffer : register(b0) {
-  vector<float, 4> coord;
-};
-
-float sub_func(float param) {
-  return coord.x;
-}
-
-void frag_main() {
-  float v = sub_func(1.00000000f);
-  return;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_Called_By_EntryPoint_With_Uniform) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -1571,80 +831,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_Called_By_EntryPoint_With_StorageBuffer) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kStorageBuffer, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(coord_var.get());
-
-  mod()->AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  sub_func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(sub_func));
-
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::CallExpression>(
-      std::make_unique<ast::IdentifierExpression>("sub_func"),
-      std::move(expr)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
-
-float sub_func(float param) {
-  return asfloat(coord.Load((4 * 0)));
-}
-
-void frag_main() {
-  float v = sub_func(1.00000000f);
-  return;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_Called_By_EntryPoint_With_StorageBuffer) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -1717,171 +903,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_Called_Two_EntryPoints_WithGlobals) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(foo_var.get());
-  td().RegisterVariableForTesting(bar_var.get());
-
-  mod()->AddGlobalVariable(std::move(foo_var));
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  sub_func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("sub_func"),
-          ast::ExpressionList{})));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  auto ep2 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_2", "frag_1_main");
-  mod()->AddEntryPoint(std::move(ep1));
-  mod()->AddEntryPoint(std::move(ep2));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(struct ep_1_in {
-  float foo : TEXCOORD0;
-};
-
-struct ep_1_out {
-  float bar : SV_Target1;
-};
-
-struct ep_2_in {
-  float foo : TEXCOORD0;
-};
-
-struct ep_2_out {
-  float bar : SV_Target1;
-};
-
-float sub_func_ep_1(in ep_1_in tint_in, out ep_1_out tint_out) {
-  tint_out.bar = tint_in.foo;
-  return tint_in.foo;
-}
-
-float sub_func_ep_2(in ep_2_in tint_in_0, out ep_2_out tint_out_0) {
-  tint_out_0.bar = tint_in_0.foo;
-  return tint_in_0.foo;
-}
-
-ep_1_out ep_1(ep_1_in tint_in) {
-  ep_1_out tint_out;
-  tint_out.bar = sub_func_ep_1(tint_in, tint_out);
-  return tint_out;
-}
-
-ep_2_out ep_2(ep_2_in tint_in_0) {
-  ep_2_out tint_out_0;
-  tint_out_0.bar = sub_func_ep_2(tint_in_0, tint_out_0);
-  return tint_out_0;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_EntryPoints_WithGlobal_Nested_Return) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32));
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  td().RegisterVariableForTesting(bar_var.get());
-  mod()->AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::ScalarConstructorExpression>(
-          std::make_unique<ast::FloatLiteral>(&f32, 1.0f))));
-
-  auto list = std::make_unique<ast::BlockStatement>();
-  list->append(std::make_unique<ast::ReturnStatement>());
-
-  body->append(std::make_unique<ast::IfStatement>(
-      std::make_unique<ast::BinaryExpression>(
-          ast::BinaryOp::kEqual,
-          std::make_unique<ast::ScalarConstructorExpression>(
-              std::make_unique<ast::SintLiteral>(&i32, 1)),
-          std::make_unique<ast::ScalarConstructorExpression>(
-              std::make_unique<ast::SintLiteral>(&i32, 1))),
-      std::move(list)));
-
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  mod()->AddEntryPoint(std::move(ep1));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(struct ep_1_out {
-  float bar : SV_Target1;
-};
-
-ep_1_out ep_1() {
-  ep_1_out tint_out;
-  tint_out.bar = 1.00000000f;
-  if ((1 == 1)) {
-    return tint_out;
-  }
-  return tint_out;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoints_WithGlobal_Nested_Return) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -1944,103 +965,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_Called_Two_EntryPoints_WithoutGlobals) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-
-  ast::VariableList params;
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::ScalarConstructorExpression>(
-          std::make_unique<ast::FloatLiteral>(&f32, 1.0))));
-  sub_func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  auto var = std::make_unique<ast::Variable>(
-      "foo", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::CallExpression>(
-      std::make_unique<ast::IdentifierExpression>("sub_func"),
-      ast::ExpressionList{}));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  auto ep2 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_2", "frag_1_main");
-  mod()->AddEntryPoint(std::move(ep1));
-  mod()->AddEntryPoint(std::move(ep2));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(float sub_func() {
-  return 1.00000000f;
-}
-
-void ep_1() {
-  float foo = sub_func();
-  return;
-}
-
-void ep_2() {
-  float foo = sub_func();
-  return;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function, Emit_Function_EntryPoint_WithName) {
-  ast::type::VoidType void_type;
-
-  auto func = std::make_unique<ast::Function>("frag_main", ast::VariableList{},
-                                              &void_type);
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                              "my_main", "frag_main");
-
-  mod()->AddFunction(std::move(func));
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(void my_main() {
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_EntryPoint_WithNameCollision) {
-  ast::type::VoidType void_type;
-
-  auto func = std::make_unique<ast::Function>("frag_main", ast::VariableList{},
-                                              &void_type);
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                              "GeometryShader", "frag_main");
-
-  mod()->AddFunction(std::move(func));
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"(void GeometryShader_tint_0() {
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_WithNameCollision) {
   ast::type::VoidType void_type;
 
@@ -2058,33 +982,6 @@
 )");
 }
 
-TEST_F(HlslGeneratorImplTest_Function, Emit_Function_EntryPoint_Compute) {
-  ast::type::VoidType void_type;
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("comp_main", std::move(params),
-                                              &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
-                                              "main", "comp_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"([numthreads(1, 1, 1)]
-void main() {
-  return;
-}
-
-)");
-}
-
 TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_Compute) {
   ast::type::VoidType void_type;
@@ -2112,35 +1009,6 @@
 }
 
 TEST_F(HlslGeneratorImplTest_Function,
-       Emit_Function_EntryPoint_Compute_WithWorkgroup) {
-  ast::type::VoidType void_type;
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("comp_main", std::move(params),
-                                              &void_type);
-  func->add_decoration(std::make_unique<ast::WorkgroupDecoration>(2u, 4u, 6u));
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod()->AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
-                                              "main", "comp_main");
-  mod()->AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td().Determine()) << td().error();
-  ASSERT_TRUE(gen().Generate(out())) << gen().error();
-  EXPECT_EQ(result(), R"([numthreads(2, 4, 6)]
-void main() {
-  return;
-}
-
-)");
-}
-
-TEST_F(HlslGeneratorImplTest_Function,
        Emit_FunctionDecoration_EntryPoint_Compute_WithWorkgroup) {
   ast::type::VoidType void_type;
 
diff --git a/src/writer/hlsl/generator_impl_test.cc b/src/writer/hlsl/generator_impl_test.cc
index 4055bb9..26b7003 100644
--- a/src/writer/hlsl/generator_impl_test.cc
+++ b/src/writer/hlsl/generator_impl_test.cc
@@ -14,7 +14,6 @@
 
 #include <memory>
 
-#include "src/ast/entry_point.h"
 #include "src/ast/function.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/module.h"
@@ -30,10 +29,9 @@
 
 TEST_F(HlslGeneratorImplTest, Generate) {
   ast::type::VoidType void_type;
-  mod()->AddFunction(std::make_unique<ast::Function>(
-      "my_func", ast::VariableList{}, &void_type));
-  mod()->AddEntryPoint(std::make_unique<ast::EntryPoint>(
-      ast::PipelineStage::kFragment, "", "my_func"));
+  auto func = std::make_unique<ast::Function>("my_func", ast::VariableList{},
+                                              &void_type);
+  mod()->AddFunction(std::move(func));
 
   ASSERT_TRUE(gen().Generate(out())) << gen().error();
   EXPECT_EQ(result(), R"(void my_func() {
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index 34c9d6b..c6d45f7 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -124,12 +124,6 @@
     }
   }
 
-  for (const auto& ep : module_->entry_points()) {
-    if (!EmitEntryPointData(ep.get())) {
-      return false;
-    }
-  }
-
   // Make sure all entry point data is emitted before the entry point functions
   for (const auto& func : module_->functions()) {
     if (!func->IsEntryPoint()) {
@@ -147,12 +141,6 @@
     }
   }
 
-  for (const auto& ep : module_->entry_points()) {
-    if (!EmitEntryPointFunction(ep.get())) {
-      return false;
-    }
-    out_ << std::endl;
-  }
   for (const auto& func : module_->functions()) {
     if (!func->IsEntryPoint()) {
       continue;
@@ -904,133 +892,6 @@
   return true;
 }
 
-bool GeneratorImpl::EmitEntryPointData(ast::EntryPoint* ep) {
-  auto* func = module_->FindFunctionByName(ep->function_name());
-  if (func == nullptr) {
-    error_ = "Unable to find entry point function: " + ep->function_name();
-    return false;
-  }
-
-  std::vector<std::pair<ast::Variable*, uint32_t>> in_locations;
-  std::vector<std::pair<ast::Variable*, ast::VariableDecoration*>>
-      out_variables;
-  for (auto data : func->referenced_location_variables()) {
-    auto* var = data.first;
-    auto* deco = data.second;
-
-    if (var->storage_class() == ast::StorageClass::kInput) {
-      in_locations.push_back({var, deco->value()});
-    } else if (var->storage_class() == ast::StorageClass::kOutput) {
-      out_variables.push_back({var, deco});
-    }
-  }
-
-  for (auto data : func->referenced_builtin_variables()) {
-    auto* var = data.first;
-    auto* deco = data.second;
-
-    if (var->storage_class() == ast::StorageClass::kOutput) {
-      out_variables.push_back({var, deco});
-    }
-  }
-
-  auto ep_name = ep->name();
-  if (ep_name.empty()) {
-    ep_name = ep->function_name();
-  }
-
-  // TODO(dsinclair): There is a potential bug here. Entry points can have the
-  // same name in WGSL if they have different pipeline stages. This does not
-  // take that into account and will emit duplicate struct names. I'm ignoring
-  // this until https://github.com/gpuweb/gpuweb/issues/662 is resolved as it
-  // may remove this issue and entry point names will need to be unique.
-  if (!in_locations.empty()) {
-    auto in_struct_name = generate_name(ep_name + "_" + kInStructNameSuffix);
-    auto in_var_name = generate_name(kTintStructInVarPrefix);
-    ep_name_to_in_data_[ep_name] = {in_struct_name, in_var_name};
-
-    make_indent();
-    out_ << "struct " << in_struct_name << " {" << std::endl;
-
-    increment_indent();
-
-    for (auto& data : in_locations) {
-      auto* var = data.first;
-      uint32_t loc = data.second;
-
-      make_indent();
-      if (!EmitType(var->type(), var->name())) {
-        return false;
-      }
-
-      out_ << " " << var->name() << " [[";
-      if (ep->stage() == ast::PipelineStage::kVertex) {
-        out_ << "attribute(" << loc << ")";
-      } else if (ep->stage() == ast::PipelineStage::kFragment) {
-        out_ << "user(locn" << loc << ")";
-      } else {
-        error_ = "invalid location variable for pipeline stage";
-        return false;
-      }
-      out_ << "]];" << std::endl;
-    }
-    decrement_indent();
-    make_indent();
-
-    out_ << "};" << std::endl << std::endl;
-  }
-
-  if (!out_variables.empty()) {
-    auto out_struct_name = generate_name(ep_name + "_" + kOutStructNameSuffix);
-    auto out_var_name = generate_name(kTintStructOutVarPrefix);
-    ep_name_to_out_data_[ep_name] = {out_struct_name, out_var_name};
-
-    make_indent();
-    out_ << "struct " << out_struct_name << " {" << std::endl;
-
-    increment_indent();
-    for (auto& data : out_variables) {
-      auto* var = data.first;
-      auto* deco = data.second;
-
-      make_indent();
-      if (!EmitType(var->type(), var->name())) {
-        return false;
-      }
-
-      out_ << " " << var->name() << " [[";
-
-      if (deco->IsLocation()) {
-        auto loc = deco->AsLocation()->value();
-        if (ep->stage() == ast::PipelineStage::kVertex) {
-          out_ << "user(locn" << loc << ")";
-        } else if (ep->stage() == ast::PipelineStage::kFragment) {
-          out_ << "color(" << loc << ")";
-        } else {
-          error_ = "invalid location variable for pipeline stage";
-          return false;
-        }
-      } else if (deco->IsBuiltin()) {
-        auto attr = builtin_to_attribute(deco->AsBuiltin()->value());
-        if (attr.empty()) {
-          error_ = "unsupported builtin";
-          return false;
-        }
-        out_ << attr;
-      } else {
-        error_ = "unsupported variable decoration for entry point output";
-        return false;
-      }
-      out_ << "]];" << std::endl;
-    }
-    decrement_indent();
-    make_indent();
-    out_ << "};" << std::endl << std::endl;
-  }
-
-  return true;
-}
-
 bool GeneratorImpl::EmitEntryPointData(ast::Function* func) {
   std::vector<std::pair<ast::Variable*, uint32_t>> in_locations;
   std::vector<std::pair<ast::Variable*, ast::VariableDecoration*>>
@@ -1230,7 +1091,7 @@
   make_indent();
 
   // Entry points will be emitted later, skip for now.
-  if (func->IsEntryPoint() || module_->IsFunctionEntryPoint(func->name())) {
+  if (func->IsEntryPoint()) {
     return true;
   }
 
@@ -1402,141 +1263,6 @@
   return "";
 }
 
-bool GeneratorImpl::EmitEntryPointFunction(ast::EntryPoint* ep) {
-  make_indent();
-
-  current_ep_name_ = ep->name();
-  if (current_ep_name_.empty()) {
-    current_ep_name_ = ep->function_name();
-  }
-
-  auto* func = module_->FindFunctionByName(ep->function_name());
-  if (func == nullptr) {
-    error_ = "unable to find function for entry point: " + ep->function_name();
-    return false;
-  }
-
-  EmitStage(ep->stage());
-  out_ << " ";
-
-  // This is an entry point, the return type is the entry point output structure
-  // if one exists, or void otherwise.
-  auto out_data = ep_name_to_out_data_.find(current_ep_name_);
-  bool has_out_data = out_data != ep_name_to_out_data_.end();
-  if (has_out_data) {
-    out_ << out_data->second.struct_name;
-  } else {
-    out_ << "void";
-  }
-  out_ << " " << namer_.NameFor(current_ep_name_) << "(";
-
-  bool first = true;
-  auto in_data = ep_name_to_in_data_.find(current_ep_name_);
-  if (in_data != ep_name_to_in_data_.end()) {
-    out_ << in_data->second.struct_name << " " << in_data->second.var_name
-         << " [[stage_in]]";
-    first = false;
-  }
-
-  for (auto data : func->referenced_builtin_variables()) {
-    auto* var = data.first;
-    if (var->storage_class() != ast::StorageClass::kInput) {
-      continue;
-    }
-
-    if (!first) {
-      out_ << ", ";
-    }
-    first = false;
-
-    auto* builtin = data.second;
-
-    if (!EmitType(var->type(), "")) {
-      return false;
-    }
-
-    auto attr = builtin_to_attribute(builtin->value());
-    if (attr.empty()) {
-      error_ = "unknown builtin";
-      return false;
-    }
-    out_ << " " << var->name() << " [[" << attr << "]]";
-  }
-
-  for (auto data : func->referenced_uniform_variables()) {
-    if (!first) {
-      out_ << ", ";
-    }
-    first = false;
-
-    auto* var = data.first;
-    // TODO(dsinclair): We're using the binding to make up the buffer number but
-    // we should instead be using a provided mapping that uses both buffer and
-    // set. https://bugs.chromium.org/p/tint/issues/detail?id=104
-    auto* binding = data.second.binding;
-    if (binding == nullptr) {
-      error_ = "unable to find binding information for uniform: " + var->name();
-      return false;
-    }
-    // auto* set = data.second.set;
-
-    out_ << "constant ";
-    // TODO(dsinclair): Can you have a uniform array? If so, this needs to be
-    // updated to handle arrays property.
-    if (!EmitType(var->type(), "")) {
-      return false;
-    }
-    out_ << "& " << var->name() << " [[buffer(" << binding->value() << ")]]";
-  }
-
-  for (auto data : func->referenced_storagebuffer_variables()) {
-    if (!first) {
-      out_ << ", ";
-    }
-    first = false;
-
-    auto* var = data.first;
-    // TODO(dsinclair): We're using the binding to make up the buffer number but
-    // we should instead be using a provided mapping that uses both buffer and
-    // set. https://bugs.chromium.org/p/tint/issues/detail?id=104
-    auto* binding = data.second.binding;
-    // auto* set = data.second.set;
-
-    out_ << "device ";
-    // TODO(dsinclair): Can you have a storagebuffer have an array? If so, this
-    // needs to be updated to handle arrays property.
-    if (!EmitType(var->type(), "")) {
-      return false;
-    }
-    out_ << "& " << var->name() << " [[buffer(" << binding->value() << ")]]";
-  }
-
-  out_ << ") {" << std::endl;
-
-  increment_indent();
-
-  if (has_out_data) {
-    make_indent();
-    out_ << out_data->second.struct_name << " " << out_data->second.var_name
-         << " = {};" << std::endl;
-  }
-
-  generating_entry_point_ = true;
-  for (const auto& s : *(func->body())) {
-    if (!EmitStatement(s.get())) {
-      return false;
-    }
-  }
-  generating_entry_point_ = false;
-
-  decrement_indent();
-  make_indent();
-  out_ << "}" << std::endl;
-
-  current_ep_name_ = "";
-  return true;
-}
-
 bool GeneratorImpl::EmitEntryPointFunction(ast::Function* func) {
   make_indent();
 
diff --git a/src/writer/msl/generator_impl.h b/src/writer/msl/generator_impl.h
index 5852f30..459cac5 100644
--- a/src/writer/msl/generator_impl.h
+++ b/src/writer/msl/generator_impl.h
@@ -118,18 +118,10 @@
   /// @returns true if the statement was emitted
   bool EmitElse(ast::ElseStatement* stmt);
   /// Handles emitting information for an entry point
-  /// @param ep the entry point
-  /// @returns true if the entry point data was emitted
-  bool EmitEntryPointData(ast::EntryPoint* ep);
-  /// Handles emitting information for an entry point
   /// @param func the entry point function
   /// @returns true if the entry point data was emitted
   bool EmitEntryPointData(ast::Function* func);
   /// Handles emitting the entry point function
-  /// @param ep the entry point
-  /// @returns true if the entry point function was emitted
-  bool EmitEntryPointFunction(ast::EntryPoint* ep);
-  /// Handles emitting the entry point function
   /// @param func the entry point function
   /// @returns true if the entry point function was emitted
   bool EmitEntryPointFunction(ast::Function* func);
diff --git a/src/writer/msl/generator_impl_entry_point_test.cc b/src/writer/msl/generator_impl_entry_point_test.cc
deleted file mode 100644
index 74ad69a..0000000
--- a/src/writer/msl/generator_impl_entry_point_test.cc
+++ /dev/null
@@ -1,493 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "gtest/gtest.h"
-#include "src/ast/assignment_statement.h"
-#include "src/ast/decorated_variable.h"
-#include "src/ast/entry_point.h"
-#include "src/ast/identifier_expression.h"
-#include "src/ast/location_decoration.h"
-#include "src/ast/member_accessor_expression.h"
-#include "src/ast/module.h"
-#include "src/ast/type/f32_type.h"
-#include "src/ast/type/i32_type.h"
-#include "src/ast/type/vector_type.h"
-#include "src/ast/type/void_type.h"
-#include "src/ast/variable.h"
-#include "src/context.h"
-#include "src/type_determiner.h"
-#include "src/writer/msl/generator_impl.h"
-
-namespace tint {
-namespace writer {
-namespace msl {
-namespace {
-
-using MslGeneratorImplTest = testing::Test;
-
-TEST_F(MslGeneratorImplTest, EmitEntryPointData_Vertex_Input) {
-  // [[location(0)]] var<in> foo : f32;
-  // [[location(1)]] var<in> bar : i32;
-  //
-  // struct vtx_main_in {
-  //   float foo [[attribute(0)]];
-  //   int bar [[attribute(1)]];
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kInput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("vtx_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex, "",
-                                              "vtx_main");
-  auto* ep_ptr = ep.get();
-
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitEntryPointData(ep_ptr)) << g.error();
-  EXPECT_EQ(g.result(), R"(struct vtx_main_in {
-  float foo [[attribute(0)]];
-  int bar [[attribute(1)]];
-};
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest, EmitEntryPointData_Vertex_Output) {
-  // [[location(0)]] var<out> foo : f32;
-  // [[location(1)]] var<out> bar : i32;
-  //
-  // struct vtx_main_out {
-  //   float foo [[user(locn0)]];
-  //   int bar [[user(locn1)]];
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kOutput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("vtx_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kVertex, "",
-                                              "vtx_main");
-  auto* ep_ptr = ep.get();
-
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitEntryPointData(ep_ptr)) << g.error();
-  EXPECT_EQ(g.result(), R"(struct vtx_main_out {
-  float foo [[user(locn0)]];
-  int bar [[user(locn1)]];
-};
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest, EmitEntryPointData_Fragment_Input) {
-  // [[location(0)]] var<in> foo : f32;
-  // [[location(1)]] var<in> bar : i32;
-  //
-  // struct frag_main_in {
-  //   float foo [[user(locn0)]];
-  //   int bar [[user(locn1)]];
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kInput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("frag_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                              "main", "frag_main");
-  auto* ep_ptr = ep.get();
-
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitEntryPointData(ep_ptr)) << g.error();
-  EXPECT_EQ(g.result(), R"(struct main_in {
-  float foo [[user(locn0)]];
-  int bar [[user(locn1)]];
-};
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest, EmitEntryPointData_Fragment_Output) {
-  // [[location(0)]] var<out> foo : f32;
-  // [[location(1)]] var<out> bar : i32;
-  //
-  // struct frag_main_out {
-  //   float foo [[color(0)]];
-  //   int bar [[color(1)]];
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kOutput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("frag_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                              "main", "frag_main");
-  auto* ep_ptr = ep.get();
-
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitEntryPointData(ep_ptr)) << g.error();
-  EXPECT_EQ(g.result(), R"(struct main_out {
-  float foo [[color(0)]];
-  int bar [[color(1)]];
-};
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest, EmitEntryPointData_Compute_Input) {
-  // [[location(0)]] var<in> foo : f32;
-  // [[location(1)]] var<in> bar : i32;
-  //
-  // -> Error, not allowed
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kInput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("comp_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
-                                              "main", "comp_main");
-  auto* ep_ptr = ep.get();
-
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_FALSE(g.EmitEntryPointData(ep_ptr)) << g.error();
-  EXPECT_EQ(g.error(), R"(invalid location variable for pipeline stage)");
-}
-
-TEST_F(MslGeneratorImplTest, EmitEntryPointData_Compute_Output) {
-  // [[location(0)]] var<out> foo : f32;
-  // [[location(1)]] var<out> bar : i32;
-  //
-  // -> Error not allowed
-
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kOutput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &i32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func =
-      std::make_unique<ast::Function>("comp_main", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("bar")));
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
-                                              "main", "comp_main");
-  auto* ep_ptr = ep.get();
-
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_FALSE(g.EmitEntryPointData(ep_ptr)) << g.error();
-  EXPECT_EQ(g.error(), R"(invalid location variable for pipeline stage)");
-}
-
-TEST_F(MslGeneratorImplTest, EmitEntryPointData_Builtins) {
-  // Output builtins go in the output struct, input builtins will be passed
-  // as input parameters to the entry point function.
-
-  // [[builtin(frag_coord)]] var<in> coord : vec4<f32>;
-  // [[builtin(frag_depth)]] var<out> depth : f32;
-  //
-  // struct main_out {
-  //   float depth [[depth(any)]];
-  // };
-
-  ast::type::F32Type f32;
-  ast::type::VoidType void_type;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kInput, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragCoord));
-  coord_var->set_decorations(std::move(decos));
-
-  auto depth_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "depth", ast::StorageClass::kOutput, &f32));
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth));
-  depth_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(coord_var.get());
-  td.RegisterVariableForTesting(depth_var.get());
-
-  mod.AddGlobalVariable(std::move(coord_var));
-  mod.AddGlobalVariable(std::move(depth_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                              "main", "frag_main");
-  auto* ep_ptr = ep.get();
-
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.EmitEntryPointData(ep_ptr)) << g.error();
-  EXPECT_EQ(g.result(), R"(struct main_out {
-  float depth [[depth(any)]];
-};
-
-)");
-}
-
-}  // namespace
-}  // namespace msl
-}  // namespace writer
-}  // namespace tint
diff --git a/src/writer/msl/generator_impl_function_test.cc b/src/writer/msl/generator_impl_function_test.cc
index f162b6b..564e286 100644
--- a/src/writer/msl/generator_impl_function_test.cc
+++ b/src/writer/msl/generator_impl_function_test.cc
@@ -135,93 +135,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_NoName) {
-  ast::type::VoidType void_type;
-
-  auto func = std::make_unique<ast::Function>("frag_main", ast::VariableList{},
-                                              &void_type);
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-
-  ast::Module m;
-  m.AddFunction(std::move(func));
-  m.AddEntryPoint(std::move(ep));
-
-  GeneratorImpl g(&m);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-fragment void frag_main() {
-}
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_WithInOutVars) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-struct frag_main_in {
-  float foo [[user(locn0)]];
-};
-
-struct frag_main_out {
-  float bar [[color(1)]];
-};
-
-fragment frag_main_out frag_main(frag_main_in tint_in [[stage_in]]) {
-  frag_main_out tint_out = {};
-  tint_out.bar = tint_in.foo;
-  return tint_out;
-}
-
-)");
-}
-
 TEST_F(MslGeneratorImplTest, Emit_FunctionDecoration_EntryPoint_WithInOutVars) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -285,74 +198,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_WithInOut_Builtins) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kInput, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragCoord));
-  coord_var->set_decorations(std::move(decos));
-
-  auto depth_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "depth", ast::StorageClass::kOutput, &f32));
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth));
-  depth_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(coord_var.get());
-  td.RegisterVariableForTesting(depth_var.get());
-
-  mod.AddGlobalVariable(std::move(coord_var));
-  mod.AddGlobalVariable(std::move(depth_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-struct frag_main_out {
-  float depth [[depth(any)]];
-};
-
-fragment frag_main_out frag_main(float4 coord [[position]]) {
-  frag_main_out tint_out = {};
-  tint_out.depth = coord.x;
-  return tint_out;
-}
-
-)");
-}
-
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_EntryPoint_WithInOut_Builtins) {
   ast::type::VoidType void_type;
@@ -420,62 +265,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_With_Uniform) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kUniform, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(coord_var.get());
-
-  mod.AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::MemberAccessorExpression>(
-      std::make_unique<ast::IdentifierExpression>("coord"),
-      std::make_unique<ast::IdentifierExpression>("x")));
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-fragment void frag_main(constant float4& coord [[buffer(0)]]) {
-  float v = coord.x;
-  return;
-}
-
-)");
-}
-
 TEST_F(MslGeneratorImplTest, Emit_FunctionDecoration_EntryPoint_With_Uniform) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -530,62 +319,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_With_StorageBuffer) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kStorageBuffer, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(coord_var.get());
-
-  mod.AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::MemberAccessorExpression>(
-      std::make_unique<ast::IdentifierExpression>("coord"),
-      std::make_unique<ast::IdentifierExpression>("x")));
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-fragment void frag_main(device float4& coord [[buffer(0)]]) {
-  float v = coord.x;
-  return;
-}
-
-)");
-}
-
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_EntryPoint_With_StorageBuffer) {
   ast::type::VoidType void_type;
@@ -641,110 +374,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest,
-       Emit_Function_Called_By_EntryPoints_WithLocationGlobals_And_Params) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  auto val_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("val", ast::StorageClass::kOutput, &f32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  val_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-  td.RegisterVariableForTesting(val_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-  mod.AddGlobalVariable(std::move(val_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("val"),
-      std::make_unique<ast::IdentifierExpression>("param")));
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  sub_func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("sub_func"),
-          std::move(expr))));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  mod.AddEntryPoint(std::move(ep1));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-struct ep_1_in {
-  float foo [[user(locn0)]];
-};
-
-struct ep_1_out {
-  float bar [[color(1)]];
-  float val [[color(0)]];
-};
-
-float sub_func_ep_1(thread ep_1_in& tint_in, thread ep_1_out& tint_out, float param) {
-  tint_out.bar = tint_in.foo;
-  tint_out.val = param;
-  return tint_in.foo;
-}
-
-fragment ep_1_out ep_1(ep_1_in tint_in [[stage_in]]) {
-  ep_1_out tint_out = {};
-  tint_out.bar = sub_func_ep_1(tint_in, tint_out, 1.00000000f);
-  return tint_out;
-}
-
-)");
-}
-
 TEST_F(
     MslGeneratorImplTest,
     Emit_FunctionDecoration_Called_By_EntryPoints_WithLocationGlobals_And_Params) {
@@ -849,86 +478,6 @@
 }
 
 TEST_F(MslGeneratorImplTest,
-       Emit_Function_Called_By_EntryPoints_NoUsedGlobals) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto depth_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "depth", ast::StorageClass::kOutput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth));
-  depth_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(depth_var.get());
-
-  mod.AddGlobalVariable(std::move(depth_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::IdentifierExpression>("param")));
-  sub_func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("sub_func"),
-          std::move(expr))));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  mod.AddEntryPoint(std::move(ep1));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-struct ep_1_out {
-  float depth [[depth(any)]];
-};
-
-float sub_func(float param) {
-  return param;
-}
-
-fragment ep_1_out ep_1() {
-  ep_1_out tint_out = {};
-  tint_out.depth = sub_func(1.00000000f);
-  return tint_out;
-}
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_Called_By_EntryPoints_NoUsedGlobals) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -1006,101 +555,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest,
-       Emit_Function_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kInput, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragCoord));
-  coord_var->set_decorations(std::move(decos));
-
-  auto depth_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "depth", ast::StorageClass::kOutput, &f32));
-  decos.push_back(
-      std::make_unique<ast::BuiltinDecoration>(ast::Builtin::kFragDepth));
-  depth_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(coord_var.get());
-  td.RegisterVariableForTesting(depth_var.get());
-
-  mod.AddGlobalVariable(std::move(coord_var));
-  mod.AddGlobalVariable(std::move(depth_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::IdentifierExpression>("param")));
-  sub_func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("depth"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("sub_func"),
-          std::move(expr))));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  mod.AddEntryPoint(std::move(ep1));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-struct ep_1_out {
-  float depth [[depth(any)]];
-};
-
-float sub_func_ep_1(thread ep_1_out& tint_out, thread float4& coord, float param) {
-  tint_out.depth = coord.x;
-  return param;
-}
-
-fragment ep_1_out ep_1(float4 coord [[position]]) {
-  ep_1_out tint_out = {};
-  tint_out.depth = sub_func_ep_1(tint_out, coord, 1.00000000f);
-  return tint_out;
-}
-
-)");
-}
-
 TEST_F(
     MslGeneratorImplTest,
     Emit_FunctionDecoration_Called_By_EntryPoints_WithBuiltinGlobals_And_Params) {
@@ -1195,84 +649,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_Function_Called_By_EntryPoint_With_Uniform) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kUniform, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(coord_var.get());
-
-  mod.AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  sub_func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(sub_func));
-
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::CallExpression>(
-      std::make_unique<ast::IdentifierExpression>("sub_func"),
-      std::move(expr)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-float sub_func(constant float4& coord, float param) {
-  return coord.x;
-}
-
-fragment void frag_main(constant float4& coord [[buffer(0)]]) {
-  float v = sub_func(coord, 1.00000000f);
-  return;
-}
-
-)");
-}
-
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_Called_By_EntryPoint_With_Uniform) {
   ast::type::VoidType void_type;
@@ -1351,85 +727,6 @@
 }
 
 TEST_F(MslGeneratorImplTest,
-       Emit_Function_Called_By_EntryPoint_With_StorageBuffer) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::VectorType vec4(&f32, 4);
-
-  auto coord_var =
-      std::make_unique<ast::DecoratedVariable>(std::make_unique<ast::Variable>(
-          "coord", ast::StorageClass::kStorageBuffer, &vec4));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::BindingDecoration>(0));
-  decos.push_back(std::make_unique<ast::SetDecoration>(1));
-  coord_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(coord_var.get());
-
-  mod.AddGlobalVariable(std::move(coord_var));
-
-  ast::VariableList params;
-  params.push_back(std::make_unique<ast::Variable>(
-      "param", ast::StorageClass::kFunction, &f32));
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::MemberAccessorExpression>(
-          std::make_unique<ast::IdentifierExpression>("coord"),
-          std::make_unique<ast::IdentifierExpression>("x"))));
-  sub_func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(sub_func));
-
-  auto func = std::make_unique<ast::Function>("frag_main", std::move(params),
-                                              &void_type);
-
-  ast::ExpressionList expr;
-  expr.push_back(std::make_unique<ast::ScalarConstructorExpression>(
-      std::make_unique<ast::FloatLiteral>(&f32, 1.0f)));
-
-  auto var =
-      std::make_unique<ast::Variable>("v", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::CallExpression>(
-      std::make_unique<ast::IdentifierExpression>("sub_func"),
-      std::move(expr)));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func));
-
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment, "",
-                                              "frag_main");
-  mod.AddEntryPoint(std::move(ep));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-float sub_func(device float4& coord, float param) {
-  return coord.x;
-}
-
-fragment void frag_main(device float4& coord [[buffer(0)]]) {
-  float v = sub_func(coord, 1.00000000f);
-  return;
-}
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_Called_By_EntryPoint_With_StorageBuffer) {
   ast::type::VoidType void_type;
   ast::type::F32Type f32;
@@ -1506,184 +803,6 @@
 )");
 }
 
-TEST_F(MslGeneratorImplTest, Emit_Function_Called_Two_EntryPoints_WithGlobals) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-
-  auto foo_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("foo", ast::StorageClass::kInput, &f32));
-
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(0));
-  foo_var->set_decorations(std::move(decos));
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32));
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(foo_var.get());
-  td.RegisterVariableForTesting(bar_var.get());
-
-  mod.AddGlobalVariable(std::move(foo_var));
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::IdentifierExpression>("foo")));
-  sub_func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::CallExpression>(
-          std::make_unique<ast::IdentifierExpression>("sub_func"),
-          ast::ExpressionList{})));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  auto ep2 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_2", "frag_1_main");
-  mod.AddEntryPoint(std::move(ep1));
-  mod.AddEntryPoint(std::move(ep2));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-struct ep_1_in {
-  float foo [[user(locn0)]];
-};
-
-struct ep_1_out {
-  float bar [[color(1)]];
-};
-
-struct ep_2_in {
-  float foo [[user(locn0)]];
-};
-
-struct ep_2_out {
-  float bar [[color(1)]];
-};
-
-float sub_func_ep_1(thread ep_1_in& tint_in, thread ep_1_out& tint_out) {
-  tint_out.bar = tint_in.foo;
-  return tint_in.foo;
-}
-
-float sub_func_ep_2(thread ep_2_in& tint_in, thread ep_2_out& tint_out) {
-  tint_out.bar = tint_in.foo;
-  return tint_in.foo;
-}
-
-fragment ep_1_out ep_1(ep_1_in tint_in [[stage_in]]) {
-  ep_1_out tint_out = {};
-  tint_out.bar = sub_func_ep_1(tint_in, tint_out);
-  return tint_out;
-}
-
-fragment ep_2_out ep_2(ep_2_in tint_in [[stage_in]]) {
-  ep_2_out tint_out = {};
-  tint_out.bar = sub_func_ep_2(tint_in, tint_out);
-  return tint_out;
-}
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest,
-       Emit_Function_EntryPoints_WithGlobal_Nested_Return) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-  ast::type::I32Type i32;
-
-  auto bar_var = std::make_unique<ast::DecoratedVariable>(
-      std::make_unique<ast::Variable>("bar", ast::StorageClass::kOutput, &f32));
-  ast::VariableDecorationList decos;
-  decos.push_back(std::make_unique<ast::LocationDecoration>(1));
-  bar_var->set_decorations(std::move(decos));
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(bar_var.get());
-  mod.AddGlobalVariable(std::move(bar_var));
-
-  ast::VariableList params;
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("bar"),
-      std::make_unique<ast::ScalarConstructorExpression>(
-          std::make_unique<ast::FloatLiteral>(&f32, 1.0f))));
-
-  auto list = std::make_unique<ast::BlockStatement>();
-  list->append(std::make_unique<ast::ReturnStatement>());
-
-  body->append(std::make_unique<ast::IfStatement>(
-      std::make_unique<ast::BinaryExpression>(
-          ast::BinaryOp::kEqual,
-          std::make_unique<ast::ScalarConstructorExpression>(
-              std::make_unique<ast::SintLiteral>(&i32, 1)),
-          std::make_unique<ast::ScalarConstructorExpression>(
-              std::make_unique<ast::SintLiteral>(&i32, 1))),
-      std::move(list)));
-
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  mod.AddEntryPoint(std::move(ep1));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-struct ep_1_out {
-  float bar [[color(1)]];
-};
-
-fragment ep_1_out ep_1() {
-  ep_1_out tint_out = {};
-  tint_out.bar = 1.00000000f;
-  if ((1 == 1)) {
-    return tint_out;
-  }
-  return tint_out;
-}
-
-)");
-}
-
 TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_EntryPoints_WithGlobal_Nested_Return) {
   ast::type::VoidType void_type;
@@ -1754,117 +873,6 @@
 }
 
 TEST_F(MslGeneratorImplTest,
-       Emit_Function_Called_Two_EntryPoints_WithoutGlobals) {
-  ast::type::VoidType void_type;
-  ast::type::F32Type f32;
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-
-  ast::VariableList params;
-  auto sub_func =
-      std::make_unique<ast::Function>("sub_func", std::move(params), &f32);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::ReturnStatement>(
-      std::make_unique<ast::ScalarConstructorExpression>(
-          std::make_unique<ast::FloatLiteral>(&f32, 1.0))));
-  sub_func->set_body(std::move(body));
-
-  mod.AddFunction(std::move(sub_func));
-
-  auto func_1 = std::make_unique<ast::Function>("frag_1_main",
-                                                std::move(params), &void_type);
-
-  auto var = std::make_unique<ast::Variable>(
-      "foo", ast::StorageClass::kFunction, &f32);
-  var->set_constructor(std::make_unique<ast::CallExpression>(
-      std::make_unique<ast::IdentifierExpression>("sub_func"),
-      ast::ExpressionList{}));
-
-  body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::VariableDeclStatement>(std::move(var)));
-  body->append(std::make_unique<ast::ReturnStatement>());
-  func_1->set_body(std::move(body));
-
-  mod.AddFunction(std::move(func_1));
-
-  auto ep1 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_1", "frag_1_main");
-  auto ep2 = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kFragment,
-                                               "ep_2", "frag_1_main");
-  mod.AddEntryPoint(std::move(ep1));
-  mod.AddEntryPoint(std::move(ep2));
-
-  ASSERT_TRUE(td.Determine()) << td.error();
-
-  GeneratorImpl g(&mod);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-float sub_func() {
-  return 1.00000000f;
-}
-
-fragment void ep_1() {
-  float foo = sub_func();
-  return;
-}
-
-fragment void ep_2() {
-  float foo = sub_func();
-  return;
-}
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_WithName) {
-  ast::type::VoidType void_type;
-
-  auto func = std::make_unique<ast::Function>("comp_main", ast::VariableList{},
-                                              &void_type);
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
-                                              "my_main", "comp_main");
-
-  ast::Module m;
-  m.AddFunction(std::move(func));
-  m.AddEntryPoint(std::move(ep));
-
-  GeneratorImpl g(&m);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-kernel void my_main() {
-}
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest, Emit_Function_EntryPoint_WithNameCollision) {
-  ast::type::VoidType void_type;
-
-  auto func = std::make_unique<ast::Function>("comp_main", ast::VariableList{},
-                                              &void_type);
-  auto ep = std::make_unique<ast::EntryPoint>(ast::PipelineStage::kCompute,
-                                              "main", "comp_main");
-
-  ast::Module m;
-  m.AddFunction(std::move(func));
-  m.AddEntryPoint(std::move(ep));
-
-  GeneratorImpl g(&m);
-  ASSERT_TRUE(g.Generate()) << g.error();
-  EXPECT_EQ(g.result(), R"(#include <metal_stdlib>
-
-kernel void main_tint_0() {
-}
-
-)");
-}
-
-TEST_F(MslGeneratorImplTest,
        Emit_FunctionDecoration_EntryPoint_WithNameCollision) {
   ast::type::VoidType void_type;
 
diff --git a/src/writer/msl/generator_impl_test.cc b/src/writer/msl/generator_impl_test.cc
index af5600e..9f29067 100644
--- a/src/writer/msl/generator_impl_test.cc
+++ b/src/writer/msl/generator_impl_test.cc
@@ -17,11 +17,11 @@
 #include <memory>
 
 #include "gtest/gtest.h"
-#include "src/ast/entry_point.h"
 #include "src/ast/function.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/module.h"
 #include "src/ast/pipeline_stage.h"
+#include "src/ast/stage_decoration.h"
 #include "src/ast/struct.h"
 #include "src/ast/struct_member.h"
 #include "src/ast/struct_member_offset_decoration.h"
@@ -48,10 +48,12 @@
 TEST_F(MslGeneratorImplTest, Generate) {
   ast::type::VoidType void_type;
   ast::Module m;
-  m.AddFunction(std::make_unique<ast::Function>("my_func", ast::VariableList{},
-                                                &void_type));
-  m.AddEntryPoint(std::make_unique<ast::EntryPoint>(
-      ast::PipelineStage::kCompute, "", "my_func"));
+
+  auto func = std::make_unique<ast::Function>("my_func", ast::VariableList{},
+                                              &void_type);
+  func->add_decoration(
+      std::make_unique<ast::StageDecoration>(ast::PipelineStage::kCompute));
+  m.AddFunction(std::move(func));
 
   GeneratorImpl g(&m);
 
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index a952234..8bfd9a1 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -185,19 +185,6 @@
     }
   }
 
-  // Note, the entry points must be generated after the functions as they need
-  // to be able to lookup the function information based on the name.
-  for (const auto& ep : mod_->entry_points()) {
-    if (!GenerateEntryPoint(ep.get())) {
-      return false;
-    }
-  }
-  for (const auto& ep : mod_->entry_points()) {
-    if (!GenerateExecutionModes(ep.get())) {
-      return false;
-    }
-  }
-
   return true;
 }
 
@@ -301,52 +288,6 @@
   return true;
 }
 
-bool Builder::GenerateEntryPoint(ast::EntryPoint* ep) {
-  auto name = ep->name();
-  if (name.empty()) {
-    name = ep->function_name();
-  }
-  const auto id = id_for_entry_point(ep);
-  if (id == 0) {
-    return false;
-  }
-
-  auto stage = pipeline_stage_to_execution_model(ep->stage());
-  if (stage == SpvExecutionModelMax) {
-    error_ = "Unknown pipeline stage provided";
-    return false;
-  }
-
-  OperandList operands = {Operand::Int(stage), Operand::Int(id),
-                          Operand::String(name)};
-
-  auto* func = func_name_to_func_[ep->function_name()];
-  if (func == nullptr) {
-    error_ = "processing an entry point when the function has not been seen.";
-    return false;
-  }
-
-  for (const auto* var : func->referenced_module_variables()) {
-    // For SPIR-V 1.3 we only output Input/output variables. If we update to
-    // SPIR-V 1.4 or later this should be all variables.
-    if (var->storage_class() != ast::StorageClass::kInput &&
-        var->storage_class() != ast::StorageClass::kOutput) {
-      continue;
-    }
-
-    uint32_t var_id;
-    if (!scope_stack_.get(var->name(), &var_id)) {
-      error_ = "unable to find ID for global variable: " + var->name();
-      return false;
-    }
-
-    operands.push_back(Operand::Int(var_id));
-  }
-  push_preamble(spv::Op::OpEntryPoint, operands);
-
-  return true;
-}
-
 bool Builder::GenerateEntryPoint(ast::Function* func, uint32_t id) {
   auto stage = pipeline_stage_to_execution_model(func->pipeline_stage());
   if (stage == SpvExecutionModelMax) {
@@ -378,32 +319,6 @@
   return true;
 }
 
-bool Builder::GenerateExecutionModes(ast::EntryPoint* ep) {
-  const auto id = id_for_entry_point(ep);
-  if (id == 0) {
-    return false;
-  }
-
-  // WGSL fragment shader origin is upper left
-  if (ep->stage() == ast::PipelineStage::kFragment) {
-    push_preamble(
-        spv::Op::OpExecutionMode,
-        {Operand::Int(id), Operand::Int(SpvExecutionModeOriginUpperLeft)});
-  } else if (ep->stage() == ast::PipelineStage::kCompute) {
-    auto* func = func_name_to_func_[ep->function_name()];
-
-    uint32_t x = 0;
-    uint32_t y = 0;
-    uint32_t z = 0;
-    std::tie(x, y, z) = func->workgroup_size();
-    push_preamble(spv::Op::OpExecutionMode,
-                  {Operand::Int(id), Operand::Int(SpvExecutionModeLocalSize),
-                   Operand::Int(x), Operand::Int(y), Operand::Int(z)});
-  }
-
-  return true;
-}
-
 bool Builder::GenerateExecutionModes(ast::Function* func, uint32_t id) {
   // WGSL fragment shader origin is upper left
   if (func->pipeline_stage() == ast::PipelineStage::kFragment) {
diff --git a/src/writer/spirv/builder.h b/src/writer/spirv/builder.h
index 42d762e..2a467fc 100644
--- a/src/writer/spirv/builder.h
+++ b/src/writer/spirv/builder.h
@@ -185,19 +185,11 @@
   /// @returns true if the statement was successfully generated
   bool GenerateDiscardStatement(ast::DiscardStatement* stmt);
   /// Generates an entry point instruction
-  /// @param ep the entry point
-  /// @returns true if the instruction was generated, false otherwise
-  bool GenerateEntryPoint(ast::EntryPoint* ep);
-  /// Generates an entry point instruction
   /// @param func the function
   /// @param id the id of the function
   /// @returns true if the instruction was generated, false otherwise
   bool GenerateEntryPoint(ast::Function* func, uint32_t id);
   /// Generates execution modes for an entry point
-  /// @param ep the entry point
-  /// @returns false on failure
-  bool GenerateExecutionModes(ast::EntryPoint* ep);
-  /// Generates execution modes for an entry point
   /// @param func the function
   /// @param id the id of the function
   /// @returns false on failure
@@ -424,19 +416,6 @@
     return func_name_to_id_[name];
   }
 
-  /// Retrieves the id for an entry point function, or 0 if not found.
-  /// Emits an error if not found.
-  /// @param ep the entry point
-  /// @returns 0 on error
-  uint32_t id_for_entry_point(ast::EntryPoint* ep) {
-    auto id = id_for_func_name(ep->function_name());
-    if (id == 0) {
-      error_ = "unable to find ID for function: " + ep->function_name();
-      return 0;
-    }
-    return id;
-  }
-
   ast::Module* mod_;
   std::string error_;
   uint32_t next_id_ = 1;
diff --git a/src/writer/spirv/builder_entry_point_test.cc b/src/writer/spirv/builder_entry_point_test.cc
deleted file mode 100644
index c594edb..0000000
--- a/src/writer/spirv/builder_entry_point_test.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <string>
-
-#include "gtest/gtest.h"
-#include "spirv/unified1/spirv.h"
-#include "spirv/unified1/spirv.hpp11"
-#include "src/ast/assignment_statement.h"
-#include "src/ast/entry_point.h"
-#include "src/ast/function.h"
-#include "src/ast/identifier_expression.h"
-#include "src/ast/pipeline_stage.h"
-#include "src/ast/type/f32_type.h"
-#include "src/ast/type/void_type.h"
-#include "src/ast/variable.h"
-#include "src/ast/workgroup_decoration.h"
-#include "src/context.h"
-#include "src/type_determiner.h"
-#include "src/writer/spirv/builder.h"
-#include "src/writer/spirv/spv_dump.h"
-
-namespace tint {
-namespace writer {
-namespace spirv {
-namespace {
-
-// TODO(dsinclair): These have all been ported to stage decorations and this
-// whole file can be deleted when we remove EntryPoint.
-
-using BuilderTest = testing::Test;
-
-TEST_F(BuilderTest, EntryPoint) {
-  ast::type::VoidType void_type;
-
-  ast::Function func("frag_main", {}, &void_type);
-  ast::EntryPoint ep(ast::PipelineStage::kFragment, "main", "frag_main");
-
-  ast::Module mod;
-  Builder b(&mod);
-  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
-  ASSERT_TRUE(b.GenerateEntryPoint(&ep)) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.preamble()), R"(OpEntryPoint Fragment %3 "main"
-)");
-}
-
-TEST_F(BuilderTest, EntryPoint_WithoutName) {
-  ast::type::VoidType void_type;
-
-  ast::Function func("compute_main", {}, &void_type);
-  ast::EntryPoint ep(ast::PipelineStage::kCompute, "", "compute_main");
-
-  ast::Module mod;
-  Builder b(&mod);
-  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
-  ASSERT_TRUE(b.GenerateEntryPoint(&ep)) << b.error();
-
-  EXPECT_EQ(DumpInstructions(b.preamble()),
-            R"(OpEntryPoint GLCompute %3 "compute_main"
-)");
-}
-
-TEST_F(BuilderTest, EntryPoint_BadFunction) {
-  ast::EntryPoint ep(ast::PipelineStage::kFragment, "main", "frag_main");
-
-  ast::Module mod;
-  Builder b(&mod);
-  EXPECT_FALSE(b.GenerateEntryPoint(&ep));
-  EXPECT_EQ(b.error(), "unable to find ID for function: frag_main");
-}
-
-struct EntryPointStageData {
-  ast::PipelineStage stage;
-  SpvExecutionModel model;
-};
-inline std::ostream& operator<<(std::ostream& out, EntryPointStageData data) {
-  out << data.stage;
-  return out;
-}
-using EntryPointStageTest = testing::TestWithParam<EntryPointStageData>;
-TEST_P(EntryPointStageTest, Emit) {
-  auto params = GetParam();
-
-  ast::type::VoidType void_type;
-
-  ast::Function func("main", {}, &void_type);
-  ast::EntryPoint ep(params.stage, "", "main");
-
-  ast::Module mod;
-  Builder b(&mod);
-  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
-  ASSERT_TRUE(b.GenerateEntryPoint(&ep)) << b.error();
-
-  auto preamble = b.preamble();
-  ASSERT_EQ(preamble.size(), 1u);
-  EXPECT_EQ(preamble[0].opcode(), spv::Op::OpEntryPoint);
-
-  ASSERT_GE(preamble[0].operands().size(), 3u);
-  EXPECT_EQ(preamble[0].operands()[0].to_i(), params.model);
-}
-INSTANTIATE_TEST_SUITE_P(
-    BuilderTest,
-    EntryPointStageTest,
-    testing::Values(EntryPointStageData{ast::PipelineStage::kVertex,
-                                        SpvExecutionModelVertex},
-                    EntryPointStageData{ast::PipelineStage::kFragment,
-                                        SpvExecutionModelFragment},
-                    EntryPointStageData{ast::PipelineStage::kCompute,
-                                        SpvExecutionModelGLCompute}));
-
-TEST_F(BuilderTest, EntryPoint_WithUnusedInterfaceIds) {
-  ast::type::F32Type f32;
-  ast::type::VoidType void_type;
-
-  ast::Function func("main", {}, &void_type);
-
-  auto v_in =
-      std::make_unique<ast::Variable>("my_in", ast::StorageClass::kInput, &f32);
-  auto v_out = std::make_unique<ast::Variable>(
-      "my_out", ast::StorageClass::kOutput, &f32);
-  auto v_wg = std::make_unique<ast::Variable>(
-      "my_wg", ast::StorageClass::kWorkgroup, &f32);
-  ast::EntryPoint ep(ast::PipelineStage::kVertex, "", "main");
-
-  ast::Module mod;
-  Builder b(&mod);
-  EXPECT_TRUE(b.GenerateGlobalVariable(v_in.get())) << b.error();
-  EXPECT_TRUE(b.GenerateGlobalVariable(v_out.get())) << b.error();
-  EXPECT_TRUE(b.GenerateGlobalVariable(v_wg.get())) << b.error();
-
-  mod.AddGlobalVariable(std::move(v_in));
-  mod.AddGlobalVariable(std::move(v_out));
-  mod.AddGlobalVariable(std::move(v_wg));
-
-  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
-  ASSERT_TRUE(b.GenerateEntryPoint(&ep)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "my_in"
-OpName %4 "my_out"
-OpName %7 "my_wg"
-OpName %11 "main"
-)");
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
-%2 = OpTypePointer Input %3
-%1 = OpVariable %2 Input
-%5 = OpTypePointer Output %3
-%6 = OpConstantNull %3
-%4 = OpVariable %5 Output %6
-%8 = OpTypePointer Workgroup %3
-%7 = OpVariable %8 Workgroup
-%10 = OpTypeVoid
-%9 = OpTypeFunction %10
-)");
-  EXPECT_EQ(DumpInstructions(b.preamble()),
-            R"(OpEntryPoint Vertex %11 "main"
-)");
-}
-
-TEST_F(BuilderTest, EntryPoint_WithUsedInterfaceIds) {
-  ast::type::F32Type f32;
-  ast::type::VoidType void_type;
-
-  ast::Function func("main", {}, &void_type);
-
-  auto body = std::make_unique<ast::BlockStatement>();
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("my_out"),
-      std::make_unique<ast::IdentifierExpression>("my_in")));
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("my_wg"),
-      std::make_unique<ast::IdentifierExpression>("my_wg")));
-  // Add duplicate usages so we show they don't get output multiple times.
-  body->append(std::make_unique<ast::AssignmentStatement>(
-      std::make_unique<ast::IdentifierExpression>("my_out"),
-      std::make_unique<ast::IdentifierExpression>("my_in")));
-  func.set_body(std::move(body));
-
-  auto v_in =
-      std::make_unique<ast::Variable>("my_in", ast::StorageClass::kInput, &f32);
-  auto v_out = std::make_unique<ast::Variable>(
-      "my_out", ast::StorageClass::kOutput, &f32);
-  auto v_wg = std::make_unique<ast::Variable>(
-      "my_wg", ast::StorageClass::kWorkgroup, &f32);
-  ast::EntryPoint ep(ast::PipelineStage::kVertex, "", "main");
-
-  Context ctx;
-  ast::Module mod;
-  TypeDeterminer td(&ctx, &mod);
-  td.RegisterVariableForTesting(v_in.get());
-  td.RegisterVariableForTesting(v_out.get());
-  td.RegisterVariableForTesting(v_wg.get());
-
-  ASSERT_TRUE(td.DetermineFunction(&func)) << td.error();
-
-  Builder b(&mod);
-
-  EXPECT_TRUE(b.GenerateGlobalVariable(v_in.get())) << b.error();
-  EXPECT_TRUE(b.GenerateGlobalVariable(v_out.get())) << b.error();
-  EXPECT_TRUE(b.GenerateGlobalVariable(v_wg.get())) << b.error();
-
-  mod.AddGlobalVariable(std::move(v_in));
-  mod.AddGlobalVariable(std::move(v_out));
-  mod.AddGlobalVariable(std::move(v_wg));
-
-  ASSERT_TRUE(b.GenerateFunction(&func)) << b.error();
-  ASSERT_TRUE(b.GenerateEntryPoint(&ep)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.debug()), R"(OpName %1 "my_in"
-OpName %4 "my_out"
-OpName %7 "my_wg"
-OpName %11 "main"
-)");
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
-%2 = OpTypePointer Input %3
-%1 = OpVariable %2 Input
-%5 = OpTypePointer Output %3
-%6 = OpConstantNull %3
-%4 = OpVariable %5 Output %6
-%8 = OpTypePointer Workgroup %3
-%7 = OpVariable %8 Workgroup
-%10 = OpTypeVoid
-%9 = OpTypeFunction %10
-)");
-  EXPECT_EQ(DumpInstructions(b.preamble()),
-            R"(OpEntryPoint Vertex %11 "main" %4 %1
-)");
-}
-
-}  // namespace
-}  // namespace spirv
-}  // namespace writer
-}  // namespace tint
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index 8b9e63c..da4aed2 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -84,14 +84,6 @@
     out_ << std::endl;
   }
 
-  for (const auto& ep : module.entry_points()) {
-    if (!EmitEntryPoint(ep.get())) {
-      return false;
-    }
-  }
-  if (!module.entry_points().empty())
-    out_ << std::endl;
-
   for (auto* const alias : module.alias_types()) {
     if (!EmitAliasType(alias)) {
       return false;
@@ -132,27 +124,8 @@
     out_ << std::endl;
   }
 
-  bool found_entry_point = false;
-  std::string ep_function_name = "";
-  for (const auto& ep : module.entry_points()) {
-    std::string ep_name = ep->name();
-    if (ep_name.empty()) {
-      ep_name = ep->function_name();
-    }
-    ep_function_name = ep->function_name();
-
-    if (ep->stage() != stage || ep_name != name) {
-      continue;
-    }
-    if (!EmitEntryPoint(ep.get())) {
-      return false;
-    }
-    found_entry_point = true;
-    break;
-  }
-  out_ << std::endl;
-
-  if (!found_entry_point) {
+  auto* func = module.FindFunctionByNameAndStage(name, stage);
+  if (func == nullptr) {
     error_ = "Unable to find requested entry point: " + name;
     return false;
   }
@@ -178,12 +151,6 @@
     }
   }
 
-  auto* func = module.FindFunctionByName(ep_function_name);
-  if (!func) {
-    error_ = "Unable to find entry point function: " + ep_function_name;
-    return false;
-  }
-
   bool found_func_variable = false;
   for (auto* var : func->referenced_module_variables()) {
     if (!EmitVariable(var)) {
@@ -225,17 +192,6 @@
   return true;
 }
 
-bool GeneratorImpl::EmitEntryPoint(const ast::EntryPoint* ep) {
-  make_indent();
-  out_ << "entry_point " << ep->stage() << " ";
-  if (!ep->name().empty() && ep->name() != ep->function_name()) {
-    out_ << R"(as ")" << ep->name() << R"(" )";
-  }
-  out_ << "= " << ep->function_name() << ";" << std::endl;
-
-  return true;
-}
-
 bool GeneratorImpl::EmitExpression(ast::Expression* expr) {
   if (expr->IsArrayAccessor()) {
     return EmitArrayAccessor(expr->AsArrayAccessor());
diff --git a/src/writer/wgsl/generator_impl.h b/src/writer/wgsl/generator_impl.h
index e01b66b..1ae3853 100644
--- a/src/writer/wgsl/generator_impl.h
+++ b/src/writer/wgsl/generator_impl.h
@@ -20,7 +20,6 @@
 
 #include "src/ast/array_accessor_expression.h"
 #include "src/ast/constructor_expression.h"
-#include "src/ast/entry_point.h"
 #include "src/ast/identifier_expression.h"
 #include "src/ast/import.h"
 #include "src/ast/module.h"
@@ -116,10 +115,6 @@
   /// @param stmt the statement to emit
   /// @returns true if the statement was emitted
   bool EmitElse(ast::ElseStatement* stmt);
-  /// Handles generating an entry_point command
-  /// @param ep the entry point
-  /// @returns true if the entry point was emitted
-  bool EmitEntryPoint(const ast::EntryPoint* ep);
   /// Handles generate an Expression
   /// @param expr the expression
   /// @returns true if the expression was emitted
diff --git a/src/writer/wgsl/generator_impl_entry_point_test.cc b/src/writer/wgsl/generator_impl_entry_point_test.cc
deleted file mode 100644
index bab9e91..0000000
--- a/src/writer/wgsl/generator_impl_entry_point_test.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2020 The Tint Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "gtest/gtest.h"
-#include "src/writer/wgsl/generator_impl.h"
-
-namespace tint {
-namespace writer {
-namespace wgsl {
-namespace {
-
-using WgslGeneratorImplTest = testing::Test;
-
-TEST_F(WgslGeneratorImplTest, EmitEntryPoint_NoName) {
-  ast::EntryPoint ep(ast::PipelineStage::kFragment, "", "frag_main");
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitEntryPoint(&ep)) << g.error();
-  EXPECT_EQ(g.result(), R"(entry_point fragment = frag_main;
-)");
-}
-
-TEST_F(WgslGeneratorImplTest, EmitEntryPoint_WithName) {
-  ast::EntryPoint ep(ast::PipelineStage::kFragment, "main", "frag_main");
-
-  GeneratorImpl g;
-  ASSERT_TRUE(g.EmitEntryPoint(&ep)) << g.error();
-  EXPECT_EQ(g.result(), R"(entry_point fragment as "main" = frag_main;
-)");
-}
-
-}  // namespace
-}  // namespace wgsl
-}  // namespace writer
-}  // namespace tint
diff --git a/test/compute_boids.wgsl b/test/compute_boids.wgsl
index ba4d2de..16642d2 100644
--- a/test/compute_boids.wgsl
+++ b/test/compute_boids.wgsl
@@ -21,7 +21,8 @@
 [[location(2)]] var<in> a_pos : vec2<f32>;
 [[builtin(position)]] var<out> gl_Position : vec4<f32>;
 
-fn vtx_main() -> void {
+[[stage(vertex)]]
+fn vert_main() -> void {
   var angle : f32 = -std::atan2(a_particleVel.x, a_particleVel.y);
   var pos : vec2<f32> = vec2<f32>(
       (a_pos.x * std::cos(angle)) - (a_pos.y * std::sin(angle)),
@@ -29,16 +30,15 @@
   gl_Position = vec4<f32>(pos + a_particlePos, 0.0, 1.0);
   return;
 }
-entry_point vertex as "vert_main" = vtx_main;
 
 # fragment shader
 [[location(0)]] var<out> fragColor : vec4<f32>;
 
+[[stage(fragment)]]
 fn frag_main() -> void {
   fragColor = vec4<f32>(1.0, 1.0, 1.0, 1.0);
   return;
 }
-entry_point fragment as "frag_main" = frag_main;
 
 # compute shader
 type Particle = [[block]] struct {
@@ -67,7 +67,8 @@
 [[builtin(global_invocation_id)]] var<in> gl_GlobalInvocationID : vec3<u32>;
 
 # https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp
-fn compute_main() -> void {
+[[stage(compute)]]
+fn comp_main() -> void {
   var index : u32 = gl_GlobalInvocationID.x;
   if (index >= 5) {
     return;
@@ -149,5 +150,3 @@
 
   return;
 }
-entry_point compute as "comp_main" = compute_main;
-
diff --git a/test/cube.wgsl b/test/cube.wgsl
index 6dd5c0a..1a86a89 100644
--- a/test/cube.wgsl
+++ b/test/cube.wgsl
@@ -12,9 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-entry_point vertex = vtx_main;
-entry_point fragment = frag_main;
-
 # Vertex shader
 type Uniforms = [[block]] struct {
   [[offset(0)]] modelViewProjectionMatrix : mat4x4<f32>;
@@ -27,6 +24,7 @@
 [[location(0)]] var<out> vtxFragColor : vec4<f32>;
 [[builtin(position)]] var<out> Position : vec4<f32>;
 
+[[stage(vertex)]]
 fn vtx_main() -> void {
    Position = uniforms.modelViewProjectionMatrix * cur_position;
    vtxFragColor = color;
@@ -37,6 +35,7 @@
 [[location(0)]] var<in> fragColor : vec4<f32>;
 [[location(0)]] var<out> outColor : vec4<f32>;
 
+[[stage(fragment)]]
 fn frag_main() -> void {
   outColor = fragColor;
   return;
diff --git a/test/simple.wgsl b/test/simple.wgsl
index e8ec8c6..ff5f38e 100644
--- a/test/simple.wgsl
+++ b/test/simple.wgsl
@@ -18,10 +18,10 @@
   return;
 }
 
+[[stage(fragment)]]
 fn main() -> void {
     var a : vec2<f32> = vec2<f32>();
     gl_FragColor = vec4<f32>(0.4, 0.4, 0.8, 1.0);
     bar();
     return;
 }
-entry_point fragment = main;
diff --git a/test/switch-case-selector-must-have-the-same-type-as-the-selector-expression-2.fail.wgsl b/test/switch-case-selector-must-have-the-same-type-as-the-selector-expression-2.fail.wgsl
index 288dede..aeb7c4b 100644
--- a/test/switch-case-selector-must-have-the-same-type-as-the-selector-expression-2.fail.wgsl
+++ b/test/switch-case-selector-must-have-the-same-type-as-the-selector-expression-2.fail.wgsl
@@ -1,6 +1,6 @@
 # v-switch03: line 7: the case selector values must have the same type as the selector expression
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   var a: i32 = -2;
   switch (a) {
diff --git a/test/switch-case-selector-must-have-the-same-type-as-the-selector-expression.fail.wgsl b/test/switch-case-selector-must-have-the-same-type-as-the-selector-expression.fail.wgsl
index 3bee45b..a1abbfd 100644
--- a/test/switch-case-selector-must-have-the-same-type-as-the-selector-expression.fail.wgsl
+++ b/test/switch-case-selector-must-have-the-same-type-as-the-selector-expression.fail.wgsl
@@ -1,6 +1,6 @@
 # v-switch03: line 7: the case selector values must have the same type as the selector expression
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   var a: u32 = 2;
   switch (a) {
diff --git a/test/switch-case-selector-value-must-be-unique.fail.wgsl b/test/switch-case-selector-value-must-be-unique.fail.wgsl
index 36efdc2..a16e800 100644
--- a/test/switch-case-selector-value-must-be-unique.fail.wgsl
+++ b/test/switch-case-selector-value-must-be-unique.fail.wgsl
@@ -1,7 +1,7 @@
 # v-switch04: line 9: a literal value must not appear more than once in the case selectors for a
 # switch statement: '0'
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   var a: u32 = 2;
   switch (a) {
diff --git a/test/switch-fallthrough-must-not-be-last-stmt-of-last-clause.fail.wgsl b/test/switch-fallthrough-must-not-be-last-stmt-of-last-clause.fail.wgsl
index 99c1a53..0867b62 100644
--- a/test/switch-fallthrough-must-not-be-last-stmt-of-last-clause.fail.wgsl
+++ b/test/switch-fallthrough-must-not-be-last-stmt-of-last-clause.fail.wgsl
@@ -1,7 +1,7 @@
 # v-switch05: line 9: a fallthrough statement must not appear as the last statement in last clause
 # of a switch
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   var a: i32 = -2;
   switch (a) {
diff --git a/test/switch-must-have-exactly-one-default-clause-2.fail.wgsl b/test/switch-must-have-exactly-one-default-clause-2.fail.wgsl
index b3178e1..d6720be 100644
--- a/test/switch-must-have-exactly-one-default-clause-2.fail.wgsl
+++ b/test/switch-must-have-exactly-one-default-clause-2.fail.wgsl
@@ -1,6 +1,6 @@
 # v-0008: line 6: switch statement must have exactly one default clause
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   var a: i32 = 2;
   switch (a) {
diff --git a/test/switch-must-have-exactly-one-default-clause.fail.wgsl b/test/switch-must-have-exactly-one-default-clause.fail.wgsl
index b0cfd2d..3d0adf2 100644
--- a/test/switch-must-have-exactly-one-default-clause.fail.wgsl
+++ b/test/switch-must-have-exactly-one-default-clause.fail.wgsl
@@ -1,6 +1,6 @@
 # v-0008: line 6: switch statement must have exactly one default clause
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   var a: i32 = 2;
   switch (a) {
diff --git a/test/switch-selector-expression-must-be-scalar-integer-type.fail.wgsl b/test/switch-selector-expression-must-be-scalar-integer-type.fail.wgsl
index da49514..2fc35a2 100644
--- a/test/switch-selector-expression-must-be-scalar-integer-type.fail.wgsl
+++ b/test/switch-selector-expression-must-be-scalar-integer-type.fail.wgsl
@@ -1,6 +1,6 @@
 # v-switch01: line 6: switch statement selector expression must be of a scalar integer type
 
-entry_point vertex = main;
+[[stage(vertex)]]
 fn main() -> void {
   var a: f32 = 3.14;
   switch (a) {
diff --git a/test/triangle.wgsl b/test/triangle.wgsl
index 44b0425..0159eb6 100644
--- a/test/triangle.wgsl
+++ b/test/triangle.wgsl
@@ -21,16 +21,17 @@
 [[builtin(position)]] var<out> Position : vec4<f32>;
 [[builtin(vertex_idx)]] var<in> VertexIndex : i32;
 
+[[stage(vertex)]]
 fn vtx_main() -> void {
   Position = vec4<f32>(pos[VertexIndex], 0.0, 1.0);
   return;
 }
-entry_point vertex as "main" = vtx_main;
 
 # Fragment shader
 [[location(0)]] var<out> outColor : vec4<f32>;
+
+[[stage(fragment)]]
 fn frag_main() -> void {
   outColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
   return;
 }
-entry_point fragment = frag_main;
diff --git a/test/undefined-variable-global-scope.wgsl b/test/undefined-variable-global-scope.wgsl
index 5fa9fb2..590ec9a 100644
--- a/test/undefined-variable-global-scope.wgsl
+++ b/test/undefined-variable-global-scope.wgsl
@@ -15,9 +15,9 @@
 # Vertex shader
 var<private> a : i32;
 
+[[stage(vertex)]]
 fn main() -> void {
   a = 2;
   return;
 }
-entry_point vertex as "main" = main;
 
diff --git a/test/undefined-variable-inner-block.wgsl b/test/undefined-variable-inner-block.wgsl
index 06fc420..0e7f509 100644
--- a/test/undefined-variable-inner-block.wgsl
+++ b/test/undefined-variable-inner-block.wgsl
@@ -12,9 +12,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+[[stage(fragment)]]
 fn main() -> void {
     var a : f32 = 2.0;
    { a = 3.14;}
     return;
 }
-entry_point fragment = main;