writer/msl: Handle private and workgroup variables
Add a transform that pushes these into the entry point and then passes
them by pointer to any functions that need them.
Since WGSL does not allow non-function storage class at
function-scope, add a DisableValidation attribute to bypass this
check.
Fixed: tint/726
Change-Id: Ic1f4cd691a54c19e77a60e8ba178508e4249bfd9
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/51962
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Auto-Submit: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/ast/disable_validation_decoration.cc b/src/ast/disable_validation_decoration.cc
index ef93c0f..59eb291 100644
--- a/src/ast/disable_validation_decoration.cc
+++ b/src/ast/disable_validation_decoration.cc
@@ -34,6 +34,8 @@
return "disable_validation__function_has_no_body";
case DisabledValidation::kBindingPointCollision:
return "disable_validation__binding_point_collision";
+ case DisabledValidation::kFunctionVarStorageClass:
+ return "disable_validation__function_var_storage_class";
}
return "<invalid>";
}
diff --git a/src/ast/disable_validation_decoration.h b/src/ast/disable_validation_decoration.h
index a825230..c94f166 100644
--- a/src/ast/disable_validation_decoration.h
+++ b/src/ast/disable_validation_decoration.h
@@ -31,6 +31,9 @@
/// When applied to a module-scoped variable, the validator will not complain
/// if two resource variables have the same binding points.
kBindingPointCollision,
+ /// When applied to a function-scoped variable, the validator will not
+ /// complain if the storage class is not `function`.
+ kFunctionVarStorageClass,
};
/// An internal decoration used to tell the validator to ignore specific
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 8058ab0..15e7f3e 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -2360,7 +2360,10 @@
}
if (!var->is_const()) {
- if (info->storage_class != ast::StorageClass::kFunction) {
+ if (info->storage_class != ast::StorageClass::kFunction &&
+ !IsValidationDisabled(
+ var->decorations(),
+ ast::DisabledValidation::kFunctionVarStorageClass)) {
if (info->storage_class != ast::StorageClass::kNone) {
diagnostics_.add_error(
"function variable has a non-function storage class",
diff --git a/src/transform/msl.cc b/src/transform/msl.cc
index 764287d..a19b155 100644
--- a/src/transform/msl.cc
+++ b/src/transform/msl.cc
@@ -14,8 +14,16 @@
#include "src/transform/msl.h"
+#include <unordered_map>
#include <utility>
+#include <vector>
+#include "src/ast/disable_validation_decoration.h"
+#include "src/program_builder.h"
+#include "src/sem/call.h"
+#include "src/sem/function.h"
+#include "src/sem/statement.h"
+#include "src/sem/variable.h"
#include "src/transform/canonicalize_entry_point_io.h"
#include "src/transform/external_texture_transform.h"
#include "src/transform/manager.h"
@@ -34,7 +42,162 @@
if (!out.program.IsValid()) {
return out;
}
- return Output{Program(std::move(out.program))};
+
+ ProgramBuilder builder;
+ CloneContext ctx(&builder, &out.program);
+ // TODO(jrprice): Consider making this a standalone transform, with target
+ // storage class(es) as transform options.
+ HandlePrivateAndWorkgroupVariables(ctx);
+ ctx.Clone();
+ return Output{Program(std::move(builder))};
+}
+
+void Msl::HandlePrivateAndWorkgroupVariables(CloneContext& ctx) const {
+ // MSL does not allow private and workgroup variables at module-scope, so we
+ // push these declarations into the entry point function and then pass them as
+ // pointer parameters to any function that references them.
+ //
+ // Since WGSL does not allow function-scope variables to have these storage
+ // classes, we annotate the new variable declarations with an attribute that
+ // bypasses that validation rule.
+ //
+ // Before:
+ // ```
+ // var<private> v : f32 = 2.0;
+ //
+ // fn foo() {
+ // v = v + 1.0;
+ // }
+ //
+ // [[stage(compute)]]
+ // fn main() {
+ // foo();
+ // }
+ // ```
+ //
+ // After:
+ // ```
+ // fn foo(v : ptr<private, f32>) {
+ // *v = *v + 1.0;
+ // }
+ //
+ // [[stage(compute)]]
+ // fn main() {
+ // var<private> v : f32 = 2.0;
+ // let v_ptr : ptr<private, f32> = &f32;
+ // foo(v_ptr);
+ // }
+ // ```
+
+ // Predetermine the list of function calls that need to be replaced.
+ using CallList = std::vector<const ast::CallExpression*>;
+ std::unordered_map<const ast::Function*, CallList> calls_to_replace;
+
+ std::vector<ast::Function*> functions_to_process;
+
+ // Build a list of functions that transitively reference any private or
+ // workgroup variables.
+ for (auto* func_ast : ctx.src->AST().Functions()) {
+ auto* func_sem = ctx.src->Sem().Get(func_ast);
+
+ bool needs_processing = false;
+ for (auto* var : func_sem->ReferencedModuleVariables()) {
+ if (var->StorageClass() == ast::StorageClass::kPrivate ||
+ var->StorageClass() == ast::StorageClass::kWorkgroup) {
+ needs_processing = true;
+ break;
+ }
+ }
+
+ if (needs_processing) {
+ functions_to_process.push_back(func_ast);
+
+ // Find all of the calls to this function that will need to be replaced.
+ for (auto* call : func_sem->CallSites()) {
+ auto* call_sem = ctx.src->Sem().Get(call);
+ calls_to_replace[call_sem->Stmt()->Function()].push_back(call);
+ }
+ }
+ }
+
+ for (auto* func_ast : functions_to_process) {
+ auto* func_sem = ctx.src->Sem().Get(func_ast);
+
+ // Map module-scope variables onto their function-scope replacement.
+ std::unordered_map<const sem::Variable*, Symbol> var_to_symbol;
+
+ for (auto* var : func_sem->ReferencedModuleVariables()) {
+ if (var->StorageClass() != ast::StorageClass::kPrivate &&
+ var->StorageClass() != ast::StorageClass::kWorkgroup) {
+ continue;
+ }
+
+ // This is the symbol for the pointer that replaces the module-scope var.
+ auto new_var_symbol = ctx.dst->Sym();
+
+ auto* store_type = CreateASTTypeFor(&ctx, var->Type()->UnwrapRef());
+
+ if (func_ast->IsEntryPoint()) {
+ // For an entry point, redeclare the variable at function-scope.
+ // Disable storage class validation on this variable.
+ auto* disable_validation =
+ ctx.dst->ASTNodes().Create<ast::DisableValidationDecoration>(
+ ctx.dst->ID(),
+ ast::DisabledValidation::kFunctionVarStorageClass);
+ auto* constructor = ctx.Clone(var->Declaration()->constructor());
+ auto* local_var =
+ ctx.dst->Var(ctx.dst->Sym(), store_type, var->StorageClass(),
+ constructor, {disable_validation});
+ ctx.InsertBefore(func_ast->body()->statements(),
+ *func_ast->body()->begin(), ctx.dst->Decl(local_var));
+
+ // Now take the address of the variable.
+ auto* ptr = ctx.dst->Const(new_var_symbol, nullptr,
+ ctx.dst->AddressOf(local_var));
+ ctx.InsertBefore(func_ast->body()->statements(),
+ *func_ast->body()->begin(), ctx.dst->Decl(ptr));
+ } else {
+ // For a regular function, redeclare the variable as a pointer function
+ // parameter.
+ auto* ptr_type = ctx.dst->ty.pointer(store_type, var->StorageClass());
+ ctx.InsertBack(func_ast->params(),
+ ctx.dst->Param(new_var_symbol, ptr_type));
+ }
+
+ // Replace all uses of the module-scope variable with the pointer
+ // replacement (dereferenced).
+ for (auto* user : var->Users()) {
+ if (user->Stmt()->Function() == func_ast) {
+ ctx.Replace(user->Declaration(), ctx.dst->Deref(new_var_symbol));
+ }
+ }
+
+ var_to_symbol[var] = new_var_symbol;
+ }
+
+ // Pass the pointers through to any functions that need them.
+ for (auto* call : calls_to_replace[func_ast]) {
+ auto* target = ctx.src->AST().Functions().Find(call->func()->symbol());
+ auto* target_sem = ctx.src->Sem().Get(target);
+
+ // Add new arguments for any referenced private and workgroup variables.
+ for (auto* target_var : target_sem->ReferencedModuleVariables()) {
+ if (target_var->StorageClass() == ast::StorageClass::kPrivate ||
+ target_var->StorageClass() == ast::StorageClass::kWorkgroup) {
+ ctx.InsertBack(call->params(),
+ ctx.dst->Expr(var_to_symbol[target_var]));
+ }
+ }
+ }
+ }
+
+ // Now remove all module-scope private and workgroup variables.
+ for (auto* var : ctx.src->AST().GlobalVariables()) {
+ if (var->declared_storage_class() == ast::StorageClass::kPrivate ||
+ var->declared_storage_class() == ast::StorageClass::kWorkgroup) {
+ ctx.Remove(ctx.src->AST().GlobalDeclarations(), var);
+ }
+ }
}
} // namespace transform
diff --git a/src/transform/msl.h b/src/transform/msl.h
index 3121b67..7e83032 100644
--- a/src/transform/msl.h
+++ b/src/transform/msl.h
@@ -34,6 +34,12 @@
/// @param data optional extra transform-specific input data
/// @returns the transformation result
Output Run(const Program* program, const DataMap& data = {}) override;
+
+ private:
+ /// Pushes module-scope variables with private or workgroup storage classes
+ /// into the entry point function, and passes them as function parameters to
+ /// any functions that need them.
+ void HandlePrivateAndWorkgroupVariables(CloneContext& ctx) const;
};
} // namespace transform
diff --git a/src/transform/msl_test.cc b/src/transform/msl_test.cc
index d257b2b..70f50d8 100644
--- a/src/transform/msl_test.cc
+++ b/src/transform/msl_test.cc
@@ -20,6 +20,210 @@
namespace transform {
namespace {
+using MslTest = TransformTest;
+
+TEST_F(MslTest, HandlePrivateAndWorkgroupVariables_Basic) {
+ auto* src = R"(
+var<private> p : f32;
+var<workgroup> w : f32;
+
+[[stage(compute)]]
+fn main() {
+ w = p;
+}
+)";
+
+ auto* expect = R"(
+[[stage(compute)]]
+fn main() {
+ [[internal(disable_validation__function_var_storage_class)]] var<workgroup> tint_symbol_1 : f32;
+ let tint_symbol = &(tint_symbol_1);
+ [[internal(disable_validation__function_var_storage_class)]] var<private> tint_symbol_3 : f32;
+ let tint_symbol_2 = &(tint_symbol_3);
+ *(tint_symbol) = *(tint_symbol_2);
+}
+)";
+
+ auto got = Run<Msl>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(MslTest, HandlePrivateAndWorkgroupVariables_FunctionCalls) {
+ auto* src = R"(
+var<private> p : f32;
+var<workgroup> w : f32;
+
+fn no_uses() {
+}
+
+fn bar(a : f32, b : f32) {
+ p = a;
+ w = b;
+}
+
+fn foo(a : f32) {
+ let b : f32 = 2.0;
+ bar(a, b);
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main() {
+ foo(1.0);
+}
+)";
+
+ auto* expect = R"(
+fn no_uses() {
+}
+
+fn bar(a : f32, b : f32, tint_symbol : ptr<private, f32>, tint_symbol_1 : ptr<workgroup, f32>) {
+ *(tint_symbol) = a;
+ *(tint_symbol_1) = b;
+}
+
+fn foo(a : f32, tint_symbol_2 : ptr<private, f32>, tint_symbol_3 : ptr<workgroup, f32>) {
+ let b : f32 = 2.0;
+ bar(a, b, tint_symbol_2, tint_symbol_3);
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main() {
+ [[internal(disable_validation__function_var_storage_class)]] var<private> tint_symbol_5 : f32;
+ let tint_symbol_4 = &(tint_symbol_5);
+ [[internal(disable_validation__function_var_storage_class)]] var<workgroup> tint_symbol_7 : f32;
+ let tint_symbol_6 = &(tint_symbol_7);
+ foo(1.0, tint_symbol_4, tint_symbol_6);
+}
+)";
+
+ auto got = Run<Msl>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(MslTest, HandlePrivateAndWorkgroupVariables_Constructors) {
+ auto* src = R"(
+var<private> a : f32 = 1.0;
+var<private> b : f32 = f32();
+
+[[stage(compute)]]
+fn main() {
+ let x : f32 = a + b;
+}
+)";
+
+ auto* expect = R"(
+[[stage(compute)]]
+fn main() {
+ [[internal(disable_validation__function_var_storage_class)]] var<private> tint_symbol_1 : f32 = 1.0;
+ let tint_symbol = &(tint_symbol_1);
+ [[internal(disable_validation__function_var_storage_class)]] var<private> tint_symbol_3 : f32 = f32();
+ let tint_symbol_2 = &(tint_symbol_3);
+ let x : f32 = (*(tint_symbol) + *(tint_symbol_2));
+}
+)";
+
+ auto got = Run<Msl>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(MslTest, HandlePrivateAndWorkgroupVariables_Pointers) {
+ auto* src = R"(
+var<private> p : f32;
+var<workgroup> w : f32;
+
+[[stage(compute)]]
+fn main() {
+ let p_ptr : ptr<private, f32> = &p;
+ let w_ptr : ptr<workgroup, f32> = &w;
+ let x : f32 = *p_ptr + *w_ptr;
+ *p_ptr = x;
+}
+)";
+
+ auto* expect = R"(
+[[stage(compute)]]
+fn main() {
+ [[internal(disable_validation__function_var_storage_class)]] var<private> tint_symbol_1 : f32;
+ let tint_symbol = &(tint_symbol_1);
+ [[internal(disable_validation__function_var_storage_class)]] var<workgroup> tint_symbol_3 : f32;
+ let tint_symbol_2 = &(tint_symbol_3);
+ let p_ptr : ptr<private, f32> = &(*(tint_symbol));
+ let w_ptr : ptr<workgroup, f32> = &(*(tint_symbol_2));
+ let x : f32 = (*(p_ptr) + *(w_ptr));
+ *(p_ptr) = x;
+}
+)";
+
+ auto got = Run<Msl>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(MslTest, HandlePrivateAndWorkgroupVariables_UnusedVariables) {
+ auto* src = R"(
+var<private> p : f32;
+var<workgroup> w : f32;
+
+[[stage(compute)]]
+fn main() {
+}
+)";
+
+ auto* expect = R"(
+[[stage(compute)]]
+fn main() {
+}
+)";
+
+ auto got = Run<Msl>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(MslTest, HandlePrivateAndWorkgroupVariables_OtherVariables) {
+ auto* src = R"(
+[[block]]
+struct S {
+};
+
+[[group(0), binding(0)]]
+var<uniform> u : S;
+
+[[stage(compute)]]
+fn main() {
+}
+)";
+
+ auto* expect = R"(
+[[block]]
+struct S {
+};
+
+[[group(0), binding(0)]] var<uniform> u : S;
+
+[[stage(compute)]]
+fn main() {
+}
+)";
+
+ auto got = Run<Msl>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(MslTest, HandlePrivateAndWorkgroupVariables_EmtpyModule) {
+ auto* src = "";
+
+ auto got = Run<Msl>(src);
+
+ EXPECT_EQ(src, str(got));
+}
+
} // namespace
} // namespace transform
} // namespace tint
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index ed64bf9..11ee9a1 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -22,6 +22,7 @@
#include "src/ast/alias.h"
#include "src/ast/bool_literal.h"
#include "src/ast/call_statement.h"
+#include "src/ast/disable_validation_decoration.h"
#include "src/ast/fallthrough_statement.h"
#include "src/ast/float_literal.h"
#include "src/ast/module.h"
@@ -107,9 +108,10 @@
switch (sem->StorageClass()) {
case ast::StorageClass::kPrivate:
case ast::StorageClass::kWorkgroup:
- TINT_UNIMPLEMENTED(diagnostics_)
- << "crbug.com/tint/726: module-scope private and workgroup "
- "variables not yet implemented";
+ // These are pushed into the entry point by the sanitizer.
+ TINT_ICE(diagnostics_)
+ << "module-scope variables in the private/workgroup storage "
+ "class should have been handled by the MSL sanitizer";
break;
default:
break; // Handled by another code path
@@ -2198,11 +2200,28 @@
auto* decl = var->Declaration();
- // TODO(dsinclair): Handle variable decorations
- if (!decl->decorations().empty()) {
- diagnostics_.add_error("Variable decorations are not handled yet");
- return false;
+ for (auto* deco : decl->decorations()) {
+ if (!deco->Is<ast::InternalDecoration>()) {
+ TINT_ICE(diagnostics_) << "unexpected variable decoration";
+ return false;
+ }
}
+
+ switch (var->StorageClass()) {
+ case ast::StorageClass::kFunction:
+ case ast::StorageClass::kNone:
+ break;
+ case ast::StorageClass::kPrivate:
+ out_ << "thread ";
+ break;
+ case ast::StorageClass::kWorkgroup:
+ out_ << "threadgroup ";
+ break;
+ default:
+ TINT_ICE(diagnostics_) << "unhandled variable storage class";
+ return false;
+ }
+
auto* type = var->Type()->UnwrapRef();
std::string name = program_->Symbols().NameFor(decl->symbol());
@@ -2225,6 +2244,7 @@
}
} else if (var->StorageClass() == ast::StorageClass::kPrivate ||
var->StorageClass() == ast::StorageClass::kFunction ||
+ var->StorageClass() == ast::StorageClass::kWorkgroup ||
var->StorageClass() == ast::StorageClass::kNone ||
var->StorageClass() == ast::StorageClass::kOutput) {
if (!EmitZeroValue(type)) {
diff --git a/src/writer/msl/generator_impl_type_test.cc b/src/writer/msl/generator_impl_type_test.cc
index 8054ebd..9c682a6 100644
--- a/src/writer/msl/generator_impl_type_test.cc
+++ b/src/writer/msl/generator_impl_type_test.cc
@@ -160,15 +160,14 @@
EXPECT_EQ(gen.result(), "float2x3");
}
-// TODO(dsinclair): How to annotate as workgroup?
-TEST_F(MslGeneratorImplTest, DISABLED_EmitType_Pointer) {
+TEST_F(MslGeneratorImplTest, EmitType_Pointer) {
auto* f32 = create<sem::F32>();
auto* p = create<sem::Pointer>(f32, ast::StorageClass::kWorkgroup);
GeneratorImpl& gen = Build();
ASSERT_TRUE(gen.EmitType(p, "")) << gen.error();
- EXPECT_EQ(gen.result(), "float*");
+ EXPECT_EQ(gen.result(), "threadgroup float* ");
}
TEST_F(MslGeneratorImplTest, EmitType_Struct) {
diff --git a/src/writer/msl/generator_impl_variable_decl_statement_test.cc b/src/writer/msl/generator_impl_variable_decl_statement_test.cc
index 487a5a8..f403097 100644
--- a/src/writer/msl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/msl/generator_impl_variable_decl_statement_test.cc
@@ -110,34 +110,44 @@
EXPECT_EQ(gen.result(), " float3x2 a = float3x2(0.0f);\n");
}
-// TODO(crbug.com/tint/726): module-scope private and workgroup variables not
-// yet implemented
-TEST_F(MslGeneratorImplTest, DISABLED_Emit_VariableDeclStatement_Private) {
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Private) {
Global("a", ty.f32(), ast::StorageClass::kPrivate);
WrapInFunction(Expr("a"));
- GeneratorImpl& gen = Build();
+ GeneratorImpl& gen = SanitizeAndBuild();
gen.increment_indent();
ASSERT_TRUE(gen.Generate()) << gen.error();
- EXPECT_THAT(gen.result(), HasSubstr(" float a = 0.0f;\n"));
+ EXPECT_THAT(gen.result(), HasSubstr("thread float tint_symbol_2 = 0.0f;\n"));
}
-// TODO(crbug.com/tint/726): module-scope private and workgroup variables not
-// yet implemented
-TEST_F(MslGeneratorImplTest,
- DISABLED_Emit_VariableDeclStatement_Initializer_Private) {
- Global("initializer", ty.f32(), ast::StorageClass::kInput);
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_Private) {
+ GlobalConst("initializer", ty.f32(), Expr(0.f));
Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr("initializer"));
WrapInFunction(Expr("a"));
- GeneratorImpl& gen = Build();
+ GeneratorImpl& gen = SanitizeAndBuild();
ASSERT_TRUE(gen.Generate()) << gen.error();
- EXPECT_THAT(gen.result(), HasSubstr("float a = initializer;\n"));
+ EXPECT_THAT(gen.result(),
+ HasSubstr("thread float tint_symbol_2 = initializer;\n"));
+}
+
+TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Workgroup) {
+ Global("a", ty.f32(), ast::StorageClass::kWorkgroup);
+
+ WrapInFunction(Expr("a"));
+
+ GeneratorImpl& gen = SanitizeAndBuild();
+
+ gen.increment_indent();
+
+ ASSERT_TRUE(gen.Generate()) << gen.error();
+ EXPECT_THAT(gen.result(),
+ HasSubstr("threadgroup float tint_symbol_2 = 0.0f;\n"));
}
TEST_F(MslGeneratorImplTest, Emit_VariableDeclStatement_Initializer_ZeroVec) {
diff --git a/test/bug/tint/749.spvasm.expected.msl b/test/bug/tint/749.spvasm.expected.msl
index 1b2d3db..5a7c08c 100644
--- a/test/bug/tint/749.spvasm.expected.msl
+++ b/test/bug/tint/749.spvasm.expected.msl
@@ -1 +1 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+SKIP: crbug.com/tint/831
diff --git a/test/ptr_ref/load/global/i32.spvasm.expected.msl b/test/ptr_ref/load/global/i32.spvasm.expected.msl
index 1b2d3db..c59e28e 100644
--- a/test/ptr_ref/load/global/i32.spvasm.expected.msl
+++ b/test/ptr_ref/load/global/i32.spvasm.expected.msl
@@ -1 +1,11 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+#include <metal_stdlib>
+
+using namespace metal;
+kernel void tint_symbol() {
+ thread int tint_symbol_2 = 0;
+ thread int* const tint_symbol_1 = &(tint_symbol_2);
+ int const x_9 = *(tint_symbol_1);
+ int const x_11 = (x_9 + 1);
+ return;
+}
+
diff --git a/test/ptr_ref/load/global/i32.wgsl.expected.msl b/test/ptr_ref/load/global/i32.wgsl.expected.msl
index 1b2d3db..9431488 100644
--- a/test/ptr_ref/load/global/i32.wgsl.expected.msl
+++ b/test/ptr_ref/load/global/i32.wgsl.expected.msl
@@ -1 +1,11 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+#include <metal_stdlib>
+
+using namespace metal;
+kernel void tint_symbol() {
+ thread int tint_symbol_2 = 0;
+ thread int* const tint_symbol_1 = &(tint_symbol_2);
+ int const i = *(tint_symbol_1);
+ int const use = (i + 1);
+ return;
+}
+
diff --git a/test/ptr_ref/load/global/struct_field.spvasm.expected.msl b/test/ptr_ref/load/global/struct_field.spvasm.expected.msl
index 1b2d3db..5a7c08c 100644
--- a/test/ptr_ref/load/global/struct_field.spvasm.expected.msl
+++ b/test/ptr_ref/load/global/struct_field.spvasm.expected.msl
@@ -1 +1 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+SKIP: crbug.com/tint/831
diff --git a/test/ptr_ref/load/global/struct_field.wgsl.expected.msl b/test/ptr_ref/load/global/struct_field.wgsl.expected.msl
index 1b2d3db..5a7c08c 100644
--- a/test/ptr_ref/load/global/struct_field.wgsl.expected.msl
+++ b/test/ptr_ref/load/global/struct_field.wgsl.expected.msl
@@ -1 +1 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+SKIP: crbug.com/tint/831
diff --git a/test/ptr_ref/load/local/ptr_private.wgsl.expected.msl b/test/ptr_ref/load/local/ptr_private.wgsl.expected.msl
index 1b2d3db..b41e142 100644
--- a/test/ptr_ref/load/local/ptr_private.wgsl.expected.msl
+++ b/test/ptr_ref/load/local/ptr_private.wgsl.expected.msl
@@ -1 +1,11 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+#include <metal_stdlib>
+
+using namespace metal;
+kernel void tint_symbol() {
+ thread int tint_symbol_2 = 123;
+ thread int* const tint_symbol_1 = &(tint_symbol_2);
+ thread int* const p = &(*(tint_symbol_1));
+ int const use = (*(p) + 1);
+ return;
+}
+
diff --git a/test/ptr_ref/load/local/ptr_workgroup.wgsl.expected.msl b/test/ptr_ref/load/local/ptr_workgroup.wgsl.expected.msl
index 1b2d3db..983ba40 100644
--- a/test/ptr_ref/load/local/ptr_workgroup.wgsl.expected.msl
+++ b/test/ptr_ref/load/local/ptr_workgroup.wgsl.expected.msl
@@ -1 +1,12 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+#include <metal_stdlib>
+
+using namespace metal;
+kernel void tint_symbol() {
+ threadgroup int tint_symbol_2 = 0;
+ threadgroup int* const tint_symbol_1 = &(tint_symbol_2);
+ *(tint_symbol_1) = 123;
+ threadgroup int* const p = &(*(tint_symbol_1));
+ int const use = (*(p) + 1);
+ return;
+}
+
diff --git a/test/ptr_ref/store/global/i32.spvasm.expected.msl b/test/ptr_ref/store/global/i32.spvasm.expected.msl
index 1b2d3db..92fbc80 100644
--- a/test/ptr_ref/store/global/i32.spvasm.expected.msl
+++ b/test/ptr_ref/store/global/i32.spvasm.expected.msl
@@ -1 +1,11 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+#include <metal_stdlib>
+
+using namespace metal;
+kernel void tint_symbol() {
+ thread int tint_symbol_2 = 0;
+ thread int* const tint_symbol_1 = &(tint_symbol_2);
+ *(tint_symbol_1) = 123;
+ *(tint_symbol_1) = ((100 + 20) + 3);
+ return;
+}
+
diff --git a/test/ptr_ref/store/global/i32.wgsl.expected.msl b/test/ptr_ref/store/global/i32.wgsl.expected.msl
index 1b2d3db..92fbc80 100644
--- a/test/ptr_ref/store/global/i32.wgsl.expected.msl
+++ b/test/ptr_ref/store/global/i32.wgsl.expected.msl
@@ -1 +1,11 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+#include <metal_stdlib>
+
+using namespace metal;
+kernel void tint_symbol() {
+ thread int tint_symbol_2 = 0;
+ thread int* const tint_symbol_1 = &(tint_symbol_2);
+ *(tint_symbol_1) = 123;
+ *(tint_symbol_1) = ((100 + 20) + 3);
+ return;
+}
+
diff --git a/test/ptr_ref/store/global/struct_field.spvasm.expected.msl b/test/ptr_ref/store/global/struct_field.spvasm.expected.msl
index 1b2d3db..3bde281 100644
--- a/test/ptr_ref/store/global/struct_field.spvasm.expected.msl
+++ b/test/ptr_ref/store/global/struct_field.spvasm.expected.msl
@@ -1 +1 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
+SKIP: crbug.com/tint/831
\ No newline at end of file
diff --git a/test/types/module_scope_declarations.wgsl b/test/types/module_scope_declarations.wgsl
deleted file mode 100644
index 6be0c4a..0000000
--- a/test/types/module_scope_declarations.wgsl
+++ /dev/null
@@ -1,27 +0,0 @@
-struct S {
-};
-
-var<private> bool_var : bool = bool();
-let bool_let : bool = bool();
-var<private> i32_var : i32 = i32();
-let i32_let : i32 = i32();
-var<private> u32_var : u32 = u32();
-let u32_let : u32 = u32();
-var<private> f32_var : f32 = f32();
-let f32_let : f32 = f32();
-var<private> v2i32_var : vec2<i32> = vec2<i32>();
-let v2i32_let : vec2<i32> = vec2<i32>();
-var<private> v3u32_var : vec3<u32> = vec3<u32>();
-let v3u32_let : vec3<u32> = vec3<u32>();
-var<private> v4f32_var : vec4<f32> = vec4<f32>();
-let v4f32_let : vec4<f32> = vec4<f32>();
-var<private> m2x3_var : mat2x3<f32> = mat2x3<f32>();
-let m3x4_let : mat3x4<f32> = mat3x4<f32>();
-var<private> arr_var : array<f32, 4> = array<f32, 4>();
-let arr_let : array<f32, 4> = array<f32, 4>();
-var<private> struct_var : S = S();
-let struct_let : S = S();
-
-[[stage(compute)]]
-fn main() {
-}
diff --git a/test/types/module_scope_declarations.wgsl.expected.msl b/test/types/module_scope_declarations.wgsl.expected.msl
deleted file mode 100644
index 1b2d3db..0000000
--- a/test/types/module_scope_declarations.wgsl.expected.msl
+++ /dev/null
@@ -1 +0,0 @@
-SKIP: TINT_UNIMPLEMENTED crbug.com/tint/726: module-scope private and workgroup variables not yet implemented
diff --git a/test/types/module_scope_declarations.wgsl.expected.spvasm b/test/types/module_scope_declarations.wgsl.expected.spvasm
deleted file mode 100644
index 9d09d23..0000000
--- a/test/types/module_scope_declarations.wgsl.expected.spvasm
+++ /dev/null
@@ -1,82 +0,0 @@
-; SPIR-V
-; Version: 1.3
-; Generator: Google Tint Compiler; 0
-; Bound: 49
-; Schema: 0
- OpCapability Shader
- OpMemoryModel Logical GLSL450
- OpEntryPoint GLCompute %main "main"
- OpExecutionMode %main LocalSize 1 1 1
- OpName %bool_var "bool_var"
- OpName %bool_let "bool_let"
- OpName %i32_var "i32_var"
- OpName %i32_let "i32_let"
- OpName %u32_var "u32_var"
- OpName %u32_let "u32_let"
- OpName %f32_var "f32_var"
- OpName %f32_let "f32_let"
- OpName %v2i32_var "v2i32_var"
- OpName %v2i32_let "v2i32_let"
- OpName %v3u32_var "v3u32_var"
- OpName %v3u32_let "v3u32_let"
- OpName %v4f32_var "v4f32_var"
- OpName %v4f32_let "v4f32_let"
- OpName %m2x3_var "m2x3_var"
- OpName %m3x4_let "m3x4_let"
- OpName %arr_var "arr_var"
- OpName %arr_let "arr_let"
- OpName %S "S"
- OpName %struct_var "struct_var"
- OpName %struct_let "struct_let"
- OpName %main "main"
- OpDecorate %_arr_float_uint_4 ArrayStride 4
- %bool = OpTypeBool
- %bool_let = OpConstantNull %bool
-%_ptr_Private_bool = OpTypePointer Private %bool
- %bool_var = OpVariable %_ptr_Private_bool Private %bool_let
- %int = OpTypeInt 32 1
- %i32_let = OpConstantNull %int
-%_ptr_Private_int = OpTypePointer Private %int
- %i32_var = OpVariable %_ptr_Private_int Private %i32_let
- %uint = OpTypeInt 32 0
- %u32_let = OpConstantNull %uint
-%_ptr_Private_uint = OpTypePointer Private %uint
- %u32_var = OpVariable %_ptr_Private_uint Private %u32_let
- %float = OpTypeFloat 32
- %f32_let = OpConstantNull %float
-%_ptr_Private_float = OpTypePointer Private %float
- %f32_var = OpVariable %_ptr_Private_float Private %f32_let
- %v2int = OpTypeVector %int 2
- %v2i32_let = OpConstantNull %v2int
-%_ptr_Private_v2int = OpTypePointer Private %v2int
- %v2i32_var = OpVariable %_ptr_Private_v2int Private %v2i32_let
- %v3uint = OpTypeVector %uint 3
- %v3u32_let = OpConstantNull %v3uint
-%_ptr_Private_v3uint = OpTypePointer Private %v3uint
- %v3u32_var = OpVariable %_ptr_Private_v3uint Private %v3u32_let
- %v4float = OpTypeVector %float 4
- %v4f32_let = OpConstantNull %v4float
-%_ptr_Private_v4float = OpTypePointer Private %v4float
- %v4f32_var = OpVariable %_ptr_Private_v4float Private %v4f32_let
- %v3float = OpTypeVector %float 3
-%mat2v3float = OpTypeMatrix %v3float 2
- %31 = OpConstantNull %mat2v3float
-%_ptr_Private_mat2v3float = OpTypePointer Private %mat2v3float
- %m2x3_var = OpVariable %_ptr_Private_mat2v3float Private %31
-%mat3v4float = OpTypeMatrix %v4float 3
- %m3x4_let = OpConstantNull %mat3v4float
- %uint_4 = OpConstant %uint 4
-%_arr_float_uint_4 = OpTypeArray %float %uint_4
- %arr_let = OpConstantNull %_arr_float_uint_4
-%_ptr_Private__arr_float_uint_4 = OpTypePointer Private %_arr_float_uint_4
- %arr_var = OpVariable %_ptr_Private__arr_float_uint_4 Private %arr_let
- %S = OpTypeStruct
- %struct_let = OpConstantNull %S
-%_ptr_Private_S = OpTypePointer Private %S
- %struct_var = OpVariable %_ptr_Private_S Private %struct_let
- %void = OpTypeVoid
- %45 = OpTypeFunction %void
- %main = OpFunction %void None %45
- %48 = OpLabel
- OpReturn
- OpFunctionEnd
diff --git a/test/types/module_scope_declarations.wgsl.expected.wgsl b/test/types/module_scope_declarations.wgsl.expected.wgsl
deleted file mode 100644
index e6edc6e..0000000
--- a/test/types/module_scope_declarations.wgsl.expected.wgsl
+++ /dev/null
@@ -1,46 +0,0 @@
-struct S {
-};
-
-var<private> bool_var : bool = bool();
-
-let bool_let : bool = bool();
-
-var<private> i32_var : i32 = i32();
-
-let i32_let : i32 = i32();
-
-var<private> u32_var : u32 = u32();
-
-let u32_let : u32 = u32();
-
-var<private> f32_var : f32 = f32();
-
-let f32_let : f32 = f32();
-
-var<private> v2i32_var : vec2<i32> = vec2<i32>();
-
-let v2i32_let : vec2<i32> = vec2<i32>();
-
-var<private> v3u32_var : vec3<u32> = vec3<u32>();
-
-let v3u32_let : vec3<u32> = vec3<u32>();
-
-var<private> v4f32_var : vec4<f32> = vec4<f32>();
-
-let v4f32_let : vec4<f32> = vec4<f32>();
-
-var<private> m2x3_var : mat2x3<f32> = mat2x3<f32>();
-
-let m3x4_let : mat3x4<f32> = mat3x4<f32>();
-
-var<private> arr_var : array<f32, 4> = array<f32, 4>();
-
-let arr_let : array<f32, 4> = array<f32, 4>();
-
-var<private> struct_var : S = S();
-
-let struct_let : S = S();
-
-[[stage(compute)]]
-fn main() {
-}
diff --git a/test/types/module_scope_let.wgsl b/test/types/module_scope_let.wgsl
new file mode 100644
index 0000000..16181dd
--- /dev/null
+++ b/test/types/module_scope_let.wgsl
@@ -0,0 +1,17 @@
+struct S {
+};
+
+let bool_let : bool = bool();
+let i32_let : i32 = i32();
+let u32_let : u32 = u32();
+let f32_let : f32 = f32();
+let v2i32_let : vec2<i32> = vec2<i32>();
+let v3u32_let : vec3<u32> = vec3<u32>();
+let v4f32_let : vec4<f32> = vec4<f32>();
+let m3x4_let : mat3x4<f32> = mat3x4<f32>();
+let arr_let : array<f32, 4> = array<f32, 4>();
+let struct_let : S = S();
+
+[[stage(compute)]]
+fn main() {
+}
diff --git a/test/types/module_scope_declarations.wgsl.expected.hlsl b/test/types/module_scope_let.wgsl.expected.hlsl
similarity index 100%
rename from test/types/module_scope_declarations.wgsl.expected.hlsl
rename to test/types/module_scope_let.wgsl.expected.hlsl
diff --git a/test/types/module_scope_let.wgsl.expected.msl b/test/types/module_scope_let.wgsl.expected.msl
new file mode 100644
index 0000000..e3a2d4e
--- /dev/null
+++ b/test/types/module_scope_let.wgsl.expected.msl
@@ -0,0 +1,20 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S {
+};
+
+constant bool bool_let = bool();
+constant int i32_let = int();
+constant uint u32_let = uint();
+constant float f32_let = float();
+constant int2 v2i32_let = int2();
+constant uint3 v3u32_let = uint3();
+constant float4 v4f32_let = float4();
+constant float3x4 m3x4_let = float3x4();
+constant float arr_let[4] = {};
+constant S struct_let = {};
+kernel void tint_symbol() {
+ return;
+}
+
diff --git a/test/types/module_scope_let.wgsl.expected.spvasm b/test/types/module_scope_let.wgsl.expected.spvasm
new file mode 100644
index 0000000..947c62f
--- /dev/null
+++ b/test/types/module_scope_let.wgsl.expected.spvasm
@@ -0,0 +1,49 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpName %bool_let "bool_let"
+ OpName %i32_let "i32_let"
+ OpName %u32_let "u32_let"
+ OpName %f32_let "f32_let"
+ OpName %v2i32_let "v2i32_let"
+ OpName %v3u32_let "v3u32_let"
+ OpName %v4f32_let "v4f32_let"
+ OpName %m3x4_let "m3x4_let"
+ OpName %arr_let "arr_let"
+ OpName %S "S"
+ OpName %struct_let "struct_let"
+ OpName %main "main"
+ OpDecorate %_arr_float_uint_4 ArrayStride 4
+ %bool = OpTypeBool
+ %bool_let = OpConstantNull %bool
+ %int = OpTypeInt 32 1
+ %i32_let = OpConstantNull %int
+ %uint = OpTypeInt 32 0
+ %u32_let = OpConstantNull %uint
+ %float = OpTypeFloat 32
+ %f32_let = OpConstantNull %float
+ %v2int = OpTypeVector %int 2
+ %v2i32_let = OpConstantNull %v2int
+ %v3uint = OpTypeVector %uint 3
+ %v3u32_let = OpConstantNull %v3uint
+ %v4float = OpTypeVector %float 4
+ %v4f32_let = OpConstantNull %v4float
+%mat3v4float = OpTypeMatrix %v4float 3
+ %m3x4_let = OpConstantNull %mat3v4float
+ %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+ %arr_let = OpConstantNull %_arr_float_uint_4
+ %S = OpTypeStruct
+ %struct_let = OpConstantNull %S
+ %void = OpTypeVoid
+ %22 = OpTypeFunction %void
+ %main = OpFunction %void None %22
+ %25 = OpLabel
+ OpReturn
+ OpFunctionEnd
diff --git a/test/types/module_scope_let.wgsl.expected.wgsl b/test/types/module_scope_let.wgsl.expected.wgsl
new file mode 100644
index 0000000..eebe89e
--- /dev/null
+++ b/test/types/module_scope_let.wgsl.expected.wgsl
@@ -0,0 +1,26 @@
+struct S {
+};
+
+let bool_let : bool = bool();
+
+let i32_let : i32 = i32();
+
+let u32_let : u32 = u32();
+
+let f32_let : f32 = f32();
+
+let v2i32_let : vec2<i32> = vec2<i32>();
+
+let v3u32_let : vec3<u32> = vec3<u32>();
+
+let v4f32_let : vec4<f32> = vec4<f32>();
+
+let m3x4_let : mat3x4<f32> = mat3x4<f32>();
+
+let arr_let : array<f32, 4> = array<f32, 4>();
+
+let struct_let : S = S();
+
+[[stage(compute)]]
+fn main() {
+}
diff --git a/test/types/module_scope_var.wgsl b/test/types/module_scope_var.wgsl
new file mode 100644
index 0000000..c2442e4
--- /dev/null
+++ b/test/types/module_scope_var.wgsl
@@ -0,0 +1,28 @@
+struct S {
+};
+
+var<private> bool_var : bool;
+var<private> i32_var : i32;
+var<private> u32_var : u32;
+var<private> f32_var : f32;
+var<private> v2i32_var : vec2<i32>;
+var<private> v3u32_var : vec3<u32>;
+var<private> v4f32_var : vec4<f32>;
+var<private> m2x3_var : mat2x3<f32>;
+var<private> arr_var : array<f32, 4>;
+var<private> struct_var : S;
+
+[[stage(compute)]]
+fn main() {
+ // Reference the module-scope variables to stop them from being removed.
+ bool_var = bool();
+ i32_var = i32();
+ u32_var = u32();
+ f32_var = f32();
+ v2i32_var = vec2<i32>();
+ v3u32_var = vec3<u32>();
+ v4f32_var = vec4<f32>();
+ m2x3_var = mat2x3<f32>();
+ arr_var = array<f32, 4>();
+ struct_var = S();
+}
diff --git a/test/types/module_scope_var.wgsl.expected.hlsl b/test/types/module_scope_var.wgsl.expected.hlsl
new file mode 100644
index 0000000..b28e796
--- /dev/null
+++ b/test/types/module_scope_var.wgsl.expected.hlsl
@@ -0,0 +1,31 @@
+struct S {
+};
+
+static bool bool_var;
+static int i32_var;
+static uint u32_var;
+static float f32_var;
+static int2 v2i32_var;
+static uint3 v3u32_var;
+static float4 v4f32_var;
+static float2x3 m2x3_var;
+static float arr_var[4];
+static S struct_var;
+
+[numthreads(1, 1, 1)]
+void main() {
+ bool_var = false;
+ i32_var = 0;
+ u32_var = 0u;
+ f32_var = 0.0f;
+ v2i32_var = int2(0, 0);
+ v3u32_var = uint3(0u, 0u, 0u);
+ v4f32_var = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ m2x3_var = float2x3(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+ const float tint_symbol[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ arr_var = tint_symbol;
+ const S tint_symbol_1 = {};
+ struct_var = tint_symbol_1;
+ return;
+}
+
diff --git a/test/types/module_scope_var.wgsl.expected.msl b/test/types/module_scope_var.wgsl.expected.msl
new file mode 100644
index 0000000..77696be
--- /dev/null
+++ b/test/types/module_scope_var.wgsl.expected.msl
@@ -0,0 +1 @@
+SKIP: crbug.com/tint/814
diff --git a/test/types/module_scope_var.wgsl.expected.spvasm b/test/types/module_scope_var.wgsl.expected.spvasm
new file mode 100644
index 0000000..2fa75f1
--- /dev/null
+++ b/test/types/module_scope_var.wgsl.expected.spvasm
@@ -0,0 +1,80 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 47
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpName %bool_var "bool_var"
+ OpName %i32_var "i32_var"
+ OpName %u32_var "u32_var"
+ OpName %f32_var "f32_var"
+ OpName %v2i32_var "v2i32_var"
+ OpName %v3u32_var "v3u32_var"
+ OpName %v4f32_var "v4f32_var"
+ OpName %m2x3_var "m2x3_var"
+ OpName %arr_var "arr_var"
+ OpName %S "S"
+ OpName %struct_var "struct_var"
+ OpName %main "main"
+ OpDecorate %_arr_float_uint_4 ArrayStride 4
+ %bool = OpTypeBool
+%_ptr_Private_bool = OpTypePointer Private %bool
+ %4 = OpConstantNull %bool
+ %bool_var = OpVariable %_ptr_Private_bool Private %4
+ %int = OpTypeInt 32 1
+%_ptr_Private_int = OpTypePointer Private %int
+ %8 = OpConstantNull %int
+ %i32_var = OpVariable %_ptr_Private_int Private %8
+ %uint = OpTypeInt 32 0
+%_ptr_Private_uint = OpTypePointer Private %uint
+ %12 = OpConstantNull %uint
+ %u32_var = OpVariable %_ptr_Private_uint Private %12
+ %float = OpTypeFloat 32
+%_ptr_Private_float = OpTypePointer Private %float
+ %16 = OpConstantNull %float
+ %f32_var = OpVariable %_ptr_Private_float Private %16
+ %v2int = OpTypeVector %int 2
+%_ptr_Private_v2int = OpTypePointer Private %v2int
+ %20 = OpConstantNull %v2int
+ %v2i32_var = OpVariable %_ptr_Private_v2int Private %20
+ %v3uint = OpTypeVector %uint 3
+%_ptr_Private_v3uint = OpTypePointer Private %v3uint
+ %24 = OpConstantNull %v3uint
+ %v3u32_var = OpVariable %_ptr_Private_v3uint Private %24
+ %v4float = OpTypeVector %float 4
+%_ptr_Private_v4float = OpTypePointer Private %v4float
+ %28 = OpConstantNull %v4float
+ %v4f32_var = OpVariable %_ptr_Private_v4float Private %28
+ %v3float = OpTypeVector %float 3
+%mat2v3float = OpTypeMatrix %v3float 2
+%_ptr_Private_mat2v3float = OpTypePointer Private %mat2v3float
+ %33 = OpConstantNull %mat2v3float
+ %m2x3_var = OpVariable %_ptr_Private_mat2v3float Private %33
+ %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+%_ptr_Private__arr_float_uint_4 = OpTypePointer Private %_arr_float_uint_4
+ %38 = OpConstantNull %_arr_float_uint_4
+ %arr_var = OpVariable %_ptr_Private__arr_float_uint_4 Private %38
+ %S = OpTypeStruct
+%_ptr_Private_S = OpTypePointer Private %S
+ %42 = OpConstantNull %S
+ %struct_var = OpVariable %_ptr_Private_S Private %42
+ %void = OpTypeVoid
+ %43 = OpTypeFunction %void
+ %main = OpFunction %void None %43
+ %46 = OpLabel
+ OpStore %bool_var %4
+ OpStore %i32_var %8
+ OpStore %u32_var %12
+ OpStore %f32_var %16
+ OpStore %v2i32_var %20
+ OpStore %v3u32_var %24
+ OpStore %v4f32_var %28
+ OpStore %m2x3_var %33
+ OpStore %arr_var %38
+ OpStore %struct_var %42
+ OpReturn
+ OpFunctionEnd
diff --git a/test/types/module_scope_var.wgsl.expected.wgsl b/test/types/module_scope_var.wgsl.expected.wgsl
new file mode 100644
index 0000000..7e53714
--- /dev/null
+++ b/test/types/module_scope_var.wgsl.expected.wgsl
@@ -0,0 +1,36 @@
+struct S {
+};
+
+var<private> bool_var : bool;
+
+var<private> i32_var : i32;
+
+var<private> u32_var : u32;
+
+var<private> f32_var : f32;
+
+var<private> v2i32_var : vec2<i32>;
+
+var<private> v3u32_var : vec3<u32>;
+
+var<private> v4f32_var : vec4<f32>;
+
+var<private> m2x3_var : mat2x3<f32>;
+
+var<private> arr_var : array<f32, 4>;
+
+var<private> struct_var : S;
+
+[[stage(compute)]]
+fn main() {
+ bool_var = bool();
+ i32_var = i32();
+ u32_var = u32();
+ f32_var = f32();
+ v2i32_var = vec2<i32>();
+ v3u32_var = vec3<u32>();
+ v4f32_var = vec4<f32>();
+ m2x3_var = mat2x3<f32>();
+ arr_var = array<f32, 4>();
+ struct_var = S();
+}
diff --git a/test/types/module_scope_var_initializers.wgsl b/test/types/module_scope_var_initializers.wgsl
new file mode 100644
index 0000000..3994d12
--- /dev/null
+++ b/test/types/module_scope_var_initializers.wgsl
@@ -0,0 +1,28 @@
+struct S {
+};
+
+var<private> bool_var : bool = bool();
+var<private> i32_var : i32 = i32();
+var<private> u32_var : u32 = u32();
+var<private> f32_var : f32 = f32();
+var<private> v2i32_var : vec2<i32> = vec2<i32>();
+var<private> v3u32_var : vec3<u32> = vec3<u32>();
+var<private> v4f32_var : vec4<f32> = vec4<f32>();
+var<private> m2x3_var : mat2x3<f32> = mat2x3<f32>();
+var<private> arr_var : array<f32, 4> = array<f32, 4>();
+var<private> struct_var : S = S();
+
+[[stage(compute)]]
+fn main() {
+ // Reference the module-scope variables to stop them from being removed.
+ bool_var = bool();
+ i32_var = i32();
+ u32_var = u32();
+ f32_var = f32();
+ v2i32_var = vec2<i32>();
+ v3u32_var = vec3<u32>();
+ v4f32_var = vec4<f32>();
+ m2x3_var = mat2x3<f32>();
+ arr_var = array<f32, 4>();
+ struct_var = S();
+}
diff --git a/test/types/module_scope_var_initializers.wgsl.expected.hlsl b/test/types/module_scope_var_initializers.wgsl.expected.hlsl
new file mode 100644
index 0000000..562a445
--- /dev/null
+++ b/test/types/module_scope_var_initializers.wgsl.expected.hlsl
@@ -0,0 +1,31 @@
+struct S {
+};
+
+static bool bool_var = false;
+static int i32_var = 0;
+static uint u32_var = 0u;
+static float f32_var = 0.0f;
+static int2 v2i32_var = int2(0, 0);
+static uint3 v3u32_var = uint3(0u, 0u, 0u);
+static float4 v4f32_var = float4(0.0f, 0.0f, 0.0f, 0.0f);
+static float2x3 m2x3_var = float2x3(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+static float arr_var[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+static S struct_var = {};
+
+[numthreads(1, 1, 1)]
+void main() {
+ bool_var = false;
+ i32_var = 0;
+ u32_var = 0u;
+ f32_var = 0.0f;
+ v2i32_var = int2(0, 0);
+ v3u32_var = uint3(0u, 0u, 0u);
+ v4f32_var = float4(0.0f, 0.0f, 0.0f, 0.0f);
+ m2x3_var = float2x3(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+ const float tint_symbol[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ arr_var = tint_symbol;
+ const S tint_symbol_1 = {};
+ struct_var = tint_symbol_1;
+ return;
+}
+
diff --git a/test/types/module_scope_var_initializers.wgsl.expected.msl b/test/types/module_scope_var_initializers.wgsl.expected.msl
new file mode 100644
index 0000000..77696be
--- /dev/null
+++ b/test/types/module_scope_var_initializers.wgsl.expected.msl
@@ -0,0 +1 @@
+SKIP: crbug.com/tint/814
diff --git a/test/types/module_scope_var_initializers.wgsl.expected.spvasm b/test/types/module_scope_var_initializers.wgsl.expected.spvasm
new file mode 100644
index 0000000..0fb2909
--- /dev/null
+++ b/test/types/module_scope_var_initializers.wgsl.expected.spvasm
@@ -0,0 +1,80 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 47
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main "main"
+ OpExecutionMode %main LocalSize 1 1 1
+ OpName %bool_var "bool_var"
+ OpName %i32_var "i32_var"
+ OpName %u32_var "u32_var"
+ OpName %f32_var "f32_var"
+ OpName %v2i32_var "v2i32_var"
+ OpName %v3u32_var "v3u32_var"
+ OpName %v4f32_var "v4f32_var"
+ OpName %m2x3_var "m2x3_var"
+ OpName %arr_var "arr_var"
+ OpName %S "S"
+ OpName %struct_var "struct_var"
+ OpName %main "main"
+ OpDecorate %_arr_float_uint_4 ArrayStride 4
+ %bool = OpTypeBool
+ %2 = OpConstantNull %bool
+%_ptr_Private_bool = OpTypePointer Private %bool
+ %bool_var = OpVariable %_ptr_Private_bool Private %2
+ %int = OpTypeInt 32 1
+ %6 = OpConstantNull %int
+%_ptr_Private_int = OpTypePointer Private %int
+ %i32_var = OpVariable %_ptr_Private_int Private %6
+ %uint = OpTypeInt 32 0
+ %10 = OpConstantNull %uint
+%_ptr_Private_uint = OpTypePointer Private %uint
+ %u32_var = OpVariable %_ptr_Private_uint Private %10
+ %float = OpTypeFloat 32
+ %14 = OpConstantNull %float
+%_ptr_Private_float = OpTypePointer Private %float
+ %f32_var = OpVariable %_ptr_Private_float Private %14
+ %v2int = OpTypeVector %int 2
+ %18 = OpConstantNull %v2int
+%_ptr_Private_v2int = OpTypePointer Private %v2int
+ %v2i32_var = OpVariable %_ptr_Private_v2int Private %18
+ %v3uint = OpTypeVector %uint 3
+ %22 = OpConstantNull %v3uint
+%_ptr_Private_v3uint = OpTypePointer Private %v3uint
+ %v3u32_var = OpVariable %_ptr_Private_v3uint Private %22
+ %v4float = OpTypeVector %float 4
+ %26 = OpConstantNull %v4float
+%_ptr_Private_v4float = OpTypePointer Private %v4float
+ %v4f32_var = OpVariable %_ptr_Private_v4float Private %26
+ %v3float = OpTypeVector %float 3
+%mat2v3float = OpTypeMatrix %v3float 2
+ %31 = OpConstantNull %mat2v3float
+%_ptr_Private_mat2v3float = OpTypePointer Private %mat2v3float
+ %m2x3_var = OpVariable %_ptr_Private_mat2v3float Private %31
+ %uint_4 = OpConstant %uint 4
+%_arr_float_uint_4 = OpTypeArray %float %uint_4
+ %36 = OpConstantNull %_arr_float_uint_4
+%_ptr_Private__arr_float_uint_4 = OpTypePointer Private %_arr_float_uint_4
+ %arr_var = OpVariable %_ptr_Private__arr_float_uint_4 Private %36
+ %S = OpTypeStruct
+ %40 = OpConstantNull %S
+%_ptr_Private_S = OpTypePointer Private %S
+ %struct_var = OpVariable %_ptr_Private_S Private %40
+ %void = OpTypeVoid
+ %43 = OpTypeFunction %void
+ %main = OpFunction %void None %43
+ %46 = OpLabel
+ OpStore %bool_var %2
+ OpStore %i32_var %6
+ OpStore %u32_var %10
+ OpStore %f32_var %14
+ OpStore %v2i32_var %18
+ OpStore %v3u32_var %22
+ OpStore %v4f32_var %26
+ OpStore %m2x3_var %31
+ OpStore %arr_var %36
+ OpStore %struct_var %40
+ OpReturn
+ OpFunctionEnd
diff --git a/test/types/module_scope_var_initializers.wgsl.expected.wgsl b/test/types/module_scope_var_initializers.wgsl.expected.wgsl
new file mode 100644
index 0000000..7b16c09
--- /dev/null
+++ b/test/types/module_scope_var_initializers.wgsl.expected.wgsl
@@ -0,0 +1,36 @@
+struct S {
+};
+
+var<private> bool_var : bool = bool();
+
+var<private> i32_var : i32 = i32();
+
+var<private> u32_var : u32 = u32();
+
+var<private> f32_var : f32 = f32();
+
+var<private> v2i32_var : vec2<i32> = vec2<i32>();
+
+var<private> v3u32_var : vec3<u32> = vec3<u32>();
+
+var<private> v4f32_var : vec4<f32> = vec4<f32>();
+
+var<private> m2x3_var : mat2x3<f32> = mat2x3<f32>();
+
+var<private> arr_var : array<f32, 4> = array<f32, 4>();
+
+var<private> struct_var : S = S();
+
+[[stage(compute)]]
+fn main() {
+ bool_var = bool();
+ i32_var = i32();
+ u32_var = u32();
+ f32_var = f32();
+ v2i32_var = vec2<i32>();
+ v3u32_var = vec3<u32>();
+ v4f32_var = vec4<f32>();
+ m2x3_var = mat2x3<f32>();
+ arr_var = array<f32, 4>();
+ struct_var = S();
+}
diff --git a/test/var/private.wgsl b/test/var/private.wgsl
new file mode 100644
index 0000000..e03db87
--- /dev/null
+++ b/test/var/private.wgsl
@@ -0,0 +1,49 @@
+var<private> a : i32;
+var<private> b : i32;
+var<private> c : i32; // unused
+
+fn uses_a() {
+ a = a + 1;
+}
+
+fn uses_b() {
+ b = b * 2;
+}
+
+fn uses_a_and_b() {
+ b = a;
+}
+
+fn no_uses() {
+}
+
+fn outer() {
+ a = 0;
+ uses_a();
+ uses_a_and_b();
+ uses_b();
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main1() {
+ a = 42;
+ uses_a();
+}
+
+[[stage(compute)]]
+fn main2() {
+ b = 7;
+ uses_b();
+}
+
+[[stage(compute)]]
+fn main3() {
+ outer();
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main4() {
+ no_uses();
+}
diff --git a/test/var/private.wgsl.expected.hlsl b/test/var/private.wgsl.expected.hlsl
new file mode 100644
index 0000000..f30875e
--- /dev/null
+++ b/test/var/private.wgsl.expected.hlsl
@@ -0,0 +1,54 @@
+static int a;
+
+static int b;
+
+void uses_a() {
+ a = (a + 1);
+}
+
+void uses_b() {
+ b = (b * 2);
+}
+
+void uses_a_and_b() {
+ b = a;
+}
+
+void no_uses() {
+}
+
+void outer() {
+ a = 0;
+ uses_a();
+ uses_a_and_b();
+ uses_b();
+ no_uses();
+}
+
+[numthreads(1, 1, 1)]
+void main1() {
+ a = 42;
+ uses_a();
+ return;
+}
+
+[numthreads(1, 1, 1)]
+void main2() {
+ b = 7;
+ uses_b();
+ return;
+}
+
+[numthreads(1, 1, 1)]
+void main3() {
+ outer();
+ no_uses();
+ return;
+}
+
+[numthreads(1, 1, 1)]
+void main4() {
+ no_uses();
+ return;
+}
+
diff --git a/test/var/private.wgsl.expected.msl b/test/var/private.wgsl.expected.msl
new file mode 100644
index 0000000..f17afeb
--- /dev/null
+++ b/test/var/private.wgsl.expected.msl
@@ -0,0 +1,57 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void uses_a(thread int* const tint_symbol) {
+ *(tint_symbol) = (*(tint_symbol) + 1);
+}
+
+void uses_b(thread int* const tint_symbol_1) {
+ *(tint_symbol_1) = (*(tint_symbol_1) * 2);
+}
+
+void uses_a_and_b(thread int* const tint_symbol_2, thread int* const tint_symbol_3) {
+ *(tint_symbol_2) = *(tint_symbol_3);
+}
+
+void no_uses() {
+}
+
+void outer(thread int* const tint_symbol_4, thread int* const tint_symbol_5) {
+ *(tint_symbol_4) = 0;
+ uses_a(tint_symbol_4);
+ uses_a_and_b(tint_symbol_5, tint_symbol_4);
+ uses_b(tint_symbol_5);
+ no_uses();
+}
+
+kernel void main1() {
+ thread int tint_symbol_7 = 0;
+ thread int* const tint_symbol_6 = &(tint_symbol_7);
+ *(tint_symbol_6) = 42;
+ uses_a(tint_symbol_6);
+ return;
+}
+
+kernel void main2() {
+ thread int tint_symbol_9 = 0;
+ thread int* const tint_symbol_8 = &(tint_symbol_9);
+ *(tint_symbol_8) = 7;
+ uses_b(tint_symbol_8);
+ return;
+}
+
+kernel void main3() {
+ thread int tint_symbol_11 = 0;
+ thread int* const tint_symbol_10 = &(tint_symbol_11);
+ thread int tint_symbol_13 = 0;
+ thread int* const tint_symbol_12 = &(tint_symbol_13);
+ outer(tint_symbol_10, tint_symbol_12);
+ no_uses();
+ return;
+}
+
+kernel void main4() {
+ no_uses();
+ return;
+}
+
diff --git a/test/var/private.wgsl.expected.spvasm b/test/var/private.wgsl.expected.spvasm
new file mode 100644
index 0000000..daa3cfb
--- /dev/null
+++ b/test/var/private.wgsl.expected.spvasm
@@ -0,0 +1,96 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main1 "main1"
+ OpEntryPoint GLCompute %main2 "main2"
+ OpEntryPoint GLCompute %main3 "main3"
+ OpEntryPoint GLCompute %main4 "main4"
+ OpExecutionMode %main1 LocalSize 1 1 1
+ OpExecutionMode %main2 LocalSize 1 1 1
+ OpExecutionMode %main3 LocalSize 1 1 1
+ OpExecutionMode %main4 LocalSize 1 1 1
+ OpName %a "a"
+ OpName %b "b"
+ OpName %c "c"
+ OpName %uses_a "uses_a"
+ OpName %uses_b "uses_b"
+ OpName %uses_a_and_b "uses_a_and_b"
+ OpName %no_uses "no_uses"
+ OpName %outer "outer"
+ OpName %main1 "main1"
+ OpName %main2 "main2"
+ OpName %main3 "main3"
+ OpName %main4 "main4"
+ %int = OpTypeInt 32 1
+%_ptr_Private_int = OpTypePointer Private %int
+ %4 = OpConstantNull %int
+ %a = OpVariable %_ptr_Private_int Private %4
+ %b = OpVariable %_ptr_Private_int Private %4
+ %c = OpVariable %_ptr_Private_int Private %4
+ %void = OpTypeVoid
+ %7 = OpTypeFunction %void
+ %int_1 = OpConstant %int 1
+ %int_2 = OpConstant %int 2
+ %int_0 = OpConstant %int 0
+ %int_42 = OpConstant %int 42
+ %int_7 = OpConstant %int 7
+ %uses_a = OpFunction %void None %7
+ %10 = OpLabel
+ %11 = OpLoad %int %a
+ %13 = OpIAdd %int %11 %int_1
+ OpStore %a %13
+ OpReturn
+ OpFunctionEnd
+ %uses_b = OpFunction %void None %7
+ %15 = OpLabel
+ %16 = OpLoad %int %b
+ %18 = OpIMul %int %16 %int_2
+ OpStore %b %18
+ OpReturn
+ OpFunctionEnd
+%uses_a_and_b = OpFunction %void None %7
+ %20 = OpLabel
+ %21 = OpLoad %int %a
+ OpStore %b %21
+ OpReturn
+ OpFunctionEnd
+ %no_uses = OpFunction %void None %7
+ %23 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %outer = OpFunction %void None %7
+ %25 = OpLabel
+ OpStore %a %int_0
+ %27 = OpFunctionCall %void %uses_a
+ %28 = OpFunctionCall %void %uses_a_and_b
+ %29 = OpFunctionCall %void %uses_b
+ %30 = OpFunctionCall %void %no_uses
+ OpReturn
+ OpFunctionEnd
+ %main1 = OpFunction %void None %7
+ %32 = OpLabel
+ OpStore %a %int_42
+ %34 = OpFunctionCall %void %uses_a
+ OpReturn
+ OpFunctionEnd
+ %main2 = OpFunction %void None %7
+ %36 = OpLabel
+ OpStore %b %int_7
+ %38 = OpFunctionCall %void %uses_b
+ OpReturn
+ OpFunctionEnd
+ %main3 = OpFunction %void None %7
+ %40 = OpLabel
+ %41 = OpFunctionCall %void %outer
+ %42 = OpFunctionCall %void %no_uses
+ OpReturn
+ OpFunctionEnd
+ %main4 = OpFunction %void None %7
+ %44 = OpLabel
+ %45 = OpFunctionCall %void %no_uses
+ OpReturn
+ OpFunctionEnd
diff --git a/test/var/private.wgsl.expected.wgsl b/test/var/private.wgsl.expected.wgsl
new file mode 100644
index 0000000..ef20cc2
--- /dev/null
+++ b/test/var/private.wgsl.expected.wgsl
@@ -0,0 +1,51 @@
+var<private> a : i32;
+
+var<private> b : i32;
+
+var<private> c : i32;
+
+fn uses_a() {
+ a = (a + 1);
+}
+
+fn uses_b() {
+ b = (b * 2);
+}
+
+fn uses_a_and_b() {
+ b = a;
+}
+
+fn no_uses() {
+}
+
+fn outer() {
+ a = 0;
+ uses_a();
+ uses_a_and_b();
+ uses_b();
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main1() {
+ a = 42;
+ uses_a();
+}
+
+[[stage(compute)]]
+fn main2() {
+ b = 7;
+ uses_b();
+}
+
+[[stage(compute)]]
+fn main3() {
+ outer();
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main4() {
+ no_uses();
+}
diff --git a/test/var/workgroup.wgsl b/test/var/workgroup.wgsl
new file mode 100644
index 0000000..1d0ba40
--- /dev/null
+++ b/test/var/workgroup.wgsl
@@ -0,0 +1,49 @@
+var<workgroup> a : i32;
+var<workgroup> b : i32;
+var<workgroup> c : i32; // unused
+
+fn uses_a() {
+ a = a + 1;
+}
+
+fn uses_b() {
+ b = b * 2;
+}
+
+fn uses_a_and_b() {
+ b = a;
+}
+
+fn no_uses() {
+}
+
+fn outer() {
+ a = 0;
+ uses_a();
+ uses_a_and_b();
+ uses_b();
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main1() {
+ a = 42;
+ uses_a();
+}
+
+[[stage(compute)]]
+fn main2() {
+ b = 7;
+ uses_b();
+}
+
+[[stage(compute)]]
+fn main3() {
+ outer();
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main4() {
+ no_uses();
+}
diff --git a/test/var/workgroup.wgsl.expected.hlsl b/test/var/workgroup.wgsl.expected.hlsl
new file mode 100644
index 0000000..83c32ca
--- /dev/null
+++ b/test/var/workgroup.wgsl.expected.hlsl
@@ -0,0 +1,54 @@
+groupshared int a;
+
+groupshared int b;
+
+void uses_a() {
+ a = (a + 1);
+}
+
+void uses_b() {
+ b = (b * 2);
+}
+
+void uses_a_and_b() {
+ b = a;
+}
+
+void no_uses() {
+}
+
+void outer() {
+ a = 0;
+ uses_a();
+ uses_a_and_b();
+ uses_b();
+ no_uses();
+}
+
+[numthreads(1, 1, 1)]
+void main1() {
+ a = 42;
+ uses_a();
+ return;
+}
+
+[numthreads(1, 1, 1)]
+void main2() {
+ b = 7;
+ uses_b();
+ return;
+}
+
+[numthreads(1, 1, 1)]
+void main3() {
+ outer();
+ no_uses();
+ return;
+}
+
+[numthreads(1, 1, 1)]
+void main4() {
+ no_uses();
+ return;
+}
+
diff --git a/test/var/workgroup.wgsl.expected.msl b/test/var/workgroup.wgsl.expected.msl
new file mode 100644
index 0000000..3c53df8
--- /dev/null
+++ b/test/var/workgroup.wgsl.expected.msl
@@ -0,0 +1,57 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void uses_a(threadgroup int* const tint_symbol) {
+ *(tint_symbol) = (*(tint_symbol) + 1);
+}
+
+void uses_b(threadgroup int* const tint_symbol_1) {
+ *(tint_symbol_1) = (*(tint_symbol_1) * 2);
+}
+
+void uses_a_and_b(threadgroup int* const tint_symbol_2, threadgroup int* const tint_symbol_3) {
+ *(tint_symbol_2) = *(tint_symbol_3);
+}
+
+void no_uses() {
+}
+
+void outer(threadgroup int* const tint_symbol_4, threadgroup int* const tint_symbol_5) {
+ *(tint_symbol_4) = 0;
+ uses_a(tint_symbol_4);
+ uses_a_and_b(tint_symbol_5, tint_symbol_4);
+ uses_b(tint_symbol_5);
+ no_uses();
+}
+
+kernel void main1() {
+ threadgroup int tint_symbol_7 = 0;
+ threadgroup int* const tint_symbol_6 = &(tint_symbol_7);
+ *(tint_symbol_6) = 42;
+ uses_a(tint_symbol_6);
+ return;
+}
+
+kernel void main2() {
+ threadgroup int tint_symbol_9 = 0;
+ threadgroup int* const tint_symbol_8 = &(tint_symbol_9);
+ *(tint_symbol_8) = 7;
+ uses_b(tint_symbol_8);
+ return;
+}
+
+kernel void main3() {
+ threadgroup int tint_symbol_11 = 0;
+ threadgroup int* const tint_symbol_10 = &(tint_symbol_11);
+ threadgroup int tint_symbol_13 = 0;
+ threadgroup int* const tint_symbol_12 = &(tint_symbol_13);
+ outer(tint_symbol_10, tint_symbol_12);
+ no_uses();
+ return;
+}
+
+kernel void main4() {
+ no_uses();
+ return;
+}
+
diff --git a/test/var/workgroup.wgsl.expected.spvasm b/test/var/workgroup.wgsl.expected.spvasm
new file mode 100644
index 0000000..66c1f0c
--- /dev/null
+++ b/test/var/workgroup.wgsl.expected.spvasm
@@ -0,0 +1,95 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 45
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %main1 "main1"
+ OpEntryPoint GLCompute %main2 "main2"
+ OpEntryPoint GLCompute %main3 "main3"
+ OpEntryPoint GLCompute %main4 "main4"
+ OpExecutionMode %main1 LocalSize 1 1 1
+ OpExecutionMode %main2 LocalSize 1 1 1
+ OpExecutionMode %main3 LocalSize 1 1 1
+ OpExecutionMode %main4 LocalSize 1 1 1
+ OpName %a "a"
+ OpName %b "b"
+ OpName %c "c"
+ OpName %uses_a "uses_a"
+ OpName %uses_b "uses_b"
+ OpName %uses_a_and_b "uses_a_and_b"
+ OpName %no_uses "no_uses"
+ OpName %outer "outer"
+ OpName %main1 "main1"
+ OpName %main2 "main2"
+ OpName %main3 "main3"
+ OpName %main4 "main4"
+ %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+ %a = OpVariable %_ptr_Workgroup_int Workgroup
+ %b = OpVariable %_ptr_Workgroup_int Workgroup
+ %c = OpVariable %_ptr_Workgroup_int Workgroup
+ %void = OpTypeVoid
+ %6 = OpTypeFunction %void
+ %int_1 = OpConstant %int 1
+ %int_2 = OpConstant %int 2
+ %int_0 = OpConstant %int 0
+ %int_42 = OpConstant %int 42
+ %int_7 = OpConstant %int 7
+ %uses_a = OpFunction %void None %6
+ %9 = OpLabel
+ %10 = OpLoad %int %a
+ %12 = OpIAdd %int %10 %int_1
+ OpStore %a %12
+ OpReturn
+ OpFunctionEnd
+ %uses_b = OpFunction %void None %6
+ %14 = OpLabel
+ %15 = OpLoad %int %b
+ %17 = OpIMul %int %15 %int_2
+ OpStore %b %17
+ OpReturn
+ OpFunctionEnd
+%uses_a_and_b = OpFunction %void None %6
+ %19 = OpLabel
+ %20 = OpLoad %int %a
+ OpStore %b %20
+ OpReturn
+ OpFunctionEnd
+ %no_uses = OpFunction %void None %6
+ %22 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ %outer = OpFunction %void None %6
+ %24 = OpLabel
+ OpStore %a %int_0
+ %26 = OpFunctionCall %void %uses_a
+ %27 = OpFunctionCall %void %uses_a_and_b
+ %28 = OpFunctionCall %void %uses_b
+ %29 = OpFunctionCall %void %no_uses
+ OpReturn
+ OpFunctionEnd
+ %main1 = OpFunction %void None %6
+ %31 = OpLabel
+ OpStore %a %int_42
+ %33 = OpFunctionCall %void %uses_a
+ OpReturn
+ OpFunctionEnd
+ %main2 = OpFunction %void None %6
+ %35 = OpLabel
+ OpStore %b %int_7
+ %37 = OpFunctionCall %void %uses_b
+ OpReturn
+ OpFunctionEnd
+ %main3 = OpFunction %void None %6
+ %39 = OpLabel
+ %40 = OpFunctionCall %void %outer
+ %41 = OpFunctionCall %void %no_uses
+ OpReturn
+ OpFunctionEnd
+ %main4 = OpFunction %void None %6
+ %43 = OpLabel
+ %44 = OpFunctionCall %void %no_uses
+ OpReturn
+ OpFunctionEnd
diff --git a/test/var/workgroup.wgsl.expected.wgsl b/test/var/workgroup.wgsl.expected.wgsl
new file mode 100644
index 0000000..1861fb8
--- /dev/null
+++ b/test/var/workgroup.wgsl.expected.wgsl
@@ -0,0 +1,51 @@
+var<workgroup> a : i32;
+
+var<workgroup> b : i32;
+
+var<workgroup> c : i32;
+
+fn uses_a() {
+ a = (a + 1);
+}
+
+fn uses_b() {
+ b = (b * 2);
+}
+
+fn uses_a_and_b() {
+ b = a;
+}
+
+fn no_uses() {
+}
+
+fn outer() {
+ a = 0;
+ uses_a();
+ uses_a_and_b();
+ uses_b();
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main1() {
+ a = 42;
+ uses_a();
+}
+
+[[stage(compute)]]
+fn main2() {
+ b = 7;
+ uses_b();
+}
+
+[[stage(compute)]]
+fn main3() {
+ outer();
+ no_uses();
+}
+
+[[stage(compute)]]
+fn main4() {
+ no_uses();
+}