writer/spirv: Fix trailing emission of OpReturn

writer::spirv::Function attempted to append a trailing OpReturn if the
function does not end with a terminating instruction. This wasn't
considering functions that have a non-void return type.

This has now been moved to spirv::Builder::GenerateFunction(),
where we can actually examine the function return type, and generate a
zero-expression to return if we need to.

Note: this was masked by WGSL validation that required all functions to
end with a return statement.

Bug: tint:1302
Change-Id: Iddfeda25a956622c318b8235dc6fc093a2a5c26d
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/71604
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 1bbb2b6..ea82524 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -653,6 +653,15 @@
     }
   }
 
+  if (!LastIsTerminator(func_ast->body)) {
+    if (func->ReturnType()->Is<sem::Void>()) {
+      push_function_inst(spv::Op::OpReturn, {});
+    } else {
+      auto zero = GenerateConstantNullIfNeeded(func->ReturnType());
+      push_function_inst(spv::Op::OpReturnValue, {Operand::Int(zero)});
+    }
+  }
+
   if (func_ast->IsEntryPoint()) {
     if (!GenerateEntryPoint(func_ast, func_id)) {
       return false;
diff --git a/src/writer/spirv/builder_accessor_expression_test.cc b/src/writer/spirv/builder_accessor_expression_test.cc
index 2e154fb..75f90e0 100644
--- a/src/writer/spirv/builder_accessor_expression_test.cc
+++ b/src/writer/spirv/builder_accessor_expression_test.cc
@@ -787,6 +787,7 @@
   EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
             R"(%18 = OpCompositeExtract %6 %16 1
+OpReturn
 )");
 
   Validate(b);
@@ -832,6 +833,7 @@
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
             R"(%18 = OpCompositeExtract %6 %16 2
 %20 = OpCompositeExtract %7 %18 1
+OpReturn
 )");
 
   Validate(b);
@@ -931,6 +933,7 @@
   EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), "");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
             R"(%15 = OpCompositeExtract %6 %12 2
+OpReturn
 )");
 
   Validate(b);
@@ -979,6 +982,7 @@
 %20 = OpLoad %18 %16
 %22 = OpAccessChain %21 %13 %20
 %23 = OpLoad %6 %22
+OpReturn
 )");
 
   Validate(b);
@@ -1031,6 +1035,7 @@
 %22 = OpLoad %20 %18
 %24 = OpAccessChain %23 %15 %22
 %25 = OpLoad %6 %24
+OpReturn
 )");
 
   Validate(b);
diff --git a/src/writer/spirv/builder_call_test.cc b/src/writer/spirv/builder_call_test.cc
index 24035d9..ba9d852 100644
--- a/src/writer/spirv/builder_call_test.cc
+++ b/src/writer/spirv/builder_call_test.cc
@@ -29,23 +29,15 @@
   func_params.push_back(Param("a", ty.f32()));
   func_params.push_back(Param("b", ty.f32()));
 
-  auto* a_func =
-      Func("a_func", func_params, ty.f32(),
-           ast::StatementList{Return(Add("a", "b"))}, ast::DecorationList{});
-
+  auto* a_func = Func("a_func", func_params, ty.f32(), {Return(Add("a", "b"))});
   auto* func =
-      Func("main", {}, ty.void_(), ast::StatementList{}, ast::DecorationList{});
-
-  auto* expr = Call("a_func", 1.f, 1.f);
-
-  WrapInFunction(expr);
+      Func("main", {}, ty.void_(), {Assign(Phony(), Call("a_func", 1.f, 1.f))});
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(a_func)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 12u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(OpName %3 "a_func"
 OpName %4 "a"
 OpName %5 "b"
@@ -75,23 +67,16 @@
   func_params.push_back(Param("a", ty.f32()));
   func_params.push_back(Param("b", ty.f32()));
 
-  auto* a_func =
-      Func("a_func", func_params, ty.f32(),
-           ast::StatementList{Return(Add("a", "b"))}, ast::DecorationList{});
+  auto* a_func = Func("a_func", func_params, ty.f32(), {Return(Add("a", "b"))});
 
   auto* func =
-      Func("main", {}, ty.void_(), ast::StatementList{}, ast::DecorationList{});
-
-  auto* expr = CallStmt(Call("a_func", 1.f, 1.f));
-
-  WrapInFunction(expr);
+      Func("main", {}, ty.void_(), {CallStmt(Call("a_func", 1.f, 1.f))});
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(a_func)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_TRUE(b.GenerateStatement(expr)) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(OpName %3 "a_func"
 OpName %4 "a"
 OpName %5 "b"
diff --git a/src/writer/spirv/builder_if_test.cc b/src/writer/spirv/builder_if_test.cc
index 1206347..d47fe24 100644
--- a/src/writer/spirv/builder_if_test.cc
+++ b/src/writer/spirv/builder_if_test.cc
@@ -452,26 +452,24 @@
   // if (true) {
   //   return;
   // }
-  auto* if_body = Block(Return());
 
-  auto* expr =
-      create<ast::IfStatement>(Expr(true), if_body, ast::ElseStatementList{});
-  WrapInFunction(expr);
+  auto* fn = Func("f", {}, ty.void_(), {If(true, Block(Return()))});
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
-
-  EXPECT_TRUE(b.GenerateIfStatement(expr)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
-%2 = OpConstantTrue %1
+  EXPECT_TRUE(b.GenerateFunction(fn)) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
+%1 = OpTypeFunction %2
+%5 = OpTypeBool
+%6 = OpConstantTrue %5
 )");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(OpSelectionMerge %3 None
-OpBranchConditional %2 %4 %3
-%4 = OpLabel
+            R"(OpSelectionMerge %7 None
+OpBranchConditional %6 %8 %7
+%8 = OpLabel
 OpReturn
-%3 = OpLabel
+%7 = OpLabel
+OpReturn
 )");
 }
 
@@ -480,24 +478,28 @@
   //   return false;
   // }
   // return true;
-  auto* if_body = Block(Return(false));
-  auto* expr = If(Expr(true), if_body);
-  Func("test", {}, ty.bool_(), {expr, Return(true)}, {});
+
+  auto* fn = Func("f", {}, ty.bool_(),
+                  {
+                      If(true, Block(Return(false))),
+                      Return(true),
+                  });
+
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
-
-  EXPECT_TRUE(b.GenerateIfStatement(expr)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
-%2 = OpConstantTrue %1
-%5 = OpConstantFalse %1
+  EXPECT_TRUE(b.GenerateFunction(fn)) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeBool
+%1 = OpTypeFunction %2
+%5 = OpConstantTrue %2
+%8 = OpConstantFalse %2
 )");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(OpSelectionMerge %3 None
-OpBranchConditional %2 %4 %3
-%4 = OpLabel
+            R"(OpSelectionMerge %6 None
+OpBranchConditional %5 %7 %6
+%7 = OpLabel
+OpReturnValue %8
+%6 = OpLabel
 OpReturnValue %5
-%3 = OpLabel
 )");
 }
 
@@ -512,24 +514,28 @@
   //  }
   // }
   // return true;
-  auto* if_body = Block(Block(Block(Block(Return(false)))));
-  auto* expr = If(Expr(true), if_body);
-  Func("test", {}, ty.bool_(), {expr, Return(true)}, {});
+
+  auto* fn = Func("f", {}, ty.bool_(),
+                  {
+                      If(true, Block(Block(Block(Block(Return(false)))))),
+                      Return(true),
+                  });
+
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
-
-  EXPECT_TRUE(b.GenerateIfStatement(expr)) << b.error();
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeBool
-%2 = OpConstantTrue %1
-%5 = OpConstantFalse %1
+  EXPECT_TRUE(b.GenerateFunction(fn)) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeBool
+%1 = OpTypeFunction %2
+%5 = OpConstantTrue %2
+%8 = OpConstantFalse %2
 )");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(OpSelectionMerge %3 None
-OpBranchConditional %2 %4 %3
-%4 = OpLabel
+            R"(OpSelectionMerge %6 None
+OpBranchConditional %5 %7 %6
+%7 = OpLabel
+OpReturnValue %8
+%6 = OpLabel
 OpReturnValue %5
-%3 = OpLabel
 )");
 }
 
@@ -539,29 +545,27 @@
   // }
 
   auto* var = Global("a", ty.bool_(), ast::StorageClass::kPrivate);
-
-  auto* expr =
-      create<ast::IfStatement>(Expr("a"), Block(), ast::ElseStatementList{});
-  WrapInFunction(expr);
+  auto* fn = Func("f", {}, ty.void_(), {If("a", Block())});
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
-
-  EXPECT_TRUE(b.GenerateIfStatement(expr)) << b.error();
+  EXPECT_TRUE(b.GenerateFunction(fn)) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeBool
 %2 = OpTypePointer Private %3
 %4 = OpConstantNull %3
 %1 = OpVariable %2 Private %4
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
 )");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%5 = OpLoad %3 %1
-OpSelectionMerge %6 None
-OpBranchConditional %5 %7 %6
-%7 = OpLabel
-OpBranch %6
-%6 = OpLabel
+            R"(%9 = OpLoad %3 %1
+OpSelectionMerge %10 None
+OpBranchConditional %9 %11 %10
+%11 = OpLabel
+OpBranch %10
+%10 = OpLabel
+OpReturn
 )");
 }
 
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index f7c068d..490eb04 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -16,6 +16,7 @@
 #include "src/ast/stage_decoration.h"
 #include "src/ast/struct_block_decoration.h"
 #include "src/sem/depth_texture_type.h"
+#include "src/utils/string.h"
 #include "src/writer/spirv/spv_dump.h"
 #include "src/writer/spirv/test_helper.h"
 
@@ -41,53 +42,60 @@
 using IntrinsicBoolTest = IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(IntrinsicBoolTest, Call_Bool_Scalar) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.bool_(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeBool
 %2 = OpTypePointer Private %3
 %4 = OpConstantNull %3
 %1 = OpVariable %2 Private %4
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
 )");
 
   // both any and all are 'passthrough' for scalar booleans
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            "%6 = OpLoad %3 %1\n");
+            "%10 = OpLoad %3 %1\nOpReturn\n");
 }
 
 TEST_P(IntrinsicBoolTest, Call_Bool_Vector) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.vec3<bool>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeBool
 %3 = OpTypeVector %4 3
 %2 = OpTypePointer Private %3
 %5 = OpConstantNull %3
 %1 = OpVariable %2 Private %5
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%6 = )" + param.op +
-                " %4 %7\n");
+
+  auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1
+%10 = ${op} %4 %11
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 INSTANTIATE_TEST_SUITE_P(IntrinsicBuilderTest,
                          IntrinsicBoolTest,
@@ -97,56 +105,66 @@
 using IntrinsicFloatTest = IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(IntrinsicFloatTest, Call_Float_Scalar) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
 %2 = OpTypePointer Private %3
 %4 = OpConstantNull %3
 %1 = OpVariable %2 Private %4
-%6 = OpTypeBool
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+%10 = OpTypeBool
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%5 = )" + param.op +
-                " %6 %7\n");
+
+  auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1
+%9 = ${op} %10 %11
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 
 TEST_P(IntrinsicFloatTest, Call_Float_Vector) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
 %3 = OpTypeVector %4 3
 %2 = OpTypePointer Private %3
 %5 = OpConstantNull %3
 %1 = OpVariable %2 Private %5
-%8 = OpTypeBool
-%7 = OpTypeVector %8 3
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
+%12 = OpTypeBool
+%11 = OpTypeVector %12 3
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%6 = )" + param.op +
-                " %7 %9\n");
+
+  auto expected = utils::ReplaceAll(R"(%13 = OpLoad %3 %1
+%10 = ${op} %11 %13
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 INSTANTIATE_TEST_SUITE_P(IntrinsicBuilderTest,
                          IntrinsicFloatTest,
@@ -155,75 +173,81 @@
 
 TEST_F(IntrinsicBuilderTest, IsFinite_Scalar) {
   auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("isFinite", "v");
-  WrapInFunction(expr);
-
-  spirv::Builder& b = Build();
-
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
-
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
-%2 = OpTypePointer Private %3
-%4 = OpConstantNull %3
-%1 = OpVariable %2 Private %4
-%6 = OpTypeBool
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%8 = OpIsInf %6 %7
-%9 = OpIsNan %6 %7
-%10 = OpLogicalOr %6 %8 %9
-%5 = OpLogicalNot %6 %10
-)");
-}
-
-TEST_F(IntrinsicBuilderTest, IsFinite_Vector) {
-  auto* var = Global("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-
-  auto* expr = Call("isFinite", "v");
-  WrapInFunction(expr);
-
-  spirv::Builder& b = Build();
-
-  b.push_function(Function{});
-  ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
-
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
-  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
-%3 = OpTypeVector %4 3
-%2 = OpTypePointer Private %3
-%5 = OpConstantNull %3
-%1 = OpVariable %2 Private %5
-%8 = OpTypeBool
-%7 = OpTypeVector %8 3
-)");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%9 = OpLoad %3 %1
-%10 = OpIsInf %7 %9
-%11 = OpIsNan %7 %9
-%12 = OpLogicalOr %7 %10 %11
-%6 = OpLogicalNot %7 %12
-)");
-}
-
-TEST_F(IntrinsicBuilderTest, IsNormal_Scalar) {
-  auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate);
-
-  auto* expr = Call("isNormal", "v");
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 9u) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypePointer Private %3
+%4 = OpConstantNull %3
+%1 = OpVariable %2 Private %4
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
+%10 = OpTypeBool
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%11 = OpLoad %3 %1
+%12 = OpIsInf %10 %11
+%13 = OpIsNan %10 %11
+%14 = OpLogicalOr %10 %12 %13
+%9 = OpLogicalNot %10 %14
+OpReturn
+)");
+}
+
+TEST_F(IntrinsicBuilderTest, IsFinite_Vector) {
+  auto* var = Global("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+  auto* expr = Call("isFinite", "v");
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
+
+  spirv::Builder& b = Build();
+
+  ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
+%12 = OpTypeBool
+%11 = OpTypeVector %12 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            R"(%13 = OpLoad %3 %1
+%14 = OpIsInf %11 %13
+%15 = OpIsNan %11 %13
+%16 = OpLogicalOr %11 %14 %15
+%10 = OpLogicalNot %11 %16
+OpReturn
+)");
+}
+
+TEST_F(IntrinsicBuilderTest, IsNormal_Scalar) {
+  auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate);
+  auto* expr = Call("isNormal", "v");
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
+
+  spirv::Builder& b = Build();
+
+  ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
+
   auto got = DumpBuilder(b);
   EXPECT_EQ(got, R"(%12 = OpExtInstImport "GLSL.std.450"
 OpName %1 "v"
@@ -253,19 +277,17 @@
 
 TEST_F(IntrinsicBuilderTest, IsNormal_Vector) {
   auto* var = Global("v", ty.vec2<f32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("isNormal", "v");
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 10u) << b.error();
   auto got = DumpBuilder(b);
   EXPECT_EQ(got, R"(%14 = OpExtInstImport "GLSL.std.450"
 OpName %1 "v"
@@ -302,104 +324,124 @@
 using IntrinsicIntTest = IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(IntrinsicIntTest, Call_SInt_Scalar) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.i32(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 1
 %2 = OpTypePointer Private %3
 %4 = OpConstantNull %3
 %1 = OpVariable %2 Private %4
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%6 = OpLoad %3 %1
-%5 = )" + param.op +
-                " %3 %6\n");
+
+  auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1
+%9 = ${op} %3 %10
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 
 TEST_P(IntrinsicIntTest, Call_SInt_Vector) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
 %3 = OpTypeVector %4 3
 %2 = OpTypePointer Private %3
 %5 = OpConstantNull %3
 %1 = OpVariable %2 Private %5
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%6 = )" + param.op +
-                " %3 %7\n");
+
+  auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1
+%10 = ${op} %3 %11
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 
 TEST_P(IntrinsicIntTest, Call_UInt_Scalar) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.u32(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeInt 32 0
 %2 = OpTypePointer Private %3
 %4 = OpConstantNull %3
 %1 = OpVariable %2 Private %4
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%6 = OpLoad %3 %1
-%5 = )" + param.op +
-                " %3 %6\n");
+
+  auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1
+%9 = ${op} %3 %10
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 
 TEST_P(IntrinsicIntTest, Call_UInt_Vector) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
 %3 = OpTypeVector %4 3
 %2 = OpTypePointer Private %3
 %5 = OpConstantNull %3
 %1 = OpVariable %2 Private %5
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%6 = )" + param.op +
-                " %3 %7\n");
+
+  auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1
+%10 = ${op} %3 %11
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 INSTANTIATE_TEST_SUITE_P(
     IntrinsicBuilderTest,
@@ -409,141 +451,151 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Dot_F32) {
   auto* var = Global("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("dot", "v", "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
 %3 = OpTypeVector %4 3
 %2 = OpTypePointer Private %3
 %5 = OpConstantNull %3
 %1 = OpVariable %2 Private %5
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%8 = OpLoad %3 %1
-%6 = OpDot %4 %7 %8
+            R"(%11 = OpLoad %3 %1
+%12 = OpLoad %3 %1
+%10 = OpDot %4 %11 %12
+OpReturn
 )");
 }
 
 TEST_F(IntrinsicBuilderTest, Call_Dot_U32) {
   auto* var = Global("v", ty.vec3<u32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("dot", "v", "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 0
 %3 = OpTypeVector %4 3
 %2 = OpTypePointer Private %3
 %5 = OpConstantNull %3
 %1 = OpVariable %2 Private %5
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%8 = OpLoad %3 %1
-%9 = OpCompositeExtract %4 %7 0
-%10 = OpCompositeExtract %4 %8 0
-%11 = OpIMul %4 %9 %10
-%12 = OpCompositeExtract %4 %7 1
-%13 = OpCompositeExtract %4 %8 1
-%14 = OpIMul %4 %12 %13
-%15 = OpIAdd %4 %11 %14
-%16 = OpCompositeExtract %4 %7 2
-%17 = OpCompositeExtract %4 %8 2
+            R"(%11 = OpLoad %3 %1
+%12 = OpLoad %3 %1
+%13 = OpCompositeExtract %4 %11 0
+%14 = OpCompositeExtract %4 %12 0
+%15 = OpIMul %4 %13 %14
+%16 = OpCompositeExtract %4 %11 1
+%17 = OpCompositeExtract %4 %12 1
 %18 = OpIMul %4 %16 %17
-%6 = OpIAdd %4 %15 %18
+%19 = OpIAdd %4 %15 %18
+%20 = OpCompositeExtract %4 %11 2
+%21 = OpCompositeExtract %4 %12 2
+%22 = OpIMul %4 %20 %21
+%10 = OpIAdd %4 %19 %22
+OpReturn
 )");
 }
 
 TEST_F(IntrinsicBuilderTest, Call_Dot_I32) {
   auto* var = Global("v", ty.vec3<i32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("dot", "v", "v");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeInt 32 1
 %3 = OpTypeVector %4 3
 %2 = OpTypePointer Private %3
 %5 = OpConstantNull %3
 %1 = OpVariable %2 Private %5
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%8 = OpLoad %3 %1
-%9 = OpCompositeExtract %4 %7 0
-%10 = OpCompositeExtract %4 %8 0
-%11 = OpIMul %4 %9 %10
-%12 = OpCompositeExtract %4 %7 1
-%13 = OpCompositeExtract %4 %8 1
-%14 = OpIMul %4 %12 %13
-%15 = OpIAdd %4 %11 %14
-%16 = OpCompositeExtract %4 %7 2
-%17 = OpCompositeExtract %4 %8 2
+            R"(%11 = OpLoad %3 %1
+%12 = OpLoad %3 %1
+%13 = OpCompositeExtract %4 %11 0
+%14 = OpCompositeExtract %4 %12 0
+%15 = OpIMul %4 %13 %14
+%16 = OpCompositeExtract %4 %11 1
+%17 = OpCompositeExtract %4 %12 1
 %18 = OpIMul %4 %16 %17
-%6 = OpIAdd %4 %15 %18
+%19 = OpIAdd %4 %15 %18
+%20 = OpCompositeExtract %4 %11 2
+%21 = OpCompositeExtract %4 %12 2
+%22 = OpIMul %4 %20 %21
+%10 = OpIAdd %4 %19 %22
+OpReturn
 )");
 }
 
 using IntrinsicDeriveTest = IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(IntrinsicDeriveTest, Call_Derivative_Scalar) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  Func("func", {}, ty.void_(), {CallStmt(expr)},
-       {create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
+  auto* func = Func("func", {}, ty.void_(), {CallStmt(expr)},
+                    {Stage(ast::PipelineStage::kFragment)});
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
 %2 = OpTypePointer Private %3
 %4 = OpConstantNull %3
 %1 = OpVariable %2 Private %4
+%6 = OpTypeVoid
+%5 = OpTypeFunction %6
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%6 = OpLoad %3 %1
-%5 = )" + param.op +
-                " %3 %6\n");
+
+  auto expected = utils::ReplaceAll(R"(%10 = OpLoad %3 %1
+%9 = ${op} %3 %10
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 
 TEST_P(IntrinsicDeriveTest, Call_Derivative_Vector) {
   auto param = GetParam();
-
   auto* var = Global("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call(param.name, "v");
-  Func("func", {}, ty.void_(), {CallStmt(expr)},
-       {create<ast::StageDecoration>(ast::PipelineStage::kFragment)});
+  auto* func = Func("func", {}, ty.void_(), {CallStmt(expr)},
+                    {Stage(ast::PipelineStage::kFragment)});
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
-
-  EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
   if (param.name != "dpdx" && param.name != "dpdy" && param.name != "fwidth") {
     EXPECT_EQ(DumpInstructions(b.capabilities()),
@@ -556,11 +608,16 @@
 %2 = OpTypePointer Private %3
 %5 = OpConstantNull %3
 %1 = OpVariable %2 Private %5
+%7 = OpTypeVoid
+%6 = OpTypeFunction %7
 )");
-  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%7 = OpLoad %3 %1
-%6 = )" + param.op +
-                " %3 %7\n");
+
+  auto expected = utils::ReplaceAll(R"(%11 = OpLoad %3 %1
+%10 = ${op} %3 %11
+OpReturn
+)",
+                                    "${op}", param.op);
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), expected);
 }
 INSTANTIATE_TEST_SUITE_P(
     IntrinsicBuilderTest,
@@ -580,17 +637,17 @@
 
   auto* bool_v3 =
       Global("bool_v3", ty.vec3<bool>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("select", "v3", "v3", "bool_v3");
-  WrapInFunction(expr);
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
-  b.push_function(Function{});
   ASSERT_TRUE(b.GenerateGlobalVariable(v3)) << b.error();
   ASSERT_TRUE(b.GenerateGlobalVariable(bool_v3)) << b.error();
-
-  EXPECT_EQ(b.GenerateCallExpression(expr), 11u) << b.error();
+  ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
   EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
 %3 = OpTypeVector %4 3
@@ -602,12 +659,15 @@
 %7 = OpTypePointer Private %8
 %10 = OpConstantNull %8
 %6 = OpVariable %7 Private %10
+%12 = OpTypeVoid
+%11 = OpTypeFunction %12
 )");
   EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
-            R"(%12 = OpLoad %8 %6
-%13 = OpLoad %3 %1
-%14 = OpLoad %3 %1
-%11 = OpSelect %3 %12 %13 %14
+            R"(%16 = OpLoad %8 %6
+%17 = OpLoad %3 %1
+%18 = OpLoad %3 %1
+%15 = OpSelect %3 %16 %17 %18
+OpReturn
 )");
 }
 
@@ -674,19 +734,17 @@
 
 TEST_F(IntrinsicBuilderTest, Call_GLSLMethod_WithLoad) {
   auto* var = Global("ident", ty.f32(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("round", "ident");
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 9u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%10 = OpExtInstImport "GLSL.std.450"
 OpName %1 "ident"
 OpName %7 "a_func"
@@ -709,18 +767,16 @@
     IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Builtin_SingleParam_Float_Test, Call_Scalar) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, 1.0f);
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -738,18 +794,16 @@
 
 TEST_P(Intrinsic_Builtin_SingleParam_Float_Test, Call_Vector) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, vec2<f32>(1.0f, 1.0f));
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -794,17 +848,15 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Length_Scalar) {
   auto* expr = Call("length", 1.0f);
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -821,16 +873,15 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Length_Vector) {
   auto* expr = Call("length", vec2<f32>(1.0f, 1.0f));
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -849,16 +900,15 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Normalize) {
   auto* expr = Call("normalize", vec2<f32>(1.0f, 1.0f));
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -879,19 +929,16 @@
     IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Builtin_DualParam_Float_Test, Call_Scalar) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, 1.0f, 1.0f);
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -909,19 +956,16 @@
 
 TEST_P(Intrinsic_Builtin_DualParam_Float_Test, Call_Vector) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -948,17 +992,15 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Reflect_Vector) {
   auto* expr = Call("reflect", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -977,17 +1019,15 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Distance_Scalar) {
   auto* expr = Call("distance", 1.0f, 1.0f);
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1004,17 +1044,15 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Distance_Vector) {
   auto* expr = Call("distance", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1034,17 +1072,15 @@
 TEST_F(IntrinsicBuilderTest, Call_Cross) {
   auto* expr =
       Call("cross", vec3<f32>(1.0f, 1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 1.0f));
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1065,18 +1101,16 @@
     IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Builtin_ThreeParam_Float_Test, Call_Scalar) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, 1.0f, 1.0f, 1.0f);
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1094,20 +1128,17 @@
 
 TEST_P(Intrinsic_Builtin_ThreeParam_Float_Test, Call_Vector) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f),
                     vec2<f32>(1.0f, 1.0f));
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1136,17 +1167,15 @@
 TEST_F(IntrinsicBuilderTest, Call_FaceForward_Vector) {
   auto* expr = Call("faceForward", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f),
                     vec2<f32>(1.0f, 1.0f));
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1167,18 +1196,16 @@
     IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Builtin_SingleParam_Sint_Test, Call_Scalar) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, 1);
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1196,18 +1223,16 @@
 
 TEST_P(Intrinsic_Builtin_SingleParam_Sint_Test, Call_Vector) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, vec2<i32>(1, 1));
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1232,16 +1257,15 @@
 using Intrinsic_Builtin_Abs_Uint_Test = IntrinsicBuilderTest;
 TEST_F(Intrinsic_Builtin_Abs_Uint_Test, Call_Scalar) {
   auto* expr = Call("abs", 1u);
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 7u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(OpName %3 "a_func"
 %2 = OpTypeVoid
 %1 = OpTypeFunction %2
@@ -1256,16 +1280,15 @@
 
 TEST_F(Intrinsic_Builtin_Abs_Uint_Test, Call_Vector) {
   auto* expr = Call("abs", vec2<u32>(1u, 1u));
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 9u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(OpName %3 "a_func"
 %2 = OpTypeVoid
 %1 = OpTypeFunction %2
@@ -1284,18 +1307,16 @@
     IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Builtin_DualParam_SInt_Test, Call_Scalar) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, 1, 1);
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1313,18 +1334,16 @@
 
 TEST_P(Intrinsic_Builtin_DualParam_SInt_Test, Call_Vector) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, vec2<i32>(1, 1), vec2<i32>(1, 1));
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1350,18 +1369,16 @@
     IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Builtin_DualParam_UInt_Test, Call_Scalar) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, 1u, 1u);
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1379,18 +1396,16 @@
 
 TEST_P(Intrinsic_Builtin_DualParam_UInt_Test, Call_Vector) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, vec2<u32>(1u, 1u), vec2<u32>(1u, 1u));
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1416,18 +1431,16 @@
     IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Builtin_ThreeParam_Sint_Test, Call_Scalar) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, 1, 1, 1);
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1445,20 +1458,17 @@
 
 TEST_P(Intrinsic_Builtin_ThreeParam_Sint_Test, Call_Vector) {
   auto param = GetParam();
-
   auto* expr =
       Call(param.name, vec2<i32>(1, 1), vec2<i32>(1, 1), vec2<i32>(1, 1));
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1483,18 +1493,16 @@
     IntrinsicBuilderTestWithParam<IntrinsicData>;
 TEST_P(Intrinsic_Builtin_ThreeParam_Uint_Test, Call_Scalar) {
   auto param = GetParam();
-
   auto* expr = Call(param.name, 1u, 1u, 1u);
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1512,20 +1520,17 @@
 
 TEST_P(Intrinsic_Builtin_ThreeParam_Uint_Test, Call_Vector) {
   auto param = GetParam();
-
   auto* expr =
       Call(param.name, vec2<u32>(1u, 1u), vec2<u32>(1u, 1u), vec2<u32>(1u, 1u));
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
   EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
 %2 = OpTypeVoid
@@ -1628,35 +1633,32 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Determinant) {
   auto* var = Global("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("determinant", "var");
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
+  ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
-  EXPECT_EQ(b.GenerateCallExpression(expr), 11u) << b.error();
-
   EXPECT_EQ(DumpBuilder(b), R"(%12 = OpExtInstImport "GLSL.std.450"
-OpName %3 "a_func"
-OpName %5 "var"
-%2 = OpTypeVoid
-%1 = OpTypeFunction %2
-%9 = OpTypeFloat 32
-%8 = OpTypeVector %9 3
-%7 = OpTypeMatrix %8 3
-%6 = OpTypePointer Private %7
-%10 = OpConstantNull %7
-%5 = OpVariable %6 Private %10
-%3 = OpFunction %2 None %1
-%4 = OpLabel
-%13 = OpLoad %7 %5
-%11 = OpExtInst %9 %12 Determinant %13
+OpName %1 "var"
+OpName %9 "a_func"
+%5 = OpTypeFloat 32
+%4 = OpTypeVector %5 3
+%3 = OpTypeMatrix %4 3
+%2 = OpTypePointer Private %3
+%6 = OpConstantNull %3
+%1 = OpVariable %2 Private %6
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+%9 = OpFunction %8 None %7
+%10 = OpLabel
+%13 = OpLoad %3 %1
+%11 = OpExtInst %5 %12 Determinant %13
 OpReturn
 OpFunctionEnd
 )");
@@ -1664,35 +1666,32 @@
 
 TEST_F(IntrinsicBuilderTest, Call_Transpose) {
   auto* var = Global("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
-
   auto* expr = Call("transpose", "var");
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Assign(Phony(), expr),
+                    });
 
   spirv::Builder& b = Build();
 
+  ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
-  EXPECT_EQ(b.GenerateCallExpression(expr), 11u) << b.error();
-
-  EXPECT_EQ(DumpBuilder(b), R"(OpName %3 "a_func"
-OpName %5 "var"
-%2 = OpTypeVoid
-%1 = OpTypeFunction %2
-%9 = OpTypeFloat 32
-%8 = OpTypeVector %9 3
-%7 = OpTypeMatrix %8 2
-%6 = OpTypePointer Private %7
-%10 = OpConstantNull %7
-%5 = OpVariable %6 Private %10
-%13 = OpTypeVector %9 2
+  EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "var"
+OpName %9 "a_func"
+%5 = OpTypeFloat 32
+%4 = OpTypeVector %5 3
+%3 = OpTypeMatrix %4 2
+%2 = OpTypePointer Private %3
+%6 = OpConstantNull %3
+%1 = OpVariable %2 Private %6
+%8 = OpTypeVoid
+%7 = OpTypeFunction %8
+%13 = OpTypeVector %5 2
 %12 = OpTypeMatrix %13 3
-%3 = OpFunction %2 None %1
-%4 = OpLabel
-%14 = OpLoad %7 %5
+%9 = OpFunction %8 None %7
+%10 = OpLabel
+%14 = OpLoad %3 %1
 %11 = OpTranspose %12 %14
 OpReturn
 OpFunctionEnd
@@ -1707,10 +1706,9 @@
              create<ast::BindingDecoration>(1),
              create<ast::GroupDecoration>(2),
          });
-
   auto* expr = Call("arrayLength", AddressOf(MemberAccessor("b", "a")));
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            CallStmt(expr),
        },
@@ -1737,6 +1735,7 @@
   EXPECT_EQ(expected_types, got_types);
 
   auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 0
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -1756,10 +1755,9 @@
              create<ast::BindingDecoration>(1),
              create<ast::GroupDecoration>(2),
          });
-
   auto* expr = Call("arrayLength", AddressOf(MemberAccessor("b", "a")));
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            CallStmt(expr),
        },
@@ -1786,6 +1784,7 @@
   EXPECT_EQ(expected_types, got_types);
 
   auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 1
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -1806,7 +1805,7 @@
   auto* p2 = Const("p2", nullptr, AddressOf(MemberAccessor(Deref(p), "a")));
   auto* expr = Call("arrayLength", p2);
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            Decl(p),
            Decl(p2),
@@ -1835,6 +1834,7 @@
   EXPECT_EQ(expected_types, got_types);
 
   auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 0
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -1867,7 +1867,7 @@
   auto* p3 = Const("p3", nullptr, AddressOf(MemberAccessor(Deref(p2), "a")));
   auto* expr = Call("arrayLength", AddressOf(Deref(p3)));
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            Decl(p),
            Decl(p2),
@@ -1897,6 +1897,7 @@
   EXPECT_EQ(expected_types, got_types);
 
   auto* expected_instructions = R"(%10 = OpArrayLength %11 %1 0
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -1928,7 +1929,7 @@
              create<ast::GroupDecoration>(2),
          });
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            Decl(Const("u", ty.u32(),
                       Call("atomicLoad", AddressOf(MemberAccessor("b", "u"))))),
@@ -1962,6 +1963,7 @@
 %10 = OpAtomicLoad %4 %15 %11 %12
 %19 = OpAccessChain %18 %1 %11
 %16 = OpAtomicLoad %5 %19 %11 %12
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -1995,7 +1997,7 @@
              create<ast::GroupDecoration>(2),
          });
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            Decl(Var("u", nullptr, Expr(1u))),
            Decl(Var("i", nullptr, Expr(2))),
@@ -2040,6 +2042,7 @@
 %27 = OpAccessChain %26 %1 %10
 %28 = OpLoad %5 %15
 OpAtomicStore %27 %10 %19 %28
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -2071,7 +2074,7 @@
              create<ast::GroupDecoration>(2),
          });
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            Decl(Var("v", nullptr, Expr(10))),
            Decl(Const("x", ty.i32(),
@@ -2108,6 +2111,7 @@
 %20 = OpLoad %4 %10
 )";
   expected_instructions += "%13 = " + GetParam().op + " %4 %19 %15 %16 %20\n";
+  expected_instructions += "OpReturn\n";
 
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -2148,7 +2152,7 @@
              create<ast::GroupDecoration>(2),
          });
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            Decl(Var("v", nullptr, Expr(10u))),
            Decl(Const("x", ty.u32(),
@@ -2184,6 +2188,7 @@
 %19 = OpLoad %4 %10
 )";
   expected_instructions += "%13 = " + GetParam().op + " %4 %18 %14 %15 %19\n";
+  expected_instructions += "OpReturn\n";
 
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -2226,7 +2231,7 @@
              create<ast::GroupDecoration>(2),
          });
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            Decl(Var("u", nullptr, Expr(10u))),
            Decl(Var("i", nullptr, Expr(10))),
@@ -2274,6 +2279,7 @@
 %28 = OpAccessChain %27 %1 %19
 %29 = OpLoad %5 %15
 %25 = OpAtomicExchange %5 %28 %19 %20 %29
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -2305,7 +2311,7 @@
              create<ast::GroupDecoration>(2),
          });
 
-  Func("a_func", ast::VariableList{}, ty.void_(),
+  Func("a_func", {}, ty.void_(),
        ast::StatementList{
            Decl(Const("u", ty.vec2<u32>(),
                       Call("atomicCompareExchangeWeak",
@@ -2356,6 +2362,7 @@
 %31 = OpIEqual %19 %30 %28
 %34 = OpSelect %5 %31 %33 %32
 %23 = OpCompositeConstruct %24 %30 %34
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -2371,16 +2378,12 @@
   bool pack4 = param.name == "pack4x8snorm" || param.name == "pack4x8unorm";
   auto* call = pack4 ? Call(param.name, vec4<float>(1.0f, 1.0f, 1.0f, 1.0f))
                      : Call(param.name, vec2<float>(1.0f, 1.0f));
-  WrapInFunction(call);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(), {CallStmt(call)});
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(call), 5u) << b.error();
   if (pack4) {
     EXPECT_EQ(DumpBuilder(b), R"(%7 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
@@ -2433,17 +2436,12 @@
   auto param = GetParam();
 
   bool pack4 = param.name == "unpack4x8snorm" || param.name == "unpack4x8unorm";
-  auto* call = Call(param.name, 1u);
-  WrapInFunction(call);
-
-  auto* func = Func("a_func", ast::VariableList{}, ty.void_(),
-                    ast::StatementList{}, ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(), {CallStmt(Call(param.name, 1u))});
 
   spirv::Builder& b = Build();
 
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_EQ(b.GenerateCallExpression(call), 5u) << b.error();
   if (pack4) {
     EXPECT_EQ(DumpBuilder(b), R"(%8 = OpExtInstImport "GLSL.std.450"
 OpName %3 "a_func"
@@ -2489,7 +2487,7 @@
                     IntrinsicData{"unpack2x16float", "UnpackHalf2x16"}));
 
 TEST_F(IntrinsicBuilderTest, Call_WorkgroupBarrier) {
-  Func("f", ast::VariableList{}, ty.void_(),
+  Func("f", {}, ty.void_(),
        ast::StatementList{
            CallStmt(Call("workgroupBarrier")),
        },
@@ -2514,6 +2512,7 @@
   EXPECT_EQ(expected_types, got_types);
 
   auto* expected_instructions = R"(OpControlBarrier %7 %7 %8
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -2522,7 +2521,7 @@
 }
 
 TEST_F(IntrinsicBuilderTest, Call_StorageBarrier) {
-  Func("f", ast::VariableList{}, ty.void_(),
+  Func("f", {}, ty.void_(),
        ast::StatementList{
            CallStmt(Call("storageBarrier")),
        },
@@ -2547,6 +2546,7 @@
   EXPECT_EQ(expected_types, got_types);
 
   auto* expected_instructions = R"(OpControlBarrier %7 %7 %8
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[0].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
@@ -2585,6 +2585,7 @@
   EXPECT_EQ(expected_types, got_types);
 
   auto* expected_instructions = R"(%15 = OpFunctionCall %2 %3 %16 %17 %18
+OpReturn
 )";
   auto got_instructions = DumpInstructions(b.functions()[1].instructions());
   EXPECT_EQ(expected_instructions, got_instructions);
diff --git a/src/writer/spirv/builder_switch_test.cc b/src/writer/spirv/builder_switch_test.cc
index 462ce25..42d7de6 100644
--- a/src/writer/spirv/builder_switch_test.cc
+++ b/src/writer/spirv/builder_switch_test.cc
@@ -60,13 +60,13 @@
   auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
   auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
 
-  auto* expr = Switch("a", /**/
-                      Case(Expr(1), Block(Assign("v", 1))),
-                      Case(Expr(2), Block(Assign("v", 2))), DefaultCase());
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
-                    ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Switch("a",                                   //
+                               Case(Expr(1), Block(Assign("v", 1))),  //
+                               Case(Expr(2), Block(Assign("v", 2))),  //
+                               DefaultCase()),
+                    });
 
   spirv::Builder& b = Build();
 
@@ -74,8 +74,6 @@
   ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_TRUE(b.GenerateSwitchStatement(expr)) << b.error();
-
   EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
 OpName %5 "a"
 OpName %8 "a_func"
@@ -119,13 +117,13 @@
   auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
   auto* a = Global("a", ty.u32(), ast::StorageClass::kPrivate);
 
-  auto* expr = Switch("a", Case(Expr(1u), Block(Assign("v", 1))),
-                      Case(Expr(2u), Block(Assign("v", 2))), DefaultCase());
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
-                    ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Switch("a",                                    //
+                               Case(Expr(1u), Block(Assign("v", 1))),  //
+                               Case(Expr(2u), Block(Assign("v", 2))),  //
+                               DefaultCase()),
+                    });
 
   spirv::Builder& b = Build();
 
@@ -133,8 +131,6 @@
   ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_TRUE(b.GenerateSwitchStatement(expr)) << b.error();
-
   EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
 OpName %5 "a"
 OpName %11 "a_func"
@@ -178,18 +174,11 @@
   auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
   auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
 
-  auto* default_body = Block(Assign("v", 1));
-
-  ast::CaseStatementList cases;
-  cases.push_back(
-      create<ast::CaseStatement>(ast::CaseSelectorList{}, default_body));
-
-  auto* expr = create<ast::SwitchStatement>(Expr("a"), cases);
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
-                    ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Switch("a",                                  //
+                               DefaultCase(Block(Assign("v", 1)))),  //
+                    });
 
   spirv::Builder& b = Build();
 
@@ -197,8 +186,6 @@
   ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_TRUE(b.GenerateSwitchStatement(expr)) << b.error();
-
   EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
 OpName %5 "a"
 OpName %8 "a_func"
@@ -237,31 +224,15 @@
   auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
   auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
 
-  auto* case_1_body = Block(Assign("v", Expr(1)));
-
-  auto* case_2_body = Block(Assign("v", Expr(2)));
-
-  auto* default_body = Block(Assign("v", Expr(3)));
-
-  ast::CaseSelectorList selector_1;
-  selector_1.push_back(Expr(1));
-
-  ast::CaseSelectorList selector_2;
-  selector_2.push_back(Expr(2));
-  selector_2.push_back(Expr(3));
-
-  ast::CaseStatementList cases;
-  cases.push_back(create<ast::CaseStatement>(selector_1, case_1_body));
-  cases.push_back(create<ast::CaseStatement>(selector_2, case_2_body));
-  cases.push_back(
-      create<ast::CaseStatement>(ast::CaseSelectorList{}, default_body));
-
-  auto* expr = create<ast::SwitchStatement>(Expr("a"), cases);
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
-                    ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Switch(Expr("a"),                    //
+                               Case(Expr(1),                 //
+                                    Block(Assign("v", 1))),  //
+                               Case({Expr(2), Expr(3)},      //
+                                    Block(Assign("v", 2))),  //
+                               DefaultCase(Block(Assign("v", 3)))),
+                    });
 
   spirv::Builder& b = Build();
 
@@ -269,8 +240,6 @@
   ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_TRUE(b.GenerateSwitchStatement(expr)) << b.error();
-
   EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
 OpName %5 "a"
 OpName %8 "a_func"
@@ -318,31 +287,15 @@
   auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
   auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
 
-  auto* case_1_body =
-      Block(Assign("v", Expr(1)), create<ast::FallthroughStatement>());
-
-  auto* case_2_body = Block(Assign("v", Expr(2)));
-
-  auto* default_body = Block(Assign("v", Expr(3)));
-
-  ast::CaseSelectorList selector_1;
-  selector_1.push_back(Expr(1));
-
-  ast::CaseSelectorList selector_2;
-  selector_2.push_back(Expr(2));
-
-  ast::CaseStatementList cases;
-  cases.push_back(create<ast::CaseStatement>(selector_1, case_1_body));
-  cases.push_back(create<ast::CaseStatement>(selector_2, case_2_body));
-  cases.push_back(
-      create<ast::CaseStatement>(ast::CaseSelectorList{}, default_body));
-
-  auto* expr = create<ast::SwitchStatement>(Expr("a"), cases);
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
-                    ast::DecorationList{});
+  auto* func = Func("a_func", {}, ty.void_(),
+                    {
+                        Switch(Expr("a"),                                   //
+                               Case(Expr(1),                                //
+                                    Block(Assign("v", 1), Fallthrough())),  //
+                               Case(Expr(2),                                //
+                                    Block(Assign("v", 2))),                 //
+                               DefaultCase(Block(Assign("v", 3)))),
+                    });
 
   spirv::Builder& b = Build();
 
@@ -350,8 +303,6 @@
   ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_TRUE(b.GenerateSwitchStatement(expr)) << b.error();
-
   EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
 OpName %5 "a"
 OpName %8 "a_func"
@@ -398,17 +349,16 @@
   auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
   auto* a = Global("a", ty.i32(), ast::StorageClass::kPrivate);
 
-  auto* expr = Switch(
-      "a",                /**/
-      Case(Expr(1), Block(/**/
+  auto* func = Func(
+      "a_func", {}, ty.void_(),
+      {
+          Switch("a",           //
+                 Case(Expr(1),  //
+                      Block(    //
                           If(Expr(true), Block(create<ast::BreakStatement>())),
                           Assign("v", 1))),
-      DefaultCase());
-
-  WrapInFunction(expr);
-
-  auto* func = Func("a_func", {}, ty.void_(), ast::StatementList{},
-                    ast::DecorationList{});
+                 DefaultCase()),
+      });
 
   spirv::Builder& b = Build();
 
@@ -416,8 +366,6 @@
   ASSERT_TRUE(b.GenerateGlobalVariable(a)) << b.error();
   ASSERT_TRUE(b.GenerateFunction(func)) << b.error();
 
-  EXPECT_TRUE(b.GenerateSwitchStatement(expr)) << b.error();
-
   EXPECT_EQ(DumpBuilder(b), R"(OpName %1 "v"
 OpName %5 "a"
 OpName %8 "a_func"
diff --git a/src/writer/spirv/function.cc b/src/writer/spirv/function.cc
index 5c5c726..01da96c 100644
--- a/src/writer/spirv/function.cc
+++ b/src/writer/spirv/function.cc
@@ -17,15 +17,6 @@
 namespace tint {
 namespace writer {
 namespace spirv {
-namespace {
-
-// Returns true if the given Op is a function terminator
-bool OpIsFunctionTerminator(spv::Op op) {
-  return op == spv::Op::OpReturn || op == spv::Op::OpReturnValue ||
-         op == spv::Op::OpKill;
-}
-
-}  // namespace
 
 Function::Function()
     : declaration_(Instruction{spv::Op::OpNop, {}}),
@@ -56,17 +47,6 @@
     cb(inst);
   }
 
-  bool needs_terminator = false;
-  if (instructions_.empty()) {
-    needs_terminator = true;
-  } else {
-    const auto& last = instructions_.back();
-    needs_terminator = !OpIsFunctionTerminator(last.opcode());
-  }
-  if (needs_terminator) {
-    cb(Instruction{spv::Op::OpReturn, {}});
-  }
-
   cb(Instruction{spv::Op::OpFunctionEnd, {}});
 }