Add user call tests

This CL adds end-to-end tests for user calls.

Bug: tint:1718
Change-Id: I076d64ffa5097d6184027f0fa3c868a1dd3b9507
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/162261
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/msl/writer/printer/printer.cc b/src/tint/lang/msl/writer/printer/printer.cc
index 436e88f..cbb92ed 100644
--- a/src/tint/lang/msl/writer/printer/printer.cc
+++ b/src/tint/lang/msl/writer/printer/printer.cc
@@ -262,7 +262,15 @@
                 [&](core::ir::Load*) { MaybeEmitInstruction(inst); },       //
                 [&](core::ir::Construct*) { MaybeEmitInstruction(inst); },  //
                 [&](core::ir::Access*) { MaybeEmitInstruction(inst); },     //
-                [&](core::ir::UserCall*) { MaybeEmitInstruction(inst); },   //
+                [&](core::ir::UserCall* c) {
+                    if (c->Result()->Type()->Is<core::type::Void>()) {
+                        auto out = Line();
+                        EmitValue(out, c->Result());
+                        out << ";";
+                    } else {
+                        MaybeEmitInstruction(inst);
+                    }
+                },  //
                 TINT_ICE_ON_NO_MATCH);
         }
     }
diff --git a/test/tint/expressions/user_call/call_with_call_param.wgsl b/test/tint/expressions/user_call/call_with_call_param.wgsl
new file mode 100644
index 0000000..d321f7e
--- /dev/null
+++ b/test/tint/expressions/user_call/call_with_call_param.wgsl
@@ -0,0 +1,11 @@
+fn b(i: i32) -> f32 {
+    return 2.3f;
+}
+
+fn c(u: u32) -> i32 {
+    return 1;
+}
+
+fn a() {
+    var a = b(c(2));
+}
diff --git a/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.dxc.hlsl b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..1379922
--- /dev/null
+++ b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.dxc.hlsl
@@ -0,0 +1,17 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float b(int i) {
+  return 2.29999995231628417969f;
+}
+
+int c(uint u) {
+  return 1;
+}
+
+void a() {
+  const int tint_symbol = c(2u);
+  float a_1 = b(tint_symbol);
+}
diff --git a/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.fxc.hlsl b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..1379922
--- /dev/null
+++ b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.fxc.hlsl
@@ -0,0 +1,17 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+float b(int i) {
+  return 2.29999995231628417969f;
+}
+
+int c(uint u) {
+  return 1;
+}
+
+void a() {
+  const int tint_symbol = c(2u);
+  float a_1 = b(tint_symbol);
+}
diff --git a/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.glsl b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.glsl
new file mode 100644
index 0000000..d94bb1a
--- /dev/null
+++ b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.glsl
@@ -0,0 +1,19 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+float b(int i) {
+  return 2.29999995231628417969f;
+}
+
+int c(uint u) {
+  return 1;
+}
+
+void a() {
+  int tint_symbol = c(2u);
+  float a_1 = b(tint_symbol);
+}
+
diff --git a/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.ir.msl b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.ir.msl
new file mode 100644
index 0000000..2454f50
--- /dev/null
+++ b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.ir.msl
@@ -0,0 +1,27 @@
+SKIP: FAILED
+
+#include <metal_stdlib>
+using namespace metal;
+
+float b() {
+  return 2.29999995231628417969f;
+}
+int c() {
+  return 1;
+}
+void a() {
+  float a = b(c(2u));
+}
+program_source:11:15: error: no matching function for call to 'c'
+  float a = b(c(2u));
+              ^
+program_source:7:5: note: candidate function not viable: requires 0 arguments, but 1 was provided
+int c() {
+    ^
+program_source:11:13: error: no matching function for call to 'b'
+  float a = b(c(2u));
+            ^
+program_source:4:7: note: candidate function not viable: requires 0 arguments, but 1 was provided
+float b() {
+      ^
+
diff --git a/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.msl b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.msl
new file mode 100644
index 0000000..2c2d9f6
--- /dev/null
+++ b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.msl
@@ -0,0 +1,16 @@
+#include <metal_stdlib>
+
+using namespace metal;
+float b(int i) {
+  return 2.29999995231628417969f;
+}
+
+int c(uint u) {
+  return 1;
+}
+
+void a() {
+  int const tint_symbol = c(2u);
+  float a_1 = b(tint_symbol);
+}
+
diff --git a/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.spvasm b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.spvasm
new file mode 100644
index 0000000..808124e
--- /dev/null
+++ b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.spvasm
@@ -0,0 +1,50 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %b "b"
+               OpName %i "i"
+               OpName %c "c"
+               OpName %u "u"
+               OpName %a "a"
+               OpName %a_1 "a_1"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %float %int
+%float_2_29999995 = OpConstant %float 2.29999995
+       %uint = OpTypeInt 32 0
+         %12 = OpTypeFunction %int %uint
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+%_ptr_Function_float = OpTypePointer Function %float
+         %25 = OpConstantNull %float
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %b = OpFunction %float None %5
+          %i = OpFunctionParameter %int
+         %10 = OpLabel
+               OpReturnValue %float_2_29999995
+               OpFunctionEnd
+          %c = OpFunction %int None %12
+          %u = OpFunctionParameter %uint
+         %16 = OpLabel
+               OpReturnValue %int_1
+               OpFunctionEnd
+          %a = OpFunction %void None %1
+         %19 = OpLabel
+        %a_1 = OpVariable %_ptr_Function_float Function %25
+         %20 = OpFunctionCall %int %c %uint_2
+         %22 = OpFunctionCall %float %b %20
+               OpStore %a_1 %22
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.wgsl b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.wgsl
new file mode 100644
index 0000000..8d46d7d
--- /dev/null
+++ b/test/tint/expressions/user_call/call_with_call_param.wgsl.expected.wgsl
@@ -0,0 +1,11 @@
+fn b(i : i32) -> f32 {
+  return 2.29999995231628417969f;
+}
+
+fn c(u : u32) -> i32 {
+  return 1;
+}
+
+fn a() {
+  var a = b(c(2));
+}
diff --git a/test/tint/expressions/user_call/multi_param_no_return.wgsl b/test/tint/expressions/user_call/multi_param_no_return.wgsl
new file mode 100644
index 0000000..d645f8c
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_no_return.wgsl
@@ -0,0 +1,9 @@
+fn c(x: i32, y: i32, z: i32) {
+    var a = 1 + x + y + z;
+    a = a + 2;
+}
+
+fn b() {
+    c(1, 2, 3);
+    c(4, 5, 6);
+}
diff --git a/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.dxc.hlsl b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..b0b3b00
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.dxc.hlsl
@@ -0,0 +1,14 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void c(int x, int y, int z) {
+  int a = (((1 + x) + y) + z);
+  a = (a + 2);
+}
+
+void b() {
+  c(1, 2, 3);
+  c(4, 5, 6);
+}
diff --git a/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.fxc.hlsl b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..b0b3b00
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.fxc.hlsl
@@ -0,0 +1,14 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void c(int x, int y, int z) {
+  int a = (((1 + x) + y) + z);
+  a = (a + 2);
+}
+
+void b() {
+  c(1, 2, 3);
+  c(4, 5, 6);
+}
diff --git a/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.glsl b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.glsl
new file mode 100644
index 0000000..a562693
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.glsl
@@ -0,0 +1,16 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+void c(int x, int y, int z) {
+  int a = (((1 + x) + y) + z);
+  a = (a + 2);
+}
+
+void b() {
+  c(1, 2, 3);
+  c(4, 5, 6);
+}
+
diff --git a/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.ir.msl
new file mode 100644
index 0000000..03d2607
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.ir.msl
@@ -0,0 +1,9 @@
+SKIP: FAILED
+
+<dawn>/src/tint/lang/msl/writer/printer/printer.cc:315 internal compiler error: Switch() matched no cases. Type: tint::core::ir::FunctionParam
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
diff --git a/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.msl b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.msl
new file mode 100644
index 0000000..92d7d2d
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void c(int x, int y, int z) {
+  int a = as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(1) + as_type<uint>(x)))) + as_type<uint>(y)))) + as_type<uint>(z)));
+  a = as_type<int>((as_type<uint>(a) + as_type<uint>(2)));
+}
+
+void b() {
+  c(1, 2, 3);
+  c(4, 5, 6);
+}
+
diff --git a/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.spvasm b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.spvasm
new file mode 100644
index 0000000..9a14a1d
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %c "c"
+               OpName %x "x"
+               OpName %y "y"
+               OpName %z "z"
+               OpName %a "a"
+               OpName %b "b"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %void %int %int %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+      %int_6 = OpConstant %int 6
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %c = OpFunction %void None %5
+          %x = OpFunctionParameter %int
+          %y = OpFunctionParameter %int
+          %z = OpFunctionParameter %int
+         %11 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function %18
+         %13 = OpIAdd %int %int_1 %x
+         %14 = OpIAdd %int %13 %y
+         %15 = OpIAdd %int %14 %z
+               OpStore %a %15
+         %19 = OpLoad %int %a
+         %21 = OpIAdd %int %19 %int_2
+               OpStore %a %21
+               OpReturn
+               OpFunctionEnd
+          %b = OpFunction %void None %1
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %c %int_1 %int_2 %int_3
+         %26 = OpFunctionCall %void %c %int_4 %int_5 %int_6
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.wgsl b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.wgsl
new file mode 100644
index 0000000..0a306fe
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_no_return.wgsl.expected.wgsl
@@ -0,0 +1,9 @@
+fn c(x : i32, y : i32, z : i32) {
+  var a = (((1 + x) + y) + z);
+  a = (a + 2);
+}
+
+fn b() {
+  c(1, 2, 3);
+  c(4, 5, 6);
+}
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl b/test/tint/expressions/user_call/multi_param_return.wgsl
new file mode 100644
index 0000000..d19250f
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl
@@ -0,0 +1,10 @@
+fn c(x: i32, y: i32, z: i32) -> i32 {
+    var a = 1 + x + y + z;
+    a = a + 2;
+    return a;
+}
+
+fn b() {
+    var b = c(2, 3, 4);
+    b += c(3, 4, 5);
+}
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.dxc.hlsl b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..0fef91f
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.dxc.hlsl
@@ -0,0 +1,17 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int c(int x, int y, int z) {
+  int a = (((1 + x) + y) + z);
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c(2, 3, 4);
+  const int tint_symbol = b_1;
+  const int tint_symbol_1 = c(3, 4, 5);
+  b_1 = (tint_symbol + tint_symbol_1);
+}
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.fxc.hlsl b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..0fef91f
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.fxc.hlsl
@@ -0,0 +1,17 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int c(int x, int y, int z) {
+  int a = (((1 + x) + y) + z);
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c(2, 3, 4);
+  const int tint_symbol = b_1;
+  const int tint_symbol_1 = c(3, 4, 5);
+  b_1 = (tint_symbol + tint_symbol_1);
+}
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.glsl b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.glsl
new file mode 100644
index 0000000..76eca0b
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.glsl
@@ -0,0 +1,19 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+int c(int x, int y, int z) {
+  int a = (((1 + x) + y) + z);
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c(2, 3, 4);
+  int tint_symbol = b_1;
+  int tint_symbol_1 = c(3, 4, 5);
+  b_1 = (tint_symbol + tint_symbol_1);
+}
+
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.ir.msl
new file mode 100644
index 0000000..03d2607
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.ir.msl
@@ -0,0 +1,9 @@
+SKIP: FAILED
+
+<dawn>/src/tint/lang/msl/writer/printer/printer.cc:315 internal compiler error: Switch() matched no cases. Type: tint::core::ir::FunctionParam
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.msl b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.msl
new file mode 100644
index 0000000..9fa6f40
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.msl
@@ -0,0 +1,16 @@
+#include <metal_stdlib>
+
+using namespace metal;
+int c(int x, int y, int z) {
+  int a = as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(as_type<int>((as_type<uint>(1) + as_type<uint>(x)))) + as_type<uint>(y)))) + as_type<uint>(z)));
+  a = as_type<int>((as_type<uint>(a) + as_type<uint>(2)));
+  return a;
+}
+
+void b() {
+  int b_1 = c(2, 3, 4);
+  int const tint_symbol = b_1;
+  int const tint_symbol_1 = c(3, 4, 5);
+  b_1 = as_type<int>((as_type<uint>(tint_symbol) + as_type<uint>(tint_symbol_1)));
+}
+
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.spvasm b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.spvasm
new file mode 100644
index 0000000..e5e4eed
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.spvasm
@@ -0,0 +1,59 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %c "c"
+               OpName %x "x"
+               OpName %y "y"
+               OpName %z "z"
+               OpName %a "a"
+               OpName %b "b"
+               OpName %b_1 "b_1"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int %int %int %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+      %int_4 = OpConstant %int 4
+      %int_5 = OpConstant %int 5
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %c = OpFunction %int None %5
+          %x = OpFunctionParameter %int
+          %y = OpFunctionParameter %int
+          %z = OpFunctionParameter %int
+         %11 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function %18
+         %13 = OpIAdd %int %int_1 %x
+         %14 = OpIAdd %int %13 %y
+         %15 = OpIAdd %int %14 %z
+               OpStore %a %15
+         %19 = OpLoad %int %a
+         %21 = OpIAdd %int %19 %int_2
+               OpStore %a %21
+         %22 = OpLoad %int %a
+               OpReturnValue %22
+               OpFunctionEnd
+          %b = OpFunction %void None %1
+         %24 = OpLabel
+        %b_1 = OpVariable %_ptr_Function_int Function %18
+         %25 = OpFunctionCall %int %c %int_2 %int_3 %int_4
+               OpStore %b_1 %25
+         %29 = OpLoad %int %b_1
+         %30 = OpFunctionCall %int %c %int_3 %int_4 %int_5
+         %32 = OpIAdd %int %29 %30
+               OpStore %b_1 %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/expressions/user_call/multi_param_return.wgsl.expected.wgsl b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.wgsl
new file mode 100644
index 0000000..adc6f92
--- /dev/null
+++ b/test/tint/expressions/user_call/multi_param_return.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+fn c(x : i32, y : i32, z : i32) -> i32 {
+  var a = (((1 + x) + y) + z);
+  a = (a + 2);
+  return a;
+}
+
+fn b() {
+  var b = c(2, 3, 4);
+  b += c(3, 4, 5);
+}
diff --git a/test/tint/expressions/user_call/nested.wgsl b/test/tint/expressions/user_call/nested.wgsl
new file mode 100644
index 0000000..2c86186
--- /dev/null
+++ b/test/tint/expressions/user_call/nested.wgsl
@@ -0,0 +1,13 @@
+fn a() {
+    b();
+}
+
+fn b() {
+    c();
+}
+
+fn c() {
+    d();
+}
+
+fn d() {}
diff --git a/test/tint/expressions/user_call/nested.wgsl.expected.dxc.hlsl b/test/tint/expressions/user_call/nested.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..dcc36ce
--- /dev/null
+++ b/test/tint/expressions/user_call/nested.wgsl.expected.dxc.hlsl
@@ -0,0 +1,19 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void d() {
+}
+
+void c() {
+  d();
+}
+
+void b() {
+  c();
+}
+
+void a() {
+  b();
+}
diff --git a/test/tint/expressions/user_call/nested.wgsl.expected.fxc.hlsl b/test/tint/expressions/user_call/nested.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..dcc36ce
--- /dev/null
+++ b/test/tint/expressions/user_call/nested.wgsl.expected.fxc.hlsl
@@ -0,0 +1,19 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void d() {
+}
+
+void c() {
+  d();
+}
+
+void b() {
+  c();
+}
+
+void a() {
+  b();
+}
diff --git a/test/tint/expressions/user_call/nested.wgsl.expected.glsl b/test/tint/expressions/user_call/nested.wgsl.expected.glsl
new file mode 100644
index 0000000..811774e
--- /dev/null
+++ b/test/tint/expressions/user_call/nested.wgsl.expected.glsl
@@ -0,0 +1,21 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+void d() {
+}
+
+void c() {
+  d();
+}
+
+void b() {
+  c();
+}
+
+void a() {
+  b();
+}
+
diff --git a/test/tint/expressions/user_call/nested.wgsl.expected.ir.msl b/test/tint/expressions/user_call/nested.wgsl.expected.ir.msl
new file mode 100644
index 0000000..16f9ef8
--- /dev/null
+++ b/test/tint/expressions/user_call/nested.wgsl.expected.ir.msl
@@ -0,0 +1,14 @@
+#include <metal_stdlib>
+using namespace metal;
+
+void d() {
+}
+void c() {
+  d();
+}
+void b() {
+  c();
+}
+void a() {
+  b();
+}
diff --git a/test/tint/expressions/user_call/nested.wgsl.expected.msl b/test/tint/expressions/user_call/nested.wgsl.expected.msl
new file mode 100644
index 0000000..ff536f1
--- /dev/null
+++ b/test/tint/expressions/user_call/nested.wgsl.expected.msl
@@ -0,0 +1,18 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void d() {
+}
+
+void c() {
+  d();
+}
+
+void b() {
+  c();
+}
+
+void a() {
+  b();
+}
+
diff --git a/test/tint/expressions/user_call/nested.wgsl.expected.spvasm b/test/tint/expressions/user_call/nested.wgsl.expected.spvasm
new file mode 100644
index 0000000..047ecf4
--- /dev/null
+++ b/test/tint/expressions/user_call/nested.wgsl.expected.spvasm
@@ -0,0 +1,39 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 16
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %d "d"
+               OpName %c "c"
+               OpName %b "b"
+               OpName %a "a"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %d = OpFunction %void None %1
+          %6 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %c = OpFunction %void None %1
+          %8 = OpLabel
+          %9 = OpFunctionCall %void %d
+               OpReturn
+               OpFunctionEnd
+          %b = OpFunction %void None %1
+         %11 = OpLabel
+         %12 = OpFunctionCall %void %c
+               OpReturn
+               OpFunctionEnd
+          %a = OpFunction %void None %1
+         %14 = OpLabel
+         %15 = OpFunctionCall %void %b
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/expressions/user_call/nested.wgsl.expected.wgsl b/test/tint/expressions/user_call/nested.wgsl.expected.wgsl
new file mode 100644
index 0000000..2b7f165
--- /dev/null
+++ b/test/tint/expressions/user_call/nested.wgsl.expected.wgsl
@@ -0,0 +1,14 @@
+fn a() {
+  b();
+}
+
+fn b() {
+  c();
+}
+
+fn c() {
+  d();
+}
+
+fn d() {
+}
diff --git a/test/tint/expressions/user_call/no_params_no_return.wgsl b/test/tint/expressions/user_call/no_params_no_return.wgsl
new file mode 100644
index 0000000..716949c
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_no_return.wgsl
@@ -0,0 +1,9 @@
+fn c() {
+    var a = 1;
+    a = a + 2;
+}
+
+fn b() {
+    c();
+    c();
+}
diff --git a/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.dxc.hlsl b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..17aec4b
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.dxc.hlsl
@@ -0,0 +1,14 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void c() {
+  int a = 1;
+  a = (a + 2);
+}
+
+void b() {
+  c();
+  c();
+}
diff --git a/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.fxc.hlsl b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..17aec4b
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.fxc.hlsl
@@ -0,0 +1,14 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void c() {
+  int a = 1;
+  a = (a + 2);
+}
+
+void b() {
+  c();
+  c();
+}
diff --git a/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.glsl b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.glsl
new file mode 100644
index 0000000..e6fcda4
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.glsl
@@ -0,0 +1,16 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+void c() {
+  int a = 1;
+  a = (a + 2);
+}
+
+void b() {
+  c();
+  c();
+}
+
diff --git a/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.ir.msl
new file mode 100644
index 0000000..1e88c74
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.ir.msl
@@ -0,0 +1,9 @@
+SKIP: FAILED
+
+<dawn>/src/tint/lang/msl/writer/printer/printer.cc:274 internal compiler error: Switch() matched no cases. Type: tint::core::ir::Store
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
diff --git a/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.msl b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.msl
new file mode 100644
index 0000000..1c3993d
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void c() {
+  int a = 1;
+  a = as_type<int>((as_type<uint>(a) + as_type<uint>(2)));
+}
+
+void b() {
+  c();
+  c();
+}
+
diff --git a/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.spvasm b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.spvasm
new file mode 100644
index 0000000..a27d3e0
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.spvasm
@@ -0,0 +1,39 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 19
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %c "c"
+               OpName %a "a"
+               OpName %b "b"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %11 = OpConstantNull %int
+      %int_2 = OpConstant %int 2
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %c = OpFunction %void None %1
+          %6 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function %11
+               OpStore %a %int_1
+         %12 = OpLoad %int %a
+         %14 = OpIAdd %int %12 %int_2
+               OpStore %a %14
+               OpReturn
+               OpFunctionEnd
+          %b = OpFunction %void None %1
+         %16 = OpLabel
+         %17 = OpFunctionCall %void %c
+         %18 = OpFunctionCall %void %c
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.wgsl b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.wgsl
new file mode 100644
index 0000000..3dd9256
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_no_return.wgsl.expected.wgsl
@@ -0,0 +1,9 @@
+fn c() {
+  var a = 1;
+  a = (a + 2);
+}
+
+fn b() {
+  c();
+  c();
+}
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl b/test/tint/expressions/user_call/no_params_return.wgsl
new file mode 100644
index 0000000..42424f9
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_return.wgsl
@@ -0,0 +1,10 @@
+fn c() -> i32 {
+    var a = 1;
+    a = a + 2;
+    return a;
+}
+
+fn b() {
+    var b = c();
+    b += c();
+}
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl.expected.dxc.hlsl b/test/tint/expressions/user_call/no_params_return.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..411c9f3
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_return.wgsl.expected.dxc.hlsl
@@ -0,0 +1,17 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int c() {
+  int a = 1;
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c();
+  const int tint_symbol = b_1;
+  const int tint_symbol_1 = c();
+  b_1 = (tint_symbol + tint_symbol_1);
+}
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl.expected.fxc.hlsl b/test/tint/expressions/user_call/no_params_return.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..411c9f3
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_return.wgsl.expected.fxc.hlsl
@@ -0,0 +1,17 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int c() {
+  int a = 1;
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c();
+  const int tint_symbol = b_1;
+  const int tint_symbol_1 = c();
+  b_1 = (tint_symbol + tint_symbol_1);
+}
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl.expected.glsl b/test/tint/expressions/user_call/no_params_return.wgsl.expected.glsl
new file mode 100644
index 0000000..dc7ed0f
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_return.wgsl.expected.glsl
@@ -0,0 +1,19 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+int c() {
+  int a = 1;
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c();
+  int tint_symbol = b_1;
+  int tint_symbol_1 = c();
+  b_1 = (tint_symbol + tint_symbol_1);
+}
+
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/no_params_return.wgsl.expected.ir.msl
new file mode 100644
index 0000000..1e88c74
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_return.wgsl.expected.ir.msl
@@ -0,0 +1,9 @@
+SKIP: FAILED
+
+<dawn>/src/tint/lang/msl/writer/printer/printer.cc:274 internal compiler error: Switch() matched no cases. Type: tint::core::ir::Store
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl.expected.msl b/test/tint/expressions/user_call/no_params_return.wgsl.expected.msl
new file mode 100644
index 0000000..4bce79f
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_return.wgsl.expected.msl
@@ -0,0 +1,16 @@
+#include <metal_stdlib>
+
+using namespace metal;
+int c() {
+  int a = 1;
+  a = as_type<int>((as_type<uint>(a) + as_type<uint>(2)));
+  return a;
+}
+
+void b() {
+  int b_1 = c();
+  int const tint_symbol = b_1;
+  int const tint_symbol_1 = c();
+  b_1 = as_type<int>((as_type<uint>(tint_symbol) + as_type<uint>(tint_symbol_1)));
+}
+
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl.expected.spvasm b/test/tint/expressions/user_call/no_params_return.wgsl.expected.spvasm
new file mode 100644
index 0000000..c2e8fea
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_return.wgsl.expected.spvasm
@@ -0,0 +1,47 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %c "c"
+               OpName %a "a"
+               OpName %b "b"
+               OpName %b_1 "b_1"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+      %int_2 = OpConstant %int 2
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %c = OpFunction %int None %5
+          %8 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function %12
+               OpStore %a %int_1
+         %13 = OpLoad %int %a
+         %15 = OpIAdd %int %13 %int_2
+               OpStore %a %15
+         %16 = OpLoad %int %a
+               OpReturnValue %16
+               OpFunctionEnd
+          %b = OpFunction %void None %1
+         %18 = OpLabel
+        %b_1 = OpVariable %_ptr_Function_int Function %12
+         %19 = OpFunctionCall %int %c
+               OpStore %b_1 %19
+         %21 = OpLoad %int %b_1
+         %22 = OpFunctionCall %int %c
+         %23 = OpIAdd %int %21 %22
+               OpStore %b_1 %23
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/expressions/user_call/no_params_return.wgsl.expected.wgsl b/test/tint/expressions/user_call/no_params_return.wgsl.expected.wgsl
new file mode 100644
index 0000000..58fd09c
--- /dev/null
+++ b/test/tint/expressions/user_call/no_params_return.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+fn c() -> i32 {
+  var a = 1;
+  a = (a + 2);
+  return a;
+}
+
+fn b() {
+  var b = c();
+  b += c();
+}
diff --git a/test/tint/expressions/user_call/one_param_no_return.wgsl b/test/tint/expressions/user_call/one_param_no_return.wgsl
new file mode 100644
index 0000000..f6d2cb9
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_no_return.wgsl
@@ -0,0 +1,9 @@
+fn c(z: i32) {
+    var a = 1 + z;
+    a = a + 2;
+}
+
+fn b() {
+    c(2);
+    c(3);
+}
diff --git a/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.dxc.hlsl b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..33eec45
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.dxc.hlsl
@@ -0,0 +1,14 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void c(int z) {
+  int a = (1 + z);
+  a = (a + 2);
+}
+
+void b() {
+  c(2);
+  c(3);
+}
diff --git a/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.fxc.hlsl b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..33eec45
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.fxc.hlsl
@@ -0,0 +1,14 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+void c(int z) {
+  int a = (1 + z);
+  a = (a + 2);
+}
+
+void b() {
+  c(2);
+  c(3);
+}
diff --git a/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.glsl b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.glsl
new file mode 100644
index 0000000..12d1a71
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.glsl
@@ -0,0 +1,16 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+void c(int z) {
+  int a = (1 + z);
+  a = (a + 2);
+}
+
+void b() {
+  c(2);
+  c(3);
+}
+
diff --git a/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.ir.msl
new file mode 100644
index 0000000..03d2607
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.ir.msl
@@ -0,0 +1,9 @@
+SKIP: FAILED
+
+<dawn>/src/tint/lang/msl/writer/printer/printer.cc:315 internal compiler error: Switch() matched no cases. Type: tint::core::ir::FunctionParam
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
diff --git a/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.msl b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.msl
new file mode 100644
index 0000000..cd4a68d
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.msl
@@ -0,0 +1,13 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void c(int z) {
+  int a = as_type<int>((as_type<uint>(1) + as_type<uint>(z)));
+  a = as_type<int>((as_type<uint>(a) + as_type<uint>(2)));
+}
+
+void b() {
+  c(2);
+  c(3);
+}
+
diff --git a/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.spvasm b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.spvasm
new file mode 100644
index 0000000..96fdb24
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.spvasm
@@ -0,0 +1,44 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 23
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %c "c"
+               OpName %z "z"
+               OpName %a "a"
+               OpName %b "b"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %void %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %c = OpFunction %void None %5
+          %z = OpFunctionParameter %int
+          %9 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function %14
+         %11 = OpIAdd %int %int_1 %z
+               OpStore %a %11
+         %15 = OpLoad %int %a
+         %17 = OpIAdd %int %15 %int_2
+               OpStore %a %17
+               OpReturn
+               OpFunctionEnd
+          %b = OpFunction %void None %1
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %c %int_2
+         %21 = OpFunctionCall %void %c %int_3
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.wgsl b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.wgsl
new file mode 100644
index 0000000..104ad3b
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_no_return.wgsl.expected.wgsl
@@ -0,0 +1,9 @@
+fn c(z : i32) {
+  var a = (1 + z);
+  a = (a + 2);
+}
+
+fn b() {
+  c(2);
+  c(3);
+}
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl b/test/tint/expressions/user_call/one_param_return.wgsl
new file mode 100644
index 0000000..c657dac
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_return.wgsl
@@ -0,0 +1,10 @@
+fn c(z: i32) -> i32 {
+    var a = 1 + z;
+    a = a + 2;
+    return a;
+}
+
+fn b() {
+    var b = c(2);
+    b += c(3);
+}
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl.expected.dxc.hlsl b/test/tint/expressions/user_call/one_param_return.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..d11dfc8
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_return.wgsl.expected.dxc.hlsl
@@ -0,0 +1,17 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int c(int z) {
+  int a = (1 + z);
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c(2);
+  const int tint_symbol = b_1;
+  const int tint_symbol_1 = c(3);
+  b_1 = (tint_symbol + tint_symbol_1);
+}
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl.expected.fxc.hlsl b/test/tint/expressions/user_call/one_param_return.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..d11dfc8
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_return.wgsl.expected.fxc.hlsl
@@ -0,0 +1,17 @@
+[numthreads(1, 1, 1)]
+void unused_entry_point() {
+  return;
+}
+
+int c(int z) {
+  int a = (1 + z);
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c(2);
+  const int tint_symbol = b_1;
+  const int tint_symbol_1 = c(3);
+  b_1 = (tint_symbol + tint_symbol_1);
+}
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl.expected.glsl b/test/tint/expressions/user_call/one_param_return.wgsl.expected.glsl
new file mode 100644
index 0000000..b9e7dfb
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_return.wgsl.expected.glsl
@@ -0,0 +1,19 @@
+#version 310 es
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void unused_entry_point() {
+  return;
+}
+int c(int z) {
+  int a = (1 + z);
+  a = (a + 2);
+  return a;
+}
+
+void b() {
+  int b_1 = c(2);
+  int tint_symbol = b_1;
+  int tint_symbol_1 = c(3);
+  b_1 = (tint_symbol + tint_symbol_1);
+}
+
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl.expected.ir.msl b/test/tint/expressions/user_call/one_param_return.wgsl.expected.ir.msl
new file mode 100644
index 0000000..03d2607
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_return.wgsl.expected.ir.msl
@@ -0,0 +1,9 @@
+SKIP: FAILED
+
+<dawn>/src/tint/lang/msl/writer/printer/printer.cc:315 internal compiler error: Switch() matched no cases. Type: tint::core::ir::FunctionParam
+********************************************************************
+*  The tint shader compiler has encountered an unexpected error.   *
+*                                                                  *
+*  Please help us fix this issue by submitting a bug report at     *
+*  crbug.com/tint with the source program that triggered the bug.  *
+********************************************************************
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl.expected.msl b/test/tint/expressions/user_call/one_param_return.wgsl.expected.msl
new file mode 100644
index 0000000..ac77cee
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_return.wgsl.expected.msl
@@ -0,0 +1,16 @@
+#include <metal_stdlib>
+
+using namespace metal;
+int c(int z) {
+  int a = as_type<int>((as_type<uint>(1) + as_type<uint>(z)));
+  a = as_type<int>((as_type<uint>(a) + as_type<uint>(2)));
+  return a;
+}
+
+void b() {
+  int b_1 = c(2);
+  int const tint_symbol = b_1;
+  int const tint_symbol_1 = c(3);
+  b_1 = as_type<int>((as_type<uint>(tint_symbol) + as_type<uint>(tint_symbol_1)));
+}
+
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl.expected.spvasm b/test/tint/expressions/user_call/one_param_return.wgsl.expected.spvasm
new file mode 100644
index 0000000..84085d1
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_return.wgsl.expected.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 27
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %unused_entry_point "unused_entry_point"
+               OpExecutionMode %unused_entry_point LocalSize 1 1 1
+               OpName %unused_entry_point "unused_entry_point"
+               OpName %c "c"
+               OpName %z "z"
+               OpName %a "a"
+               OpName %b "b"
+               OpName %b_1 "b_1"
+       %void = OpTypeVoid
+          %1 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+          %5 = OpTypeFunction %int %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+      %int_2 = OpConstant %int 2
+      %int_3 = OpConstant %int 3
+%unused_entry_point = OpFunction %void None %1
+          %4 = OpLabel
+               OpReturn
+               OpFunctionEnd
+          %c = OpFunction %int None %5
+          %z = OpFunctionParameter %int
+          %9 = OpLabel
+          %a = OpVariable %_ptr_Function_int Function %14
+         %11 = OpIAdd %int %int_1 %z
+               OpStore %a %11
+         %15 = OpLoad %int %a
+         %17 = OpIAdd %int %15 %int_2
+               OpStore %a %17
+         %18 = OpLoad %int %a
+               OpReturnValue %18
+               OpFunctionEnd
+          %b = OpFunction %void None %1
+         %20 = OpLabel
+        %b_1 = OpVariable %_ptr_Function_int Function %14
+         %21 = OpFunctionCall %int %c %int_2
+               OpStore %b_1 %21
+         %23 = OpLoad %int %b_1
+         %24 = OpFunctionCall %int %c %int_3
+         %26 = OpIAdd %int %23 %24
+               OpStore %b_1 %26
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/expressions/user_call/one_param_return.wgsl.expected.wgsl b/test/tint/expressions/user_call/one_param_return.wgsl.expected.wgsl
new file mode 100644
index 0000000..458039a
--- /dev/null
+++ b/test/tint/expressions/user_call/one_param_return.wgsl.expected.wgsl
@@ -0,0 +1,10 @@
+fn c(z : i32) -> i32 {
+  var a = (1 + z);
+  a = (a + 2);
+  return a;
+}
+
+fn b() {
+  var b = c(2);
+  b += c(3);
+}