tint: Add support for atomic ops to spirv reader

The following operations are supported:

OpAtomicLoad
OpAtomicStore
OpAtomicExchange
OpAtomicCompareExchange
OpAtomicCompareExchangeWeak
OpAtomicIIncrement
OpAtomicIDecrement
OpAtomicIAdd
OpAtomicISub
OpAtomicSMin
OpAtomicUMin
OpAtomicSMax
OpAtomicUMax
OpAtomicAnd
OpAtomicOr
OpAtomicXor

These are not, but may be supported in the future:

OpAtomicFlagTestAndSet
OpAtomicFlagClear
OpAtomicFMinEXT
OpAtomicFMaxEXT
OpAtomicFAddEXT

Bug: tint:1441
Change-Id: Ifd53643b38d43664905a0dddfca609add4914670
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94121
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index d3303bf..8108e54 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -526,6 +526,8 @@
     "transform/simplify_pointers.h",
     "transform/single_entry_point.cc",
     "transform/single_entry_point.h",
+    "transform/spirv_atomic.cc",
+    "transform/spirv_atomic.h",
     "transform/transform.cc",
     "transform/transform.h",
     "transform/unshadow.cc",
@@ -1190,6 +1192,7 @@
       "transform/robustness_test.cc",
       "transform/simplify_pointers_test.cc",
       "transform/single_entry_point_test.cc",
+      "transform/spirv_atomic_test.cc",
       "transform/test_helper.h",
       "transform/transform_test.cc",
       "transform/unshadow_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 1ea1a6f..3475b1b 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -450,6 +450,8 @@
   transform/simplify_pointers.h
   transform/single_entry_point.cc
   transform/single_entry_point.h
+  transform/spirv_atomic.cc
+  transform/spirv_atomic.h
   transform/transform.cc
   transform/transform.h
   transform/unshadow.cc
@@ -1114,6 +1116,7 @@
       transform/robustness_test.cc
       transform/simplify_pointers_test.cc
       transform/single_entry_point_test.cc
+      transform/spirv_atomic_test.cc
       transform/test_helper.h
       transform/unshadow_test.cc
       transform/unwind_discard_functions_test.cc
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 2a5169b..3b333df 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -36,6 +36,7 @@
 #include "src/tint/sem/builtin_type.h"
 #include "src/tint/sem/depth_texture.h"
 #include "src/tint/sem/sampled_texture.h"
+#include "src/tint/transform/spirv_atomic.h"
 
 // Terms:
 //    CFG: the control flow graph of the function, where basic blocks are the
@@ -501,6 +502,38 @@
 }
 
 // @param opcode a SPIR-V opcode
+// @returns true if the given instruction is an atomic operation.
+bool IsAtomicOp(SpvOp opcode) {
+    switch (opcode) {
+        case SpvOpAtomicLoad:
+        case SpvOpAtomicStore:
+        case SpvOpAtomicExchange:
+        case SpvOpAtomicCompareExchange:
+        case SpvOpAtomicCompareExchangeWeak:
+        case SpvOpAtomicIIncrement:
+        case SpvOpAtomicIDecrement:
+        case SpvOpAtomicIAdd:
+        case SpvOpAtomicISub:
+        case SpvOpAtomicSMin:
+        case SpvOpAtomicUMin:
+        case SpvOpAtomicSMax:
+        case SpvOpAtomicUMax:
+        case SpvOpAtomicAnd:
+        case SpvOpAtomicOr:
+        case SpvOpAtomicXor:
+        case SpvOpAtomicFlagTestAndSet:
+        case SpvOpAtomicFlagClear:
+        case SpvOpAtomicFMinEXT:
+        case SpvOpAtomicFMaxEXT:
+        case SpvOpAtomicFAddEXT:
+            return true;
+        default:
+            break;
+    }
+    return false;
+}
+
+// @param opcode a SPIR-V opcode
 // @returns true if the given instruction is an image sampling, gather,
 // or gather-compare operation.
 bool IsImageSamplingOrGatherOrDrefGather(SpvOp opcode) {
@@ -3487,6 +3520,10 @@
         return EmitImageAccess(inst);
     }
 
+    if (IsAtomicOp(inst.opcode())) {
+        return EmitAtomicOp(inst);
+    }
+
     switch (inst.opcode()) {
         case SpvOpNop:
             return true;
@@ -5417,6 +5454,115 @@
     return Fail() << "unhandled image query: " << inst.PrettyPrint();
 }
 
+bool FunctionEmitter::EmitAtomicOp(const spvtools::opt::Instruction& inst) {
+    auto emit_atomic = [&](sem::BuiltinType builtin, std::initializer_list<TypedExpression> args) {
+        // Split args into params and expressions
+        ast::ParameterList params;
+        params.reserve(args.size());
+        ast::ExpressionList exprs;
+        exprs.reserve(args.size());
+        size_t i = 0;
+        for (auto& a : args) {
+            params.emplace_back(builder_.Param("p" + std::to_string(i++), a.type->Build(builder_)));
+            exprs.emplace_back(a.expr);
+        }
+
+        // Function return type
+        const ast::Type* ret_type = nullptr;
+        if (inst.type_id() != 0) {
+            ret_type = parser_impl_.ConvertType(inst.type_id())->Build(builder_);
+        } else {
+            ret_type = builder_.ty.void_();
+        }
+
+        // Emit stub, will be removed by transform::SpirvAtomic
+        auto sym = builder_.Symbols().New(std::string("stub_") + sem::str(builtin));
+        auto* stub_deco =
+            builder_.ASTNodes().Create<transform::SpirvAtomic::Stub>(builder_.ID(), builtin);
+        auto* stub =
+            create<ast::Function>(Source{}, sym, std::move(params), ret_type,
+                                  /* body */ nullptr,
+                                  ast::AttributeList{
+                                      stub_deco,
+                                      builder_.Disable(ast::DisabledValidation::kFunctionHasNoBody),
+                                  },
+                                  ast::AttributeList{});
+        builder_.AST().AddFunction(stub);
+
+        // Emit call to stub, will be replaced with call to atomic builtin by transform::SpirvAtomic
+        auto* call = builder_.Call(Source{}, sym, exprs);
+        if (inst.type_id() != 0) {
+            auto* result_type = parser_impl_.ConvertType(inst.type_id());
+            TypedExpression expr{result_type, call};
+            return EmitConstDefOrWriteToHoistedVar(inst, expr);
+        }
+        AddStatement(create<ast::CallStatement>(call));
+
+        return true;
+    };
+
+    auto oper = [&](uint32_t index) -> TypedExpression {  //
+        return MakeOperand(inst, index);
+    };
+
+    auto lit = [&](int v) -> TypedExpression {
+        auto* result_type = parser_impl_.ConvertType(inst.type_id());
+        if (result_type->Is<I32>()) {
+            return TypedExpression(result_type, builder_.Expr(i32(v)));
+        } else if (result_type->Is<U32>()) {
+            return TypedExpression(result_type, builder_.Expr(u32(v)));
+        }
+        return {};
+    };
+
+    switch (inst.opcode()) {
+        case SpvOpAtomicLoad:
+            return emit_atomic(sem::BuiltinType::kAtomicLoad, {oper(/*ptr*/ 0)});
+        case SpvOpAtomicStore:
+            return emit_atomic(sem::BuiltinType::kAtomicStore,
+                               {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicExchange:
+            return emit_atomic(sem::BuiltinType::kAtomicExchange,
+                               {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicCompareExchange:
+        case SpvOpAtomicCompareExchangeWeak:
+            return emit_atomic(sem::BuiltinType::kAtomicCompareExchangeWeak,
+                               {oper(/*ptr*/ 0), /*value*/ oper(5), /*comparator*/ oper(4)});
+        case SpvOpAtomicIIncrement:
+            return emit_atomic(sem::BuiltinType::kAtomicAdd, {oper(/*ptr*/ 0), lit(1)});
+        case SpvOpAtomicIDecrement:
+            return emit_atomic(sem::BuiltinType::kAtomicSub, {oper(/*ptr*/ 0), lit(1)});
+        case SpvOpAtomicIAdd:
+            return emit_atomic(sem::BuiltinType::kAtomicAdd, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicISub:
+            return emit_atomic(sem::BuiltinType::kAtomicSub, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicSMin:
+            return emit_atomic(sem::BuiltinType::kAtomicMin, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicUMin:
+            return emit_atomic(sem::BuiltinType::kAtomicMin, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicSMax:
+            return emit_atomic(sem::BuiltinType::kAtomicMax, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicUMax:
+            return emit_atomic(sem::BuiltinType::kAtomicMax, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicAnd:
+            return emit_atomic(sem::BuiltinType::kAtomicAnd, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicOr:
+            return emit_atomic(sem::BuiltinType::kAtomicOr, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicXor:
+            return emit_atomic(sem::BuiltinType::kAtomicXor, {oper(/*ptr*/ 0), oper(/*value*/ 3)});
+        case SpvOpAtomicFlagTestAndSet:
+        case SpvOpAtomicFlagClear:
+        case SpvOpAtomicFMinEXT:
+        case SpvOpAtomicFMaxEXT:
+        case SpvOpAtomicFAddEXT:
+            return Fail() << "unsupported atomic op: " << inst.PrettyPrint();
+
+        default:
+            break;
+    }
+    return Fail() << "unhandled atomic op: " << inst.PrettyPrint();
+}
+
 ast::ExpressionList FunctionEmitter::MakeCoordinateOperandsForImageAccess(
     const spvtools::opt::Instruction& inst) {
     if (!parser_impl_.success()) {
diff --git a/src/tint/reader/spirv/function.h b/src/tint/reader/spirv/function.h
index b1c3f57..ae9ef31 100644
--- a/src/tint/reader/spirv/function.h
+++ b/src/tint/reader/spirv/function.h
@@ -1060,7 +1060,7 @@
     /// Emits a texture builtin function call for a SPIR-V instruction that
     /// accesses an image or sampled image.
     /// @param inst the SPIR-V instruction
-    /// @returns an expression
+    /// @returns true on success, false on error
     bool EmitImageAccess(const spvtools::opt::Instruction& inst);
 
     /// Emits statements to implement a SPIR-V image query.
@@ -1068,6 +1068,11 @@
     /// @returns an expression
     bool EmitImageQuery(const spvtools::opt::Instruction& inst);
 
+    /// Emits statements to implement a SPIR-V atomic op.
+    /// @param inst the SPIR-V instruction
+    /// @returns true on success, false on error
+    bool EmitAtomicOp(const spvtools::opt::Instruction& inst);
+
     /// Converts the given texel to match the type required for the storage
     /// texture with the given type. In WGSL the texel value is always provided
     /// as a 4-element vector, but the component type is determined by the
diff --git a/src/tint/reader/spirv/parser.cc b/src/tint/reader/spirv/parser.cc
index f430d94..ac43b9e 100644
--- a/src/tint/reader/spirv/parser.cc
+++ b/src/tint/reader/spirv/parser.cc
@@ -22,6 +22,7 @@
 #include "src/tint/transform/manager.h"
 #include "src/tint/transform/remove_unreachable_statements.h"
 #include "src/tint/transform/simplify_pointers.h"
+#include "src/tint/transform/spirv_atomic.h"
 #include "src/tint/transform/unshadow.h"
 
 namespace tint::reader::spirv {
@@ -55,6 +56,7 @@
     manager.Add<transform::DecomposeStridedMatrix>();
     manager.Add<transform::DecomposeStridedArray>();
     manager.Add<transform::RemoveUnreachableStatements>();
+    manager.Add<transform::SpirvAtomic>();
     return manager.Run(&program).program;
 }
 
diff --git a/src/tint/tint.natvis b/src/tint/tint.natvis
index c1fd1f3..bce3ce5 100644
--- a/src/tint/tint.natvis
+++ b/src/tint/tint.natvis
@@ -101,11 +101,7 @@
 		<DisplayString>{*variable};</DisplayString>
 	</Type>
 
-	<Type Name="tint::ast::UintLiteralExpression">
-		<DisplayString>{value}</DisplayString>
-	</Type>
-
-	<Type Name="tint::ast::SintLiteralExpression">
+	<Type Name="tint::ast::IntLiteralExpression">
 		<DisplayString>{value}</DisplayString>
 	</Type>
 
diff --git a/src/tint/transform/spirv_atomic.cc b/src/tint/transform/spirv_atomic.cc
new file mode 100644
index 0000000..d820a87
--- /dev/null
+++ b/src/tint/transform/spirv_atomic.cc
@@ -0,0 +1,231 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/transform/spirv_atomic.h"
+
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/block_statement.h"
+#include "src/tint/sem/function.h"
+#include "src/tint/sem/index_accessor_expression.h"
+#include "src/tint/sem/member_accessor_expression.h"
+#include "src/tint/sem/reference.h"
+#include "src/tint/sem/statement.h"
+#include "src/tint/utils/map.h"
+#include "src/tint/utils/unique_vector.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::SpirvAtomic);
+TINT_INSTANTIATE_TYPEINFO(tint::transform::SpirvAtomic::Stub);
+
+namespace tint::transform {
+
+/// Private implementation of transform
+struct SpirvAtomic::State {
+  private:
+    /// A struct that has been forked because a subset of members were made atomic.
+    struct ForkedStruct {
+        Symbol name;
+        std::unordered_set<size_t> atomic_members;
+    };
+
+    CloneContext& ctx;
+    ProgramBuilder& b = *ctx.dst;
+    std::unordered_map<const ast::Struct*, ForkedStruct> forked_structs;
+    std::unordered_set<const sem::Variable*> atomic_variables;
+    utils::UniqueVector<const sem::Expression*> atomic_expressions;
+
+  public:
+    /// Constructor
+    /// @param c the clone context
+    explicit State(CloneContext& c) : ctx(c) {}
+
+    /// Runs the transform
+    void Run() {
+        // Look for stub functions generated by the SPIR-V reader, which are used as placeholders
+        // for atomic builtin calls.
+        for (auto* fn : ctx.src->AST().Functions()) {
+            if (auto* stub = ast::GetAttribute<Stub>(fn->attributes)) {
+                auto* sem = ctx.src->Sem().Get(fn);
+
+                for (auto* call : sem->CallSites()) {
+                    // The first argument is always the atomic.
+                    // The stub passes this by value, whereas the builtin wants a pointer.
+                    // Take the address of the atomic argument.
+                    auto& args = call->Declaration()->args;
+                    auto out_args = ctx.Clone(args);
+                    out_args[0] = b.AddressOf(out_args[0]);
+
+                    // Replace all callsites of this stub to a call to the real builtin
+                    if (stub->builtin == sem::BuiltinType::kAtomicCompareExchangeWeak) {
+                        // atomicCompareExchangeWeak returns a struct, so insert a call to it above
+                        // the current statement, and replace the current call with the struct's
+                        // `old_value` member.
+                        auto* block = call->Stmt()->Block()->Declaration();
+                        auto old_value = b.Symbols().New("old_value");
+                        auto old_value_decl = b.Decl(b.Let(
+                            old_value, nullptr,
+                            b.MemberAccessor(b.Call(sem::str(stub->builtin), std::move(out_args)),
+                                             b.Expr("old_value"))));
+                        ctx.InsertBefore(block->statements, call->Stmt()->Declaration(),
+                                         old_value_decl);
+                        ctx.Replace(call->Declaration(), b.Expr(old_value));
+                    } else {
+                        ctx.Replace(call->Declaration(),
+                                    b.Call(sem::str(stub->builtin), std::move(out_args)));
+                    }
+
+                    // Keep track of this expression. We'll need to modify the source variable /
+                    // structure to be atomic.
+                    atomic_expressions.add(ctx.src->Sem().Get(args[0]));
+                }
+
+                // Remove the stub from the output program
+                ctx.Remove(ctx.src->AST().GlobalDeclarations(), fn);
+            }
+        }
+
+        // Transform all variables and structure members that were used in atomic operations as
+        // atomic types. This propagates up originating expression chains.
+        ProcessAtomicExpressions();
+
+        // If we need to change structure members, then fork them.
+        if (!forked_structs.empty()) {
+            ctx.ReplaceAll([&](const ast::Struct* str) {
+                // Always emit the original structure. This allows unrelated usage of the structure
+                // to continue working.
+                // auto* original = ctx.CloneWithoutTransform(str);
+
+                // Is `str` a structure we need to fork?
+                if (auto it = forked_structs.find(str); it != forked_structs.end()) {
+                    const auto& forked = it->second;
+
+                    // Re-create the structure swapping in the atomic-flavoured members
+                    std::vector<const ast::StructMember*> members(str->members.size());
+                    for (size_t i = 0; i < str->members.size(); i++) {
+                        auto* member = str->members[i];
+                        if (forked.atomic_members.count(i)) {
+                            auto* type = AtomicTypeFor(ctx.src->Sem().Get(member)->Type());
+                            auto name = ctx.src->Symbols().NameFor(member->symbol);
+                            members[i] = b.Member(name, type, ctx.Clone(member->attributes));
+                        } else {
+                            members[i] = ctx.Clone(member);
+                        }
+                    }
+                    b.Structure(forked.name, std::move(members));
+                }
+
+                // return original;
+                return nullptr;
+            });
+        }
+
+        ctx.Clone();
+    }
+
+  private:
+    ForkedStruct& Fork(const ast::Struct* str) {
+        auto& forked = forked_structs[str];
+        if (!forked.name.IsValid()) {
+            forked.name = b.Symbols().New(ctx.src->Symbols().NameFor(str->name) + "_atomic");
+        }
+        return forked;
+    }
+
+    void ProcessAtomicExpressions() {
+        for (size_t i = 0; i < atomic_expressions.size(); i++) {
+            Switch(
+                atomic_expressions[i],  //
+                [&](const sem::VariableUser* user) {
+                    auto* v = user->Variable()->Declaration();
+                    if (v->type && atomic_variables.emplace(user->Variable()).second) {
+                        ctx.Replace(v->type, AtomicTypeFor(user->Variable()->Type()));
+                    }
+                    if (auto* ctor = user->Variable()->Constructor()) {
+                        atomic_expressions.add(ctor);
+                    }
+                },
+                [&](const sem::StructMemberAccess* access) {
+                    // Fork the struct (the first time) and mark member(s) that need to be made
+                    // atomic.
+                    auto* member = access->Member();
+                    Fork(member->Struct()->Declaration()).atomic_members.emplace(member->Index());
+                    atomic_expressions.add(access->Object());
+                },
+                [&](const sem::IndexAccessorExpression* index) {
+                    atomic_expressions.add(index->Object());
+                },
+                [&](const sem::Expression* e) {
+                    if (auto* unary = e->Declaration()->As<ast::UnaryOpExpression>()) {
+                        atomic_expressions.add(ctx.src->Sem().Get(unary->expr));
+                    }
+                });
+        }
+    }
+
+    const ast::Type* AtomicTypeFor(const sem::Type* ty) {
+        return Switch(
+            ty,  //
+            [&](const sem::I32*) { return b.ty.atomic(CreateASTTypeFor(ctx, ty)); },
+            [&](const sem::U32*) { return b.ty.atomic(CreateASTTypeFor(ctx, ty)); },
+            [&](const sem::Struct* str) { return b.ty.type_name(Fork(str->Declaration()).name); },
+            [&](const sem::Array* arr) {
+                return arr->IsRuntimeSized()
+                           ? b.ty.array(AtomicTypeFor(arr->ElemType()))
+                           : b.ty.array(AtomicTypeFor(arr->ElemType()), u32(arr->Count()));
+            },
+            [&](const sem::Pointer* ptr) {
+                return b.ty.pointer(AtomicTypeFor(ptr->StoreType()), ptr->StorageClass(),
+                                    ptr->Access());
+            },
+            [&](const sem::Reference* ref) { return AtomicTypeFor(ref->StoreType()); },
+            [&](Default) {
+                TINT_ICE(Transform, b.Diagnostics())
+                    << "unhandled type: " << ty->FriendlyName(ctx.src->Symbols());
+                return nullptr;
+            });
+    }
+};
+
+SpirvAtomic::SpirvAtomic() = default;
+SpirvAtomic::~SpirvAtomic() = default;
+
+SpirvAtomic::Stub::Stub(ProgramID pid, sem::BuiltinType b) : Base(pid), builtin(b) {}
+SpirvAtomic::Stub::~Stub() = default;
+std::string SpirvAtomic::Stub::InternalName() const {
+    return "@internal(spirv-atomic " + std::string(sem::str(builtin)) + ")";
+}
+
+const SpirvAtomic::Stub* SpirvAtomic::Stub::Clone(CloneContext* ctx) const {
+    return ctx->dst->ASTNodes().Create<SpirvAtomic::Stub>(ctx->dst->ID(), builtin);
+}
+
+bool SpirvAtomic::ShouldRun(const Program* program, const DataMap&) const {
+    for (auto* fn : program->AST().Functions()) {
+        if (ast::HasAttribute<Stub>(fn->attributes)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void SpirvAtomic::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+    State{ctx}.Run();
+}
+
+}  // namespace tint::transform
diff --git a/src/tint/transform/spirv_atomic.h b/src/tint/transform/spirv_atomic.h
new file mode 100644
index 0000000..36ac842
--- /dev/null
+++ b/src/tint/transform/spirv_atomic.h
@@ -0,0 +1,84 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_TINT_TRANSFORM_SPIRV_ATOMIC_H_
+#define SRC_TINT_TRANSFORM_SPIRV_ATOMIC_H_
+
+#include <string>
+
+#include "src/tint/ast/internal_attribute.h"
+#include "src/tint/sem/builtin_type.h"
+#include "src/tint/transform/transform.h"
+
+// Forward declarations
+namespace tint {
+class CloneContext;
+}  // namespace tint
+
+namespace tint::transform {
+
+/// SpirvAtomic is a transform that replaces calls to stub functions created by the SPIR-V reader
+/// with calls to the WGSL atomic builtin. It also makes sure to replace variable declarations that
+/// are the target of the atomic operations with an atomic declaration of the same type. For
+/// structs, it creates a copy of the original struct with atomic members.
+class SpirvAtomic final : public Castable<SpirvAtomic, Transform> {
+  public:
+    /// Constructor
+    SpirvAtomic();
+    /// Destructor
+    ~SpirvAtomic() override;
+
+    /// Stub is an attribute applied to stub SPIR-V reader generated functions that need to be
+    /// translated to an atomic builtin.
+    class Stub final : public Castable<Stub, ast::InternalAttribute> {
+      public:
+        /// @param program_id the identifier of the program that owns this node
+        /// @param builtin the atomic builtin this stub represents
+        Stub(ProgramID program_id, sem::BuiltinType builtin);
+        /// Destructor
+        ~Stub() override;
+
+        /// @return a short description of the internal attribute which will be
+        /// displayed as `@internal(<name>)`
+        std::string InternalName() const override;
+
+        /// Performs a deep clone of this object using the CloneContext `ctx`.
+        /// @param ctx the clone context
+        /// @return the newly cloned object
+        const Stub* Clone(CloneContext* ctx) const override;
+
+        /// The type of the intrinsic
+        const sem::BuiltinType builtin;
+    };
+
+    /// @param program the program to inspect
+    /// @param data optional extra transform-specific input data
+    /// @returns true if this transform should be run for the given program
+    bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+
+  protected:
+    struct State;
+
+    /// Runs the transform using the CloneContext built for transforming a
+    /// program. Run() is responsible for calling Clone() on the CloneContext.
+    /// @param ctx the CloneContext primed with the input program and
+    /// ProgramBuilder
+    /// @param inputs optional extra transform-specific input data
+    /// @param outputs optional extra transform-specific output data
+    void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+};
+
+}  // namespace tint::transform
+
+#endif  // SRC_TINT_TRANSFORM_SPIRV_ATOMIC_H_
diff --git a/src/tint/transform/spirv_atomic_test.cc b/src/tint/transform/spirv_atomic_test.cc
new file mode 100644
index 0000000..804193e
--- /dev/null
+++ b/src/tint/transform/spirv_atomic_test.cc
@@ -0,0 +1,1022 @@
+// Copyright 2022 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/tint/transform/spirv_atomic.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/tint/reader/wgsl/parser_impl.h"
+#include "src/tint/transform/test_helper.h"
+
+using namespace tint::number_suffixes;  // NOLINT
+
+namespace tint::transform {
+namespace {
+
+class SpirvAtomicTest : public TransformTest {
+  public:
+    Output Run(std::string in) {
+        auto file = std::make_unique<Source::File>("test", std::move(in));
+        auto parser = reader::wgsl::ParserImpl(file.get());
+        parser.Parse();
+
+        auto& b = parser.builder();
+
+        sem::BuiltinType two_params[] = {
+            sem::BuiltinType::kAtomicExchange, sem::BuiltinType::kAtomicAdd,
+            sem::BuiltinType::kAtomicSub,      sem::BuiltinType::kAtomicMin,
+            sem::BuiltinType::kAtomicMax,      sem::BuiltinType::kAtomicAnd,
+            sem::BuiltinType::kAtomicOr,       sem::BuiltinType::kAtomicXor,
+        };
+        for (auto& a : two_params) {
+            b.Func(std::string{"stub_"} + sem::str(a) + "_u32",
+                   {
+                       b.Param("p0", b.ty.u32()),
+                       b.Param("p1", b.ty.u32()),
+                   },
+                   b.ty.u32(), {b.Return(0_u)},
+                   {b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), a)});
+            b.Func(std::string{"stub_"} + sem::str(a) + "_i32",
+                   {
+                       b.Param("p0", b.ty.i32()),
+                       b.Param("p1", b.ty.i32()),
+                   },
+                   b.ty.i32(), {b.Return(0_i)},
+                   {b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), a)});
+        }
+
+        b.Func("stub_atomicLoad_u32",
+               {
+                   b.Param("p0", b.ty.u32()),
+               },
+               b.ty.u32(), {b.Return(0_u)},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), sem::BuiltinType::kAtomicLoad),
+               });
+        b.Func("stub_atomicLoad_i32",
+               {
+                   b.Param("p0", b.ty.i32()),
+               },
+               b.ty.i32(), {b.Return(0_i)},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), sem::BuiltinType::kAtomicLoad),
+               });
+
+        b.Func("stub_atomicStore_u32",
+               {
+                   b.Param("p0", b.ty.u32()),
+                   b.Param("p1", b.ty.u32()),
+               },
+               b.ty.void_(), {},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), sem::BuiltinType::kAtomicStore),
+               });
+        b.Func("stub_atomicStore_i32",
+               {
+                   b.Param("p0", b.ty.i32()),
+                   b.Param("p1", b.ty.i32()),
+               },
+               b.ty.void_(), {},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(b.ID(), sem::BuiltinType::kAtomicStore),
+               });
+
+        b.Func("stub_atomic_compare_exchange_weak_u32",
+               {
+                   b.Param("p0", b.ty.u32()),
+                   b.Param("p1", b.ty.u32()),
+                   b.Param("p2", b.ty.u32()),
+               },
+               b.ty.u32(), {b.Return(0_u)},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(
+                       b.ID(), sem::BuiltinType::kAtomicCompareExchangeWeak),
+               });
+        b.Func("stub_atomic_compare_exchange_weak_i32",
+               {b.Param("p0", b.ty.i32()), b.Param("p1", b.ty.i32()), b.Param("p2", b.ty.i32())},
+               b.ty.i32(), {b.Return(0_i)},
+               {
+                   b.ASTNodes().Create<SpirvAtomic::Stub>(
+                       b.ID(), sem::BuiltinType::kAtomicCompareExchangeWeak),
+               });
+
+        // Keep this pointer alive after Transform() returns
+        files_.emplace_back(std::move(file));
+
+        return TransformTest::Run<SpirvAtomic>(Program(std::move(b)));
+    }
+
+  private:
+    std::vector<std::unique_ptr<Source::File>> files_;
+};
+
+TEST_F(SpirvAtomicTest, ArrayOfU32) {
+    auto* src = R"(
+var<workgroup> wg : array<u32, 4>;
+
+fn f() {
+  stub_atomicStore_u32(wg[1], 1u);
+}
+)";
+
+    auto* expect = R"(
+var<workgroup> wg : array<atomic<u32>, 4u>;
+
+fn f() {
+  atomicStore(&(wg[1]), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ArraysOfU32) {
+    auto* src = R"(
+var<workgroup> wg : array<array<array<u32, 1>, 2>, 3>;
+
+fn f() {
+  stub_atomicStore_u32(wg[2][1][0], 1u);
+}
+)";
+
+    auto* expect = R"(
+var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
+
+fn f() {
+  atomicStore(&(wg[2][1][0]), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AliasedArraysOfU32) {
+    auto* src = R"(
+type A0 = u32;
+
+type A1 = array<A0, 1>;
+
+type A2 = array<A1, 2>;
+
+type A3 = array<A2, 3>;
+
+var<workgroup> wg : A3;
+
+fn f() {
+  stub_atomicStore_u32(wg[2][1][0], 1u);
+}
+)";
+
+    auto* expect = R"(
+type A0 = u32;
+
+type A1 = array<A0, 1>;
+
+type A2 = array<A1, 2>;
+
+type A3 = array<A2, 3>;
+
+var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
+
+fn f() {
+  atomicStore(&(wg[2][1][0]), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, FlatStructSingleAtomic) {
+    auto* src = R"(
+struct S {
+  a : u32,
+}
+
+var<workgroup> wg : S;
+
+fn f() {
+  stub_atomicStore_u32(wg.a, 1u);
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : atomic<u32>,
+}
+
+struct S {
+  a : u32,
+}
+
+var<workgroup> wg : S_atomic;
+
+fn f() {
+  atomicStore(&(wg.a), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, FlatStructMultipleAtomic) {
+    auto* src = R"(
+struct S {
+  a : u32,
+  b : i32,
+}
+
+var<workgroup> wg : S;
+
+fn f1() {
+  stub_atomicStore_u32(wg.a, 1u);
+}
+
+fn f2() {
+  stub_atomicStore_i32(wg.b, 2i);
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : atomic<u32>,
+  b : atomic<i32>,
+}
+
+struct S {
+  a : u32,
+  b : i32,
+}
+
+var<workgroup> wg : S_atomic;
+
+fn f1() {
+  atomicStore(&(wg.a), 1u);
+}
+
+fn f2() {
+  atomicStore(&(wg.b), 2i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, NestedStruct) {
+    auto* src = R"(
+struct S0 {
+  a : u32,
+  b : i32,
+  c : u32,
+}
+
+struct S1 {
+  a : i32,
+  b : u32,
+  c : S0,
+}
+
+struct S2 {
+  a : i32,
+  b : S1,
+  c : u32,
+}
+
+var<workgroup> wg : S2;
+
+fn f() {
+  stub_atomicStore_u32(wg.b.c.a, 1u);
+}
+)";
+
+    auto* expect = R"(
+struct S0_atomic {
+  a : atomic<u32>,
+  b : i32,
+  c : u32,
+}
+
+struct S0 {
+  a : u32,
+  b : i32,
+  c : u32,
+}
+
+struct S1_atomic {
+  a : i32,
+  b : u32,
+  c : S0_atomic,
+}
+
+struct S1 {
+  a : i32,
+  b : u32,
+  c : S0,
+}
+
+struct S2_atomic {
+  a : i32,
+  b : S1_atomic,
+  c : u32,
+}
+
+struct S2 {
+  a : i32,
+  b : S1,
+  c : u32,
+}
+
+var<workgroup> wg : S2_atomic;
+
+fn f() {
+  atomicStore(&(wg.b.c.a), 1u);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ArrayOfStruct) {
+    auto* src = R"(
+struct S {
+  a : u32,
+  b : i32,
+  c : u32,
+}
+
+@group(0) @binding(1) var<storage, read_write> arr : array<S>;
+
+fn f() {
+  stub_atomicStore_i32(arr[4].b, 1i);
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : u32,
+  b : atomic<i32>,
+  c : u32,
+}
+
+struct S {
+  a : u32,
+  b : i32,
+  c : u32,
+}
+
+@group(0) @binding(1) var<storage, read_write> arr : array<S_atomic>;
+
+fn f() {
+  atomicStore(&(arr[4].b), 1i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, StructOfArray) {
+    auto* src = R"(
+struct S {
+  a : array<i32>,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  stub_atomicStore_i32(s.a[4], 1i);
+}
+)";
+
+    auto* expect = R"(
+struct S_atomic {
+  a : array<atomic<i32>>,
+}
+
+struct S {
+  a : array<i32>,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  atomicStore(&(s.a[4]), 1i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, ViaPtrLet) {
+    auto* src = R"(
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  let p0 = &(s);
+  let p1 : ptr<storage, i32, read_write> = &((*(p0)).i);
+  stub_atomicStore_i32(*p1, 1i);
+}
+)";
+
+    auto* expect =
+        R"(
+struct S_atomic {
+  i : atomic<i32>,
+}
+
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  let p0 = &(s);
+  let p1 : ptr<storage, atomic<i32>, read_write> = &((*(p0)).i);
+  atomicStore(&(*(p1)), 1i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, StructIsolatedMixedUsage) {
+    auto* src = R"(
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  stub_atomicStore_i32(s.i, 1i);
+}
+
+fn another_usage() {
+  var s : S;
+  let x : i32 = s.i;
+  s.i = 3i;
+}
+)";
+
+    auto* expect =
+        R"(
+struct S_atomic {
+  i : atomic<i32>,
+}
+
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  atomicStore(&(s.i), 1i);
+}
+
+fn another_usage() {
+  var s : S;
+  let x : i32 = s.i;
+  s.i = 3i;
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+// This sort of mixed usage isn't handled yet. Not sure if we need to just yet.
+// If we don't, then the transform should give sensible diagnostics instead of producing invalid
+// WGSL.
+// TODO(crbug.com/tint/1595)
+TEST_F(SpirvAtomicTest, DISABLED_StructComplexMixedUsage) {
+    auto* src = R"(
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S;
+
+fn f() {
+  let x : i32 = s.i;
+  stub_atomicStore_i32(s.i, 1i);
+  s.i = 3i;
+}
+)";
+
+    auto* expect =
+        R"(
+struct S_atomic {
+  i : atomic<i32>,
+}
+
+struct S {
+  i : i32,
+}
+
+@group(0) @binding(1) var<storage, read_write> s : S_atomic;
+
+fn f() {
+  let x : i32 = atomicLoad(&s.i);
+  stub_atomicStore_i32(s.i, 1i);
+  atomicStore(&(s.i), 1i);
+}
+)";
+
+    auto got = Run(src);
+
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicLoad) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicLoad_u32(wg_u32);}
+  {let r = stub_atomicLoad_i32(wg_i32);}
+  {let r = stub_atomicLoad_u32(sg_u32);}
+  {let r = stub_atomicLoad_i32(sg_i32);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicLoad(&(wg_u32));
+  }
+  {
+    let r = atomicLoad(&(wg_i32));
+  }
+  {
+    let r = atomicLoad(&(sg_u32));
+  }
+  {
+    let r = atomicLoad(&(sg_i32));
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicExchange) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicExchange_u32(wg_u32, 123u);}
+  {let r = stub_atomicExchange_i32(wg_i32, 123i);}
+  {let r = stub_atomicExchange_u32(sg_u32, 123u);}
+  {let r = stub_atomicExchange_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicExchange(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicExchange(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicExchange(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicExchange(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicAdd) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicAdd_u32(wg_u32, 123u);}
+  {let r = stub_atomicAdd_i32(wg_i32, 123i);}
+  {let r = stub_atomicAdd_u32(sg_u32, 123u);}
+  {let r = stub_atomicAdd_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicAdd(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicAdd(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicAdd(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicAdd(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicSub) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicSub_u32(wg_u32, 123u);}
+  {let r = stub_atomicSub_i32(wg_i32, 123i);}
+  {let r = stub_atomicSub_u32(sg_u32, 123u);}
+  {let r = stub_atomicSub_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicSub(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicSub(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicSub(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicSub(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicMin) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicMin_u32(wg_u32, 123u);}
+  {let r = stub_atomicMin_i32(wg_i32, 123i);}
+  {let r = stub_atomicMin_u32(sg_u32, 123u);}
+  {let r = stub_atomicMin_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicMin(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicMin(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicMin(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicMin(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicMax) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicMax_u32(wg_u32, 123u);}
+  {let r = stub_atomicMax_i32(wg_i32, 123i);}
+  {let r = stub_atomicMax_u32(sg_u32, 123u);}
+  {let r = stub_atomicMax_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicMax(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicMax(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicMax(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicMax(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicAnd) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicAnd_u32(wg_u32, 123u);}
+  {let r = stub_atomicAnd_i32(wg_i32, 123i);}
+  {let r = stub_atomicAnd_u32(sg_u32, 123u);}
+  {let r = stub_atomicAnd_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicAnd(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicAnd(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicAnd(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicAnd(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicOr) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicOr_u32(wg_u32, 123u);}
+  {let r = stub_atomicOr_i32(wg_i32, 123i);}
+  {let r = stub_atomicOr_u32(sg_u32, 123u);}
+  {let r = stub_atomicOr_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicOr(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicOr(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicOr(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicOr(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicXor) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomicXor_u32(wg_u32, 123u);}
+  {let r = stub_atomicXor_i32(wg_i32, 123i);}
+  {let r = stub_atomicXor_u32(sg_u32, 123u);}
+  {let r = stub_atomicXor_i32(sg_i32, 123i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let r = atomicXor(&(wg_u32), 123u);
+  }
+  {
+    let r = atomicXor(&(wg_i32), 123i);
+  }
+  {
+    let r = atomicXor(&(sg_u32), 123u);
+  }
+  {
+    let r = atomicXor(&(sg_i32), 123i);
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SpirvAtomicTest, AtomicCompareExchangeWeak) {
+    auto* src = R"(
+var<workgroup> wg_u32 : u32;
+var<workgroup> wg_i32 : i32;
+@group(0) @binding(0) var<storage, read_write> sg_u32 : u32;
+@group(0) @binding(1) var<storage, read_write> sg_i32 : i32;
+
+fn f() {
+  {let r = stub_atomic_compare_exchange_weak_u32(wg_u32, 123u, 456u);}
+  {let r = stub_atomic_compare_exchange_weak_i32(wg_i32, 123i, 456i);}
+  {let r = stub_atomic_compare_exchange_weak_u32(sg_u32, 123u, 456u);}
+  {let r = stub_atomic_compare_exchange_weak_i32(sg_i32, 123i, 456i);}
+}
+)";
+
+    auto* expect =
+        R"(
+var<workgroup> wg_u32 : atomic<u32>;
+
+var<workgroup> wg_i32 : atomic<i32>;
+
+@group(0) @binding(0) var<storage, read_write> sg_u32 : atomic<u32>;
+
+@group(0) @binding(1) var<storage, read_write> sg_i32 : atomic<i32>;
+
+fn f() {
+  {
+    let old_value = atomicCompareExchangeWeak(&(wg_u32), 123u, 456u).old_value;
+    let r = old_value;
+  }
+  {
+    let old_value_2 = atomicCompareExchangeWeak(&(wg_i32), 123i, 456i).old_value;
+    let r = old_value_2;
+  }
+  {
+    let old_value_1 = atomicCompareExchangeWeak(&(sg_u32), 123u, 456u).old_value;
+    let r = old_value_1;
+  }
+  {
+    let old_value_3 = atomicCompareExchangeWeak(&(sg_i32), 123i, 456i).old_value;
+    let r = old_value_3;
+  }
+}
+)";
+
+    auto got = Run(src);
+    EXPECT_EQ(expect, str(got));
+}
+
+}  // namespace
+}  // namespace tint::transform
diff --git a/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm
new file mode 100644
index 0000000..0dbd964
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm
@@ -0,0 +1,88 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 58
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpDecorate %_arr_uint_uint_1 ArrayStride 4
+               OpDecorate %_arr__arr_uint_uint_1_uint_2 ArrayStride 4
+               OpDecorate %_arr__arr__arr_uint_uint_1_uint_2_uint_3 ArrayStride 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+     %uint_1 = OpConstant %uint 1
+%_arr_uint_uint_1 = OpTypeArray %uint %uint_1
+     %uint_2 = OpConstant %uint 2
+%_arr__arr_uint_uint_1_uint_2 = OpTypeArray %_arr_uint_uint_1 %uint_2
+     %uint_3 = OpConstant %uint 3
+%_arr__arr__arr_uint_uint_1_uint_2_uint_3 = OpTypeArray %_arr__arr_uint_uint_1_uint_2 %uint_3
+%_ptr_Workgroup__arr__arr__arr_uint_uint_1_uint_2_uint_3 = OpTypePointer Workgroup %_arr__arr__arr_uint_uint_1_uint_2_uint_3
+         %wg = OpVariable %_ptr_Workgroup__arr__arr__arr_uint_uint_1_uint_2_uint_3 Workgroup
+       %void = OpTypeVoid
+         %12 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %19 = OpConstantNull %uint
+     %uint_6 = OpConstant %uint 6
+       %bool = OpTypeBool
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+        %int = OpTypeInt 32 1
+      %int_2 = OpConstant %int 2
+      %int_1 = OpConstant %int 1
+         %51 = OpConstantNull %int
+         %53 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %12
+%local_invocation_index = OpFunctionParameter %uint
+         %16 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %19
+               OpStore %idx %local_invocation_index
+               OpBranch %20
+         %20 = OpLabel
+               OpLoopMerge %21 %22 None
+               OpBranch %23
+         %23 = OpLabel
+         %25 = OpLoad %uint %idx
+         %27 = OpULessThan %bool %25 %uint_6
+         %24 = OpLogicalNot %bool %27
+               OpSelectionMerge %29 None
+               OpBranchConditional %24 %30 %29
+         %30 = OpLabel
+               OpBranch %21
+         %29 = OpLabel
+         %31 = OpLoad %uint %idx
+         %32 = OpUDiv %uint %31 %uint_2
+         %33 = OpLoad %uint %idx
+         %34 = OpUMod %uint %33 %uint_2
+         %35 = OpLoad %uint %idx
+         %36 = OpUMod %uint %35 %uint_1
+         %41 = OpAccessChain %_ptr_Workgroup_uint %wg %32 %34 %36
+               OpAtomicStore %41 %uint_2 %uint_0 %19
+               OpBranch %22
+         %22 = OpLabel
+         %42 = OpLoad %uint %idx
+         %43 = OpIAdd %uint %42 %uint_1
+               OpStore %idx %43
+               OpBranch %20
+         %21 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %52 = OpAccessChain %_ptr_Workgroup_uint %wg %int_2 %int_1 %51
+               OpAtomicStore %52 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %53
+         %55 = OpLabel
+         %57 = OpLoad %uint %local_invocation_index_1
+         %56 = OpFunctionCall %void %compute_main_inner %57
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.glsl b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.glsl
new file mode 100644
index 0000000..4cdd505
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.glsl
@@ -0,0 +1,41 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint wg[3][2][1];
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  {
+    for(; !(!((idx < 6u))); idx = (idx + 1u)) {
+      atomicExchange(wg[(idx / 2u)][(idx % 2u)][(idx % 1u)], 0u);
+    }
+  }
+  barrier();
+  atomicExchange(wg[2][1][0], 1u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 6u); idx_1 = (idx_1 + 1u)) {
+      uint i = (idx_1 / 2u);
+      uint i_1 = (idx_1 % 2u);
+      uint i_2 = (idx_1 % 1u);
+      atomicExchange(wg[i][i_1][i_2], 0u);
+    }
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.hlsl
new file mode 100644
index 0000000..16f5d2e
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.hlsl
@@ -0,0 +1,47 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint wg[3][2][1];
+
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  {
+    [loop] for(; !(!((idx < 6u))); idx = (idx + 1u)) {
+      uint atomic_result = 0u;
+      InterlockedExchange(wg[(idx / 2u)][(idx % 2u)][(idx % 1u)], 0u, atomic_result);
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg[2][1][0], 1u, atomic_result_1);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    [loop] for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 6u); idx_1 = (idx_1 + 1u)) {
+      const uint i = (idx_1 / 2u);
+      const uint i_1 = (idx_1 % 2u);
+      const uint i_2 = (idx_1 % 1u);
+      uint atomic_result_2 = 0u;
+      InterlockedExchange(wg[i][i_1][i_2], 0u, atomic_result_2);
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.msl b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.msl
new file mode 100644
index 0000000..896696e
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.msl
@@ -0,0 +1,74 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  uint arr[1];
+};
+
+struct tint_array_wrapper_1 {
+  tint_array_wrapper arr[2];
+};
+
+struct tint_array_wrapper_2 {
+  tint_array_wrapper_1 arr[3];
+};
+
+struct tint_array_wrapper_5 {
+  atomic_uint arr[1];
+};
+
+struct tint_array_wrapper_4 {
+  tint_array_wrapper_5 arr[2];
+};
+
+struct tint_array_wrapper_3 {
+  tint_array_wrapper_4 arr[3];
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup tint_array_wrapper_3* const tint_symbol) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  while (true) {
+    uint const x_25 = idx;
+    if (!((x_25 < 6u))) {
+      break;
+    }
+    uint const x_31 = idx;
+    uint const x_33 = idx;
+    uint const x_35 = idx;
+    atomic_store_explicit(&((*(tint_symbol)).arr[(x_31 / 2u)].arr[(x_33 % 2u)].arr[(x_35 % 1u)]), 0u, memory_order_relaxed);
+    {
+      uint const x_42 = idx;
+      idx = (x_42 + 1u);
+    }
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).arr[2].arr[1].arr[0]), 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup tint_array_wrapper_3* const tint_symbol_2) {
+  uint const x_57 = *(tint_symbol_1);
+  compute_main_inner(x_57, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup tint_array_wrapper_3* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 6u); idx_1 = (idx_1 + 1u)) {
+    uint const i = (idx_1 / 2u);
+    uint const i_1 = (idx_1 % 2u);
+    uint const i_2 = (idx_1 % 1u);
+    atomic_store_explicit(&((*(tint_symbol_3)).arr[i].arr[i_1].arr[i_2]), 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup tint_array_wrapper_3 tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.spvasm
new file mode 100644
index 0000000..8091c97
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.spvasm
@@ -0,0 +1,140 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 90
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %idx_1 "idx_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpDecorate %_arr_uint_uint_1 ArrayStride 4
+               OpDecorate %_arr__arr_uint_uint_1_uint_2 ArrayStride 4
+               OpDecorate %_arr__arr__arr_uint_uint_1_uint_2_uint_3 ArrayStride 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+     %uint_1 = OpConstant %uint 1
+%_arr_uint_uint_1 = OpTypeArray %uint %uint_1
+     %uint_2 = OpConstant %uint 2
+%_arr__arr_uint_uint_1_uint_2 = OpTypeArray %_arr_uint_uint_1 %uint_2
+     %uint_3 = OpConstant %uint 3
+%_arr__arr__arr_uint_uint_1_uint_2_uint_3 = OpTypeArray %_arr__arr_uint_uint_1_uint_2 %uint_3
+%_ptr_Workgroup__arr__arr__arr_uint_uint_1_uint_2_uint_3 = OpTypePointer Workgroup %_arr__arr__arr_uint_uint_1_uint_2_uint_3
+         %wg = OpVariable %_ptr_Workgroup__arr__arr__arr_uint_uint_1_uint_2_uint_3 Workgroup
+       %void = OpTypeVoid
+         %15 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_6 = OpConstant %uint 6
+       %bool = OpTypeBool
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+        %int = OpTypeInt 32 1
+      %int_2 = OpConstant %int 2
+      %int_1 = OpConstant %int 1
+         %53 = OpConstantNull %int
+         %55 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %15
+%local_invocation_index = OpFunctionParameter %uint
+         %19 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx %6
+               OpStore %idx %local_invocation_index
+               OpBranch %22
+         %22 = OpLabel
+               OpLoopMerge %23 %24 None
+               OpBranch %25
+         %25 = OpLabel
+         %26 = OpLoad %uint %idx
+         %29 = OpULessThan %bool %26 %uint_6
+         %27 = OpLogicalNot %bool %29
+               OpSelectionMerge %31 None
+               OpBranchConditional %27 %32 %31
+         %32 = OpLabel
+               OpBranch %23
+         %31 = OpLabel
+         %33 = OpLoad %uint %idx
+         %34 = OpLoad %uint %idx
+         %35 = OpLoad %uint %idx
+         %39 = OpUDiv %uint %33 %uint_2
+         %40 = OpUMod %uint %34 %uint_2
+         %41 = OpUMod %uint %35 %uint_1
+         %43 = OpAccessChain %_ptr_Workgroup_uint %wg %39 %40 %41
+               OpAtomicStore %43 %uint_2 %uint_0 %6
+               OpBranch %24
+         %24 = OpLabel
+         %44 = OpLoad %uint %idx
+         %45 = OpIAdd %uint %44 %uint_1
+               OpStore %idx %45
+               OpBranch %22
+         %23 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %54 = OpAccessChain %_ptr_Workgroup_uint %wg %int_2 %int_1 %53
+               OpAtomicStore %54 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %55
+         %57 = OpLabel
+         %58 = OpLoad %uint %local_invocation_index_1
+         %59 = OpFunctionCall %void %compute_main_inner %58
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %15
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %62 = OpLabel
+      %idx_1 = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx_1 %local_invocation_index_1_param
+               OpBranch %64
+         %64 = OpLabel
+               OpLoopMerge %65 %66 None
+               OpBranch %67
+         %67 = OpLabel
+         %69 = OpLoad %uint %idx_1
+         %70 = OpULessThan %bool %69 %uint_6
+         %68 = OpLogicalNot %bool %70
+               OpSelectionMerge %71 None
+               OpBranchConditional %68 %72 %71
+         %72 = OpLabel
+               OpBranch %65
+         %71 = OpLabel
+         %73 = OpLoad %uint %idx_1
+         %74 = OpUDiv %uint %73 %uint_2
+         %75 = OpLoad %uint %idx_1
+         %76 = OpUMod %uint %75 %uint_2
+         %77 = OpLoad %uint %idx_1
+         %78 = OpUMod %uint %77 %uint_1
+         %81 = OpAccessChain %_ptr_Workgroup_uint %wg %74 %76 %78
+               OpAtomicStore %81 %uint_2 %uint_0 %6
+               OpBranch %66
+         %66 = OpLabel
+         %82 = OpLoad %uint %idx_1
+         %83 = OpIAdd %uint %82 %uint_1
+               OpStore %idx_1 %83
+               OpBranch %64
+         %65 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %85 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %55
+         %87 = OpLabel
+         %89 = OpLoad %uint %local_invocation_index_1_param_1
+         %88 = OpFunctionCall %void %compute_main_inner_1 %89
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.wgsl
new file mode 100644
index 0000000..2b2b741
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/aliased_arrays.spvasm.expected.wgsl
@@ -0,0 +1,44 @@
+type Arr = array<u32, 1u>;
+
+type Arr_1 = array<Arr, 2u>;
+
+type Arr_2 = array<Arr_1, 3u>;
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  var idx : u32 = 0u;
+  idx = local_invocation_index;
+  loop {
+    let x_25 : u32 = idx;
+    if (!((x_25 < 6u))) {
+      break;
+    }
+    let x_31 : u32 = idx;
+    let x_33 : u32 = idx;
+    let x_35 : u32 = idx;
+    atomicStore(&(wg[(x_31 / 2u)][(x_33 % 2u)][(x_35 % 1u)]), 0u);
+
+    continuing {
+      let x_42 : u32 = idx;
+      idx = (x_42 + 1u);
+    }
+  }
+  workgroupBarrier();
+  atomicStore(&(wg[2i][1i][0i]), 1u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_57 : u32 = local_invocation_index_1;
+  compute_main_inner(x_57);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/array/aliased_arrays.wgsl b/test/tint/builtins/atomicStore/array/aliased_arrays.wgsl
new file mode 100644
index 0000000..0f6f01e
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/aliased_arrays.wgsl
@@ -0,0 +1,11 @@
+type A0 = atomic<u32>;
+type A1 = array<A0, 1>;
+type A2 = array<A1, 2>;
+type A3 = array<A2, 3>;
+
+var<workgroup> wg : A3;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  atomicStore(&wg[2][1][0], 1u);
+}
diff --git a/test/tint/builtins/atomicStore/array/array.spvasm b/test/tint/builtins/atomicStore/array/array.spvasm
new file mode 100644
index 0000000..f41ddf7
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/array.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpDecorate %_arr_uint_uint_4 ArrayStride 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+     %uint_4 = OpConstant %uint 4
+%_arr_uint_uint_4 = OpTypeArray %uint %uint_4
+%_ptr_Workgroup__arr_uint_uint_4 = OpTypePointer Workgroup %_arr_uint_uint_4
+         %wg = OpVariable %_ptr_Workgroup__arr_uint_uint_4 Workgroup
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %15 = OpConstantNull %uint
+       %bool = OpTypeBool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+     %uint_1 = OpConstant %uint 1
+   %uint_264 = OpConstant %uint 264
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+         %43 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %8
+%local_invocation_index = OpFunctionParameter %uint
+         %12 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %15
+               OpStore %idx %local_invocation_index
+               OpBranch %16
+         %16 = OpLabel
+               OpLoopMerge %17 %18 None
+               OpBranch %19
+         %19 = OpLabel
+         %21 = OpLoad %uint %idx
+         %22 = OpULessThan %bool %21 %uint_4
+         %20 = OpLogicalNot %bool %22
+               OpSelectionMerge %24 None
+               OpBranchConditional %20 %25 %24
+         %25 = OpLabel
+               OpBranch %17
+         %24 = OpLabel
+         %26 = OpLoad %uint %idx
+         %32 = OpAccessChain %_ptr_Workgroup_uint %wg %26
+               OpAtomicStore %32 %uint_2 %uint_0 %15
+               OpBranch %18
+         %18 = OpLabel
+         %33 = OpLoad %uint %idx
+         %35 = OpIAdd %uint %33 %uint_1
+               OpStore %idx %35
+               OpBranch %16
+         %17 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %42 = OpAccessChain %_ptr_Workgroup_uint %wg %int_1
+               OpAtomicStore %42 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %43
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1
+         %46 = OpFunctionCall %void %compute_main_inner %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/array/array.spvasm.expected.glsl b/test/tint/builtins/atomicStore/array/array.spvasm.expected.glsl
new file mode 100644
index 0000000..a8c2193
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/array.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint wg[4];
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  {
+    for(; !(!((idx < 4u))); idx = (idx + 1u)) {
+      atomicExchange(wg[idx], 0u);
+    }
+  }
+  barrier();
+  atomicExchange(wg[1], 1u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 4u); idx_1 = (idx_1 + 1u)) {
+      uint i = idx_1;
+      atomicExchange(wg[i], 0u);
+    }
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/array/array.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/array/array.spvasm.expected.hlsl
new file mode 100644
index 0000000..76ff725
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/array.spvasm.expected.hlsl
@@ -0,0 +1,45 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint wg[4];
+
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  {
+    [loop] for(; !(!((idx < 4u))); idx = (idx + 1u)) {
+      uint atomic_result = 0u;
+      InterlockedExchange(wg[idx], 0u, atomic_result);
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg[1], 1u, atomic_result_1);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    [loop] for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 4u); idx_1 = (idx_1 + 1u)) {
+      const uint i = idx_1;
+      uint atomic_result_2 = 0u;
+      InterlockedExchange(wg[i], 0u, atomic_result_2);
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/array/array.spvasm.expected.msl b/test/tint/builtins/atomicStore/array/array.spvasm.expected.msl
new file mode 100644
index 0000000..d2ab1ec
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/array.spvasm.expected.msl
@@ -0,0 +1,54 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  uint arr[4];
+};
+
+struct tint_array_wrapper_1 {
+  atomic_uint arr[4];
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup tint_array_wrapper_1* const tint_symbol) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  while (true) {
+    uint const x_21 = idx;
+    if (!((x_21 < 4u))) {
+      break;
+    }
+    uint const x_26 = idx;
+    atomic_store_explicit(&((*(tint_symbol)).arr[x_26]), 0u, memory_order_relaxed);
+    {
+      uint const x_33 = idx;
+      idx = (x_33 + 1u);
+    }
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).arr[1]), 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup tint_array_wrapper_1* const tint_symbol_2) {
+  uint const x_47 = *(tint_symbol_1);
+  compute_main_inner(x_47, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup tint_array_wrapper_1* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 4u); idx_1 = (idx_1 + 1u)) {
+    uint const i = idx_1;
+    atomic_store_explicit(&((*(tint_symbol_3)).arr[i]), 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup tint_array_wrapper_1 tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/array/array.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/array/array.spvasm.expected.spvasm
new file mode 100644
index 0000000..e15c3db
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/array.spvasm.expected.spvasm
@@ -0,0 +1,123 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 75
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %idx_1 "idx_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpDecorate %_arr_uint_uint_4 ArrayStride 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+     %uint_4 = OpConstant %uint 4
+%_arr_uint_uint_4 = OpTypeArray %uint %uint_4
+%_ptr_Workgroup__arr_uint_uint_4 = OpTypePointer Workgroup %_arr_uint_uint_4
+         %wg = OpVariable %_ptr_Workgroup__arr_uint_uint_4 Workgroup
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+       %bool = OpTypeBool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+     %uint_1 = OpConstant %uint 1
+   %uint_264 = OpConstant %uint 264
+        %int = OpTypeInt 32 1
+      %int_1 = OpConstant %int 1
+         %45 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %11
+%local_invocation_index = OpFunctionParameter %uint
+         %15 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx %6
+               OpStore %idx %local_invocation_index
+               OpBranch %18
+         %18 = OpLabel
+               OpLoopMerge %19 %20 None
+               OpBranch %21
+         %21 = OpLabel
+         %22 = OpLoad %uint %idx
+         %24 = OpULessThan %bool %22 %uint_4
+         %23 = OpLogicalNot %bool %24
+               OpSelectionMerge %26 None
+               OpBranchConditional %23 %27 %26
+         %27 = OpLabel
+               OpBranch %19
+         %26 = OpLabel
+         %28 = OpLoad %uint %idx
+         %34 = OpAccessChain %_ptr_Workgroup_uint %wg %28
+               OpAtomicStore %34 %uint_2 %uint_0 %6
+               OpBranch %20
+         %20 = OpLabel
+         %35 = OpLoad %uint %idx
+         %37 = OpIAdd %uint %35 %uint_1
+               OpStore %idx %37
+               OpBranch %18
+         %19 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %44 = OpAccessChain %_ptr_Workgroup_uint %wg %int_1
+               OpAtomicStore %44 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %45
+         %47 = OpLabel
+         %48 = OpLoad %uint %local_invocation_index_1
+         %49 = OpFunctionCall %void %compute_main_inner %48
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %11
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %52 = OpLabel
+      %idx_1 = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx_1 %local_invocation_index_1_param
+               OpBranch %54
+         %54 = OpLabel
+               OpLoopMerge %55 %56 None
+               OpBranch %57
+         %57 = OpLabel
+         %59 = OpLoad %uint %idx_1
+         %60 = OpULessThan %bool %59 %uint_4
+         %58 = OpLogicalNot %bool %60
+               OpSelectionMerge %61 None
+               OpBranchConditional %58 %62 %61
+         %62 = OpLabel
+               OpBranch %55
+         %61 = OpLabel
+         %63 = OpLoad %uint %idx_1
+         %66 = OpAccessChain %_ptr_Workgroup_uint %wg %63
+               OpAtomicStore %66 %uint_2 %uint_0 %6
+               OpBranch %56
+         %56 = OpLabel
+         %67 = OpLoad %uint %idx_1
+         %68 = OpIAdd %uint %67 %uint_1
+               OpStore %idx_1 %68
+               OpBranch %54
+         %55 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %70 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %45
+         %72 = OpLabel
+         %74 = OpLoad %uint %local_invocation_index_1_param_1
+         %73 = OpFunctionCall %void %compute_main_inner_1 %74
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/array/array.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/array/array.spvasm.expected.wgsl
new file mode 100644
index 0000000..c597173
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/array.spvasm.expected.wgsl
@@ -0,0 +1,38 @@
+type Arr = array<u32, 4u>;
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : array<atomic<u32>, 4u>;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  var idx : u32 = 0u;
+  idx = local_invocation_index;
+  loop {
+    let x_21 : u32 = idx;
+    if (!((x_21 < 4u))) {
+      break;
+    }
+    let x_26 : u32 = idx;
+    atomicStore(&(wg[x_26]), 0u);
+
+    continuing {
+      let x_33 : u32 = idx;
+      idx = (x_33 + 1u);
+    }
+  }
+  workgroupBarrier();
+  atomicStore(&(wg[1i]), 1u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_47 : u32 = local_invocation_index_1;
+  compute_main_inner(x_47);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/array/array.wgsl b/test/tint/builtins/atomicStore/array/array.wgsl
new file mode 100644
index 0000000..430207a
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/array.wgsl
@@ -0,0 +1,6 @@
+var<workgroup> wg : array<atomic<u32>, 4>;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  atomicStore(&wg[1], 1u);
+}
diff --git a/test/tint/builtins/atomicStore/array/arrays.spvasm b/test/tint/builtins/atomicStore/array/arrays.spvasm
new file mode 100644
index 0000000..0dbd964
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/arrays.spvasm
@@ -0,0 +1,88 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 58
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpDecorate %_arr_uint_uint_1 ArrayStride 4
+               OpDecorate %_arr__arr_uint_uint_1_uint_2 ArrayStride 4
+               OpDecorate %_arr__arr__arr_uint_uint_1_uint_2_uint_3 ArrayStride 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+     %uint_1 = OpConstant %uint 1
+%_arr_uint_uint_1 = OpTypeArray %uint %uint_1
+     %uint_2 = OpConstant %uint 2
+%_arr__arr_uint_uint_1_uint_2 = OpTypeArray %_arr_uint_uint_1 %uint_2
+     %uint_3 = OpConstant %uint 3
+%_arr__arr__arr_uint_uint_1_uint_2_uint_3 = OpTypeArray %_arr__arr_uint_uint_1_uint_2 %uint_3
+%_ptr_Workgroup__arr__arr__arr_uint_uint_1_uint_2_uint_3 = OpTypePointer Workgroup %_arr__arr__arr_uint_uint_1_uint_2_uint_3
+         %wg = OpVariable %_ptr_Workgroup__arr__arr__arr_uint_uint_1_uint_2_uint_3 Workgroup
+       %void = OpTypeVoid
+         %12 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %19 = OpConstantNull %uint
+     %uint_6 = OpConstant %uint 6
+       %bool = OpTypeBool
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+        %int = OpTypeInt 32 1
+      %int_2 = OpConstant %int 2
+      %int_1 = OpConstant %int 1
+         %51 = OpConstantNull %int
+         %53 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %12
+%local_invocation_index = OpFunctionParameter %uint
+         %16 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %19
+               OpStore %idx %local_invocation_index
+               OpBranch %20
+         %20 = OpLabel
+               OpLoopMerge %21 %22 None
+               OpBranch %23
+         %23 = OpLabel
+         %25 = OpLoad %uint %idx
+         %27 = OpULessThan %bool %25 %uint_6
+         %24 = OpLogicalNot %bool %27
+               OpSelectionMerge %29 None
+               OpBranchConditional %24 %30 %29
+         %30 = OpLabel
+               OpBranch %21
+         %29 = OpLabel
+         %31 = OpLoad %uint %idx
+         %32 = OpUDiv %uint %31 %uint_2
+         %33 = OpLoad %uint %idx
+         %34 = OpUMod %uint %33 %uint_2
+         %35 = OpLoad %uint %idx
+         %36 = OpUMod %uint %35 %uint_1
+         %41 = OpAccessChain %_ptr_Workgroup_uint %wg %32 %34 %36
+               OpAtomicStore %41 %uint_2 %uint_0 %19
+               OpBranch %22
+         %22 = OpLabel
+         %42 = OpLoad %uint %idx
+         %43 = OpIAdd %uint %42 %uint_1
+               OpStore %idx %43
+               OpBranch %20
+         %21 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %52 = OpAccessChain %_ptr_Workgroup_uint %wg %int_2 %int_1 %51
+               OpAtomicStore %52 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %53
+         %55 = OpLabel
+         %57 = OpLoad %uint %local_invocation_index_1
+         %56 = OpFunctionCall %void %compute_main_inner %57
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.glsl b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.glsl
new file mode 100644
index 0000000..4cdd505
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.glsl
@@ -0,0 +1,41 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint wg[3][2][1];
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  {
+    for(; !(!((idx < 6u))); idx = (idx + 1u)) {
+      atomicExchange(wg[(idx / 2u)][(idx % 2u)][(idx % 1u)], 0u);
+    }
+  }
+  barrier();
+  atomicExchange(wg[2][1][0], 1u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 6u); idx_1 = (idx_1 + 1u)) {
+      uint i = (idx_1 / 2u);
+      uint i_1 = (idx_1 % 2u);
+      uint i_2 = (idx_1 % 1u);
+      atomicExchange(wg[i][i_1][i_2], 0u);
+    }
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.hlsl
new file mode 100644
index 0000000..16f5d2e
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.hlsl
@@ -0,0 +1,47 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint wg[3][2][1];
+
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  {
+    [loop] for(; !(!((idx < 6u))); idx = (idx + 1u)) {
+      uint atomic_result = 0u;
+      InterlockedExchange(wg[(idx / 2u)][(idx % 2u)][(idx % 1u)], 0u, atomic_result);
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg[2][1][0], 1u, atomic_result_1);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    [loop] for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 6u); idx_1 = (idx_1 + 1u)) {
+      const uint i = (idx_1 / 2u);
+      const uint i_1 = (idx_1 % 2u);
+      const uint i_2 = (idx_1 % 1u);
+      uint atomic_result_2 = 0u;
+      InterlockedExchange(wg[i][i_1][i_2], 0u, atomic_result_2);
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.msl b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.msl
new file mode 100644
index 0000000..896696e
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.msl
@@ -0,0 +1,74 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  uint arr[1];
+};
+
+struct tint_array_wrapper_1 {
+  tint_array_wrapper arr[2];
+};
+
+struct tint_array_wrapper_2 {
+  tint_array_wrapper_1 arr[3];
+};
+
+struct tint_array_wrapper_5 {
+  atomic_uint arr[1];
+};
+
+struct tint_array_wrapper_4 {
+  tint_array_wrapper_5 arr[2];
+};
+
+struct tint_array_wrapper_3 {
+  tint_array_wrapper_4 arr[3];
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup tint_array_wrapper_3* const tint_symbol) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  while (true) {
+    uint const x_25 = idx;
+    if (!((x_25 < 6u))) {
+      break;
+    }
+    uint const x_31 = idx;
+    uint const x_33 = idx;
+    uint const x_35 = idx;
+    atomic_store_explicit(&((*(tint_symbol)).arr[(x_31 / 2u)].arr[(x_33 % 2u)].arr[(x_35 % 1u)]), 0u, memory_order_relaxed);
+    {
+      uint const x_42 = idx;
+      idx = (x_42 + 1u);
+    }
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).arr[2].arr[1].arr[0]), 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup tint_array_wrapper_3* const tint_symbol_2) {
+  uint const x_57 = *(tint_symbol_1);
+  compute_main_inner(x_57, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup tint_array_wrapper_3* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 6u); idx_1 = (idx_1 + 1u)) {
+    uint const i = (idx_1 / 2u);
+    uint const i_1 = (idx_1 % 2u);
+    uint const i_2 = (idx_1 % 1u);
+    atomic_store_explicit(&((*(tint_symbol_3)).arr[i].arr[i_1].arr[i_2]), 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup tint_array_wrapper_3 tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.spvasm
new file mode 100644
index 0000000..8091c97
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.spvasm
@@ -0,0 +1,140 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 90
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %idx_1 "idx_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpDecorate %_arr_uint_uint_1 ArrayStride 4
+               OpDecorate %_arr__arr_uint_uint_1_uint_2 ArrayStride 4
+               OpDecorate %_arr__arr__arr_uint_uint_1_uint_2_uint_3 ArrayStride 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+     %uint_1 = OpConstant %uint 1
+%_arr_uint_uint_1 = OpTypeArray %uint %uint_1
+     %uint_2 = OpConstant %uint 2
+%_arr__arr_uint_uint_1_uint_2 = OpTypeArray %_arr_uint_uint_1 %uint_2
+     %uint_3 = OpConstant %uint 3
+%_arr__arr__arr_uint_uint_1_uint_2_uint_3 = OpTypeArray %_arr__arr_uint_uint_1_uint_2 %uint_3
+%_ptr_Workgroup__arr__arr__arr_uint_uint_1_uint_2_uint_3 = OpTypePointer Workgroup %_arr__arr__arr_uint_uint_1_uint_2_uint_3
+         %wg = OpVariable %_ptr_Workgroup__arr__arr__arr_uint_uint_1_uint_2_uint_3 Workgroup
+       %void = OpTypeVoid
+         %15 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_6 = OpConstant %uint 6
+       %bool = OpTypeBool
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+        %int = OpTypeInt 32 1
+      %int_2 = OpConstant %int 2
+      %int_1 = OpConstant %int 1
+         %53 = OpConstantNull %int
+         %55 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %15
+%local_invocation_index = OpFunctionParameter %uint
+         %19 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx %6
+               OpStore %idx %local_invocation_index
+               OpBranch %22
+         %22 = OpLabel
+               OpLoopMerge %23 %24 None
+               OpBranch %25
+         %25 = OpLabel
+         %26 = OpLoad %uint %idx
+         %29 = OpULessThan %bool %26 %uint_6
+         %27 = OpLogicalNot %bool %29
+               OpSelectionMerge %31 None
+               OpBranchConditional %27 %32 %31
+         %32 = OpLabel
+               OpBranch %23
+         %31 = OpLabel
+         %33 = OpLoad %uint %idx
+         %34 = OpLoad %uint %idx
+         %35 = OpLoad %uint %idx
+         %39 = OpUDiv %uint %33 %uint_2
+         %40 = OpUMod %uint %34 %uint_2
+         %41 = OpUMod %uint %35 %uint_1
+         %43 = OpAccessChain %_ptr_Workgroup_uint %wg %39 %40 %41
+               OpAtomicStore %43 %uint_2 %uint_0 %6
+               OpBranch %24
+         %24 = OpLabel
+         %44 = OpLoad %uint %idx
+         %45 = OpIAdd %uint %44 %uint_1
+               OpStore %idx %45
+               OpBranch %22
+         %23 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %54 = OpAccessChain %_ptr_Workgroup_uint %wg %int_2 %int_1 %53
+               OpAtomicStore %54 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %55
+         %57 = OpLabel
+         %58 = OpLoad %uint %local_invocation_index_1
+         %59 = OpFunctionCall %void %compute_main_inner %58
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %15
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %62 = OpLabel
+      %idx_1 = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx_1 %local_invocation_index_1_param
+               OpBranch %64
+         %64 = OpLabel
+               OpLoopMerge %65 %66 None
+               OpBranch %67
+         %67 = OpLabel
+         %69 = OpLoad %uint %idx_1
+         %70 = OpULessThan %bool %69 %uint_6
+         %68 = OpLogicalNot %bool %70
+               OpSelectionMerge %71 None
+               OpBranchConditional %68 %72 %71
+         %72 = OpLabel
+               OpBranch %65
+         %71 = OpLabel
+         %73 = OpLoad %uint %idx_1
+         %74 = OpUDiv %uint %73 %uint_2
+         %75 = OpLoad %uint %idx_1
+         %76 = OpUMod %uint %75 %uint_2
+         %77 = OpLoad %uint %idx_1
+         %78 = OpUMod %uint %77 %uint_1
+         %81 = OpAccessChain %_ptr_Workgroup_uint %wg %74 %76 %78
+               OpAtomicStore %81 %uint_2 %uint_0 %6
+               OpBranch %66
+         %66 = OpLabel
+         %82 = OpLoad %uint %idx_1
+         %83 = OpIAdd %uint %82 %uint_1
+               OpStore %idx_1 %83
+               OpBranch %64
+         %65 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %85 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %55
+         %87 = OpLabel
+         %89 = OpLoad %uint %local_invocation_index_1_param_1
+         %88 = OpFunctionCall %void %compute_main_inner_1 %89
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.wgsl
new file mode 100644
index 0000000..2b2b741
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/arrays.spvasm.expected.wgsl
@@ -0,0 +1,44 @@
+type Arr = array<u32, 1u>;
+
+type Arr_1 = array<Arr, 2u>;
+
+type Arr_2 = array<Arr_1, 3u>;
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  var idx : u32 = 0u;
+  idx = local_invocation_index;
+  loop {
+    let x_25 : u32 = idx;
+    if (!((x_25 < 6u))) {
+      break;
+    }
+    let x_31 : u32 = idx;
+    let x_33 : u32 = idx;
+    let x_35 : u32 = idx;
+    atomicStore(&(wg[(x_31 / 2u)][(x_33 % 2u)][(x_35 % 1u)]), 0u);
+
+    continuing {
+      let x_42 : u32 = idx;
+      idx = (x_42 + 1u);
+    }
+  }
+  workgroupBarrier();
+  atomicStore(&(wg[2i][1i][0i]), 1u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_57 : u32 = local_invocation_index_1;
+  compute_main_inner(x_57);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/array/arrays.wgsl b/test/tint/builtins/atomicStore/array/arrays.wgsl
new file mode 100644
index 0000000..3415c58
--- /dev/null
+++ b/test/tint/builtins/atomicStore/array/arrays.wgsl
@@ -0,0 +1,6 @@
+var<workgroup> wg : array<array<array<atomic<u32>, 1>, 2>, 3>;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  atomicStore(&wg[2][1][0], 1u);
+}
diff --git a/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm
new file mode 100644
index 0000000..b9e0fed
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm
@@ -0,0 +1,91 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 54
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S "S"
+               OpMemberName %S 0 "x"
+               OpMemberName %S 1 "a"
+               OpMemberName %S 2 "y"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpMemberDecorate %S 2 Offset 8
+               OpDecorate %_arr_S_uint_10 ArrayStride 12
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+          %S = OpTypeStruct %int %uint %uint
+    %uint_10 = OpConstant %uint 10
+%_arr_S_uint_10 = OpTypeArray %S %uint_10
+%_ptr_Workgroup__arr_S_uint_10 = OpTypePointer Workgroup %_arr_S_uint_10
+         %wg = OpVariable %_ptr_Workgroup__arr_S_uint_10 Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+       %bool = OpTypeBool
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %32 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%_ptr_Workgroup_uint_0 = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+      %int_4 = OpConstant %int 4
+         %49 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %10
+%local_invocation_index = OpFunctionParameter %uint
+         %14 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %17
+               OpStore %idx %local_invocation_index
+               OpBranch %18
+         %18 = OpLabel
+               OpLoopMerge %19 %20 None
+               OpBranch %21
+         %21 = OpLabel
+         %23 = OpLoad %uint %idx
+         %24 = OpULessThan %bool %23 %uint_10
+         %22 = OpLogicalNot %bool %24
+               OpSelectionMerge %26 None
+               OpBranchConditional %22 %27 %26
+         %27 = OpLabel
+               OpBranch %19
+         %26 = OpLabel
+         %28 = OpLoad %uint %idx
+         %31 = OpAccessChain %_ptr_Workgroup_int %wg %28 %uint_0
+               OpStore %31 %32
+         %38 = OpAccessChain %_ptr_Workgroup_uint %wg %28 %uint_1
+               OpAtomicStore %38 %uint_2 %uint_0 %17
+         %40 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %28 %uint_2
+               OpStore %40 %17
+               OpBranch %20
+         %20 = OpLabel
+         %41 = OpLoad %uint %idx
+         %42 = OpIAdd %uint %41 %uint_1
+               OpStore %idx %42
+               OpBranch %18
+         %19 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %48 = OpAccessChain %_ptr_Workgroup_uint %wg %int_4 %uint_1
+               OpAtomicStore %48 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %49
+         %51 = OpLabel
+         %53 = OpLoad %uint %local_invocation_index_1
+         %52 = OpFunctionCall %void %compute_main_inner %53
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.glsl b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.glsl
new file mode 100644
index 0000000..4f23cb9
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.glsl
@@ -0,0 +1,56 @@
+#version 310 es
+
+struct S_atomic {
+  int x;
+  uint a;
+  uint y;
+};
+
+struct S {
+  int x;
+  uint a;
+  uint y;
+};
+
+uint local_invocation_index_1 = 0u;
+shared S_atomic wg[10];
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  {
+    for(; !(!((idx < 10u))); idx = (idx + 1u)) {
+      uint x_28 = idx;
+      wg[x_28].x = 0;
+      atomicExchange(wg[x_28].a, 0u);
+      wg[x_28].y = 0u;
+    }
+  }
+  barrier();
+  atomicExchange(wg[4].a, 1u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 10u); idx_1 = (idx_1 + 1u)) {
+      uint i = idx_1;
+      wg[i].x = 0;
+      atomicExchange(wg[i].a, 0u);
+      wg[i].y = 0u;
+    }
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.hlsl
new file mode 100644
index 0000000..9d31dd1
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.hlsl
@@ -0,0 +1,56 @@
+struct S_atomic {
+  int x;
+  uint a;
+  uint y;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared S_atomic wg[10];
+
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  {
+    [loop] for(; !(!((idx < 10u))); idx = (idx + 1u)) {
+      const uint x_28 = idx;
+      wg[x_28].x = 0;
+      uint atomic_result = 0u;
+      InterlockedExchange(wg[x_28].a, 0u, atomic_result);
+      wg[x_28].y = 0u;
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg[4].a, 1u, atomic_result_1);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    [loop] for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 10u); idx_1 = (idx_1 + 1u)) {
+      const uint i = idx_1;
+      wg[i].x = 0;
+      uint atomic_result_2 = 0u;
+      InterlockedExchange(wg[i].a, 0u, atomic_result_2);
+      wg[i].y = 0u;
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.msl b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.msl
new file mode 100644
index 0000000..5360fda
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.msl
@@ -0,0 +1,70 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S_atomic {
+  int x;
+  atomic_uint a;
+  uint y;
+};
+
+struct S {
+  int x;
+  uint a;
+  uint y;
+};
+
+struct tint_array_wrapper {
+  S arr[10];
+};
+
+struct tint_array_wrapper_1 {
+  S_atomic arr[10];
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup tint_array_wrapper_1* const tint_symbol) {
+  uint idx = 0u;
+  idx = local_invocation_index;
+  while (true) {
+    uint const x_23 = idx;
+    if (!((x_23 < 10u))) {
+      break;
+    }
+    uint const x_28 = idx;
+    (*(tint_symbol)).arr[x_28].x = 0;
+    atomic_store_explicit(&((*(tint_symbol)).arr[x_28].a), 0u, memory_order_relaxed);
+    (*(tint_symbol)).arr[x_28].y = 0u;
+    {
+      uint const x_41 = idx;
+      idx = (x_41 + 1u);
+    }
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).arr[4].a), 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup tint_array_wrapper_1* const tint_symbol_2) {
+  uint const x_53 = *(tint_symbol_1);
+  compute_main_inner(x_53, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup tint_array_wrapper_1* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 10u); idx_1 = (idx_1 + 1u)) {
+    uint const i = idx_1;
+    (*(tint_symbol_3)).arr[i].x = 0;
+    atomic_store_explicit(&((*(tint_symbol_3)).arr[i].a), 0u, memory_order_relaxed);
+    (*(tint_symbol_3)).arr[i].y = 0u;
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup tint_array_wrapper_1 tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.spvasm
new file mode 100644
index 0000000..b25f900
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.spvasm
@@ -0,0 +1,142 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 83
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S_atomic "S_atomic"
+               OpMemberName %S_atomic 0 "x"
+               OpMemberName %S_atomic 1 "a"
+               OpMemberName %S_atomic 2 "y"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %idx_1 "idx_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S_atomic 0 Offset 0
+               OpMemberDecorate %S_atomic 1 Offset 4
+               OpMemberDecorate %S_atomic 2 Offset 8
+               OpDecorate %_arr_S_atomic_uint_10 ArrayStride 12
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+   %S_atomic = OpTypeStruct %int %uint %uint
+    %uint_10 = OpConstant %uint 10
+%_arr_S_atomic_uint_10 = OpTypeArray %S_atomic %uint_10
+%_ptr_Workgroup__arr_S_atomic_uint_10 = OpTypePointer Workgroup %_arr_S_atomic_uint_10
+         %wg = OpVariable %_ptr_Workgroup__arr_S_atomic_uint_10 Workgroup
+       %void = OpTypeVoid
+         %13 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+       %bool = OpTypeBool
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %34 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%_ptr_Workgroup_uint_0 = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+      %int_4 = OpConstant %int 4
+         %51 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %13
+%local_invocation_index = OpFunctionParameter %uint
+         %17 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx %6
+               OpStore %idx %local_invocation_index
+               OpBranch %20
+         %20 = OpLabel
+               OpLoopMerge %21 %22 None
+               OpBranch %23
+         %23 = OpLabel
+         %24 = OpLoad %uint %idx
+         %26 = OpULessThan %bool %24 %uint_10
+         %25 = OpLogicalNot %bool %26
+               OpSelectionMerge %28 None
+               OpBranchConditional %25 %29 %28
+         %29 = OpLabel
+               OpBranch %21
+         %28 = OpLabel
+         %30 = OpLoad %uint %idx
+         %33 = OpAccessChain %_ptr_Workgroup_int %wg %30 %uint_0
+               OpStore %33 %34
+         %40 = OpAccessChain %_ptr_Workgroup_uint %wg %30 %uint_1
+               OpAtomicStore %40 %uint_2 %uint_0 %6
+         %42 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %30 %uint_2
+               OpStore %42 %6
+               OpBranch %22
+         %22 = OpLabel
+         %43 = OpLoad %uint %idx
+         %44 = OpIAdd %uint %43 %uint_1
+               OpStore %idx %44
+               OpBranch %20
+         %21 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %50 = OpAccessChain %_ptr_Workgroup_uint %wg %int_4 %uint_1
+               OpAtomicStore %50 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %51
+         %53 = OpLabel
+         %54 = OpLoad %uint %local_invocation_index_1
+         %55 = OpFunctionCall %void %compute_main_inner %54
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %13
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %58 = OpLabel
+      %idx_1 = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx_1 %local_invocation_index_1_param
+               OpBranch %60
+         %60 = OpLabel
+               OpLoopMerge %61 %62 None
+               OpBranch %63
+         %63 = OpLabel
+         %65 = OpLoad %uint %idx_1
+         %66 = OpULessThan %bool %65 %uint_10
+         %64 = OpLogicalNot %bool %66
+               OpSelectionMerge %67 None
+               OpBranchConditional %64 %68 %67
+         %68 = OpLabel
+               OpBranch %61
+         %67 = OpLabel
+         %69 = OpLoad %uint %idx_1
+         %70 = OpAccessChain %_ptr_Workgroup_int %wg %69 %uint_0
+               OpStore %70 %34
+         %73 = OpAccessChain %_ptr_Workgroup_uint %wg %69 %uint_1
+               OpAtomicStore %73 %uint_2 %uint_0 %6
+         %74 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %69 %uint_2
+               OpStore %74 %6
+               OpBranch %62
+         %62 = OpLabel
+         %75 = OpLoad %uint %idx_1
+         %76 = OpIAdd %uint %75 %uint_1
+               OpStore %idx_1 %76
+               OpBranch %60
+         %61 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %78 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %51
+         %80 = OpLabel
+         %82 = OpLoad %uint %local_invocation_index_1_param_1
+         %81 = OpFunctionCall %void %compute_main_inner_1 %82
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.wgsl
new file mode 100644
index 0000000..de42f48
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/array_of_struct.spvasm.expected.wgsl
@@ -0,0 +1,52 @@
+struct S_atomic {
+  x : i32,
+  a : atomic<u32>,
+  y : u32,
+}
+
+struct S {
+  x : i32,
+  a : u32,
+  y : u32,
+}
+
+type Arr = array<S, 10u>;
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : array<S_atomic, 10u>;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  var idx : u32 = 0u;
+  idx = local_invocation_index;
+  loop {
+    let x_23 : u32 = idx;
+    if (!((x_23 < 10u))) {
+      break;
+    }
+    let x_28 : u32 = idx;
+    wg[x_28].x = 0i;
+    atomicStore(&(wg[x_28].a), 0u);
+    wg[x_28].y = 0u;
+
+    continuing {
+      let x_41 : u32 = idx;
+      idx = (x_41 + 1u);
+    }
+  }
+  workgroupBarrier();
+  atomicStore(&(wg[4i].a), 1u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_53 : u32 = local_invocation_index_1;
+  compute_main_inner(x_53);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/struct/array_of_struct.wgsl b/test/tint/builtins/atomicStore/struct/array_of_struct.wgsl
new file mode 100644
index 0000000..d49a3fd
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/array_of_struct.wgsl
@@ -0,0 +1,12 @@
+struct S {
+  x : i32,
+  a : atomic<u32>,
+  y : u32,
+};
+
+var<workgroup> wg : array<S, 10>;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  atomicStore(&wg[4].a, 1u);
+}
diff --git a/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm
new file mode 100644
index 0000000..f61269c
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm
@@ -0,0 +1,62 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 40
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S "S"
+               OpMemberName %S 0 "x"
+               OpMemberName %S 1 "a"
+               OpMemberName %S 2 "b"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpMemberDecorate %S 2 Offset 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+          %S = OpTypeStruct %int %uint %uint
+%_ptr_Workgroup_S = OpTypePointer Workgroup %S
+         %wg = OpVariable %_ptr_Workgroup_S Workgroup
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %16 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+         %23 = OpConstantNull %uint
+   %uint_264 = OpConstant %uint 264
+         %35 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %8
+%local_invocation_index = OpFunctionParameter %uint
+         %12 = OpLabel
+         %15 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %15 %16
+         %22 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %22 %uint_2 %uint_0 %23
+         %26 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_2
+               OpAtomicStore %26 %uint_2 %uint_0 %23
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %31 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %31 %uint_2 %uint_0 %uint_1
+         %34 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_2
+               OpAtomicStore %34 %uint_2 %uint_0 %uint_2
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %35
+         %37 = OpLabel
+         %39 = OpLoad %uint %local_invocation_index_1
+         %38 = OpFunctionCall %void %compute_main_inner %39
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.glsl b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.glsl
new file mode 100644
index 0000000..4d0836e
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.glsl
@@ -0,0 +1,47 @@
+#version 310 es
+
+struct S_atomic {
+  int x;
+  uint a;
+  uint b;
+};
+
+struct S {
+  int x;
+  uint a;
+  uint b;
+};
+
+uint local_invocation_index_1 = 0u;
+shared S_atomic wg;
+void compute_main_inner(uint local_invocation_index) {
+  wg.x = 0;
+  atomicExchange(wg.a, 0u);
+  atomicExchange(wg.b, 0u);
+  barrier();
+  atomicExchange(wg.a, 1u);
+  atomicExchange(wg.b, 2u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    atomicExchange(wg.a, 0u);
+    atomicExchange(wg.b, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.hlsl
new file mode 100644
index 0000000..cb340f2
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.hlsl
@@ -0,0 +1,50 @@
+struct S_atomic {
+  int x;
+  uint a;
+  uint b;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared S_atomic wg;
+
+void compute_main_inner(uint local_invocation_index) {
+  wg.x = 0;
+  uint atomic_result = 0u;
+  InterlockedExchange(wg.a, 0u, atomic_result);
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg.b, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_2 = 0u;
+  InterlockedExchange(wg.a, 1u, atomic_result_2);
+  uint atomic_result_3 = 0u;
+  InterlockedExchange(wg.b, 2u, atomic_result_3);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    uint atomic_result_4 = 0u;
+    InterlockedExchange(wg.a, 0u, atomic_result_4);
+    uint atomic_result_5 = 0u;
+    InterlockedExchange(wg.b, 0u, atomic_result_5);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.msl b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.msl
new file mode 100644
index 0000000..4e1363d
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.msl
@@ -0,0 +1,49 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S_atomic {
+  int x;
+  atomic_uint a;
+  atomic_uint b;
+};
+
+struct S {
+  int x;
+  uint a;
+  uint b;
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup S_atomic* const tint_symbol) {
+  (*(tint_symbol)).x = 0;
+  atomic_store_explicit(&((*(tint_symbol)).a), 0u, memory_order_relaxed);
+  atomic_store_explicit(&((*(tint_symbol)).b), 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).a), 1u, memory_order_relaxed);
+  atomic_store_explicit(&((*(tint_symbol)).b), 2u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup S_atomic* const tint_symbol_2) {
+  uint const x_39 = *(tint_symbol_1);
+  compute_main_inner(x_39, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup S_atomic* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  {
+    (*(tint_symbol_3)).x = 0;
+    atomic_store_explicit(&((*(tint_symbol_3)).a), 0u, memory_order_relaxed);
+    atomic_store_explicit(&((*(tint_symbol_3)).b), 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup S_atomic tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.spvasm
new file mode 100644
index 0000000..5ee475d
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.spvasm
@@ -0,0 +1,88 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 58
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S_atomic "S_atomic"
+               OpMemberName %S_atomic 0 "x"
+               OpMemberName %S_atomic 1 "a"
+               OpMemberName %S_atomic 2 "b"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S_atomic 0 Offset 0
+               OpMemberDecorate %S_atomic 1 Offset 4
+               OpMemberDecorate %S_atomic 2 Offset 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+   %S_atomic = OpTypeStruct %int %uint %uint
+%_ptr_Workgroup_S_atomic = OpTypePointer Workgroup %S_atomic
+         %wg = OpVariable %_ptr_Workgroup_S_atomic Workgroup
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %19 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+         %37 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %11
+%local_invocation_index = OpFunctionParameter %uint
+         %15 = OpLabel
+         %18 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %18 %19
+         %25 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %25 %uint_2 %uint_0 %6
+         %28 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_2
+               OpAtomicStore %28 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %33 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %33 %uint_2 %uint_0 %uint_1
+         %36 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_2
+               OpAtomicStore %36 %uint_2 %uint_0 %uint_2
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %37
+         %39 = OpLabel
+         %40 = OpLoad %uint %local_invocation_index_1
+         %41 = OpFunctionCall %void %compute_main_inner %40
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %11
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %44 = OpLabel
+         %45 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %45 %19
+         %48 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %48 %uint_2 %uint_0 %6
+         %51 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_2
+               OpAtomicStore %51 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %53 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %37
+         %55 = OpLabel
+         %57 = OpLoad %uint %local_invocation_index_1_param_1
+         %56 = OpFunctionCall %void %compute_main_inner_1 %57
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.wgsl
new file mode 100644
index 0000000..20d066a
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.spvasm.expected.wgsl
@@ -0,0 +1,37 @@
+struct S_atomic {
+  x : i32,
+  a : atomic<u32>,
+  b : atomic<u32>,
+}
+
+struct S {
+  x : i32,
+  a : u32,
+  b : u32,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : S_atomic;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  wg.x = 0i;
+  atomicStore(&(wg.a), 0u);
+  atomicStore(&(wg.b), 0u);
+  workgroupBarrier();
+  atomicStore(&(wg.a), 1u);
+  atomicStore(&(wg.b), 2u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_39 : u32 = local_invocation_index_1;
+  compute_main_inner(x_39);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.wgsl b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.wgsl
new file mode 100644
index 0000000..b055a8f
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_multiple_atomics.wgsl
@@ -0,0 +1,13 @@
+struct S {
+  x : i32,
+  a : atomic<u32>,
+  b : atomic<u32>,
+};
+
+var<workgroup> wg: S;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  atomicStore(&wg.a, 1u);
+  atomicStore(&wg.b, 2u);
+}
diff --git a/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm
new file mode 100644
index 0000000..acad988
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm
@@ -0,0 +1,61 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 36
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S "S"
+               OpMemberName %S 0 "x"
+               OpMemberName %S 1 "a"
+               OpMemberName %S 2 "y"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpMemberDecorate %S 2 Offset 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+          %S = OpTypeStruct %int %uint %uint
+%_ptr_Workgroup_S = OpTypePointer Workgroup %S
+         %wg = OpVariable %_ptr_Workgroup_S Workgroup
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %16 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+         %23 = OpConstantNull %uint
+%_ptr_Workgroup_uint_0 = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+         %31 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %8
+%local_invocation_index = OpFunctionParameter %uint
+         %12 = OpLabel
+         %15 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %15 %16
+         %22 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %22 %uint_2 %uint_0 %23
+         %25 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_2
+               OpStore %25 %23
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %30 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %31
+         %33 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.glsl b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.glsl
new file mode 100644
index 0000000..2b03ef6
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.glsl
@@ -0,0 +1,46 @@
+#version 310 es
+
+struct S_atomic {
+  int x;
+  uint a;
+  uint y;
+};
+
+struct S {
+  int x;
+  uint a;
+  uint y;
+};
+
+uint local_invocation_index_1 = 0u;
+shared S_atomic wg;
+void compute_main_inner(uint local_invocation_index) {
+  wg.x = 0;
+  atomicExchange(wg.a, 0u);
+  wg.y = 0u;
+  barrier();
+  atomicExchange(wg.a, 1u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    atomicExchange(wg.a, 0u);
+    wg.y = 0u;
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.hlsl
new file mode 100644
index 0000000..403d1fb
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+struct S_atomic {
+  int x;
+  uint a;
+  uint y;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared S_atomic wg;
+
+void compute_main_inner(uint local_invocation_index) {
+  wg.x = 0;
+  uint atomic_result = 0u;
+  InterlockedExchange(wg.a, 0u, atomic_result);
+  wg.y = 0u;
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg.a, 1u, atomic_result_1);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(wg.a, 0u, atomic_result_2);
+    wg.y = 0u;
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.msl b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.msl
new file mode 100644
index 0000000..524820a
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.msl
@@ -0,0 +1,48 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S_atomic {
+  int x;
+  atomic_uint a;
+  uint y;
+};
+
+struct S {
+  int x;
+  uint a;
+  uint y;
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup S_atomic* const tint_symbol) {
+  (*(tint_symbol)).x = 0;
+  atomic_store_explicit(&((*(tint_symbol)).a), 0u, memory_order_relaxed);
+  (*(tint_symbol)).y = 0u;
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).a), 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup S_atomic* const tint_symbol_2) {
+  uint const x_35 = *(tint_symbol_1);
+  compute_main_inner(x_35, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup S_atomic* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  {
+    (*(tint_symbol_3)).x = 0;
+    atomic_store_explicit(&((*(tint_symbol_3)).a), 0u, memory_order_relaxed);
+    (*(tint_symbol_3)).y = 0u;
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup S_atomic tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.spvasm
new file mode 100644
index 0000000..35d0d2b
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.spvasm
@@ -0,0 +1,87 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 52
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S_atomic "S_atomic"
+               OpMemberName %S_atomic 0 "x"
+               OpMemberName %S_atomic 1 "a"
+               OpMemberName %S_atomic 2 "y"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S_atomic 0 Offset 0
+               OpMemberDecorate %S_atomic 1 Offset 4
+               OpMemberDecorate %S_atomic 2 Offset 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+   %S_atomic = OpTypeStruct %int %uint %uint
+%_ptr_Workgroup_S_atomic = OpTypePointer Workgroup %S_atomic
+         %wg = OpVariable %_ptr_Workgroup_S_atomic Workgroup
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %19 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%_ptr_Workgroup_uint_0 = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+         %33 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %11
+%local_invocation_index = OpFunctionParameter %uint
+         %15 = OpLabel
+         %18 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %18 %19
+         %25 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %25 %uint_2 %uint_0 %6
+         %27 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_2
+               OpStore %27 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %32 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %33
+         %35 = OpLabel
+         %36 = OpLoad %uint %local_invocation_index_1
+         %37 = OpFunctionCall %void %compute_main_inner %36
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %11
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %40 = OpLabel
+         %41 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %41 %19
+         %44 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %44 %uint_2 %uint_0 %6
+         %45 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_2
+               OpStore %45 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %47 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %33
+         %49 = OpLabel
+         %51 = OpLoad %uint %local_invocation_index_1_param_1
+         %50 = OpFunctionCall %void %compute_main_inner_1 %51
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.wgsl
new file mode 100644
index 0000000..2d90f56
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_single_atomic.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct S_atomic {
+  x : i32,
+  a : atomic<u32>,
+  y : u32,
+}
+
+struct S {
+  x : i32,
+  a : u32,
+  y : u32,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : S_atomic;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  wg.x = 0i;
+  atomicStore(&(wg.a), 0u);
+  wg.y = 0u;
+  workgroupBarrier();
+  atomicStore(&(wg.a), 1u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_35 : u32 = local_invocation_index_1;
+  compute_main_inner(x_35);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/struct/flat_single_atomic.wgsl b/test/tint/builtins/atomicStore/struct/flat_single_atomic.wgsl
new file mode 100644
index 0000000..e997e87
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/flat_single_atomic.wgsl
@@ -0,0 +1,12 @@
+struct S {
+  x : i32,
+  a : atomic<u32>,
+  y : u32,
+};
+
+var<workgroup> wg: S;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  atomicStore(&wg.a, 1u);
+}
diff --git a/test/tint/builtins/atomicStore/struct/nested.spvasm b/test/tint/builtins/atomicStore/struct/nested.spvasm
new file mode 100644
index 0000000..a8aaff3
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/nested.spvasm
@@ -0,0 +1,97 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 45
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S2 "S2"
+               OpMemberName %S2 0 "x"
+               OpMemberName %S2 1 "y"
+               OpMemberName %S2 2 "z"
+               OpMemberName %S2 3 "a"
+               OpName %S1 "S1"
+               OpMemberName %S1 0 "x"
+               OpMemberName %S1 1 "a"
+               OpName %S0 "S0"
+               OpMemberName %S0 0 "x"
+               OpMemberName %S0 1 "a"
+               OpMemberName %S0 2 "y"
+               OpMemberName %S0 3 "z"
+               OpMemberName %S1 2 "y"
+               OpMemberName %S1 3 "z"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S2 0 Offset 0
+               OpMemberDecorate %S2 1 Offset 4
+               OpMemberDecorate %S2 2 Offset 8
+               OpMemberDecorate %S2 3 Offset 12
+               OpMemberDecorate %S1 0 Offset 0
+               OpMemberDecorate %S1 1 Offset 4
+               OpMemberDecorate %S0 0 Offset 0
+               OpMemberDecorate %S0 1 Offset 4
+               OpMemberDecorate %S0 2 Offset 8
+               OpMemberDecorate %S0 3 Offset 12
+               OpMemberDecorate %S1 2 Offset 20
+               OpMemberDecorate %S1 3 Offset 24
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+         %S0 = OpTypeStruct %int %uint %int %int
+         %S1 = OpTypeStruct %int %S0 %int %int
+         %S2 = OpTypeStruct %int %int %int %S1
+%_ptr_Workgroup_S2 = OpTypePointer Workgroup %S2
+         %wg = OpVariable %_ptr_Workgroup_S2 Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %18 = OpConstantNull %int
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+         %30 = OpConstantNull %uint
+   %uint_264 = OpConstant %uint 264
+         %40 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %10
+%local_invocation_index = OpFunctionParameter %uint
+         %14 = OpLabel
+         %17 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %17 %18
+         %20 = OpAccessChain %_ptr_Workgroup_int %wg %uint_1
+               OpStore %20 %18
+         %22 = OpAccessChain %_ptr_Workgroup_int %wg %uint_2
+               OpStore %22 %18
+         %24 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_0
+               OpStore %24 %18
+         %25 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_0
+               OpStore %25 %18
+         %29 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_3 %uint_1 %uint_1
+               OpAtomicStore %29 %uint_2 %uint_0 %30
+         %31 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_2
+               OpStore %31 %18
+         %32 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_3
+               OpStore %32 %18
+         %33 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_2
+               OpStore %33 %18
+         %34 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_3
+               OpStore %34 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %39 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_3 %uint_1 %uint_1
+               OpAtomicStore %39 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %40
+         %42 = OpLabel
+         %44 = OpLoad %uint %local_invocation_index_1
+         %43 = OpFunctionCall %void %compute_main_inner %44
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.glsl b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.glsl
new file mode 100644
index 0000000..1d63b30
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.glsl
@@ -0,0 +1,90 @@
+#version 310 es
+
+struct S0_atomic {
+  int x;
+  uint a;
+  int y;
+  int z;
+};
+
+struct S0 {
+  int x;
+  uint a;
+  int y;
+  int z;
+};
+
+struct S1_atomic {
+  int x;
+  S0_atomic a;
+  int y;
+  int z;
+};
+
+struct S1 {
+  int x;
+  S0 a;
+  int y;
+  int z;
+};
+
+struct S2_atomic {
+  int x;
+  int y;
+  int z;
+  S1_atomic a;
+};
+
+struct S2 {
+  int x;
+  int y;
+  int z;
+  S1 a;
+};
+
+uint local_invocation_index_1 = 0u;
+shared S2_atomic wg;
+void compute_main_inner(uint local_invocation_index) {
+  wg.x = 0;
+  wg.y = 0;
+  wg.z = 0;
+  wg.a.x = 0;
+  wg.a.a.x = 0;
+  atomicExchange(wg.a.a.a, 0u);
+  wg.a.a.y = 0;
+  wg.a.a.z = 0;
+  wg.a.y = 0;
+  wg.a.z = 0;
+  barrier();
+  atomicExchange(wg.a.a.a, 1u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    wg.y = 0;
+    wg.z = 0;
+    wg.a.x = 0;
+    wg.a.a.x = 0;
+    atomicExchange(wg.a.a.a, 0u);
+    wg.a.a.y = 0;
+    wg.a.a.z = 0;
+    wg.a.y = 0;
+    wg.a.z = 0;
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.hlsl
new file mode 100644
index 0000000..4449c42
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.hlsl
@@ -0,0 +1,73 @@
+struct S0_atomic {
+  int x;
+  uint a;
+  int y;
+  int z;
+};
+struct S1_atomic {
+  int x;
+  S0_atomic a;
+  int y;
+  int z;
+};
+struct S2_atomic {
+  int x;
+  int y;
+  int z;
+  S1_atomic a;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared S2_atomic wg;
+
+void compute_main_inner(uint local_invocation_index) {
+  wg.x = 0;
+  wg.y = 0;
+  wg.z = 0;
+  wg.a.x = 0;
+  wg.a.a.x = 0;
+  uint atomic_result = 0u;
+  InterlockedExchange(wg.a.a.a, 0u, atomic_result);
+  wg.a.a.y = 0;
+  wg.a.a.z = 0;
+  wg.a.y = 0;
+  wg.a.z = 0;
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg.a.a.a, 1u, atomic_result_1);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    wg.y = 0;
+    wg.z = 0;
+    wg.a.x = 0;
+    wg.a.a.x = 0;
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(wg.a.a.a, 0u, atomic_result_2);
+    wg.a.a.y = 0;
+    wg.a.a.z = 0;
+    wg.a.y = 0;
+    wg.a.z = 0;
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.msl b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.msl
new file mode 100644
index 0000000..1ed439c
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.msl
@@ -0,0 +1,92 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S0_atomic {
+  int x;
+  atomic_uint a;
+  int y;
+  int z;
+};
+
+struct S0 {
+  int x;
+  uint a;
+  int y;
+  int z;
+};
+
+struct S1_atomic {
+  int x;
+  S0_atomic a;
+  int y;
+  int z;
+};
+
+struct S1 {
+  int x;
+  S0 a;
+  int y;
+  int z;
+};
+
+struct S2_atomic {
+  int x;
+  int y;
+  int z;
+  S1_atomic a;
+};
+
+struct S2 {
+  int x;
+  int y;
+  int z;
+  S1 a;
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup S2_atomic* const tint_symbol) {
+  (*(tint_symbol)).x = 0;
+  (*(tint_symbol)).y = 0;
+  (*(tint_symbol)).z = 0;
+  (*(tint_symbol)).a.x = 0;
+  (*(tint_symbol)).a.a.x = 0;
+  atomic_store_explicit(&((*(tint_symbol)).a.a.a), 0u, memory_order_relaxed);
+  (*(tint_symbol)).a.a.y = 0;
+  (*(tint_symbol)).a.a.z = 0;
+  (*(tint_symbol)).a.y = 0;
+  (*(tint_symbol)).a.z = 0;
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).a.a.a), 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup S2_atomic* const tint_symbol_2) {
+  uint const x_44 = *(tint_symbol_1);
+  compute_main_inner(x_44, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup S2_atomic* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  {
+    (*(tint_symbol_3)).x = 0;
+    (*(tint_symbol_3)).y = 0;
+    (*(tint_symbol_3)).z = 0;
+    (*(tint_symbol_3)).a.x = 0;
+    (*(tint_symbol_3)).a.a.x = 0;
+    atomic_store_explicit(&((*(tint_symbol_3)).a.a.a), 0u, memory_order_relaxed);
+    (*(tint_symbol_3)).a.a.y = 0;
+    (*(tint_symbol_3)).a.a.z = 0;
+    (*(tint_symbol_3)).a.y = 0;
+    (*(tint_symbol_3)).a.z = 0;
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup S2_atomic tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.spvasm
new file mode 100644
index 0000000..9f756f3
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.spvasm
@@ -0,0 +1,137 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 68
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S2_atomic "S2_atomic"
+               OpMemberName %S2_atomic 0 "x"
+               OpMemberName %S2_atomic 1 "y"
+               OpMemberName %S2_atomic 2 "z"
+               OpMemberName %S2_atomic 3 "a"
+               OpName %S1_atomic "S1_atomic"
+               OpMemberName %S1_atomic 0 "x"
+               OpMemberName %S1_atomic 1 "a"
+               OpName %S0_atomic "S0_atomic"
+               OpMemberName %S0_atomic 0 "x"
+               OpMemberName %S0_atomic 1 "a"
+               OpMemberName %S0_atomic 2 "y"
+               OpMemberName %S0_atomic 3 "z"
+               OpMemberName %S1_atomic 2 "y"
+               OpMemberName %S1_atomic 3 "z"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S2_atomic 0 Offset 0
+               OpMemberDecorate %S2_atomic 1 Offset 4
+               OpMemberDecorate %S2_atomic 2 Offset 8
+               OpMemberDecorate %S2_atomic 3 Offset 12
+               OpMemberDecorate %S1_atomic 0 Offset 0
+               OpMemberDecorate %S1_atomic 1 Offset 4
+               OpMemberDecorate %S0_atomic 0 Offset 0
+               OpMemberDecorate %S0_atomic 1 Offset 4
+               OpMemberDecorate %S0_atomic 2 Offset 8
+               OpMemberDecorate %S0_atomic 3 Offset 12
+               OpMemberDecorate %S1_atomic 2 Offset 20
+               OpMemberDecorate %S1_atomic 3 Offset 24
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+  %S0_atomic = OpTypeStruct %int %uint %int %int
+  %S1_atomic = OpTypeStruct %int %S0_atomic %int %int
+  %S2_atomic = OpTypeStruct %int %int %int %S1_atomic
+%_ptr_Workgroup_S2_atomic = OpTypePointer Workgroup %S2_atomic
+         %wg = OpVariable %_ptr_Workgroup_S2_atomic Workgroup
+       %void = OpTypeVoid
+         %13 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %21 = OpConstantNull %int
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_3 = OpConstant %uint 3
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+         %42 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %13
+%local_invocation_index = OpFunctionParameter %uint
+         %17 = OpLabel
+         %20 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %20 %21
+         %23 = OpAccessChain %_ptr_Workgroup_int %wg %uint_1
+               OpStore %23 %21
+         %25 = OpAccessChain %_ptr_Workgroup_int %wg %uint_2
+               OpStore %25 %21
+         %27 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_0
+               OpStore %27 %21
+         %28 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_0
+               OpStore %28 %21
+         %32 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_3 %uint_1 %uint_1
+               OpAtomicStore %32 %uint_2 %uint_0 %6
+         %33 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_2
+               OpStore %33 %21
+         %34 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_3
+               OpStore %34 %21
+         %35 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_2
+               OpStore %35 %21
+         %36 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_3
+               OpStore %36 %21
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %41 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_3 %uint_1 %uint_1
+               OpAtomicStore %41 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %42
+         %44 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1
+         %46 = OpFunctionCall %void %compute_main_inner %45
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %13
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %49 = OpLabel
+         %50 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %50 %21
+         %51 = OpAccessChain %_ptr_Workgroup_int %wg %uint_1
+               OpStore %51 %21
+         %52 = OpAccessChain %_ptr_Workgroup_int %wg %uint_2
+               OpStore %52 %21
+         %53 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_0
+               OpStore %53 %21
+         %54 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_0
+               OpStore %54 %21
+         %57 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_3 %uint_1 %uint_1
+               OpAtomicStore %57 %uint_2 %uint_0 %6
+         %58 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_2
+               OpStore %58 %21
+         %59 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_1 %uint_3
+               OpStore %59 %21
+         %60 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_2
+               OpStore %60 %21
+         %61 = OpAccessChain %_ptr_Workgroup_int %wg %uint_3 %uint_3
+               OpStore %61 %21
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %63 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %42
+         %65 = OpLabel
+         %67 = OpLoad %uint %local_invocation_index_1_param_1
+         %66 = OpFunctionCall %void %compute_main_inner_1 %67
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.wgsl
new file mode 100644
index 0000000..7f3c3b3
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/nested.spvasm.expected.wgsl
@@ -0,0 +1,73 @@
+struct S0_atomic {
+  x : i32,
+  a : atomic<u32>,
+  y : i32,
+  z : i32,
+}
+
+struct S0 {
+  x : i32,
+  a : u32,
+  y : i32,
+  z : i32,
+}
+
+struct S1_atomic {
+  x : i32,
+  a : S0_atomic,
+  y : i32,
+  z : i32,
+}
+
+struct S1 {
+  x : i32,
+  a : S0,
+  y : i32,
+  z : i32,
+}
+
+struct S2_atomic {
+  x : i32,
+  y : i32,
+  z : i32,
+  a : S1_atomic,
+}
+
+struct S2 {
+  x : i32,
+  y : i32,
+  z : i32,
+  a : S1,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : S2_atomic;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  wg.x = 0i;
+  wg.y = 0i;
+  wg.z = 0i;
+  wg.a.x = 0i;
+  wg.a.a.x = 0i;
+  atomicStore(&(wg.a.a.a), 0u);
+  wg.a.a.y = 0i;
+  wg.a.a.z = 0i;
+  wg.a.y = 0i;
+  wg.a.z = 0i;
+  workgroupBarrier();
+  atomicStore(&(wg.a.a.a), 1u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_44 : u32 = local_invocation_index_1;
+  compute_main_inner(x_44);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/struct/nested.wgsl b/test/tint/builtins/atomicStore/struct/nested.wgsl
new file mode 100644
index 0000000..e6b0abe
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/nested.wgsl
@@ -0,0 +1,27 @@
+struct S0 {
+  x : i32,
+  a : atomic<u32>,
+  y : i32,
+  z : i32,
+};
+
+struct S1 {
+  x : i32,
+  a : S0,
+  y : i32,
+  z : i32,
+};
+
+struct S2 {
+  x : i32,
+  y : i32,
+  z : i32,
+  a : S1,
+};
+
+var<workgroup> wg: S2;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  atomicStore(&wg.a.a.a, 1u);
+}
diff --git a/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm
new file mode 100644
index 0000000..9d06d13
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm
@@ -0,0 +1,91 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 54
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S "S"
+               OpMemberName %S 0 "x"
+               OpMemberName %S 1 "a"
+               OpMemberName %S 2 "y"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %_arr_uint_uint_10 ArrayStride 4
+               OpMemberDecorate %S 2 Offset 44
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+    %uint_10 = OpConstant %uint 10
+%_arr_uint_uint_10 = OpTypeArray %uint %uint_10
+          %S = OpTypeStruct %int %_arr_uint_uint_10 %uint
+%_ptr_Workgroup_S = OpTypePointer Workgroup %S
+         %wg = OpVariable %_ptr_Workgroup_S Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %18 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+         %22 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+       %bool = OpTypeBool
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint_0 = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+      %int_4 = OpConstant %int 4
+         %49 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %10
+%local_invocation_index = OpFunctionParameter %uint
+         %14 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %22
+         %17 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %17 %18
+         %21 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_2
+               OpStore %21 %22
+               OpStore %idx %local_invocation_index
+               OpBranch %25
+         %25 = OpLabel
+               OpLoopMerge %26 %27 None
+               OpBranch %28
+         %28 = OpLabel
+         %30 = OpLoad %uint %idx
+         %31 = OpULessThan %bool %30 %uint_10
+         %29 = OpLogicalNot %bool %31
+               OpSelectionMerge %33 None
+               OpBranchConditional %29 %34 %33
+         %34 = OpLabel
+               OpBranch %26
+         %33 = OpLabel
+         %35 = OpLoad %uint %idx
+         %40 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_1 %35
+               OpAtomicStore %40 %uint_2 %uint_0 %22
+               OpBranch %27
+         %27 = OpLabel
+         %41 = OpLoad %uint %idx
+         %42 = OpIAdd %uint %41 %uint_1
+               OpStore %idx %42
+               OpBranch %25
+         %26 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %48 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_1 %int_4
+               OpAtomicStore %48 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %49
+         %51 = OpLabel
+         %53 = OpLoad %uint %local_invocation_index_1
+         %52 = OpFunctionCall %void %compute_main_inner %53
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.glsl b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.glsl
new file mode 100644
index 0000000..0a37b13
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.glsl
@@ -0,0 +1,57 @@
+#version 310 es
+
+struct S_atomic {
+  int x;
+  uint a[10];
+  uint y;
+};
+
+struct S {
+  int x;
+  uint a[10];
+  uint y;
+};
+
+uint local_invocation_index_1 = 0u;
+shared S_atomic wg;
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  wg.x = 0;
+  wg.y = 0u;
+  idx = local_invocation_index;
+  {
+    for(; !(!((idx < 10u))); idx = (idx + 1u)) {
+      atomicExchange(wg.a[idx], 0u);
+    }
+  }
+  barrier();
+  atomicExchange(wg.a[4], 1u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    wg.y = 0u;
+  }
+  {
+    for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 10u); idx_1 = (idx_1 + 1u)) {
+      uint i = idx_1;
+      atomicExchange(wg.a[i], 0u);
+    }
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.hlsl
new file mode 100644
index 0000000..beb2810
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.hlsl
@@ -0,0 +1,57 @@
+struct S_atomic {
+  int x;
+  uint a[10];
+  uint y;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared S_atomic wg;
+
+void compute_main_inner(uint local_invocation_index) {
+  uint idx = 0u;
+  wg.x = 0;
+  wg.y = 0u;
+  idx = local_invocation_index;
+  {
+    [loop] for(; !(!((idx < 10u))); idx = (idx + 1u)) {
+      uint atomic_result = 0u;
+      InterlockedExchange(wg.a[idx], 0u, atomic_result);
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg.a[4], 1u, atomic_result_1);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    wg.y = 0u;
+  }
+  {
+    [loop] for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 10u); idx_1 = (idx_1 + 1u)) {
+      const uint i = idx_1;
+      uint atomic_result_2 = 0u;
+      InterlockedExchange(wg.a[i], 0u, atomic_result_2);
+    }
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.msl b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.msl
new file mode 100644
index 0000000..089c011
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.msl
@@ -0,0 +1,72 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_array_wrapper {
+  uint arr[10];
+};
+
+struct tint_array_wrapper_1 {
+  atomic_uint arr[10];
+};
+
+struct S_atomic {
+  int x;
+  tint_array_wrapper_1 a;
+  uint y;
+};
+
+struct S {
+  int x;
+  tint_array_wrapper a;
+  uint y;
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup S_atomic* const tint_symbol) {
+  uint idx = 0u;
+  (*(tint_symbol)).x = 0;
+  (*(tint_symbol)).y = 0u;
+  idx = local_invocation_index;
+  while (true) {
+    uint const x_30 = idx;
+    if (!((x_30 < 10u))) {
+      break;
+    }
+    uint const x_35 = idx;
+    atomic_store_explicit(&((*(tint_symbol)).a.arr[x_35]), 0u, memory_order_relaxed);
+    {
+      uint const x_41 = idx;
+      idx = (x_41 + 1u);
+    }
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).a.arr[4]), 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup S_atomic* const tint_symbol_2) {
+  uint const x_53 = *(tint_symbol_1);
+  compute_main_inner(x_53, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup S_atomic* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  {
+    (*(tint_symbol_3)).x = 0;
+    (*(tint_symbol_3)).y = 0u;
+  }
+  for(uint idx_1 = local_invocation_index_1_param; (idx_1 < 10u); idx_1 = (idx_1 + 1u)) {
+    uint const i = idx_1;
+    atomic_store_explicit(&((*(tint_symbol_3)).a.arr[i]), 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup S_atomic tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.spvasm
new file mode 100644
index 0000000..d774374
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.spvasm
@@ -0,0 +1,142 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 83
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S_atomic "S_atomic"
+               OpMemberName %S_atomic 0 "x"
+               OpMemberName %S_atomic 1 "a"
+               OpMemberName %S_atomic 2 "y"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %idx "idx"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %idx_1 "idx_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S_atomic 0 Offset 0
+               OpMemberDecorate %S_atomic 1 Offset 4
+               OpDecorate %_arr_uint_uint_10 ArrayStride 4
+               OpMemberDecorate %S_atomic 2 Offset 44
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+    %uint_10 = OpConstant %uint 10
+%_arr_uint_uint_10 = OpTypeArray %uint %uint_10
+   %S_atomic = OpTypeStruct %int %_arr_uint_uint_10 %uint
+%_ptr_Workgroup_S_atomic = OpTypePointer Workgroup %S_atomic
+         %wg = OpVariable %_ptr_Workgroup_S_atomic Workgroup
+       %void = OpTypeVoid
+         %13 = OpTypeFunction %void %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %23 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+       %bool = OpTypeBool
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint_0 = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+      %int_4 = OpConstant %int 4
+         %51 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %13
+%local_invocation_index = OpFunctionParameter %uint
+         %17 = OpLabel
+        %idx = OpVariable %_ptr_Function_uint Function %6
+               OpStore %idx %6
+         %22 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %22 %23
+         %26 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_2
+               OpStore %26 %6
+               OpStore %idx %local_invocation_index
+               OpBranch %27
+         %27 = OpLabel
+               OpLoopMerge %28 %29 None
+               OpBranch %30
+         %30 = OpLabel
+         %31 = OpLoad %uint %idx
+         %33 = OpULessThan %bool %31 %uint_10
+         %32 = OpLogicalNot %bool %33
+               OpSelectionMerge %35 None
+               OpBranchConditional %32 %36 %35
+         %36 = OpLabel
+               OpBranch %28
+         %35 = OpLabel
+         %37 = OpLoad %uint %idx
+         %42 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_1 %37
+               OpAtomicStore %42 %uint_2 %uint_0 %6
+               OpBranch %29
+         %29 = OpLabel
+         %43 = OpLoad %uint %idx
+         %44 = OpIAdd %uint %43 %uint_1
+               OpStore %idx %44
+               OpBranch %27
+         %28 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %50 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_1 %int_4
+               OpAtomicStore %50 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %51
+         %53 = OpLabel
+         %54 = OpLoad %uint %local_invocation_index_1
+         %55 = OpFunctionCall %void %compute_main_inner %54
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %13
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %58 = OpLabel
+      %idx_1 = OpVariable %_ptr_Function_uint Function %6
+         %59 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %59 %23
+         %60 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_2
+               OpStore %60 %6
+               OpStore %idx_1 %local_invocation_index_1_param
+               OpBranch %62
+         %62 = OpLabel
+               OpLoopMerge %63 %64 None
+               OpBranch %65
+         %65 = OpLabel
+         %67 = OpLoad %uint %idx_1
+         %68 = OpULessThan %bool %67 %uint_10
+         %66 = OpLogicalNot %bool %68
+               OpSelectionMerge %69 None
+               OpBranchConditional %66 %70 %69
+         %70 = OpLabel
+               OpBranch %63
+         %69 = OpLabel
+         %71 = OpLoad %uint %idx_1
+         %74 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_1 %71
+               OpAtomicStore %74 %uint_2 %uint_0 %6
+               OpBranch %64
+         %64 = OpLabel
+         %75 = OpLoad %uint %idx_1
+         %76 = OpIAdd %uint %75 %uint_1
+               OpStore %idx_1 %76
+               OpBranch %62
+         %63 = OpLabel
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %78 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %51
+         %80 = OpLabel
+         %82 = OpLoad %uint %local_invocation_index_1_param_1
+         %81 = OpFunctionCall %void %compute_main_inner_1 %82
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.wgsl
new file mode 100644
index 0000000..f82a9ff
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/struct_of_array.spvasm.expected.wgsl
@@ -0,0 +1,52 @@
+type Arr = array<u32, 10u>;
+
+struct S_atomic {
+  x : i32,
+  a : array<atomic<u32>, 10u>,
+  y : u32,
+}
+
+struct S {
+  x : i32,
+  a : Arr,
+  y : u32,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : S_atomic;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  var idx : u32 = 0u;
+  wg.x = 0i;
+  wg.y = 0u;
+  idx = local_invocation_index;
+  loop {
+    let x_30 : u32 = idx;
+    if (!((x_30 < 10u))) {
+      break;
+    }
+    let x_35 : u32 = idx;
+    atomicStore(&(wg.a[x_35]), 0u);
+
+    continuing {
+      let x_41 : u32 = idx;
+      idx = (x_41 + 1u);
+    }
+  }
+  workgroupBarrier();
+  atomicStore(&(wg.a[4i]), 1u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_53 : u32 = local_invocation_index_1;
+  compute_main_inner(x_53);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/struct/struct_of_array.wgsl b/test/tint/builtins/atomicStore/struct/struct_of_array.wgsl
new file mode 100644
index 0000000..ada3ef5
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/struct_of_array.wgsl
@@ -0,0 +1,12 @@
+struct S {
+  x : i32,
+  a : array<atomic<u32>, 10>,
+  y : u32,
+};
+
+var<workgroup> wg : S;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  atomicStore(&wg.a[4], 1u);
+}
diff --git a/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm
new file mode 100644
index 0000000..acad988
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm
@@ -0,0 +1,61 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 36
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S "S"
+               OpMemberName %S 0 "x"
+               OpMemberName %S 1 "a"
+               OpMemberName %S 2 "y"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpMemberDecorate %S 2 Offset 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+          %S = OpTypeStruct %int %uint %uint
+%_ptr_Workgroup_S = OpTypePointer Workgroup %S
+         %wg = OpVariable %_ptr_Workgroup_S Workgroup
+       %void = OpTypeVoid
+          %8 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %16 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+         %23 = OpConstantNull %uint
+%_ptr_Workgroup_uint_0 = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+         %31 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %8
+%local_invocation_index = OpFunctionParameter %uint
+         %12 = OpLabel
+         %15 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %15 %16
+         %22 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %22 %uint_2 %uint_0 %23
+         %25 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_2
+               OpStore %25 %23
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %30 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %31
+         %33 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.glsl b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.glsl
new file mode 100644
index 0000000..2b03ef6
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.glsl
@@ -0,0 +1,46 @@
+#version 310 es
+
+struct S_atomic {
+  int x;
+  uint a;
+  uint y;
+};
+
+struct S {
+  int x;
+  uint a;
+  uint y;
+};
+
+uint local_invocation_index_1 = 0u;
+shared S_atomic wg;
+void compute_main_inner(uint local_invocation_index) {
+  wg.x = 0;
+  atomicExchange(wg.a, 0u);
+  wg.y = 0u;
+  barrier();
+  atomicExchange(wg.a, 1u);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    atomicExchange(wg.a, 0u);
+    wg.y = 0u;
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.hlsl b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.hlsl
new file mode 100644
index 0000000..403d1fb
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+struct S_atomic {
+  int x;
+  uint a;
+  uint y;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared S_atomic wg;
+
+void compute_main_inner(uint local_invocation_index) {
+  wg.x = 0;
+  uint atomic_result = 0u;
+  InterlockedExchange(wg.a, 0u, atomic_result);
+  wg.y = 0u;
+  GroupMemoryBarrierWithGroupSync();
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(wg.a, 1u, atomic_result_1);
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    wg.x = 0;
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(wg.a, 0u, atomic_result_2);
+    wg.y = 0u;
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.msl b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.msl
new file mode 100644
index 0000000..524820a
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.msl
@@ -0,0 +1,48 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct S_atomic {
+  int x;
+  atomic_uint a;
+  uint y;
+};
+
+struct S {
+  int x;
+  uint a;
+  uint y;
+};
+
+void compute_main_inner(uint local_invocation_index, threadgroup S_atomic* const tint_symbol) {
+  (*(tint_symbol)).x = 0;
+  atomic_store_explicit(&((*(tint_symbol)).a), 0u, memory_order_relaxed);
+  (*(tint_symbol)).y = 0u;
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomic_store_explicit(&((*(tint_symbol)).a), 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_1, threadgroup S_atomic* const tint_symbol_2) {
+  uint const x_35 = *(tint_symbol_1);
+  compute_main_inner(x_35, tint_symbol_2);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup S_atomic* const tint_symbol_3, thread uint* const tint_symbol_4) {
+  {
+    (*(tint_symbol_3)).x = 0;
+    atomic_store_explicit(&((*(tint_symbol_3)).a), 0u, memory_order_relaxed);
+    (*(tint_symbol_3)).y = 0u;
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_4) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_4, tint_symbol_3);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup S_atomic tint_symbol_5;
+  thread uint tint_symbol_6 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_5), &(tint_symbol_6));
+  return;
+}
+
diff --git a/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.spvasm b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.spvasm
new file mode 100644
index 0000000..35d0d2b
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.spvasm
@@ -0,0 +1,87 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 52
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %S_atomic "S_atomic"
+               OpMemberName %S_atomic 0 "x"
+               OpMemberName %S_atomic 1 "a"
+               OpMemberName %S_atomic 2 "y"
+               OpName %wg "wg"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %S_atomic 0 Offset 0
+               OpMemberDecorate %S_atomic 1 Offset 4
+               OpMemberDecorate %S_atomic 2 Offset 8
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+   %S_atomic = OpTypeStruct %int %uint %uint
+%_ptr_Workgroup_S_atomic = OpTypePointer Workgroup %S_atomic
+         %wg = OpVariable %_ptr_Workgroup_S_atomic Workgroup
+       %void = OpTypeVoid
+         %11 = OpTypeFunction %void %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+         %19 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_1 = OpConstant %uint 1
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+%_ptr_Workgroup_uint_0 = OpTypePointer Workgroup %uint
+   %uint_264 = OpConstant %uint 264
+         %33 = OpTypeFunction %void
+%compute_main_inner = OpFunction %void None %11
+%local_invocation_index = OpFunctionParameter %uint
+         %15 = OpLabel
+         %18 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %18 %19
+         %25 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %25 %uint_2 %uint_0 %6
+         %27 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_2
+               OpStore %27 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %32 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %33
+         %35 = OpLabel
+         %36 = OpLoad %uint %local_invocation_index_1
+         %37 = OpFunctionCall %void %compute_main_inner %36
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %11
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %40 = OpLabel
+         %41 = OpAccessChain %_ptr_Workgroup_int %wg %uint_0
+               OpStore %41 %19
+         %44 = OpAccessChain %_ptr_Workgroup_uint %wg %uint_1
+               OpAtomicStore %44 %uint_2 %uint_0 %6
+         %45 = OpAccessChain %_ptr_Workgroup_uint_0 %wg %uint_2
+               OpStore %45 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %47 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %33
+         %49 = OpLabel
+         %51 = OpLoad %uint %local_invocation_index_1_param_1
+         %50 = OpFunctionCall %void %compute_main_inner_1 %51
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.wgsl b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.wgsl
new file mode 100644
index 0000000..2d90f56
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/via_ptr_let.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct S_atomic {
+  x : i32,
+  a : atomic<u32>,
+  y : u32,
+}
+
+struct S {
+  x : i32,
+  a : u32,
+  y : u32,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> wg : S_atomic;
+
+fn compute_main_inner(local_invocation_index : u32) {
+  wg.x = 0i;
+  atomicStore(&(wg.a), 0u);
+  wg.y = 0u;
+  workgroupBarrier();
+  atomicStore(&(wg.a), 1u);
+  return;
+}
+
+fn compute_main_1() {
+  let x_35 : u32 = local_invocation_index_1;
+  compute_main_inner(x_35);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomicStore/struct/via_ptr_let.wgsl b/test/tint/builtins/atomicStore/struct/via_ptr_let.wgsl
new file mode 100644
index 0000000..64eeb65
--- /dev/null
+++ b/test/tint/builtins/atomicStore/struct/via_ptr_let.wgsl
@@ -0,0 +1,14 @@
+struct S {
+  x : i32,
+  a : atomic<u32>,
+  y : u32,
+};
+
+var<workgroup> wg : S;
+
+@compute @workgroup_size(1)
+fn compute_main() {
+  let p0 = &wg;
+  let p1 = &((*p0).a);
+  atomicStore(p1, 1u);
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm
new file mode 100644
index 0000000..1167238
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicIAdd %int %15 %uint_1 %uint_0 %int_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..875dc08
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int res = 0;
+  int x_9 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int res = 0;
+  int x_9 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..358487b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicAdd(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAdd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_d32fe4() {
+  int res = 0;
+  const int x_9 = tint_atomicAdd(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..64027ff
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicAdd_d32fe4(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_add_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_d32fe4(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_d32fe4(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..13f287a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicIAdd %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..cfc64fb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_d32fe4() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicAdd(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm
new file mode 100644
index 0000000..3996635
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicIAdd %uint %14 %uint_1 %uint_0 %uint_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..48a0735
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  uint x_9 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  uint x_9 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..bdc43b0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicAdd(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAdd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicAdd(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..fa2343b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicAdd_8a199a(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_add_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_8a199a(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_8a199a(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..e1bf1e9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicIAdd %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b5d5856
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_8a199a() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicAdd(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm
new file mode 100644
index 0000000..d8903dc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicIAdd %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..b745861
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicAdd_794055() {
+  int res = 0;
+  int x_11 = atomicAdd(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..ccdf1ad
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicAdd_794055() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedAdd(arg_0, 1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..62639d7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_794055(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_add_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_794055(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..3dc0a5d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicIAdd %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..723a113
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicAdd_794055() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicAdd(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicAdd_794055();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm
new file mode 100644
index 0000000..110fb42
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicIAdd %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..97c0c6f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicAdd_d5db1d() {
+  uint res = 0u;
+  uint x_10 = atomicAdd(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..c36ae57
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicAdd_d5db1d() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedAdd(arg_0, 1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..04a890a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_d5db1d(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_add_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_d5db1d(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..5daee64
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicIAdd %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..61d34cc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAdd/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicAdd_d5db1d() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicAdd(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm
new file mode 100644
index 0000000..ee4f5dc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAnd_152966 "atomicAnd_152966"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicAnd_152966 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicAnd %int %15 %uint_1 %uint_0 %int_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAnd_152966
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicAnd_152966
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..28adfe0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAnd_152966() {
+  int res = 0;
+  int x_9 = atomicAnd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAnd_152966() {
+  int res = 0;
+  int x_9 = atomicAnd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..0cc23ee
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicAnd(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAnd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAnd_152966() {
+  int res = 0;
+  const int x_9 = tint_atomicAnd(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..919a4e1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicAnd_152966(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_and_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAnd_152966(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAnd_152966(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..0c6eb12
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAnd_152966 "atomicAnd_152966"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicAnd_152966 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicAnd %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAnd_152966
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicAnd_152966
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..c76ec1b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAnd_152966() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicAnd(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm
new file mode 100644
index 0000000..e45402c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAnd_85a8d9 "atomicAnd_85a8d9"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicAnd_85a8d9 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicAnd %uint %14 %uint_1 %uint_0 %uint_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicAnd_85a8d9
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicAnd_85a8d9
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..6d879c1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAnd_85a8d9() {
+  uint res = 0u;
+  uint x_9 = atomicAnd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAnd_85a8d9() {
+  uint res = 0u;
+  uint x_9 = atomicAnd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..897165d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicAnd(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAnd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAnd_85a8d9() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicAnd(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..2589380
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicAnd_85a8d9(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_and_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAnd_85a8d9(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAnd_85a8d9(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..25da8d5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAnd_85a8d9 "atomicAnd_85a8d9"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAnd_85a8d9 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicAnd %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicAnd_85a8d9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicAnd_85a8d9
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..2f8f459
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAnd_85a8d9() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicAnd(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm
new file mode 100644
index 0000000..696d2a3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAnd_45a819 "atomicAnd_45a819"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAnd_45a819 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicAnd %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicAnd_45a819
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..f914b2f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicAnd_45a819() {
+  int res = 0;
+  int x_11 = atomicAnd(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicAnd_45a819();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..2e86713
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicAnd_45a819() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedAnd(arg_0, 1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAnd_45a819();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..ee4b602
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAnd_45a819(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_and_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAnd_45a819(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..b7e9939
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAnd_45a819 "atomicAnd_45a819"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAnd_45a819 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicAnd %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicAnd_45a819
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..9369271
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicAnd_45a819() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicAnd(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicAnd_45a819();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm
new file mode 100644
index 0000000..3d205d2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAnd_34edd3 "atomicAnd_34edd3"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAnd_34edd3 = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicAnd %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicAnd_34edd3
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..92c68d7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicAnd_34edd3() {
+  uint res = 0u;
+  uint x_10 = atomicAnd(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicAnd_34edd3();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..fb9b516
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicAnd_34edd3() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedAnd(arg_0, 1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAnd_34edd3();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..ffc5ace
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAnd_34edd3(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_and_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAnd_34edd3(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..02a5b89
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAnd_34edd3 "atomicAnd_34edd3"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAnd_34edd3 = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicAnd %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicAnd_34edd3
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..ee27cf6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicAnd/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicAnd_34edd3() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicAnd(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicAnd_34edd3();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm
new file mode 100644
index 0000000..454abb1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm
@@ -0,0 +1,62 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicCompareExchangeWeak_1bd40a "atomicCompareExchangeWeak_1bd40a"
+               OpName %__atomic_compare_exchange_resulti32 "__atomic_compare_exchange_resulti32"
+               OpMemberName %__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 1 Offset 4
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+%__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function___atomic_compare_exchange_resulti32 = OpTypePointer Function %__atomic_compare_exchange_resulti32
+         %23 = OpConstantNull %__atomic_compare_exchange_resulti32
+%atomicCompareExchangeWeak_1bd40a = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function___atomic_compare_exchange_resulti32 Function %23
+         %17 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %19 = OpAtomicCompareExchange %int %17 %uint_1 %uint_0 %uint_0 %int_1 %int_1
+         %20 = OpIEqual %bool %19 %int_1
+          %9 = OpCompositeConstruct %__atomic_compare_exchange_resulti32 %19 %20
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicCompareExchangeWeak_1bd40a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %atomicCompareExchangeWeak_1bd40a
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..b46c227
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,102 @@
+#version 310 es
+precision mediump float;
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicCompareExchangeWeak_1bd40a() {
+  x__atomic_compare_exchange_resulti32 res = x__atomic_compare_exchange_resulti32(0, false);
+  atomic_compare_exchange_resulti32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(sb_rw.arg_0, 1, 1);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == 1;
+  atomic_compare_exchange_resulti32 tint_symbol = atomic_compare_result;
+  int old_value_1 = tint_symbol.old_value;
+  int x_19 = old_value_1;
+  x__atomic_compare_exchange_resulti32 tint_symbol_1 = x__atomic_compare_exchange_resulti32(x_19, (x_19 == 1));
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicCompareExchangeWeak_1bd40a() {
+  x__atomic_compare_exchange_resulti32 res = x__atomic_compare_exchange_resulti32(0, false);
+  atomic_compare_exchange_resulti32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(sb_rw.arg_0, 1, 1);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == 1;
+  atomic_compare_exchange_resulti32 tint_symbol = atomic_compare_result;
+  int old_value_1 = tint_symbol.old_value;
+  int x_19 = old_value_1;
+  x__atomic_compare_exchange_resulti32 tint_symbol_1 = x__atomic_compare_exchange_resulti32(x_19, (x_19 == 1));
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..a385d05
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,50 @@
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+struct atomic_compare_exchange_weak_ret_type {
+  int old_value;
+  bool exchanged;
+};
+
+atomic_compare_exchange_weak_ret_type tint_atomicCompareExchangeWeak(RWByteAddressBuffer buffer, uint offset, int compare, int value) {
+  atomic_compare_exchange_weak_ret_type result=(atomic_compare_exchange_weak_ret_type)0;
+  buffer.InterlockedCompareExchange(offset, compare, value, result.old_value);
+  result.exchanged = result.old_value == compare;
+  return result;
+}
+
+
+void atomicCompareExchangeWeak_1bd40a() {
+  x__atomic_compare_exchange_resulti32 res = {0, false};
+  const atomic_compare_exchange_weak_ret_type tint_symbol = tint_atomicCompareExchangeWeak(sb_rw, 0u, 1, 1);
+  const int old_value_1 = tint_symbol.old_value;
+  const int x_19 = old_value_1;
+  const x__atomic_compare_exchange_resulti32 tint_symbol_1 = {x_19, (x_19 == 1)};
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..3961fdd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.msl
@@ -0,0 +1,57 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+atomic_compare_exchange_resulti32 atomicCompareExchangeWeak_1(device atomic_int* atomic, int compare, int value) {
+  int old_value = compare;
+  bool exchanged = atomic_compare_exchange_weak_explicit(atomic, &old_value, value, memory_order_relaxed, memory_order_relaxed);
+  return {old_value, exchanged};
+}
+
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+void atomicCompareExchangeWeak_1bd40a(device SB_RW_atomic* const tint_symbol_2) {
+  x__atomic_compare_exchange_resulti32 res = {.old_value=0, .exchanged=false};
+  atomic_compare_exchange_resulti32 const tint_symbol = atomicCompareExchangeWeak_1(&((*(tint_symbol_2)).arg_0), 1, 1);
+  int const old_value_1 = tint_symbol.old_value;
+  int const x_19 = old_value_1;
+  x__atomic_compare_exchange_resulti32 const tint_symbol_1 = {.old_value=x_19, .exchanged=(x_19 == 1)};
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicCompareExchangeWeak_1bd40a(tint_symbol_3);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_4);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_5) {
+  atomicCompareExchangeWeak_1bd40a(tint_symbol_5);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_6 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_6);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..97cc672
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,87 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 43
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicCompareExchangeWeak_1bd40a "atomicCompareExchangeWeak_1bd40a"
+               OpName %x__atomic_compare_exchange_resulti32 "x__atomic_compare_exchange_resulti32"
+               OpMemberName %x__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %x__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %res "res"
+               OpName %__atomic_compare_exchange_resulti32 "__atomic_compare_exchange_resulti32"
+               OpMemberName %__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+               OpMemberDecorate %x__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %x__atomic_compare_exchange_resulti32 1 Offset 4
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 1 Offset 4
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+%x__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+         %11 = OpConstantNull %int
+         %12 = OpConstantNull %bool
+         %13 = OpConstantComposite %x__atomic_compare_exchange_resulti32 %11 %12
+%_ptr_Function_x__atomic_compare_exchange_resulti32 = OpTypePointer Function %x__atomic_compare_exchange_resulti32
+         %16 = OpConstantNull %x__atomic_compare_exchange_resulti32
+%__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicCompareExchangeWeak_1bd40a = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_x__atomic_compare_exchange_resulti32 Function %16
+               OpStore %res %13
+         %24 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %26 = OpAtomicCompareExchange %int %24 %uint_1 %uint_0 %uint_0 %int_1 %int_1
+         %27 = OpIEqual %bool %26 %int_1
+         %17 = OpCompositeConstruct %__atomic_compare_exchange_resulti32 %26 %27
+         %28 = OpCompositeExtract %int %17 0
+         %29 = OpIEqual %bool %28 %int_1
+         %30 = OpCompositeConstruct %x__atomic_compare_exchange_resulti32 %28 %29
+               OpStore %res %30
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %atomicCompareExchangeWeak_1bd40a
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %35 = OpLabel
+         %36 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %38 = OpLabel
+         %39 = OpFunctionCall %void %atomicCompareExchangeWeak_1bd40a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %41 = OpLabel
+         %42 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..cf8754d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,42 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+struct x__atomic_compare_exchange_resulti32 {
+  old_value : i32,
+  exchanged : bool,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicCompareExchangeWeak_1bd40a() {
+  var res : x__atomic_compare_exchange_resulti32 = x__atomic_compare_exchange_resulti32(0i, false);
+  let old_value_1 = atomicCompareExchangeWeak(&(sb_rw.arg_0), 1i, 1i).old_value;
+  let x_19 : i32 = old_value_1;
+  res = x__atomic_compare_exchange_resulti32(x_19, (x_19 == 1i));
+  return;
+}
+
+fn fragment_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm
new file mode 100644
index 0000000..09e83a0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm
@@ -0,0 +1,60 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicCompareExchangeWeak_63d8e6 "atomicCompareExchangeWeak_63d8e6"
+               OpName %__atomic_compare_exchange_resultu32 "__atomic_compare_exchange_resultu32"
+               OpMemberName %__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 1 Offset 4
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+%__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function___atomic_compare_exchange_resultu32 = OpTypePointer Function %__atomic_compare_exchange_resultu32
+         %21 = OpConstantNull %__atomic_compare_exchange_resultu32
+%atomicCompareExchangeWeak_63d8e6 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function___atomic_compare_exchange_resultu32 Function %21
+         %16 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %17 = OpAtomicCompareExchange %uint %16 %uint_1 %uint_0 %uint_0 %uint_1 %uint_1
+         %18 = OpIEqual %bool %17 %uint_1
+          %9 = OpCompositeConstruct %__atomic_compare_exchange_resultu32 %17 %18
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicCompareExchangeWeak_63d8e6
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicCompareExchangeWeak_63d8e6
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..385735d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,102 @@
+#version 310 es
+precision mediump float;
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicCompareExchangeWeak_63d8e6() {
+  x__atomic_compare_exchange_resultu32 res = x__atomic_compare_exchange_resultu32(0u, false);
+  atomic_compare_exchange_resultu32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(sb_rw.arg_0, 1u, 1u);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == 1u;
+  atomic_compare_exchange_resultu32 tint_symbol = atomic_compare_result;
+  uint old_value_1 = tint_symbol.old_value;
+  uint x_17 = old_value_1;
+  x__atomic_compare_exchange_resultu32 tint_symbol_1 = x__atomic_compare_exchange_resultu32(x_17, (x_17 == 1u));
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicCompareExchangeWeak_63d8e6() {
+  x__atomic_compare_exchange_resultu32 res = x__atomic_compare_exchange_resultu32(0u, false);
+  atomic_compare_exchange_resultu32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(sb_rw.arg_0, 1u, 1u);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == 1u;
+  atomic_compare_exchange_resultu32 tint_symbol = atomic_compare_result;
+  uint old_value_1 = tint_symbol.old_value;
+  uint x_17 = old_value_1;
+  x__atomic_compare_exchange_resultu32 tint_symbol_1 = x__atomic_compare_exchange_resultu32(x_17, (x_17 == 1u));
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..c36984d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,50 @@
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+struct atomic_compare_exchange_weak_ret_type {
+  uint old_value;
+  bool exchanged;
+};
+
+atomic_compare_exchange_weak_ret_type tint_atomicCompareExchangeWeak(RWByteAddressBuffer buffer, uint offset, uint compare, uint value) {
+  atomic_compare_exchange_weak_ret_type result=(atomic_compare_exchange_weak_ret_type)0;
+  buffer.InterlockedCompareExchange(offset, compare, value, result.old_value);
+  result.exchanged = result.old_value == compare;
+  return result;
+}
+
+
+void atomicCompareExchangeWeak_63d8e6() {
+  x__atomic_compare_exchange_resultu32 res = {0u, false};
+  const atomic_compare_exchange_weak_ret_type tint_symbol = tint_atomicCompareExchangeWeak(sb_rw, 0u, 1u, 1u);
+  const uint old_value_1 = tint_symbol.old_value;
+  const uint x_17 = old_value_1;
+  const x__atomic_compare_exchange_resultu32 tint_symbol_1 = {x_17, (x_17 == 1u)};
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..b06f2f9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.msl
@@ -0,0 +1,57 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+atomic_compare_exchange_resultu32 atomicCompareExchangeWeak_1(device atomic_uint* atomic, uint compare, uint value) {
+  uint old_value = compare;
+  bool exchanged = atomic_compare_exchange_weak_explicit(atomic, &old_value, value, memory_order_relaxed, memory_order_relaxed);
+  return {old_value, exchanged};
+}
+
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+void atomicCompareExchangeWeak_63d8e6(device SB_RW_atomic* const tint_symbol_2) {
+  x__atomic_compare_exchange_resultu32 res = {.old_value=0u, .exchanged=false};
+  atomic_compare_exchange_resultu32 const tint_symbol = atomicCompareExchangeWeak_1(&((*(tint_symbol_2)).arg_0), 1u, 1u);
+  uint const old_value_1 = tint_symbol.old_value;
+  uint const x_17 = old_value_1;
+  x__atomic_compare_exchange_resultu32 const tint_symbol_1 = {.old_value=x_17, .exchanged=(x_17 == 1u)};
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicCompareExchangeWeak_63d8e6(tint_symbol_3);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_4);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_5) {
+  atomicCompareExchangeWeak_63d8e6(tint_symbol_5);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_6 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_6);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..bb4e843
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,85 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 41
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicCompareExchangeWeak_63d8e6 "atomicCompareExchangeWeak_63d8e6"
+               OpName %x__atomic_compare_exchange_resultu32 "x__atomic_compare_exchange_resultu32"
+               OpMemberName %x__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %x__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %res "res"
+               OpName %__atomic_compare_exchange_resultu32 "__atomic_compare_exchange_resultu32"
+               OpMemberName %__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+               OpMemberDecorate %x__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %x__atomic_compare_exchange_resultu32 1 Offset 4
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %bool = OpTypeBool
+%x__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+         %11 = OpConstantNull %uint
+         %12 = OpConstantNull %bool
+         %13 = OpConstantComposite %x__atomic_compare_exchange_resultu32 %11 %12
+%_ptr_Function_x__atomic_compare_exchange_resultu32 = OpTypePointer Function %x__atomic_compare_exchange_resultu32
+         %16 = OpConstantNull %x__atomic_compare_exchange_resultu32
+%__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicCompareExchangeWeak_63d8e6 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_x__atomic_compare_exchange_resultu32 Function %16
+               OpStore %res %13
+         %23 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %24 = OpAtomicCompareExchange %uint %23 %uint_1 %uint_0 %uint_0 %uint_1 %uint_1
+         %25 = OpIEqual %bool %24 %uint_1
+         %17 = OpCompositeConstruct %__atomic_compare_exchange_resultu32 %24 %25
+         %26 = OpCompositeExtract %uint %17 0
+         %27 = OpIEqual %bool %26 %uint_1
+         %28 = OpCompositeConstruct %x__atomic_compare_exchange_resultu32 %26 %27
+               OpStore %res %28
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %atomicCompareExchangeWeak_63d8e6
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %33 = OpLabel
+         %34 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %36 = OpLabel
+         %37 = OpFunctionCall %void %atomicCompareExchangeWeak_63d8e6
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %39 = OpLabel
+         %40 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..4382a23
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,42 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+struct x__atomic_compare_exchange_resultu32 {
+  old_value : u32,
+  exchanged : bool,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicCompareExchangeWeak_63d8e6() {
+  var res : x__atomic_compare_exchange_resultu32 = x__atomic_compare_exchange_resultu32(0u, false);
+  let old_value_1 = atomicCompareExchangeWeak(&(sb_rw.arg_0), 1u, 1u).old_value;
+  let x_17 : u32 = old_value_1;
+  res = x__atomic_compare_exchange_resultu32(x_17, (x_17 == 1u));
+  return;
+}
+
+fn fragment_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm
new file mode 100644
index 0000000..a0f338c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm
@@ -0,0 +1,63 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 37
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicCompareExchangeWeak_e88938 "atomicCompareExchangeWeak_e88938"
+               OpName %__atomic_compare_exchange_resulti32 "__atomic_compare_exchange_resulti32"
+               OpMemberName %__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %bool = OpTypeBool
+%__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function___atomic_compare_exchange_resulti32 = OpTypePointer Function %__atomic_compare_exchange_resulti32
+         %22 = OpConstantNull %__atomic_compare_exchange_resulti32
+         %23 = OpTypeFunction %void %uint
+         %29 = OpConstantNull %int
+   %uint_264 = OpConstant %uint 264
+%atomicCompareExchangeWeak_e88938 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function___atomic_compare_exchange_resulti32 Function %22
+         %18 = OpAtomicCompareExchange %int %arg_0 %uint_2 %uint_0 %uint_0 %int_1 %int_1
+         %19 = OpIEqual %bool %18 %int_1
+         %11 = OpCompositeConstruct %__atomic_compare_exchange_resulti32 %18 %19
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %23
+%local_invocation_index = OpFunctionParameter %uint
+         %26 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %29
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicCompareExchangeWeak_e88938
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %34 = OpLabel
+         %36 = OpLoad %uint %local_invocation_index_1
+         %35 = OpFunctionCall %void %compute_main_inner %36
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..6ccd038
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,54 @@
+#version 310 es
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicCompareExchangeWeak_e88938() {
+  x__atomic_compare_exchange_resulti32 res = x__atomic_compare_exchange_resulti32(0, false);
+  atomic_compare_exchange_resulti32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(arg_0, 1, 1);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == 1;
+  atomic_compare_exchange_resulti32 tint_symbol = atomic_compare_result;
+  int old_value_1 = tint_symbol.old_value;
+  int x_18 = old_value_1;
+  x__atomic_compare_exchange_resulti32 tint_symbol_1 = x__atomic_compare_exchange_resulti32(x_18, (x_18 == 1));
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicCompareExchangeWeak_e88938();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..03640dd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,58 @@
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicCompareExchangeWeak_e88938() {
+  x__atomic_compare_exchange_resulti32 res = {0, false};
+  atomic_compare_exchange_resulti32 atomic_result = (atomic_compare_exchange_resulti32)0;
+  int atomic_compare_value = 1;
+  InterlockedCompareExchange(arg_0, atomic_compare_value, 1, atomic_result.old_value);
+  atomic_result.exchanged = atomic_result.old_value == atomic_compare_value;
+  const atomic_compare_exchange_resulti32 tint_symbol_2 = atomic_result;
+  const int old_value_1 = tint_symbol_2.old_value;
+  const int x_18 = old_value_1;
+  const x__atomic_compare_exchange_resulti32 tint_symbol_3 = {x_18, (x_18 == 1)};
+  res = tint_symbol_3;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicCompareExchangeWeak_e88938();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..4cfcd1c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,58 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+atomic_compare_exchange_resulti32 atomicCompareExchangeWeak_1(threadgroup atomic_int* atomic, int compare, int value) {
+  int old_value = compare;
+  bool exchanged = atomic_compare_exchange_weak_explicit(atomic, &old_value, value, memory_order_relaxed, memory_order_relaxed);
+  return {old_value, exchanged};
+}
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+void atomicCompareExchangeWeak_e88938(threadgroup atomic_int* const tint_symbol_2) {
+  x__atomic_compare_exchange_resulti32 res = {.old_value=0, .exchanged=false};
+  atomic_compare_exchange_resulti32 const tint_symbol = atomicCompareExchangeWeak_1(tint_symbol_2, 1, 1);
+  int const old_value_1 = tint_symbol.old_value;
+  int const x_18 = old_value_1;
+  x__atomic_compare_exchange_resulti32 const tint_symbol_1 = {.old_value=x_18, .exchanged=(x_18 == 1)};
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_3) {
+  atomic_store_explicit(tint_symbol_3, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicCompareExchangeWeak_e88938(tint_symbol_3);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_4, threadgroup atomic_int* const tint_symbol_5) {
+  uint const x_36 = *(tint_symbol_4);
+  compute_main_inner(x_36, tint_symbol_5);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_6, thread uint* const tint_symbol_7) {
+  {
+    atomic_store_explicit(tint_symbol_6, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_7) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_7, tint_symbol_6);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_8;
+  thread uint tint_symbol_9 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_8), &(tint_symbol_9));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..a9201f5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,97 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 57
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicCompareExchangeWeak_e88938 "atomicCompareExchangeWeak_e88938"
+               OpName %x__atomic_compare_exchange_resulti32 "x__atomic_compare_exchange_resulti32"
+               OpMemberName %x__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %x__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %res "res"
+               OpName %__atomic_compare_exchange_resulti32 "__atomic_compare_exchange_resulti32"
+               OpMemberName %__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %x__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %x__atomic_compare_exchange_resulti32 1 Offset 4
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+       %bool = OpTypeBool
+%x__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+         %16 = OpConstantNull %int
+         %17 = OpConstantNull %bool
+         %18 = OpConstantComposite %x__atomic_compare_exchange_resulti32 %16 %17
+%_ptr_Function_x__atomic_compare_exchange_resulti32 = OpTypePointer Function %x__atomic_compare_exchange_resulti32
+         %21 = OpConstantNull %x__atomic_compare_exchange_resulti32
+%__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %33 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicCompareExchangeWeak_e88938 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_x__atomic_compare_exchange_resulti32 Function %21
+               OpStore %res %18
+         %28 = OpAtomicCompareExchange %int %arg_0 %uint_2 %uint_0 %uint_0 %int_1 %int_1
+         %29 = OpIEqual %bool %28 %int_1
+         %22 = OpCompositeConstruct %__atomic_compare_exchange_resulti32 %28 %29
+         %30 = OpCompositeExtract %int %22 0
+         %31 = OpIEqual %bool %30 %int_1
+         %32 = OpCompositeConstruct %x__atomic_compare_exchange_resulti32 %30 %31
+               OpStore %res %32
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %33
+%local_invocation_index = OpFunctionParameter %uint
+         %36 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %16
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %41 = OpFunctionCall %void %atomicCompareExchangeWeak_e88938
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %43 = OpLabel
+         %44 = OpLoad %uint %local_invocation_index_1
+         %45 = OpFunctionCall %void %compute_main_inner %44
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %33
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %48 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %16
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %52 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %54 = OpLabel
+         %56 = OpLoad %uint %local_invocation_index_1_param_1
+         %55 = OpFunctionCall %void %compute_main_inner_1 %56
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..349dccf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,35 @@
+struct x__atomic_compare_exchange_resulti32 {
+  old_value : i32,
+  exchanged : bool,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicCompareExchangeWeak_e88938() {
+  var res : x__atomic_compare_exchange_resulti32 = x__atomic_compare_exchange_resulti32(0i, false);
+  let old_value_1 = atomicCompareExchangeWeak(&(arg_0), 1i, 1i).old_value;
+  let x_18 : i32 = old_value_1;
+  res = x__atomic_compare_exchange_resulti32(x_18, (x_18 == 1i));
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicCompareExchangeWeak_e88938();
+  return;
+}
+
+fn compute_main_1() {
+  let x_36 : u32 = local_invocation_index_1;
+  compute_main_inner(x_36);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm
new file mode 100644
index 0000000..8264985
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm
@@ -0,0 +1,62 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 36
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicCompareExchangeWeak_83580d "atomicCompareExchangeWeak_83580d"
+               OpName %__atomic_compare_exchange_resultu32 "__atomic_compare_exchange_resultu32"
+               OpMemberName %__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+       %bool = OpTypeBool
+%__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function___atomic_compare_exchange_resultu32 = OpTypePointer Function %__atomic_compare_exchange_resultu32
+         %21 = OpConstantNull %__atomic_compare_exchange_resultu32
+         %22 = OpTypeFunction %void %uint
+         %28 = OpConstantNull %uint
+   %uint_264 = OpConstant %uint 264
+%atomicCompareExchangeWeak_83580d = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function___atomic_compare_exchange_resultu32 Function %21
+         %17 = OpAtomicCompareExchange %uint %arg_0 %uint_2 %uint_0 %uint_0 %uint_1 %uint_1
+         %18 = OpIEqual %bool %17 %uint_1
+         %10 = OpCompositeConstruct %__atomic_compare_exchange_resultu32 %17 %18
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %28
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %31 = OpFunctionCall %void %atomicCompareExchangeWeak_83580d
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %33 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..7bd1617e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,54 @@
+#version 310 es
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicCompareExchangeWeak_83580d() {
+  x__atomic_compare_exchange_resultu32 res = x__atomic_compare_exchange_resultu32(0u, false);
+  atomic_compare_exchange_resultu32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(arg_0, 1u, 1u);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == 1u;
+  atomic_compare_exchange_resultu32 tint_symbol = atomic_compare_result;
+  uint old_value_1 = tint_symbol.old_value;
+  uint x_17 = old_value_1;
+  x__atomic_compare_exchange_resultu32 tint_symbol_1 = x__atomic_compare_exchange_resultu32(x_17, (x_17 == 1u));
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicCompareExchangeWeak_83580d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..1c61811
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,58 @@
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicCompareExchangeWeak_83580d() {
+  x__atomic_compare_exchange_resultu32 res = {0u, false};
+  atomic_compare_exchange_resultu32 atomic_result = (atomic_compare_exchange_resultu32)0;
+  uint atomic_compare_value = 1u;
+  InterlockedCompareExchange(arg_0, atomic_compare_value, 1u, atomic_result.old_value);
+  atomic_result.exchanged = atomic_result.old_value == atomic_compare_value;
+  const atomic_compare_exchange_resultu32 tint_symbol_2 = atomic_result;
+  const uint old_value_1 = tint_symbol_2.old_value;
+  const uint x_17 = old_value_1;
+  const x__atomic_compare_exchange_resultu32 tint_symbol_3 = {x_17, (x_17 == 1u)};
+  res = tint_symbol_3;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicCompareExchangeWeak_83580d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..0f6a268
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,58 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+atomic_compare_exchange_resultu32 atomicCompareExchangeWeak_1(threadgroup atomic_uint* atomic, uint compare, uint value) {
+  uint old_value = compare;
+  bool exchanged = atomic_compare_exchange_weak_explicit(atomic, &old_value, value, memory_order_relaxed, memory_order_relaxed);
+  return {old_value, exchanged};
+}
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+void atomicCompareExchangeWeak_83580d(threadgroup atomic_uint* const tint_symbol_2) {
+  x__atomic_compare_exchange_resultu32 res = {.old_value=0u, .exchanged=false};
+  atomic_compare_exchange_resultu32 const tint_symbol = atomicCompareExchangeWeak_1(tint_symbol_2, 1u, 1u);
+  uint const old_value_1 = tint_symbol.old_value;
+  uint const x_17 = old_value_1;
+  x__atomic_compare_exchange_resultu32 const tint_symbol_1 = {.old_value=x_17, .exchanged=(x_17 == 1u)};
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_3) {
+  atomic_store_explicit(tint_symbol_3, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicCompareExchangeWeak_83580d(tint_symbol_3);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_4, threadgroup atomic_uint* const tint_symbol_5) {
+  uint const x_35 = *(tint_symbol_4);
+  compute_main_inner(x_35, tint_symbol_5);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_6, thread uint* const tint_symbol_7) {
+  {
+    atomic_store_explicit(tint_symbol_6, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_7) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_7, tint_symbol_6);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_8;
+  thread uint tint_symbol_9 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_8), &(tint_symbol_9));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..26382e3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,95 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 55
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicCompareExchangeWeak_83580d "atomicCompareExchangeWeak_83580d"
+               OpName %x__atomic_compare_exchange_resultu32 "x__atomic_compare_exchange_resultu32"
+               OpMemberName %x__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %x__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %res "res"
+               OpName %__atomic_compare_exchange_resultu32 "__atomic_compare_exchange_resultu32"
+               OpMemberName %__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %x__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %x__atomic_compare_exchange_resultu32 1 Offset 4
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+       %bool = OpTypeBool
+%x__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+         %15 = OpConstantNull %bool
+         %16 = OpConstantComposite %x__atomic_compare_exchange_resultu32 %6 %15
+%_ptr_Function_x__atomic_compare_exchange_resultu32 = OpTypePointer Function %x__atomic_compare_exchange_resultu32
+         %19 = OpConstantNull %x__atomic_compare_exchange_resultu32
+%__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %31 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicCompareExchangeWeak_83580d = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_x__atomic_compare_exchange_resultu32 Function %19
+               OpStore %res %16
+         %26 = OpAtomicCompareExchange %uint %arg_0 %uint_2 %uint_0 %uint_0 %uint_1 %uint_1
+         %27 = OpIEqual %bool %26 %uint_1
+         %20 = OpCompositeConstruct %__atomic_compare_exchange_resultu32 %26 %27
+         %28 = OpCompositeExtract %uint %20 0
+         %29 = OpIEqual %bool %28 %uint_1
+         %30 = OpCompositeConstruct %x__atomic_compare_exchange_resultu32 %28 %29
+               OpStore %res %30
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %31
+%local_invocation_index = OpFunctionParameter %uint
+         %34 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %39 = OpFunctionCall %void %atomicCompareExchangeWeak_83580d
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %41 = OpLabel
+         %42 = OpLoad %uint %local_invocation_index_1
+         %43 = OpFunctionCall %void %compute_main_inner %42
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %31
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %46 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %50 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %52 = OpLabel
+         %54 = OpLoad %uint %local_invocation_index_1_param_1
+         %53 = OpFunctionCall %void %compute_main_inner_1 %54
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..9c897f9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,35 @@
+struct x__atomic_compare_exchange_resultu32 {
+  old_value : u32,
+  exchanged : bool,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicCompareExchangeWeak_83580d() {
+  var res : x__atomic_compare_exchange_resultu32 = x__atomic_compare_exchange_resultu32(0u, false);
+  let old_value_1 = atomicCompareExchangeWeak(&(arg_0), 1u, 1u).old_value;
+  let x_17 : u32 = old_value_1;
+  res = x__atomic_compare_exchange_resultu32(x_17, (x_17 == 1u));
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicCompareExchangeWeak_83580d();
+  return;
+}
+
+fn compute_main_1() {
+  let x_35 : u32 = local_invocation_index_1;
+  compute_main_inner(x_35);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm
new file mode 100644
index 0000000..c4f71e9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicExchange_f2e22f "atomicExchange_f2e22f"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicExchange_f2e22f = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicExchange %int %15 %uint_1 %uint_0 %int_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicExchange_f2e22f
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicExchange_f2e22f
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..4fdae27
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicExchange_f2e22f() {
+  int res = 0;
+  int x_9 = atomicExchange(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicExchange_f2e22f() {
+  int res = 0;
+  int x_9 = atomicExchange(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..deffe11
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicExchange(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedExchange(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicExchange_f2e22f() {
+  int res = 0;
+  const int x_9 = tint_atomicExchange(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..cbd9391
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicExchange_f2e22f(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_exchange_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicExchange_f2e22f(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicExchange_f2e22f(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..10fd286
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicExchange_f2e22f "atomicExchange_f2e22f"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicExchange_f2e22f = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicExchange %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicExchange_f2e22f
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicExchange_f2e22f
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..625f2c3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicExchange_f2e22f() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicExchange(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm
new file mode 100644
index 0000000..bdd9e1d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicExchange_d59712 "atomicExchange_d59712"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicExchange_d59712 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicExchange %uint %14 %uint_1 %uint_0 %uint_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicExchange_d59712
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicExchange_d59712
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..57f0291
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicExchange_d59712() {
+  uint res = 0u;
+  uint x_9 = atomicExchange(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicExchange_d59712() {
+  uint res = 0u;
+  uint x_9 = atomicExchange(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..7b20c96
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicExchange(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedExchange(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicExchange_d59712() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicExchange(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..06d01b5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicExchange_d59712(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_exchange_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicExchange_d59712(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicExchange_d59712(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..d282cbf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicExchange_d59712 "atomicExchange_d59712"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicExchange_d59712 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicExchange %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicExchange_d59712
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicExchange_d59712
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..e80aa80
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicExchange_d59712() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicExchange(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm
new file mode 100644
index 0000000..b541d6b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicExchange_e114ba "atomicExchange_e114ba"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicExchange_e114ba = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicExchange %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicExchange_e114ba
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..3775b8f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicExchange_e114ba() {
+  int res = 0;
+  int x_11 = atomicExchange(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicExchange_e114ba();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..b994cfe
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicExchange_e114ba() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedExchange(arg_0, 1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicExchange_e114ba();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..7126a8a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicExchange_e114ba(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_exchange_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicExchange_e114ba(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..bb829c9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicExchange_e114ba "atomicExchange_e114ba"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicExchange_e114ba = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicExchange %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicExchange_e114ba
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..4e35c0e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicExchange_e114ba() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicExchange(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicExchange_e114ba();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm
new file mode 100644
index 0000000..23dbf02
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicExchange_0a5dca "atomicExchange_0a5dca"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicExchange_0a5dca = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicExchange %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicExchange_0a5dca
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..7bfe69f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicExchange_0a5dca() {
+  uint res = 0u;
+  uint x_10 = atomicExchange(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicExchange_0a5dca();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..926f18e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicExchange_0a5dca() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedExchange(arg_0, 1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicExchange_0a5dca();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..4027c03
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicExchange_0a5dca(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_exchange_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicExchange_0a5dca(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..0aca328
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicExchange_0a5dca "atomicExchange_0a5dca"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicExchange_0a5dca = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicExchange %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicExchange_0a5dca
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..fb71c6d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicExchange/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicExchange_0a5dca() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicExchange(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicExchange_0a5dca();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm
new file mode 100644
index 0000000..d961ea5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 25
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicLoad_0806ad "atomicLoad_0806ad"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+%atomicLoad_0806ad = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicLoad %int %15 %uint_1 %uint_0
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %20 = OpLabel
+         %21 = OpFunctionCall %void %atomicLoad_0806ad
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicLoad_0806ad
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..d034197
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicLoad_0806ad() {
+  int res = 0;
+  int x_9 = atomicOr(sb_rw.arg_0, 0);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicLoad_0806ad() {
+  int res = 0;
+  int x_9 = atomicOr(sb_rw.arg_0, 0);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..8ce9cf2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicLoad(RWByteAddressBuffer buffer, uint offset) {
+  int value = 0;
+  buffer.InterlockedOr(offset, 0, value);
+  return value;
+}
+
+
+void atomicLoad_0806ad() {
+  int res = 0;
+  const int x_9 = tint_atomicLoad(sb_rw, 0u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..1ea07a1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicLoad_0806ad(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_load_explicit(&((*(tint_symbol)).arg_0), memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicLoad_0806ad(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicLoad_0806ad(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..40034c7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,65 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicLoad_0806ad "atomicLoad_0806ad"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicLoad_0806ad = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicLoad %int %18 %uint_1 %uint_0
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %20 = OpLabel
+         %21 = OpFunctionCall %void %atomicLoad_0806ad
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicLoad_0806ad
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b13dbfb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicLoad_0806ad() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicLoad(&(sb_rw.arg_0));
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm
new file mode 100644
index 0000000..776c62c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicLoad_fe6cc3 "atomicLoad_fe6cc3"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicLoad_fe6cc3 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicLoad %uint %14 %uint_1 %uint_0
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicLoad_fe6cc3
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicLoad_fe6cc3
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..cebe232
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicLoad_fe6cc3() {
+  uint res = 0u;
+  uint x_9 = atomicOr(sb_rw.arg_0, 0u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicLoad_fe6cc3() {
+  uint res = 0u;
+  uint x_9 = atomicOr(sb_rw.arg_0, 0u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..454ed3c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicLoad(RWByteAddressBuffer buffer, uint offset) {
+  uint value = 0;
+  buffer.InterlockedOr(offset, 0, value);
+  return value;
+}
+
+
+void atomicLoad_fe6cc3() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicLoad(sb_rw, 0u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..a6cce27
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicLoad_fe6cc3(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_load_explicit(&((*(tint_symbol)).arg_0), memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicLoad_fe6cc3(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicLoad_fe6cc3(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..e12b47b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicLoad_fe6cc3 "atomicLoad_fe6cc3"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicLoad_fe6cc3 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicLoad %uint %17 %uint_1 %uint_0
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicLoad_fe6cc3
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicLoad_fe6cc3
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b03da62e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicLoad_fe6cc3() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicLoad(&(sb_rw.arg_0));
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm
new file mode 100644
index 0000000..b996359
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicLoad_afcc03 "atomicLoad_afcc03"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function_int = OpTypePointer Function %int
+         %17 = OpConstantNull %int
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicLoad_afcc03 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %17
+         %11 = OpAtomicLoad %int %arg_0 %uint_2 %uint_0
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicLoad_afcc03
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..e0c158b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicLoad_afcc03() {
+  int res = 0;
+  int x_11 = atomicOr(arg_0, 0);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicLoad_afcc03();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..1593c13
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicLoad_afcc03() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedOr(arg_0, 0, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicLoad_afcc03();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..8f6ee40
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicLoad_afcc03(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_load_explicit(tint_symbol, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicLoad_afcc03(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..829b848
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,75 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 45
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicLoad_afcc03 "atomicLoad_afcc03"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicLoad_afcc03 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicLoad %int %arg_0 %uint_2 %uint_0
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicLoad_afcc03
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %31 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %33 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %21
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %36 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %40 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %42 = OpLabel
+         %44 = OpLoad %uint %local_invocation_index_1_param_1
+         %43 = OpFunctionCall %void %compute_main_inner_1 %44
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..af0c057
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicLoad_afcc03() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicLoad(&(arg_0));
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicLoad_afcc03();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm
new file mode 100644
index 0000000..20eeca7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicLoad_361bf1 "atomicLoad_361bf1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %16 = OpConstantNull %uint
+         %17 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicLoad_361bf1 = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %16
+         %10 = OpAtomicLoad %uint %arg_0 %uint_2 %uint_0
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %17
+%local_invocation_index = OpFunctionParameter %uint
+         %20 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %16
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %25 = OpFunctionCall %void %atomicLoad_361bf1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %27 = OpLabel
+         %29 = OpLoad %uint %local_invocation_index_1
+         %28 = OpFunctionCall %void %compute_main_inner %29
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..8bd40a6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicLoad_361bf1() {
+  uint res = 0u;
+  uint x_10 = atomicOr(arg_0, 0u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicLoad_361bf1();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..803eadd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicLoad_361bf1() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedOr(arg_0, 0, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicLoad_361bf1();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..d7fcb6e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicLoad_361bf1(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_load_explicit(tint_symbol, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicLoad_361bf1(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_29 = *(tint_symbol_2);
+  compute_main_inner(x_29, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..7a075ba
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,73 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 43
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicLoad_361bf1 "atomicLoad_361bf1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicLoad_361bf1 = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicLoad %uint %arg_0 %uint_2 %uint_0
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicLoad_361bf1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %29 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %19
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %34 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %38 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %40 = OpLabel
+         %42 = OpLoad %uint %local_invocation_index_1_param_1
+         %41 = OpFunctionCall %void %compute_main_inner_1 %42
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..f132c6f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicLoad/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicLoad_361bf1() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicLoad(&(arg_0));
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicLoad_361bf1();
+  return;
+}
+
+fn compute_main_1() {
+  let x_29 : u32 = local_invocation_index_1;
+  compute_main_inner(x_29);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm
new file mode 100644
index 0000000..82be997
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMax_92aa72 "atomicMax_92aa72"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicMax_92aa72 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicSMax %int %15 %uint_1 %uint_0 %int_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicMax_92aa72
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicMax_92aa72
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..6341848
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicMax_92aa72() {
+  int res = 0;
+  int x_9 = atomicMax(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicMax_92aa72() {
+  int res = 0;
+  int x_9 = atomicMax(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..4fd3948
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicMax(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedMax(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicMax_92aa72() {
+  int res = 0;
+  const int x_9 = tint_atomicMax(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..39a434c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicMax_92aa72(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_max_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicMax_92aa72(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicMax_92aa72(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..34dd4c3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMax_92aa72 "atomicMax_92aa72"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicMax_92aa72 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicSMax %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicMax_92aa72
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicMax_92aa72
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..ae52315
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicMax_92aa72() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicMax(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm
new file mode 100644
index 0000000..7fb135e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMax_51b9be "atomicMax_51b9be"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicMax_51b9be = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicUMax %uint %14 %uint_1 %uint_0 %uint_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicMax_51b9be
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicMax_51b9be
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..08dd962
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicMax_51b9be() {
+  uint res = 0u;
+  uint x_9 = atomicMax(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicMax_51b9be() {
+  uint res = 0u;
+  uint x_9 = atomicMax(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..9934904
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicMax(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedMax(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicMax_51b9be() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicMax(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..bc40d81
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicMax_51b9be(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_max_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicMax_51b9be(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicMax_51b9be(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..be00498
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMax_51b9be "atomicMax_51b9be"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicMax_51b9be = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicUMax %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicMax_51b9be
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicMax_51b9be
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..9b20062
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicMax_51b9be() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicMax(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm
new file mode 100644
index 0000000..62355e0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMax_a89cc3 "atomicMax_a89cc3"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMax_a89cc3 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicSMax %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicMax_a89cc3
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..44e03cf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicMax_a89cc3() {
+  int res = 0;
+  int x_11 = atomicMax(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicMax_a89cc3();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..bdcfb2a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicMax_a89cc3() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedMax(arg_0, 1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicMax_a89cc3();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..9421382
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicMax_a89cc3(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_max_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicMax_a89cc3(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..0360ae6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMax_a89cc3 "atomicMax_a89cc3"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMax_a89cc3 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicSMax %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicMax_a89cc3
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..0c3e2a8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicMax_a89cc3() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicMax(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicMax_a89cc3();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm
new file mode 100644
index 0000000..2636c66
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMax_beccfc "atomicMax_beccfc"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMax_beccfc = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicUMax %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicMax_beccfc
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..dc7354c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicMax_beccfc() {
+  uint res = 0u;
+  uint x_10 = atomicMax(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicMax_beccfc();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..9c405cb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicMax_beccfc() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedMax(arg_0, 1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicMax_beccfc();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..200a901
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicMax_beccfc(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_max_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicMax_beccfc(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..7f01c87
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMax_beccfc "atomicMax_beccfc"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMax_beccfc = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicUMax %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicMax_beccfc
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..39ee5df
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMax/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicMax_beccfc() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicMax(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicMax_beccfc();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm
new file mode 100644
index 0000000..660547b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMin_8e38dc "atomicMin_8e38dc"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicMin_8e38dc = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicSMin %int %15 %uint_1 %uint_0 %int_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicMin_8e38dc
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicMin_8e38dc
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..021d201
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicMin_8e38dc() {
+  int res = 0;
+  int x_9 = atomicMin(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicMin_8e38dc() {
+  int res = 0;
+  int x_9 = atomicMin(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..efe6471
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicMin(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedMin(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicMin_8e38dc() {
+  int res = 0;
+  const int x_9 = tint_atomicMin(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..efe7671
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicMin_8e38dc(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_min_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicMin_8e38dc(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicMin_8e38dc(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..42e5312
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMin_8e38dc "atomicMin_8e38dc"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicMin_8e38dc = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicSMin %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicMin_8e38dc
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicMin_8e38dc
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..667b13a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicMin_8e38dc() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicMin(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm
new file mode 100644
index 0000000..0ed9315
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMin_c67a74 "atomicMin_c67a74"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicMin_c67a74 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicUMin %uint %14 %uint_1 %uint_0 %uint_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicMin_c67a74
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicMin_c67a74
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..66cb351
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicMin_c67a74() {
+  uint res = 0u;
+  uint x_9 = atomicMin(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicMin_c67a74() {
+  uint res = 0u;
+  uint x_9 = atomicMin(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..1bb4ca0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicMin(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedMin(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicMin_c67a74() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicMin(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..e03cf33
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicMin_c67a74(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_min_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicMin_c67a74(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicMin_c67a74(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..c1fc0dc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMin_c67a74 "atomicMin_c67a74"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicMin_c67a74 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicUMin %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicMin_c67a74
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicMin_c67a74
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..62f9e6a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicMin_c67a74() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicMin(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm
new file mode 100644
index 0000000..dfe5a35
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMin_278235 "atomicMin_278235"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMin_278235 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicSMin %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicMin_278235
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..f8151e3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicMin_278235() {
+  int res = 0;
+  int x_11 = atomicMin(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicMin_278235();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..72197d7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicMin_278235() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedMin(arg_0, 1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicMin_278235();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..3c4bd98
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicMin_278235(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_min_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicMin_278235(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..cea2249
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMin_278235 "atomicMin_278235"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMin_278235 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicSMin %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicMin_278235
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..5ddda3e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicMin_278235() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicMin(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicMin_278235();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm
new file mode 100644
index 0000000..de92bee
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMin_69d383 "atomicMin_69d383"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMin_69d383 = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicUMin %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicMin_69d383
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..466b141
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicMin_69d383() {
+  uint res = 0u;
+  uint x_10 = atomicMin(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicMin_69d383();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..f91f02a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicMin_69d383() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedMin(arg_0, 1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicMin_69d383();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..226629f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicMin_69d383(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_min_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicMin_69d383(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..475d1ce
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMin_69d383 "atomicMin_69d383"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMin_69d383 = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicUMin %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicMin_69d383
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..8f37c6b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicMin/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicMin_69d383() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicMin(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicMin_69d383();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm
new file mode 100644
index 0000000..1bf00f2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicOr_8d96a0 "atomicOr_8d96a0"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicOr_8d96a0 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicOr %int %15 %uint_1 %uint_0 %int_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicOr_8d96a0
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicOr_8d96a0
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..b9557e3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicOr_8d96a0() {
+  int res = 0;
+  int x_9 = atomicOr(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicOr_8d96a0() {
+  int res = 0;
+  int x_9 = atomicOr(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..f9e84d9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicOr(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedOr(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicOr_8d96a0() {
+  int res = 0;
+  const int x_9 = tint_atomicOr(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..2a66f9f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicOr_8d96a0(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_or_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicOr_8d96a0(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicOr_8d96a0(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..a971056
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicOr_8d96a0 "atomicOr_8d96a0"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicOr_8d96a0 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicOr %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicOr_8d96a0
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicOr_8d96a0
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..bd4a06b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicOr_8d96a0() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicOr(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm
new file mode 100644
index 0000000..4778bc8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicOr_5e95d4 "atomicOr_5e95d4"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicOr_5e95d4 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicOr %uint %14 %uint_1 %uint_0 %uint_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicOr_5e95d4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicOr_5e95d4
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..5ad5f61
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicOr_5e95d4() {
+  uint res = 0u;
+  uint x_9 = atomicOr(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicOr_5e95d4() {
+  uint res = 0u;
+  uint x_9 = atomicOr(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..b0ac7e6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicOr(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedOr(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicOr_5e95d4() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicOr(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..e47c2db
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicOr_5e95d4(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_or_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicOr_5e95d4(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicOr_5e95d4(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..a46ca1d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicOr_5e95d4 "atomicOr_5e95d4"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicOr_5e95d4 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicOr %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicOr_5e95d4
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicOr_5e95d4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..4f5bf4c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicOr_5e95d4() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicOr(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm
new file mode 100644
index 0000000..cd14aa9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicOr_d09248 "atomicOr_d09248"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicOr_d09248 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicOr %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicOr_d09248
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..4fdf338
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicOr_d09248() {
+  int res = 0;
+  int x_11 = atomicOr(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicOr_d09248();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..9f15c6c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicOr_d09248() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedOr(arg_0, 1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicOr_d09248();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..74c1a58
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicOr_d09248(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_or_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicOr_d09248(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..9ef5893
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicOr_d09248 "atomicOr_d09248"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicOr_d09248 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicOr %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicOr_d09248
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..5563b2b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicOr_d09248() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicOr(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicOr_d09248();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm
new file mode 100644
index 0000000..1b9dc27
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicOr_5e3d61 "atomicOr_5e3d61"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicOr_5e3d61 = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicOr %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicOr_5e3d61
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..e9191a7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicOr_5e3d61() {
+  uint res = 0u;
+  uint x_10 = atomicOr(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicOr_5e3d61();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..22393c1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicOr_5e3d61() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedOr(arg_0, 1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicOr_5e3d61();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..2513fa7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicOr_5e3d61(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_or_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicOr_5e3d61(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..ad9af50
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicOr_5e3d61 "atomicOr_5e3d61"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicOr_5e3d61 = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicOr %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicOr_5e3d61
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..2e9093e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicOr/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicOr_5e3d61() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicOr(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicOr_5e3d61();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm
new file mode 100644
index 0000000..a471433
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm
@@ -0,0 +1,48 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 23
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicStore_d1e9a6 "atomicStore_d1e9a6"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicStore_d1e9a6 = OpFunction %void None %5
+          %8 = OpLabel
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+               OpAtomicStore %15 %uint_1 %uint_0 %int_1
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %18 = OpLabel
+         %19 = OpFunctionCall %void %atomicStore_d1e9a6
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicStore_d1e9a6
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..24fc51e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,64 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicStore_d1e9a6() {
+  atomicExchange(sb_rw.arg_0, 1);
+  return;
+}
+
+void fragment_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicStore_d1e9a6() {
+  atomicExchange(sb_rw.arg_0, 1);
+  return;
+}
+
+void compute_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..5ef62c9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,33 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+void tint_atomicStore(RWByteAddressBuffer buffer, uint offset, int value) {
+  int ignored;
+  buffer.InterlockedExchange(offset, value, ignored);
+}
+
+
+void atomicStore_d1e9a6() {
+  tint_atomicStore(sb_rw, 0u, 1);
+  return;
+}
+
+void fragment_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..03e234a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.msl
@@ -0,0 +1,36 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicStore_d1e9a6(device SB_RW_atomic* const tint_symbol) {
+  atomic_store_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicStore_d1e9a6(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicStore_d1e9a6(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..cabaea3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,60 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 29
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicStore_d1e9a6 "atomicStore_d1e9a6"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicStore_d1e9a6 = OpFunction %void None %5
+          %8 = OpLabel
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+               OpAtomicStore %15 %uint_1 %uint_0 %int_1
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %18 = OpLabel
+         %19 = OpFunctionCall %void %atomicStore_d1e9a6
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicStore_d1e9a6
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..eaf478b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,34 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicStore_d1e9a6() {
+  atomicStore(&(sb_rw.arg_0), 1i);
+  return;
+}
+
+fn fragment_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm
new file mode 100644
index 0000000..97adea0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm
@@ -0,0 +1,46 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 21
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicStore_cdc29e "atomicStore_cdc29e"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicStore_cdc29e = OpFunction %void None %5
+          %8 = OpLabel
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+               OpAtomicStore %14 %uint_1 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %16 = OpLabel
+         %17 = OpFunctionCall %void %atomicStore_cdc29e
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicStore_cdc29e
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..e9c81a2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,64 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicStore_cdc29e() {
+  atomicExchange(sb_rw.arg_0, 1u);
+  return;
+}
+
+void fragment_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicStore_cdc29e() {
+  atomicExchange(sb_rw.arg_0, 1u);
+  return;
+}
+
+void compute_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..259c472
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,33 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+void tint_atomicStore(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint ignored;
+  buffer.InterlockedExchange(offset, value, ignored);
+}
+
+
+void atomicStore_cdc29e() {
+  tint_atomicStore(sb_rw, 0u, 1u);
+  return;
+}
+
+void fragment_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..cc29ffd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.msl
@@ -0,0 +1,36 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicStore_cdc29e(device SB_RW_atomic* const tint_symbol) {
+  atomic_store_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicStore_cdc29e(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicStore_cdc29e(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..bcda0e7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,58 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 27
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicStore_cdc29e "atomicStore_cdc29e"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicStore_cdc29e = OpFunction %void None %5
+          %8 = OpLabel
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+               OpAtomicStore %14 %uint_1 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %16 = OpLabel
+         %17 = OpFunctionCall %void %atomicStore_cdc29e
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicStore_cdc29e
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..93b7fff
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,34 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicStore_cdc29e() {
+  atomicStore(&(sb_rw.arg_0), 1u);
+  return;
+}
+
+fn fragment_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm
new file mode 100644
index 0000000..a08c2e8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm
@@ -0,0 +1,49 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicStore_8bea94 "atomicStore_8bea94"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %16 = OpTypeFunction %void %uint
+         %22 = OpConstantNull %int
+   %uint_264 = OpConstant %uint 264
+%atomicStore_8bea94 = OpFunction %void None %7
+         %10 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %int_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %16
+%local_invocation_index = OpFunctionParameter %uint
+         %19 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %22
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %25 = OpFunctionCall %void %atomicStore_8bea94
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %27 = OpLabel
+         %29 = OpLoad %uint %local_invocation_index_1
+         %28 = OpFunctionCall %void %compute_main_inner %29
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..cc9e431
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,35 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicStore_8bea94() {
+  atomicExchange(arg_0, 1);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicStore_8bea94();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..c2dbd19
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,41 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicStore_8bea94() {
+  int atomic_result = 0;
+  InterlockedExchange(arg_0, 1, atomic_result);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicStore_8bea94();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..78e73c5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,37 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicStore_8bea94(threadgroup atomic_int* const tint_symbol) {
+  atomic_store_explicit(tint_symbol, 1, memory_order_relaxed);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicStore_8bea94(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_29 = *(tint_symbol_2);
+  compute_main_inner(x_29, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..319461d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicStore_8bea94 "atomicStore_8bea94"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %19 = OpTypeFunction %void %uint
+         %25 = OpConstantNull %int
+   %uint_264 = OpConstant %uint 264
+%atomicStore_8bea94 = OpFunction %void None %10
+         %13 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %int_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %25
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicStore_8bea94
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %19
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %25
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..800c8fd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,27 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicStore_8bea94() {
+  atomicStore(&(arg_0), 1i);
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicStore_8bea94();
+  return;
+}
+
+fn compute_main_1() {
+  let x_29 : u32 = local_invocation_index_1;
+  compute_main_inner(x_29);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm
new file mode 100644
index 0000000..59738ef
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm
@@ -0,0 +1,48 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 29
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicStore_726882 "atomicStore_726882"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %15 = OpTypeFunction %void %uint
+         %21 = OpConstantNull %uint
+   %uint_264 = OpConstant %uint 264
+%atomicStore_726882 = OpFunction %void None %6
+          %9 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %15
+%local_invocation_index = OpFunctionParameter %uint
+         %18 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %21
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %24 = OpFunctionCall %void %atomicStore_726882
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %26 = OpLabel
+         %28 = OpLoad %uint %local_invocation_index_1
+         %27 = OpFunctionCall %void %compute_main_inner %28
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..678f189
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,35 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicStore_726882() {
+  atomicExchange(arg_0, 1u);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicStore_726882();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..309c7df
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,41 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicStore_726882() {
+  uint atomic_result = 0u;
+  InterlockedExchange(arg_0, 1u, atomic_result);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicStore_726882();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..1a458eb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,37 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicStore_726882(threadgroup atomic_uint* const tint_symbol) {
+  atomic_store_explicit(tint_symbol, 1u, memory_order_relaxed);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicStore_726882(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_28 = *(tint_symbol_2);
+  compute_main_inner(x_28, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..6f8e362
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 42
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicStore_726882 "atomicStore_726882"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicStore_726882 = OpFunction %void None %9
+         %12 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %uint_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicStore_726882
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %28 = OpLabel
+         %29 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %29
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %18
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %33 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %37 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %39 = OpLabel
+         %41 = OpLoad %uint %local_invocation_index_1_param_1
+         %40 = OpFunctionCall %void %compute_main_inner_1 %41
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b90d965
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicStore/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,27 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicStore_726882() {
+  atomicStore(&(arg_0), 1u);
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicStore_726882();
+  return;
+}
+
+fn compute_main_1() {
+  let x_28 : u32 = local_invocation_index_1;
+  compute_main_inner(x_28);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm
new file mode 100644
index 0000000..f4b9264
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicSub_051100 "atomicSub_051100"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicSub_051100 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicISub %int %15 %uint_1 %uint_0 %int_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicSub_051100
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicSub_051100
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..8f573df
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicSub_051100() {
+  int res = 0;
+  int x_9 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicSub_051100() {
+  int res = 0;
+  int x_9 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..3624845
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicSub(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAdd(offset, -value, original_value);
+  return original_value;
+}
+
+
+void atomicSub_051100() {
+  int res = 0;
+  const int x_9 = tint_atomicSub(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..4696114
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicSub_051100(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_sub_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicSub_051100(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicSub_051100(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..9ada74b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicSub_051100 "atomicSub_051100"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicSub_051100 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicISub %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicSub_051100
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicSub_051100
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..3fde6a5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicSub_051100() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicSub(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm
new file mode 100644
index 0000000..b7cd92d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicSub_15bfc9 "atomicSub_15bfc9"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicSub_15bfc9 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicISub %uint %14 %uint_1 %uint_0 %uint_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicSub_15bfc9
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicSub_15bfc9
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..e462c21
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicSub_15bfc9() {
+  uint res = 0u;
+  uint x_9 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicSub_15bfc9() {
+  uint res = 0u;
+  uint x_9 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..1bcbe09
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicSub(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAdd(offset, -value, original_value);
+  return original_value;
+}
+
+
+void atomicSub_15bfc9() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicSub(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..4cd9ce2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicSub_15bfc9(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_sub_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicSub_15bfc9(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicSub_15bfc9(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..885f703
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicSub_15bfc9 "atomicSub_15bfc9"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicSub_15bfc9 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicISub %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicSub_15bfc9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicSub_15bfc9
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..23ba9fe
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicSub_15bfc9() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicSub(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm
new file mode 100644
index 0000000..f0471be
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicSub_77883a "atomicSub_77883a"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicSub_77883a = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicISub %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicSub_77883a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..0583afc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicSub_77883a() {
+  int res = 0;
+  int x_11 = atomicAdd(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicSub_77883a();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..75a1b4a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicSub_77883a() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedAdd(arg_0, -1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicSub_77883a();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..86f5de2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicSub_77883a(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_sub_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicSub_77883a(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..820d0ae
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicSub_77883a "atomicSub_77883a"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicSub_77883a = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicISub %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicSub_77883a
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..56079fe
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicSub_77883a() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicSub(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicSub_77883a();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm
new file mode 100644
index 0000000..0412631
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicSub_0d26c2 "atomicSub_0d26c2"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicSub_0d26c2 = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicISub %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicSub_0d26c2
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..49553d0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicSub_0d26c2() {
+  uint res = 0u;
+  uint x_10 = atomicAdd(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicSub_0d26c2();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..e6f8372
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicSub_0d26c2() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedAdd(arg_0, -1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicSub_0d26c2();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..91e3b4b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicSub_0d26c2(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_sub_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicSub_0d26c2(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..c42f0b9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicSub_0d26c2 "atomicSub_0d26c2"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicSub_0d26c2 = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicISub %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicSub_0d26c2
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..2b69af6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicSub/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicSub_0d26c2() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicSub(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicSub_0d26c2();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm
new file mode 100644
index 0000000..98ac9c2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicXor_c1b78c "atomicXor_c1b78c"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicXor_c1b78c = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicXor %int %15 %uint_1 %uint_0 %int_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicXor_c1b78c
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicXor_c1b78c
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..cc05c28
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicXor_c1b78c() {
+  int res = 0;
+  int x_9 = atomicXor(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicXor_c1b78c() {
+  int res = 0;
+  int x_9 = atomicXor(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..605a4d7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicXor(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedXor(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicXor_c1b78c() {
+  int res = 0;
+  const int x_9 = tint_atomicXor(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..fbde760
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicXor_c1b78c(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_xor_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicXor_c1b78c(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicXor_c1b78c(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..1302c1a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicXor_c1b78c "atomicXor_c1b78c"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicXor_c1b78c = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicXor %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicXor_c1b78c
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicXor_c1b78c
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..7992f58
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicXor_c1b78c() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicXor(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm
new file mode 100644
index 0000000..8b4ef21
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicXor_54510e "atomicXor_54510e"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicXor_54510e = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicXor %uint %14 %uint_1 %uint_0 %uint_1
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicXor_54510e
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicXor_54510e
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..2893791
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicXor_54510e() {
+  uint res = 0u;
+  uint x_9 = atomicXor(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicXor_54510e() {
+  uint res = 0u;
+  uint x_9 = atomicXor(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..3c2aad7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicXor(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedXor(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicXor_54510e() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicXor(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..dd37adc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicXor_54510e(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_xor_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicXor_54510e(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicXor_54510e(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..1fb9bcc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicXor_54510e "atomicXor_54510e"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicXor_54510e = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicXor %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicXor_54510e
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicXor_54510e
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..7fbc313
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicXor_54510e() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicXor(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm
new file mode 100644
index 0000000..4e729e2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicXor_75dc95 "atomicXor_75dc95"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicXor_75dc95 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicXor %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicXor_75dc95
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..b260d4d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicXor_75dc95() {
+  int res = 0;
+  int x_11 = atomicXor(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicXor_75dc95();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..5ffbd31
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicXor_75dc95() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedXor(arg_0, 1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicXor_75dc95();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..b5c2e2c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicXor_75dc95(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_xor_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicXor_75dc95(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..542376d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicXor_75dc95 "atomicXor_75dc95"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicXor_75dc95 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicXor %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicXor_75dc95
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..f14ac31
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicXor_75dc95() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicXor(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicXor_75dc95();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm
new file mode 100644
index 0000000..a683557
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicXor_c8e6be "atomicXor_c8e6be"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicXor_c8e6be = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicXor %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicXor_c8e6be
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..a15719a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicXor_c8e6be() {
+  uint res = 0u;
+  uint x_10 = atomicXor(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicXor_c8e6be();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..8296d86
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicXor_c8e6be() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedXor(arg_0, 1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicXor_c8e6be();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..6993e95
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicXor_c8e6be(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_xor_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicXor_c8e6be(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..4f19634
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicXor_c8e6be "atomicXor_c8e6be"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicXor_c8e6be = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicXor %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicXor_c8e6be
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..53f7436
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/atomicXor/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicXor_c8e6be() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicXor(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicXor_c8e6be();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm
new file mode 100644
index 0000000..fe8b450
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicIDecrement %int %15 %uint_1 %uint_0
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..875dc08
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int res = 0;
+  int x_9 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int res = 0;
+  int x_9 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..ab2b1ee
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicSub(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAdd(offset, -value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_d32fe4() {
+  int res = 0;
+  const int x_9 = tint_atomicSub(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..8a042c1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicAdd_d32fe4(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_sub_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_d32fe4(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_d32fe4(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..97fa9d7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicISub %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..e048762
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_d32fe4() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicSub(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm
new file mode 100644
index 0000000..5197526
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicIDecrement %uint %14 %uint_1 %uint_0
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..48a0735
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  uint x_9 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  uint x_9 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..6f0bb19
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicSub(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAdd(offset, -value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicSub(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..adfd3c7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicAdd_8a199a(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_sub_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_8a199a(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_8a199a(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..4ea9dce
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicISub %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..d22b2aaa
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_8a199a() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicSub(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm
new file mode 100644
index 0000000..eb2f859
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicIDecrement %int %arg_0 %uint_2 %uint_0
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..b745861
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicAdd_794055() {
+  int res = 0;
+  int x_11 = atomicAdd(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..09f225c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicAdd_794055() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedAdd(arg_0, -1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..e7d19e9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_794055(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_sub_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_794055(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..32bc60e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicISub %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..6050ff2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicAdd_794055() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicSub(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicAdd_794055();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm
new file mode 100644
index 0000000..8c3ebfc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicIDecrement %uint %arg_0 %uint_2 %uint_0
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..97c0c6f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicAdd_d5db1d() {
+  uint res = 0u;
+  uint x_10 = atomicAdd(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..33bfa08
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicAdd_d5db1d() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedAdd(arg_0, -1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..bd63c56
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_d5db1d(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_sub_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_d5db1d(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..c50dbae
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicISub %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..f7f54474
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicDecrement/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicAdd_d5db1d() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicSub(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm
new file mode 100644
index 0000000..efd1d3a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %19 = OpConstantNull %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %19
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicIIncrement %int %15 %uint_1 %uint_0
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..875dc08
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int res = 0;
+  int x_9 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int res = 0;
+  int x_9 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..358487b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicAdd(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAdd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_d32fe4() {
+  int res = 0;
+  const int x_9 = tint_atomicAdd(sb_rw, 0u, 1);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..64027ff
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicAdd_d32fe4(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_fetch_add_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_d32fe4(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_d32fe4(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..13f287a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,66 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+      %int_1 = OpConstant %int 1
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicIAdd %int %18 %uint_1 %uint_0 %int_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..cfc64fb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_d32fe4() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicAdd(&(sb_rw.arg_0), 1i);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm
new file mode 100644
index 0000000..a805c50
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicIIncrement %uint %14 %uint_1 %uint_0
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..48a0735
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  uint x_9 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  uint x_9 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..bdc43b0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicAdd(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAdd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_8a199a() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicAdd(sb_rw, 0u, 1u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..fa2343b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicAdd_8a199a(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_fetch_add_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_8a199a(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_8a199a(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..e1bf1e9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicIAdd %uint %17 %uint_1 %uint_0 %uint_1
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b5d5856
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_8a199a() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicAdd(&(sb_rw.arg_0), 1u);
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm
new file mode 100644
index 0000000..255bfe1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %11 = OpAtomicIIncrement %int %arg_0 %uint_2 %uint_0
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..b745861
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicAdd_794055() {
+  int res = 0;
+  int x_11 = atomicAdd(arg_0, 1);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..ccdf1ad
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicAdd_794055() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedAdd(arg_0, 1, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..62639d7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_794055(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_fetch_add_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_794055(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..3dc0a5d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,76 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+      %int_1 = OpConstant %int 1
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicIAdd %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..723a113
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicAdd_794055() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicAdd(&(arg_0), 1i);
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicAdd_794055();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm
new file mode 100644
index 0000000..c26936d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %10 = OpAtomicIIncrement %uint %arg_0 %uint_2 %uint_0
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..97c0c6f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicAdd_d5db1d() {
+  uint res = 0u;
+  uint x_10 = atomicAdd(arg_0, 1u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..c36ae57
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicAdd_d5db1d() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedAdd(arg_0, 1u, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..04a890a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_d5db1d(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_fetch_add_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_d5db1d(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..5daee64
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,74 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 44
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+     %uint_1 = OpConstant %uint 1
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicIAdd %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %30 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %20
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %35 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %39 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %41 = OpLabel
+         %43 = OpLoad %uint %local_invocation_index_1_param_1
+         %42 = OpFunctionCall %void %compute_main_inner_1 %43
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..61d34cc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/literal/spvAtomicIncrement/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicAdd_d5db1d() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicAdd(&(arg_0), 1u);
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm
new file mode 100644
index 0000000..2fd0e19
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+         %13 = OpAtomicIAdd %int %19 %uint_1 %uint_0 %20
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..bd9f7f8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAdd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAdd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..1b338c9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicAdd(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAdd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicAdd(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..3106aff
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicAdd_d32fe4(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  int const x_13 = atomic_fetch_add_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_d32fe4(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_d32fe4(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..c38ab55
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %14 = OpLoad %int %arg_1
+         %21 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %15 = OpAtomicIAdd %int %21 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..ea83a05
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_d32fe4() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  let x_13 : i32 = atomicAdd(&(sb_rw.arg_0), x_20);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm
new file mode 100644
index 0000000..98d9690
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+         %13 = OpAtomicIAdd %uint %17 %uint_1 %uint_0 %18
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..5c1a1b0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAdd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAdd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..845d1f7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicAdd(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAdd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicAdd(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..da05cfb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicAdd_8a199a(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_13 = atomic_fetch_add_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_8a199a(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_8a199a(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..d015d49
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %14 = OpLoad %uint %arg_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %15 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..887a798
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_8a199a() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_13 : u32 = atomicAdd(&(sb_rw.arg_0), x_18);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm
new file mode 100644
index 0000000..9a311f1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %15 = OpAtomicIAdd %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..89a4577
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicAdd_794055() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicAdd(arg_0, arg_1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..0cfd696
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicAdd_794055() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedAdd(arg_0, arg_1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..b30df09
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_794055(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  int const x_15 = atomic_fetch_add_explicit(tint_symbol, x_19, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_794055(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..3d67dcf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,81 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %24 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %20 = OpAtomicIAdd %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %20
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %24
+%local_invocation_index = OpFunctionParameter %uint
+         %27 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %34 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %36 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %24
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %43 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1_param_1
+         %46 = OpFunctionCall %void %compute_main_inner_1 %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..27e5276
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicAdd_794055() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  let x_15 : i32 = atomicAdd(&(arg_0), x_19);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicAdd_794055();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm
new file mode 100644
index 0000000..7ab1e8d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+         %14 = OpAtomicIAdd %uint %arg_0 %uint_2 %uint_0 %18
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..07177c9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicAdd_d5db1d() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicAdd(arg_0, arg_1);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..380a1a5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicAdd_d5db1d() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedAdd(arg_0, arg_1, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..29c6f4b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_d5db1d(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_14 = atomic_fetch_add_explicit(tint_symbol, x_18, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_d5db1d(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..1cab36f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,79 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpLoad %uint %arg_1
+         %18 = OpAtomicIAdd %uint %arg_0 %uint_2 %uint_0 %17
+               OpStore %res %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..1ef7a33
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAdd/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicAdd_d5db1d() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_14 : u32 = atomicAdd(&(arg_0), x_18);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm
new file mode 100644
index 0000000..18408ba
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAnd_152966 "atomicAnd_152966"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicAnd_152966 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+         %13 = OpAtomicAnd %int %19 %uint_1 %uint_0 %20
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicAnd_152966
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicAnd_152966
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..ea9ff84
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAnd_152966() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAnd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAnd_152966() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAnd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..ea4a0f8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicAnd(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAnd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAnd_152966() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicAnd(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..a9960d67
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicAnd_152966(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  int const x_13 = atomic_fetch_and_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAnd_152966(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAnd_152966(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..3257d23
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAnd_152966 "atomicAnd_152966"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicAnd_152966 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %14 = OpLoad %int %arg_1
+         %21 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %15 = OpAtomicAnd %int %21 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicAnd_152966
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicAnd_152966
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..bdc601d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAnd_152966() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  let x_13 : i32 = atomicAnd(&(sb_rw.arg_0), x_20);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAnd_152966();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm
new file mode 100644
index 0000000..d8c4b78
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAnd_85a8d9 "atomicAnd_85a8d9"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAnd_85a8d9 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+         %13 = OpAtomicAnd %uint %17 %uint_1 %uint_0 %18
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAnd_85a8d9
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicAnd_85a8d9
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..e3abb2a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAnd_85a8d9() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAnd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAnd_85a8d9() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAnd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..44c8b7d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicAnd(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAnd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAnd_85a8d9() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicAnd(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..798abe6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicAnd_85a8d9(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_13 = atomic_fetch_and_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAnd_85a8d9(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAnd_85a8d9(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..668f193
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAnd_85a8d9 "atomicAnd_85a8d9"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAnd_85a8d9 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %14 = OpLoad %uint %arg_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %15 = OpAtomicAnd %uint %19 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAnd_85a8d9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicAnd_85a8d9
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..4bf4b48
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAnd_85a8d9() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_13 : u32 = atomicAnd(&(sb_rw.arg_0), x_18);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAnd_85a8d9();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm
new file mode 100644
index 0000000..e0c9a7e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAnd_45a819 "atomicAnd_45a819"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAnd_45a819 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %15 = OpAtomicAnd %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicAnd_45a819
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..cc74ce6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicAnd_45a819() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicAnd(arg_0, arg_1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicAnd_45a819();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..cd9a7b7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicAnd_45a819() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedAnd(arg_0, arg_1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAnd_45a819();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..962a96c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAnd_45a819(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  int const x_15 = atomic_fetch_and_explicit(tint_symbol, x_19, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAnd_45a819(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..598acf2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,81 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAnd_45a819 "atomicAnd_45a819"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %24 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAnd_45a819 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %20 = OpAtomicAnd %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %20
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %24
+%local_invocation_index = OpFunctionParameter %uint
+         %27 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicAnd_45a819
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %34 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %36 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %24
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %43 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1_param_1
+         %46 = OpFunctionCall %void %compute_main_inner_1 %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..d757c09
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicAnd_45a819() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  let x_15 : i32 = atomicAnd(&(arg_0), x_19);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicAnd_45a819();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm
new file mode 100644
index 0000000..e6e94c1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAnd_34edd3 "atomicAnd_34edd3"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAnd_34edd3 = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+         %14 = OpAtomicAnd %uint %arg_0 %uint_2 %uint_0 %18
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicAnd_34edd3
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..39b8c3e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicAnd_34edd3() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicAnd(arg_0, arg_1);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicAnd_34edd3();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..37f4fee
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicAnd_34edd3() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedAnd(arg_0, arg_1, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAnd_34edd3();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..d162405
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAnd_34edd3(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_14 = atomic_fetch_and_explicit(tint_symbol, x_18, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAnd_34edd3(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..0eeed44
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,79 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAnd_34edd3 "atomicAnd_34edd3"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAnd_34edd3 = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpLoad %uint %arg_1
+         %18 = OpAtomicAnd %uint %arg_0 %uint_2 %uint_0 %17
+               OpStore %res %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicAnd_34edd3
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..dba5360
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicAnd/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicAnd_34edd3() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_14 : u32 = atomicAnd(&(arg_0), x_18);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicAnd_34edd3();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm
new file mode 100644
index 0000000..0999969
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm
@@ -0,0 +1,72 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 36
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicCompareExchangeWeak_1bd40a "atomicCompareExchangeWeak_1bd40a"
+               OpName %arg_1 "arg_1"
+               OpName %arg_2 "arg_2"
+               OpName %__atomic_compare_exchange_resulti32 "__atomic_compare_exchange_resulti32"
+               OpMemberName %__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 1 Offset 4
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %bool = OpTypeBool
+%__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%_ptr_Function___atomic_compare_exchange_resulti32 = OpTypePointer Function %__atomic_compare_exchange_resulti32
+         %29 = OpConstantNull %__atomic_compare_exchange_resulti32
+%atomicCompareExchangeWeak_1bd40a = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+      %arg_2 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function___atomic_compare_exchange_resulti32 Function %29
+               OpStore %arg_1 %int_1
+               OpStore %arg_2 %int_1
+         %22 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %23 = OpLoad %int %arg_2
+         %24 = OpLoad %int %arg_1
+         %25 = OpAtomicCompareExchange %int %22 %uint_1 %uint_0 %uint_0 %23 %24
+         %26 = OpIEqual %bool %25 %23
+         %14 = OpCompositeConstruct %__atomic_compare_exchange_resulti32 %25 %26
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %31 = OpLabel
+         %32 = OpFunctionCall %void %atomicCompareExchangeWeak_1bd40a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %34 = OpLabel
+         %35 = OpFunctionCall %void %atomicCompareExchangeWeak_1bd40a
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..286017b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,112 @@
+#version 310 es
+precision mediump float;
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicCompareExchangeWeak_1bd40a() {
+  int arg_1 = 0;
+  int arg_2 = 0;
+  x__atomic_compare_exchange_resulti32 res = x__atomic_compare_exchange_resulti32(0, false);
+  arg_1 = 1;
+  arg_2 = 1;
+  int x_23 = arg_2;
+  atomic_compare_exchange_resulti32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(sb_rw.arg_0, arg_1, x_23);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == arg_1;
+  atomic_compare_exchange_resulti32 tint_symbol = atomic_compare_result;
+  int old_value_1 = tint_symbol.old_value;
+  int x_25 = old_value_1;
+  x__atomic_compare_exchange_resulti32 tint_symbol_1 = x__atomic_compare_exchange_resulti32(x_25, (x_25 == x_23));
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicCompareExchangeWeak_1bd40a() {
+  int arg_1 = 0;
+  int arg_2 = 0;
+  x__atomic_compare_exchange_resulti32 res = x__atomic_compare_exchange_resulti32(0, false);
+  arg_1 = 1;
+  arg_2 = 1;
+  int x_23 = arg_2;
+  atomic_compare_exchange_resulti32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(sb_rw.arg_0, arg_1, x_23);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == arg_1;
+  atomic_compare_exchange_resulti32 tint_symbol = atomic_compare_result;
+  int old_value_1 = tint_symbol.old_value;
+  int x_25 = old_value_1;
+  x__atomic_compare_exchange_resulti32 tint_symbol_1 = x__atomic_compare_exchange_resulti32(x_25, (x_25 == x_23));
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..b121c66
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,55 @@
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+struct atomic_compare_exchange_weak_ret_type {
+  int old_value;
+  bool exchanged;
+};
+
+atomic_compare_exchange_weak_ret_type tint_atomicCompareExchangeWeak(RWByteAddressBuffer buffer, uint offset, int compare, int value) {
+  atomic_compare_exchange_weak_ret_type result=(atomic_compare_exchange_weak_ret_type)0;
+  buffer.InterlockedCompareExchange(offset, compare, value, result.old_value);
+  result.exchanged = result.old_value == compare;
+  return result;
+}
+
+
+void atomicCompareExchangeWeak_1bd40a() {
+  int arg_1 = 0;
+  int arg_2 = 0;
+  x__atomic_compare_exchange_resulti32 res = {0, false};
+  arg_1 = 1;
+  arg_2 = 1;
+  const int x_23 = arg_2;
+  const atomic_compare_exchange_weak_ret_type tint_symbol = tint_atomicCompareExchangeWeak(sb_rw, 0u, arg_1, x_23);
+  const int old_value_1 = tint_symbol.old_value;
+  const int x_25 = old_value_1;
+  const x__atomic_compare_exchange_resulti32 tint_symbol_1 = {x_25, (x_25 == x_23)};
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..fa2d372
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.msl
@@ -0,0 +1,63 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+atomic_compare_exchange_resulti32 atomicCompareExchangeWeak_1(device atomic_int* atomic, int compare, int value) {
+  int old_value = compare;
+  bool exchanged = atomic_compare_exchange_weak_explicit(atomic, &old_value, value, memory_order_relaxed, memory_order_relaxed);
+  return {old_value, exchanged};
+}
+
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+void atomicCompareExchangeWeak_1bd40a(device SB_RW_atomic* const tint_symbol_2) {
+  int arg_1 = 0;
+  int arg_2 = 0;
+  x__atomic_compare_exchange_resulti32 res = {.old_value=0, .exchanged=false};
+  arg_1 = 1;
+  arg_2 = 1;
+  int const x_23 = arg_2;
+  int const x_24 = arg_1;
+  atomic_compare_exchange_resulti32 const tint_symbol = atomicCompareExchangeWeak_1(&((*(tint_symbol_2)).arg_0), x_24, x_23);
+  int const old_value_1 = tint_symbol.old_value;
+  int const x_25 = old_value_1;
+  x__atomic_compare_exchange_resulti32 const tint_symbol_1 = {.old_value=x_25, .exchanged=(x_25 == x_23)};
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicCompareExchangeWeak_1bd40a(tint_symbol_3);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_4);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_5) {
+  atomicCompareExchangeWeak_1bd40a(tint_symbol_5);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_6 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_6);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..dde3fc2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,98 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicCompareExchangeWeak_1bd40a "atomicCompareExchangeWeak_1bd40a"
+               OpName %arg_1 "arg_1"
+               OpName %arg_2 "arg_2"
+               OpName %x__atomic_compare_exchange_resulti32 "x__atomic_compare_exchange_resulti32"
+               OpMemberName %x__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %x__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %res "res"
+               OpName %__atomic_compare_exchange_resulti32 "__atomic_compare_exchange_resulti32"
+               OpMemberName %__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+               OpMemberDecorate %x__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %x__atomic_compare_exchange_resulti32 1 Offset 4
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 1 Offset 4
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %bool = OpTypeBool
+%x__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+         %15 = OpConstantNull %bool
+         %16 = OpConstantComposite %x__atomic_compare_exchange_resulti32 %9 %15
+%_ptr_Function_x__atomic_compare_exchange_resulti32 = OpTypePointer Function %x__atomic_compare_exchange_resulti32
+         %19 = OpConstantNull %x__atomic_compare_exchange_resulti32
+      %int_1 = OpConstant %int 1
+%__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicCompareExchangeWeak_1bd40a = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+      %arg_2 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_x__atomic_compare_exchange_resulti32 Function %19
+               OpStore %arg_1 %9
+               OpStore %arg_2 %9
+               OpStore %res %16
+               OpStore %arg_1 %int_1
+               OpStore %arg_2 %int_1
+         %21 = OpLoad %int %arg_2
+         %22 = OpLoad %int %arg_1
+         %30 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %31 = OpAtomicCompareExchange %int %30 %uint_1 %uint_0 %uint_0 %21 %22
+         %32 = OpIEqual %bool %31 %21
+         %23 = OpCompositeConstruct %__atomic_compare_exchange_resulti32 %31 %32
+         %33 = OpCompositeExtract %int %23 0
+         %34 = OpIEqual %bool %33 %21
+         %35 = OpCompositeConstruct %x__atomic_compare_exchange_resulti32 %33 %34
+               OpStore %res %35
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %37 = OpLabel
+         %38 = OpFunctionCall %void %atomicCompareExchangeWeak_1bd40a
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %40 = OpLabel
+         %41 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %43 = OpLabel
+         %44 = OpFunctionCall %void %atomicCompareExchangeWeak_1bd40a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %46 = OpLabel
+         %47 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..84a3a29
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,48 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+struct x__atomic_compare_exchange_resulti32 {
+  old_value : i32,
+  exchanged : bool,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicCompareExchangeWeak_1bd40a() {
+  var arg_1 : i32 = 0i;
+  var arg_2 : i32 = 0i;
+  var res : x__atomic_compare_exchange_resulti32 = x__atomic_compare_exchange_resulti32(0i, false);
+  arg_1 = 1i;
+  arg_2 = 1i;
+  let x_23 : i32 = arg_2;
+  let x_24 : i32 = arg_1;
+  let old_value_1 = atomicCompareExchangeWeak(&(sb_rw.arg_0), x_24, x_23).old_value;
+  let x_25 : i32 = old_value_1;
+  res = x__atomic_compare_exchange_resulti32(x_25, (x_25 == x_23));
+  return;
+}
+
+fn fragment_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicCompareExchangeWeak_1bd40a();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm
new file mode 100644
index 0000000..3723643
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm
@@ -0,0 +1,70 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicCompareExchangeWeak_63d8e6 "atomicCompareExchangeWeak_63d8e6"
+               OpName %arg_1 "arg_1"
+               OpName %arg_2 "arg_2"
+               OpName %__atomic_compare_exchange_resultu32 "__atomic_compare_exchange_resultu32"
+               OpMemberName %__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 1 Offset 4
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+       %bool = OpTypeBool
+%__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function___atomic_compare_exchange_resultu32 = OpTypePointer Function %__atomic_compare_exchange_resultu32
+         %27 = OpConstantNull %__atomic_compare_exchange_resultu32
+%atomicCompareExchangeWeak_63d8e6 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+      %arg_2 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function___atomic_compare_exchange_resultu32 Function %27
+               OpStore %arg_1 %uint_1
+               OpStore %arg_2 %uint_1
+         %20 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %21 = OpLoad %uint %arg_2
+         %22 = OpLoad %uint %arg_1
+         %23 = OpAtomicCompareExchange %uint %20 %uint_1 %uint_0 %uint_0 %21 %22
+         %24 = OpIEqual %bool %23 %21
+         %14 = OpCompositeConstruct %__atomic_compare_exchange_resultu32 %23 %24
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicCompareExchangeWeak_63d8e6
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %atomicCompareExchangeWeak_63d8e6
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..ec3e6e1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,112 @@
+#version 310 es
+precision mediump float;
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicCompareExchangeWeak_63d8e6() {
+  uint arg_1 = 0u;
+  uint arg_2 = 0u;
+  x__atomic_compare_exchange_resultu32 res = x__atomic_compare_exchange_resultu32(0u, false);
+  arg_1 = 1u;
+  arg_2 = 1u;
+  uint x_21 = arg_2;
+  atomic_compare_exchange_resultu32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(sb_rw.arg_0, arg_1, x_21);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == arg_1;
+  atomic_compare_exchange_resultu32 tint_symbol = atomic_compare_result;
+  uint old_value_1 = tint_symbol.old_value;
+  uint x_23 = old_value_1;
+  x__atomic_compare_exchange_resultu32 tint_symbol_1 = x__atomic_compare_exchange_resultu32(x_23, (x_23 == x_21));
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicCompareExchangeWeak_63d8e6() {
+  uint arg_1 = 0u;
+  uint arg_2 = 0u;
+  x__atomic_compare_exchange_resultu32 res = x__atomic_compare_exchange_resultu32(0u, false);
+  arg_1 = 1u;
+  arg_2 = 1u;
+  uint x_21 = arg_2;
+  atomic_compare_exchange_resultu32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(sb_rw.arg_0, arg_1, x_21);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == arg_1;
+  atomic_compare_exchange_resultu32 tint_symbol = atomic_compare_result;
+  uint old_value_1 = tint_symbol.old_value;
+  uint x_23 = old_value_1;
+  x__atomic_compare_exchange_resultu32 tint_symbol_1 = x__atomic_compare_exchange_resultu32(x_23, (x_23 == x_21));
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..7a9c971
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,55 @@
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+struct atomic_compare_exchange_weak_ret_type {
+  uint old_value;
+  bool exchanged;
+};
+
+atomic_compare_exchange_weak_ret_type tint_atomicCompareExchangeWeak(RWByteAddressBuffer buffer, uint offset, uint compare, uint value) {
+  atomic_compare_exchange_weak_ret_type result=(atomic_compare_exchange_weak_ret_type)0;
+  buffer.InterlockedCompareExchange(offset, compare, value, result.old_value);
+  result.exchanged = result.old_value == compare;
+  return result;
+}
+
+
+void atomicCompareExchangeWeak_63d8e6() {
+  uint arg_1 = 0u;
+  uint arg_2 = 0u;
+  x__atomic_compare_exchange_resultu32 res = {0u, false};
+  arg_1 = 1u;
+  arg_2 = 1u;
+  const uint x_21 = arg_2;
+  const atomic_compare_exchange_weak_ret_type tint_symbol = tint_atomicCompareExchangeWeak(sb_rw, 0u, arg_1, x_21);
+  const uint old_value_1 = tint_symbol.old_value;
+  const uint x_23 = old_value_1;
+  const x__atomic_compare_exchange_resultu32 tint_symbol_1 = {x_23, (x_23 == x_21)};
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..39de0ea
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.msl
@@ -0,0 +1,63 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+atomic_compare_exchange_resultu32 atomicCompareExchangeWeak_1(device atomic_uint* atomic, uint compare, uint value) {
+  uint old_value = compare;
+  bool exchanged = atomic_compare_exchange_weak_explicit(atomic, &old_value, value, memory_order_relaxed, memory_order_relaxed);
+  return {old_value, exchanged};
+}
+
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+void atomicCompareExchangeWeak_63d8e6(device SB_RW_atomic* const tint_symbol_2) {
+  uint arg_1 = 0u;
+  uint arg_2 = 0u;
+  x__atomic_compare_exchange_resultu32 res = {.old_value=0u, .exchanged=false};
+  arg_1 = 1u;
+  arg_2 = 1u;
+  uint const x_21 = arg_2;
+  uint const x_22 = arg_1;
+  atomic_compare_exchange_resultu32 const tint_symbol = atomicCompareExchangeWeak_1(&((*(tint_symbol_2)).arg_0), x_22, x_21);
+  uint const old_value_1 = tint_symbol.old_value;
+  uint const x_23 = old_value_1;
+  x__atomic_compare_exchange_resultu32 const tint_symbol_1 = {.old_value=x_23, .exchanged=(x_23 == x_21)};
+  res = tint_symbol_1;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicCompareExchangeWeak_63d8e6(tint_symbol_3);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_4);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_5) {
+  atomicCompareExchangeWeak_63d8e6(tint_symbol_5);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_6 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_6);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..9cce848
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.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 Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicCompareExchangeWeak_63d8e6 "atomicCompareExchangeWeak_63d8e6"
+               OpName %arg_1 "arg_1"
+               OpName %arg_2 "arg_2"
+               OpName %x__atomic_compare_exchange_resultu32 "x__atomic_compare_exchange_resultu32"
+               OpMemberName %x__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %x__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %res "res"
+               OpName %__atomic_compare_exchange_resultu32 "__atomic_compare_exchange_resultu32"
+               OpMemberName %__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+               OpMemberDecorate %x__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %x__atomic_compare_exchange_resultu32 1 Offset 4
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+       %bool = OpTypeBool
+%x__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+         %15 = OpConstantNull %bool
+         %16 = OpConstantComposite %x__atomic_compare_exchange_resultu32 %9 %15
+%_ptr_Function_x__atomic_compare_exchange_resultu32 = OpTypePointer Function %x__atomic_compare_exchange_resultu32
+         %19 = OpConstantNull %x__atomic_compare_exchange_resultu32
+     %uint_1 = OpConstant %uint 1
+%__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicCompareExchangeWeak_63d8e6 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+      %arg_2 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_x__atomic_compare_exchange_resultu32 Function %19
+               OpStore %arg_1 %9
+               OpStore %arg_2 %9
+               OpStore %res %16
+               OpStore %arg_1 %uint_1
+               OpStore %arg_2 %uint_1
+         %21 = OpLoad %uint %arg_2
+         %22 = OpLoad %uint %arg_1
+         %28 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %29 = OpAtomicCompareExchange %uint %28 %uint_1 %uint_0 %uint_0 %21 %22
+         %30 = OpIEqual %bool %29 %21
+         %23 = OpCompositeConstruct %__atomic_compare_exchange_resultu32 %29 %30
+         %31 = OpCompositeExtract %uint %23 0
+         %32 = OpIEqual %bool %31 %21
+         %33 = OpCompositeConstruct %x__atomic_compare_exchange_resultu32 %31 %32
+               OpStore %res %33
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %35 = OpLabel
+         %36 = OpFunctionCall %void %atomicCompareExchangeWeak_63d8e6
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %38 = OpLabel
+         %39 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %41 = OpLabel
+         %42 = OpFunctionCall %void %atomicCompareExchangeWeak_63d8e6
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %44 = OpLabel
+         %45 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..c3b661b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,48 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+struct x__atomic_compare_exchange_resultu32 {
+  old_value : u32,
+  exchanged : bool,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicCompareExchangeWeak_63d8e6() {
+  var arg_1 : u32 = 0u;
+  var arg_2 : u32 = 0u;
+  var res : x__atomic_compare_exchange_resultu32 = x__atomic_compare_exchange_resultu32(0u, false);
+  arg_1 = 1u;
+  arg_2 = 1u;
+  let x_21 : u32 = arg_2;
+  let x_22 : u32 = arg_1;
+  let old_value_1 = atomicCompareExchangeWeak(&(sb_rw.arg_0), x_22, x_21).old_value;
+  let x_23 : u32 = old_value_1;
+  res = x__atomic_compare_exchange_resultu32(x_23, (x_23 == x_21));
+  return;
+}
+
+fn fragment_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicCompareExchangeWeak_63d8e6();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm
new file mode 100644
index 0000000..b4e3102
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm
@@ -0,0 +1,72 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 42
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicCompareExchangeWeak_e88938 "atomicCompareExchangeWeak_e88938"
+               OpName %arg_1 "arg_1"
+               OpName %arg_2 "arg_2"
+               OpName %__atomic_compare_exchange_resulti32 "__atomic_compare_exchange_resulti32"
+               OpMemberName %__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+       %bool = OpTypeBool
+%__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function___atomic_compare_exchange_resulti32 = OpTypePointer Function %__atomic_compare_exchange_resulti32
+         %28 = OpConstantNull %__atomic_compare_exchange_resulti32
+         %29 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicCompareExchangeWeak_e88938 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+      %arg_2 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function___atomic_compare_exchange_resulti32 Function %28
+               OpStore %arg_1 %int_1
+               OpStore %arg_2 %int_1
+         %22 = OpLoad %int %arg_2
+         %23 = OpLoad %int %arg_1
+         %24 = OpAtomicCompareExchange %int %arg_0 %uint_2 %uint_0 %uint_0 %22 %23
+         %25 = OpIEqual %bool %24 %22
+         %16 = OpCompositeConstruct %__atomic_compare_exchange_resulti32 %24 %25
+               OpStore %res %16
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %29
+%local_invocation_index = OpFunctionParameter %uint
+         %32 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %37 = OpFunctionCall %void %atomicCompareExchangeWeak_e88938
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %39 = OpLabel
+         %41 = OpLoad %uint %local_invocation_index_1
+         %40 = OpFunctionCall %void %compute_main_inner %41
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..c370e71
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,59 @@
+#version 310 es
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicCompareExchangeWeak_e88938() {
+  int arg_1 = 0;
+  int arg_2 = 0;
+  x__atomic_compare_exchange_resulti32 res = x__atomic_compare_exchange_resulti32(0, false);
+  arg_1 = 1;
+  arg_2 = 1;
+  int x_22 = arg_2;
+  atomic_compare_exchange_resulti32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(arg_0, arg_1, x_22);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == arg_1;
+  atomic_compare_exchange_resulti32 tint_symbol = atomic_compare_result;
+  int old_value_1 = tint_symbol.old_value;
+  int x_24 = old_value_1;
+  x__atomic_compare_exchange_resulti32 tint_symbol_1 = x__atomic_compare_exchange_resulti32(x_24, (x_24 == x_22));
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicCompareExchangeWeak_e88938();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..55ac417
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,63 @@
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicCompareExchangeWeak_e88938() {
+  int arg_1 = 0;
+  int arg_2 = 0;
+  x__atomic_compare_exchange_resulti32 res = {0, false};
+  arg_1 = 1;
+  arg_2 = 1;
+  const int x_22 = arg_2;
+  atomic_compare_exchange_resulti32 atomic_result = (atomic_compare_exchange_resulti32)0;
+  int atomic_compare_value = arg_1;
+  InterlockedCompareExchange(arg_0, atomic_compare_value, x_22, atomic_result.old_value);
+  atomic_result.exchanged = atomic_result.old_value == atomic_compare_value;
+  const atomic_compare_exchange_resulti32 tint_symbol_2 = atomic_result;
+  const int old_value_1 = tint_symbol_2.old_value;
+  const int x_24 = old_value_1;
+  const x__atomic_compare_exchange_resulti32 tint_symbol_3 = {x_24, (x_24 == x_22)};
+  res = tint_symbol_3;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicCompareExchangeWeak_e88938();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..52cfd0f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,64 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+atomic_compare_exchange_resulti32 atomicCompareExchangeWeak_1(threadgroup atomic_int* atomic, int compare, int value) {
+  int old_value = compare;
+  bool exchanged = atomic_compare_exchange_weak_explicit(atomic, &old_value, value, memory_order_relaxed, memory_order_relaxed);
+  return {old_value, exchanged};
+}
+
+struct x__atomic_compare_exchange_resulti32 {
+  int old_value;
+  bool exchanged;
+};
+
+void atomicCompareExchangeWeak_e88938(threadgroup atomic_int* const tint_symbol_2) {
+  int arg_1 = 0;
+  int arg_2 = 0;
+  x__atomic_compare_exchange_resulti32 res = {.old_value=0, .exchanged=false};
+  arg_1 = 1;
+  arg_2 = 1;
+  int const x_22 = arg_2;
+  int const x_23 = arg_1;
+  atomic_compare_exchange_resulti32 const tint_symbol = atomicCompareExchangeWeak_1(tint_symbol_2, x_23, x_22);
+  int const old_value_1 = tint_symbol.old_value;
+  int const x_24 = old_value_1;
+  x__atomic_compare_exchange_resulti32 const tint_symbol_1 = {.old_value=x_24, .exchanged=(x_24 == x_22)};
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_3) {
+  atomic_store_explicit(tint_symbol_3, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicCompareExchangeWeak_e88938(tint_symbol_3);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_4, threadgroup atomic_int* const tint_symbol_5) {
+  uint const x_41 = *(tint_symbol_4);
+  compute_main_inner(x_41, tint_symbol_5);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_6, thread uint* const tint_symbol_7) {
+  {
+    atomic_store_explicit(tint_symbol_6, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_7) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_7, tint_symbol_6);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_8;
+  thread uint tint_symbol_9 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_8), &(tint_symbol_9));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..ceae3b5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,108 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 62
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicCompareExchangeWeak_e88938 "atomicCompareExchangeWeak_e88938"
+               OpName %arg_1 "arg_1"
+               OpName %arg_2 "arg_2"
+               OpName %x__atomic_compare_exchange_resulti32 "x__atomic_compare_exchange_resulti32"
+               OpMemberName %x__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %x__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %res "res"
+               OpName %__atomic_compare_exchange_resulti32 "__atomic_compare_exchange_resulti32"
+               OpMemberName %__atomic_compare_exchange_resulti32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resulti32 1 "exchanged"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %x__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %x__atomic_compare_exchange_resulti32 1 Offset 4
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resulti32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %bool = OpTypeBool
+%x__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+         %20 = OpConstantNull %bool
+         %21 = OpConstantComposite %x__atomic_compare_exchange_resulti32 %14 %20
+%_ptr_Function_x__atomic_compare_exchange_resulti32 = OpTypePointer Function %x__atomic_compare_exchange_resulti32
+         %24 = OpConstantNull %x__atomic_compare_exchange_resulti32
+      %int_1 = OpConstant %int 1
+%__atomic_compare_exchange_resulti32 = OpTypeStruct %int %bool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %38 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicCompareExchangeWeak_e88938 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+      %arg_2 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_x__atomic_compare_exchange_resulti32 Function %24
+               OpStore %arg_1 %14
+               OpStore %arg_2 %14
+               OpStore %res %21
+               OpStore %arg_1 %int_1
+               OpStore %arg_2 %int_1
+         %26 = OpLoad %int %arg_2
+         %27 = OpLoad %int %arg_1
+         %33 = OpAtomicCompareExchange %int %arg_0 %uint_2 %uint_0 %uint_0 %26 %27
+         %34 = OpIEqual %bool %33 %26
+         %28 = OpCompositeConstruct %__atomic_compare_exchange_resulti32 %33 %34
+         %35 = OpCompositeExtract %int %28 0
+         %36 = OpIEqual %bool %35 %26
+         %37 = OpCompositeConstruct %x__atomic_compare_exchange_resulti32 %35 %36
+               OpStore %res %37
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %38
+%local_invocation_index = OpFunctionParameter %uint
+         %41 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %46 = OpFunctionCall %void %atomicCompareExchangeWeak_e88938
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %48 = OpLabel
+         %49 = OpLoad %uint %local_invocation_index_1
+         %50 = OpFunctionCall %void %compute_main_inner %49
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %38
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %53 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %57 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %59 = OpLabel
+         %61 = OpLoad %uint %local_invocation_index_1_param_1
+         %60 = OpFunctionCall %void %compute_main_inner_1 %61
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..aaa949e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,41 @@
+struct x__atomic_compare_exchange_resulti32 {
+  old_value : i32,
+  exchanged : bool,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicCompareExchangeWeak_e88938() {
+  var arg_1 : i32 = 0i;
+  var arg_2 : i32 = 0i;
+  var res : x__atomic_compare_exchange_resulti32 = x__atomic_compare_exchange_resulti32(0i, false);
+  arg_1 = 1i;
+  arg_2 = 1i;
+  let x_22 : i32 = arg_2;
+  let x_23 : i32 = arg_1;
+  let old_value_1 = atomicCompareExchangeWeak(&(arg_0), x_23, x_22).old_value;
+  let x_24 : i32 = old_value_1;
+  res = x__atomic_compare_exchange_resulti32(x_24, (x_24 == x_22));
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicCompareExchangeWeak_e88938();
+  return;
+}
+
+fn compute_main_1() {
+  let x_41 : u32 = local_invocation_index_1;
+  compute_main_inner(x_41);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm
new file mode 100644
index 0000000..b163b6f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 41
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicCompareExchangeWeak_83580d "atomicCompareExchangeWeak_83580d"
+               OpName %arg_1 "arg_1"
+               OpName %arg_2 "arg_2"
+               OpName %__atomic_compare_exchange_resultu32 "__atomic_compare_exchange_resultu32"
+               OpMemberName %__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+       %bool = OpTypeBool
+%__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function___atomic_compare_exchange_resultu32 = OpTypePointer Function %__atomic_compare_exchange_resultu32
+         %27 = OpConstantNull %__atomic_compare_exchange_resultu32
+         %28 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicCompareExchangeWeak_83580d = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+      %arg_2 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function___atomic_compare_exchange_resultu32 Function %27
+               OpStore %arg_1 %uint_1
+               OpStore %arg_2 %uint_1
+         %21 = OpLoad %uint %arg_2
+         %22 = OpLoad %uint %arg_1
+         %23 = OpAtomicCompareExchange %uint %arg_0 %uint_2 %uint_0 %uint_0 %21 %22
+         %24 = OpIEqual %bool %23 %21
+         %15 = OpCompositeConstruct %__atomic_compare_exchange_resultu32 %23 %24
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %28
+%local_invocation_index = OpFunctionParameter %uint
+         %31 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %36 = OpFunctionCall %void %atomicCompareExchangeWeak_83580d
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %38 = OpLabel
+         %40 = OpLoad %uint %local_invocation_index_1
+         %39 = OpFunctionCall %void %compute_main_inner %40
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..645a53e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,59 @@
+#version 310 es
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicCompareExchangeWeak_83580d() {
+  uint arg_1 = 0u;
+  uint arg_2 = 0u;
+  x__atomic_compare_exchange_resultu32 res = x__atomic_compare_exchange_resultu32(0u, false);
+  arg_1 = 1u;
+  arg_2 = 1u;
+  uint x_21 = arg_2;
+  atomic_compare_exchange_resultu32 atomic_compare_result;
+  atomic_compare_result.old_value = atomicCompSwap(arg_0, arg_1, x_21);
+  atomic_compare_result.exchanged = atomic_compare_result.old_value == arg_1;
+  atomic_compare_exchange_resultu32 tint_symbol = atomic_compare_result;
+  uint old_value_1 = tint_symbol.old_value;
+  uint x_23 = old_value_1;
+  x__atomic_compare_exchange_resultu32 tint_symbol_1 = x__atomic_compare_exchange_resultu32(x_23, (x_23 == x_21));
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicCompareExchangeWeak_83580d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..6a120b1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,63 @@
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicCompareExchangeWeak_83580d() {
+  uint arg_1 = 0u;
+  uint arg_2 = 0u;
+  x__atomic_compare_exchange_resultu32 res = {0u, false};
+  arg_1 = 1u;
+  arg_2 = 1u;
+  const uint x_21 = arg_2;
+  atomic_compare_exchange_resultu32 atomic_result = (atomic_compare_exchange_resultu32)0;
+  uint atomic_compare_value = arg_1;
+  InterlockedCompareExchange(arg_0, atomic_compare_value, x_21, atomic_result.old_value);
+  atomic_result.exchanged = atomic_result.old_value == atomic_compare_value;
+  const atomic_compare_exchange_resultu32 tint_symbol_2 = atomic_result;
+  const uint old_value_1 = tint_symbol_2.old_value;
+  const uint x_23 = old_value_1;
+  const x__atomic_compare_exchange_resultu32 tint_symbol_3 = {x_23, (x_23 == x_21)};
+  res = tint_symbol_3;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicCompareExchangeWeak_83580d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..b788874
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,64 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+atomic_compare_exchange_resultu32 atomicCompareExchangeWeak_1(threadgroup atomic_uint* atomic, uint compare, uint value) {
+  uint old_value = compare;
+  bool exchanged = atomic_compare_exchange_weak_explicit(atomic, &old_value, value, memory_order_relaxed, memory_order_relaxed);
+  return {old_value, exchanged};
+}
+
+struct x__atomic_compare_exchange_resultu32 {
+  uint old_value;
+  bool exchanged;
+};
+
+void atomicCompareExchangeWeak_83580d(threadgroup atomic_uint* const tint_symbol_2) {
+  uint arg_1 = 0u;
+  uint arg_2 = 0u;
+  x__atomic_compare_exchange_resultu32 res = {.old_value=0u, .exchanged=false};
+  arg_1 = 1u;
+  arg_2 = 1u;
+  uint const x_21 = arg_2;
+  uint const x_22 = arg_1;
+  atomic_compare_exchange_resultu32 const tint_symbol = atomicCompareExchangeWeak_1(tint_symbol_2, x_22, x_21);
+  uint const old_value_1 = tint_symbol.old_value;
+  uint const x_23 = old_value_1;
+  x__atomic_compare_exchange_resultu32 const tint_symbol_1 = {.old_value=x_23, .exchanged=(x_23 == x_21)};
+  res = tint_symbol_1;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_3) {
+  atomic_store_explicit(tint_symbol_3, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicCompareExchangeWeak_83580d(tint_symbol_3);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_4, threadgroup atomic_uint* const tint_symbol_5) {
+  uint const x_40 = *(tint_symbol_4);
+  compute_main_inner(x_40, tint_symbol_5);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_6, thread uint* const tint_symbol_7) {
+  {
+    atomic_store_explicit(tint_symbol_6, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_7) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_7, tint_symbol_6);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_8;
+  thread uint tint_symbol_9 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_8), &(tint_symbol_9));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..a2d0294
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,106 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 60
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicCompareExchangeWeak_83580d "atomicCompareExchangeWeak_83580d"
+               OpName %arg_1 "arg_1"
+               OpName %arg_2 "arg_2"
+               OpName %x__atomic_compare_exchange_resultu32 "x__atomic_compare_exchange_resultu32"
+               OpMemberName %x__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %x__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %res "res"
+               OpName %__atomic_compare_exchange_resultu32 "__atomic_compare_exchange_resultu32"
+               OpMemberName %__atomic_compare_exchange_resultu32 0 "old_value"
+               OpMemberName %__atomic_compare_exchange_resultu32 1 "exchanged"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+               OpMemberDecorate %x__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %x__atomic_compare_exchange_resultu32 1 Offset 4
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 0 Offset 0
+               OpMemberDecorate %__atomic_compare_exchange_resultu32 1 Offset 4
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+       %bool = OpTypeBool
+%x__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+         %18 = OpConstantNull %bool
+         %19 = OpConstantComposite %x__atomic_compare_exchange_resultu32 %6 %18
+%_ptr_Function_x__atomic_compare_exchange_resultu32 = OpTypePointer Function %x__atomic_compare_exchange_resultu32
+         %22 = OpConstantNull %x__atomic_compare_exchange_resultu32
+     %uint_1 = OpConstant %uint 1
+%__atomic_compare_exchange_resultu32 = OpTypeStruct %uint %bool
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %36 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicCompareExchangeWeak_83580d = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+      %arg_2 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_x__atomic_compare_exchange_resultu32 Function %22
+               OpStore %arg_1 %6
+               OpStore %arg_2 %6
+               OpStore %res %19
+               OpStore %arg_1 %uint_1
+               OpStore %arg_2 %uint_1
+         %24 = OpLoad %uint %arg_2
+         %25 = OpLoad %uint %arg_1
+         %31 = OpAtomicCompareExchange %uint %arg_0 %uint_2 %uint_0 %uint_0 %24 %25
+         %32 = OpIEqual %bool %31 %24
+         %26 = OpCompositeConstruct %__atomic_compare_exchange_resultu32 %31 %32
+         %33 = OpCompositeExtract %uint %26 0
+         %34 = OpIEqual %bool %33 %24
+         %35 = OpCompositeConstruct %x__atomic_compare_exchange_resultu32 %33 %34
+               OpStore %res %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %36
+%local_invocation_index = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %44 = OpFunctionCall %void %atomicCompareExchangeWeak_83580d
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %46 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1
+         %48 = OpFunctionCall %void %compute_main_inner %47
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %36
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %51 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %55 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %57 = OpLabel
+         %59 = OpLoad %uint %local_invocation_index_1_param_1
+         %58 = OpFunctionCall %void %compute_main_inner_1 %59
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..4a5fdf8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicCompareExchangeWeak/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,41 @@
+struct x__atomic_compare_exchange_resultu32 {
+  old_value : u32,
+  exchanged : bool,
+}
+
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicCompareExchangeWeak_83580d() {
+  var arg_1 : u32 = 0u;
+  var arg_2 : u32 = 0u;
+  var res : x__atomic_compare_exchange_resultu32 = x__atomic_compare_exchange_resultu32(0u, false);
+  arg_1 = 1u;
+  arg_2 = 1u;
+  let x_21 : u32 = arg_2;
+  let x_22 : u32 = arg_1;
+  let old_value_1 = atomicCompareExchangeWeak(&(arg_0), x_22, x_21).old_value;
+  let x_23 : u32 = old_value_1;
+  res = x__atomic_compare_exchange_resultu32(x_23, (x_23 == x_21));
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicCompareExchangeWeak_83580d();
+  return;
+}
+
+fn compute_main_1() {
+  let x_40 : u32 = local_invocation_index_1;
+  compute_main_inner(x_40);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm
new file mode 100644
index 0000000..755a537
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicExchange_f2e22f "atomicExchange_f2e22f"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicExchange_f2e22f = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+         %13 = OpAtomicExchange %int %19 %uint_1 %uint_0 %20
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicExchange_f2e22f
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicExchange_f2e22f
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..284be71
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicExchange_f2e22f() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicExchange(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicExchange_f2e22f() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicExchange(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..32a2a70
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicExchange(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedExchange(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicExchange_f2e22f() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicExchange(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..1f5986b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicExchange_f2e22f(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  int const x_13 = atomic_exchange_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicExchange_f2e22f(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicExchange_f2e22f(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..2b2b678
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicExchange_f2e22f "atomicExchange_f2e22f"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicExchange_f2e22f = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %14 = OpLoad %int %arg_1
+         %21 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %15 = OpAtomicExchange %int %21 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicExchange_f2e22f
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicExchange_f2e22f
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..e4dad2d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicExchange_f2e22f() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  let x_13 : i32 = atomicExchange(&(sb_rw.arg_0), x_20);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicExchange_f2e22f();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm
new file mode 100644
index 0000000..2ec90f3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicExchange_d59712 "atomicExchange_d59712"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicExchange_d59712 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+         %13 = OpAtomicExchange %uint %17 %uint_1 %uint_0 %18
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicExchange_d59712
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicExchange_d59712
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..edc360d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicExchange_d59712() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicExchange(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicExchange_d59712() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicExchange(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..84b133b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicExchange(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedExchange(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicExchange_d59712() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicExchange(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..e27ba22
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicExchange_d59712(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_13 = atomic_exchange_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicExchange_d59712(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicExchange_d59712(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..7e620f0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicExchange_d59712 "atomicExchange_d59712"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicExchange_d59712 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %14 = OpLoad %uint %arg_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %15 = OpAtomicExchange %uint %19 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicExchange_d59712
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicExchange_d59712
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..d7b8cb6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicExchange_d59712() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_13 : u32 = atomicExchange(&(sb_rw.arg_0), x_18);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicExchange_d59712();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm
new file mode 100644
index 0000000..0e6f0a8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicExchange_e114ba "atomicExchange_e114ba"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicExchange_e114ba = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %15 = OpAtomicExchange %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicExchange_e114ba
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..03a22c2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicExchange_e114ba() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicExchange(arg_0, arg_1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicExchange_e114ba();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..d0e30a4
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicExchange_e114ba() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedExchange(arg_0, arg_1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicExchange_e114ba();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..1886d0a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicExchange_e114ba(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  int const x_15 = atomic_exchange_explicit(tint_symbol, x_19, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicExchange_e114ba(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..e72725b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,81 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicExchange_e114ba "atomicExchange_e114ba"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %24 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicExchange_e114ba = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %20 = OpAtomicExchange %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %20
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %24
+%local_invocation_index = OpFunctionParameter %uint
+         %27 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicExchange_e114ba
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %34 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %36 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %24
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %43 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1_param_1
+         %46 = OpFunctionCall %void %compute_main_inner_1 %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..555469e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicExchange_e114ba() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  let x_15 : i32 = atomicExchange(&(arg_0), x_19);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicExchange_e114ba();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm
new file mode 100644
index 0000000..3bbfe18
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicExchange_0a5dca "atomicExchange_0a5dca"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicExchange_0a5dca = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+         %14 = OpAtomicExchange %uint %arg_0 %uint_2 %uint_0 %18
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicExchange_0a5dca
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..32711c9
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicExchange_0a5dca() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicExchange(arg_0, arg_1);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicExchange_0a5dca();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..f5ff3f7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicExchange_0a5dca() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedExchange(arg_0, arg_1, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicExchange_0a5dca();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..c42c47d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicExchange_0a5dca(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_14 = atomic_exchange_explicit(tint_symbol, x_18, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicExchange_0a5dca(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..6970053
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,79 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicExchange_0a5dca "atomicExchange_0a5dca"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicExchange_0a5dca = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpLoad %uint %arg_1
+         %18 = OpAtomicExchange %uint %arg_0 %uint_2 %uint_0 %17
+               OpStore %res %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicExchange_0a5dca
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..89fff60
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicExchange/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicExchange_0a5dca() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_14 : u32 = atomicExchange(&(arg_0), x_18);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicExchange_0a5dca();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm
new file mode 100644
index 0000000..d961ea5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 25
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicLoad_0806ad "atomicLoad_0806ad"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%_ptr_Function_int = OpTypePointer Function %int
+         %18 = OpConstantNull %int
+%atomicLoad_0806ad = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %18
+         %15 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+          %9 = OpAtomicLoad %int %15 %uint_1 %uint_0
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %20 = OpLabel
+         %21 = OpFunctionCall %void %atomicLoad_0806ad
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicLoad_0806ad
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..d034197
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicLoad_0806ad() {
+  int res = 0;
+  int x_9 = atomicOr(sb_rw.arg_0, 0);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicLoad_0806ad() {
+  int res = 0;
+  int x_9 = atomicOr(sb_rw.arg_0, 0);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..8ce9cf2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicLoad(RWByteAddressBuffer buffer, uint offset) {
+  int value = 0;
+  buffer.InterlockedOr(offset, 0, value);
+  return value;
+}
+
+
+void atomicLoad_0806ad() {
+  int res = 0;
+  const int x_9 = tint_atomicLoad(sb_rw, 0u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..1ea07a1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicLoad_0806ad(device SB_RW_atomic* const tint_symbol) {
+  int res = 0;
+  int const x_9 = atomic_load_explicit(&((*(tint_symbol)).arg_0), memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicLoad_0806ad(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicLoad_0806ad(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..40034c7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,65 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicLoad_0806ad "atomicLoad_0806ad"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicLoad_0806ad = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %res %9
+         %18 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %12 = OpAtomicLoad %int %18 %uint_1 %uint_0
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %20 = OpLabel
+         %21 = OpFunctionCall %void %atomicLoad_0806ad
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicLoad_0806ad
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b13dbfb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicLoad_0806ad() {
+  var res : i32 = 0i;
+  let x_9 : i32 = atomicLoad(&(sb_rw.arg_0));
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicLoad_0806ad();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm
new file mode 100644
index 0000000..776c62c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 24
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicLoad_fe6cc3 "atomicLoad_fe6cc3"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %17 = OpConstantNull %uint
+%atomicLoad_fe6cc3 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %17
+         %14 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+          %9 = OpAtomicLoad %uint %14 %uint_1 %uint_0
+               OpStore %res %9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicLoad_fe6cc3
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicLoad_fe6cc3
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..cebe232
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicLoad_fe6cc3() {
+  uint res = 0u;
+  uint x_9 = atomicOr(sb_rw.arg_0, 0u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicLoad_fe6cc3() {
+  uint res = 0u;
+  uint x_9 = atomicOr(sb_rw.arg_0, 0u);
+  res = x_9;
+  return;
+}
+
+void compute_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..454ed3c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,36 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicLoad(RWByteAddressBuffer buffer, uint offset) {
+  uint value = 0;
+  buffer.InterlockedOr(offset, 0, value);
+  return value;
+}
+
+
+void atomicLoad_fe6cc3() {
+  uint res = 0u;
+  const uint x_9 = tint_atomicLoad(sb_rw, 0u);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..a6cce27
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.msl
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicLoad_fe6cc3(device SB_RW_atomic* const tint_symbol) {
+  uint res = 0u;
+  uint const x_9 = atomic_load_explicit(&((*(tint_symbol)).arg_0), memory_order_relaxed);
+  res = x_9;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicLoad_fe6cc3(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicLoad_fe6cc3(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..e12b47b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,64 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicLoad_fe6cc3 "atomicLoad_fe6cc3"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicLoad_fe6cc3 = OpFunction %void None %5
+          %8 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %res %9
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %12 = OpAtomicLoad %uint %17 %uint_1 %uint_0
+               OpStore %res %12
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %19 = OpLabel
+         %20 = OpFunctionCall %void %atomicLoad_fe6cc3
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicLoad_fe6cc3
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b03da62e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,36 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicLoad_fe6cc3() {
+  var res : u32 = 0u;
+  let x_9 : u32 = atomicLoad(&(sb_rw.arg_0));
+  res = x_9;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicLoad_fe6cc3();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm
new file mode 100644
index 0000000..b996359
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicLoad_afcc03 "atomicLoad_afcc03"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function_int = OpTypePointer Function %int
+         %17 = OpConstantNull %int
+         %18 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicLoad_afcc03 = OpFunction %void None %7
+         %10 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %17
+         %11 = OpAtomicLoad %int %arg_0 %uint_2 %uint_0
+               OpStore %res %11
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %18
+%local_invocation_index = OpFunctionParameter %uint
+         %21 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %17
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %26 = OpFunctionCall %void %atomicLoad_afcc03
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %28 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %29 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..e0c158b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicLoad_afcc03() {
+  int res = 0;
+  int x_11 = atomicOr(arg_0, 0);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicLoad_afcc03();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..1593c13
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicLoad_afcc03() {
+  int res = 0;
+  int atomic_result = 0;
+  InterlockedOr(arg_0, 0, atomic_result);
+  const int x_11 = atomic_result;
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicLoad_afcc03();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..8f6ee40
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicLoad_afcc03(threadgroup atomic_int* const tint_symbol) {
+  int res = 0;
+  int const x_11 = atomic_load_explicit(tint_symbol, memory_order_relaxed);
+  res = x_11;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicLoad_afcc03(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_30 = *(tint_symbol_2);
+  compute_main_inner(x_30, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..829b848
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,75 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 45
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicLoad_afcc03 "atomicLoad_afcc03"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicLoad_afcc03 = OpFunction %void None %10
+         %13 = OpLabel
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %res %14
+         %17 = OpAtomicLoad %int %arg_0 %uint_2 %uint_0
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicLoad_afcc03
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %31 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %33 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %21
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %36 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %40 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %42 = OpLabel
+         %44 = OpLoad %uint %local_invocation_index_1_param_1
+         %43 = OpFunctionCall %void %compute_main_inner_1 %44
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..af0c057
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicLoad_afcc03() {
+  var res : i32 = 0i;
+  let x_11 : i32 = atomicLoad(&(arg_0));
+  res = x_11;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicLoad_afcc03();
+  return;
+}
+
+fn compute_main_1() {
+  let x_30 : u32 = local_invocation_index_1;
+  compute_main_inner(x_30);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm
new file mode 100644
index 0000000..20eeca7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm
@@ -0,0 +1,51 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicLoad_361bf1 "atomicLoad_361bf1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %16 = OpConstantNull %uint
+         %17 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicLoad_361bf1 = OpFunction %void None %6
+          %9 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %16
+         %10 = OpAtomicLoad %uint %arg_0 %uint_2 %uint_0
+               OpStore %res %10
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %17
+%local_invocation_index = OpFunctionParameter %uint
+         %20 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %16
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %25 = OpFunctionCall %void %atomicLoad_361bf1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %27 = OpLabel
+         %29 = OpLoad %uint %local_invocation_index_1
+         %28 = OpFunctionCall %void %compute_main_inner %29
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..8bd40a6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicLoad_361bf1() {
+  uint res = 0u;
+  uint x_10 = atomicOr(arg_0, 0u);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicLoad_361bf1();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..803eadd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,44 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicLoad_361bf1() {
+  uint res = 0u;
+  uint atomic_result = 0u;
+  InterlockedOr(arg_0, 0, atomic_result);
+  const uint x_10 = atomic_result;
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicLoad_361bf1();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..d7fcb6e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicLoad_361bf1(threadgroup atomic_uint* const tint_symbol) {
+  uint res = 0u;
+  uint const x_10 = atomic_load_explicit(tint_symbol, memory_order_relaxed);
+  res = x_10;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicLoad_361bf1(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_29 = *(tint_symbol_2);
+  compute_main_inner(x_29, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..7a075ba
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,73 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 43
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicLoad_361bf1 "atomicLoad_361bf1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicLoad_361bf1 = OpFunction %void None %9
+         %12 = OpLabel
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %res %6
+         %15 = OpAtomicLoad %uint %arg_0 %uint_2 %uint_0
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicLoad_361bf1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %29 = OpLabel
+         %30 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %30
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %19
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %34 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %38 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %40 = OpLabel
+         %42 = OpLoad %uint %local_invocation_index_1_param_1
+         %41 = OpFunctionCall %void %compute_main_inner_1 %42
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..f132c6f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicLoad/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,29 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicLoad_361bf1() {
+  var res : u32 = 0u;
+  let x_10 : u32 = atomicLoad(&(arg_0));
+  res = x_10;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicLoad_361bf1();
+  return;
+}
+
+fn compute_main_1() {
+  let x_29 : u32 = local_invocation_index_1;
+  compute_main_inner(x_29);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm
new file mode 100644
index 0000000..e6ff30e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMax_92aa72 "atomicMax_92aa72"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicMax_92aa72 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+         %13 = OpAtomicSMax %int %19 %uint_1 %uint_0 %20
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicMax_92aa72
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicMax_92aa72
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..d458f05
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicMax_92aa72() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicMax(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicMax_92aa72() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicMax(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..9b64baf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicMax(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedMax(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicMax_92aa72() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicMax(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..61bf2eb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicMax_92aa72(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  int const x_13 = atomic_fetch_max_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicMax_92aa72(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicMax_92aa72(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..c0a4470
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMax_92aa72 "atomicMax_92aa72"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicMax_92aa72 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %14 = OpLoad %int %arg_1
+         %21 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %15 = OpAtomicSMax %int %21 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicMax_92aa72
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicMax_92aa72
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..eea496e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicMax_92aa72() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  let x_13 : i32 = atomicMax(&(sb_rw.arg_0), x_20);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicMax_92aa72();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm
new file mode 100644
index 0000000..97bae6a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMax_51b9be "atomicMax_51b9be"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicMax_51b9be = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+         %13 = OpAtomicUMax %uint %17 %uint_1 %uint_0 %18
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicMax_51b9be
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicMax_51b9be
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..e5f7f89
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicMax_51b9be() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicMax(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicMax_51b9be() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicMax(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..e9a8f84
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicMax(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedMax(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicMax_51b9be() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicMax(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..a77e9cf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicMax_51b9be(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_13 = atomic_fetch_max_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicMax_51b9be(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicMax_51b9be(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..4ed13b0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMax_51b9be "atomicMax_51b9be"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicMax_51b9be = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %14 = OpLoad %uint %arg_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %15 = OpAtomicUMax %uint %19 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicMax_51b9be
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicMax_51b9be
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..ea27f1d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicMax_51b9be() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_13 : u32 = atomicMax(&(sb_rw.arg_0), x_18);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicMax_51b9be();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm
new file mode 100644
index 0000000..2864a14
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMax_a89cc3 "atomicMax_a89cc3"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMax_a89cc3 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %15 = OpAtomicSMax %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicMax_a89cc3
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..ce19147
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicMax_a89cc3() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicMax(arg_0, arg_1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicMax_a89cc3();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..3020cf2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicMax_a89cc3() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedMax(arg_0, arg_1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicMax_a89cc3();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..7a3987d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicMax_a89cc3(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  int const x_15 = atomic_fetch_max_explicit(tint_symbol, x_19, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicMax_a89cc3(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..696868b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,81 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMax_a89cc3 "atomicMax_a89cc3"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %24 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMax_a89cc3 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %20 = OpAtomicSMax %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %20
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %24
+%local_invocation_index = OpFunctionParameter %uint
+         %27 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicMax_a89cc3
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %34 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %36 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %24
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %43 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1_param_1
+         %46 = OpFunctionCall %void %compute_main_inner_1 %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..9086b74
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicMax_a89cc3() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  let x_15 : i32 = atomicMax(&(arg_0), x_19);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicMax_a89cc3();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm
new file mode 100644
index 0000000..03577ee
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMax_beccfc "atomicMax_beccfc"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMax_beccfc = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+         %14 = OpAtomicUMax %uint %arg_0 %uint_2 %uint_0 %18
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicMax_beccfc
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..f209644
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicMax_beccfc() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicMax(arg_0, arg_1);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicMax_beccfc();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..b87186d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicMax_beccfc() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedMax(arg_0, arg_1, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicMax_beccfc();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..edd3102
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicMax_beccfc(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_14 = atomic_fetch_max_explicit(tint_symbol, x_18, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicMax_beccfc(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..ad735e1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,79 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMax_beccfc "atomicMax_beccfc"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMax_beccfc = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpLoad %uint %arg_1
+         %18 = OpAtomicUMax %uint %arg_0 %uint_2 %uint_0 %17
+               OpStore %res %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicMax_beccfc
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..e740599
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMax/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicMax_beccfc() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_14 : u32 = atomicMax(&(arg_0), x_18);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicMax_beccfc();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm
new file mode 100644
index 0000000..f2ca9b8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMin_8e38dc "atomicMin_8e38dc"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicMin_8e38dc = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+         %13 = OpAtomicSMin %int %19 %uint_1 %uint_0 %20
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicMin_8e38dc
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicMin_8e38dc
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..e233a68
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicMin_8e38dc() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicMin(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicMin_8e38dc() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicMin(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..e169190
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicMin(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedMin(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicMin_8e38dc() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicMin(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..dfe6ecb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicMin_8e38dc(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  int const x_13 = atomic_fetch_min_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicMin_8e38dc(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicMin_8e38dc(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..7e5e670
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMin_8e38dc "atomicMin_8e38dc"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicMin_8e38dc = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %14 = OpLoad %int %arg_1
+         %21 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %15 = OpAtomicSMin %int %21 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicMin_8e38dc
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicMin_8e38dc
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..0ef4541
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicMin_8e38dc() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  let x_13 : i32 = atomicMin(&(sb_rw.arg_0), x_20);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicMin_8e38dc();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm
new file mode 100644
index 0000000..f3cc67b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMin_c67a74 "atomicMin_c67a74"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicMin_c67a74 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+         %13 = OpAtomicUMin %uint %17 %uint_1 %uint_0 %18
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicMin_c67a74
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicMin_c67a74
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..06e8d89
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicMin_c67a74() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicMin(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicMin_c67a74() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicMin(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..76447ef
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicMin(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedMin(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicMin_c67a74() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicMin(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..147d01e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicMin_c67a74(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_13 = atomic_fetch_min_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicMin_c67a74(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicMin_c67a74(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..309f258
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicMin_c67a74 "atomicMin_c67a74"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicMin_c67a74 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %14 = OpLoad %uint %arg_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %15 = OpAtomicUMin %uint %19 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicMin_c67a74
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicMin_c67a74
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..3586e32
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicMin_c67a74() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_13 : u32 = atomicMin(&(sb_rw.arg_0), x_18);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicMin_c67a74();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm
new file mode 100644
index 0000000..ddfa25a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMin_278235 "atomicMin_278235"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMin_278235 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %15 = OpAtomicSMin %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicMin_278235
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..abb7ad0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicMin_278235() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicMin(arg_0, arg_1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicMin_278235();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..cfc09f2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicMin_278235() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedMin(arg_0, arg_1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicMin_278235();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..8b6eeeb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicMin_278235(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  int const x_15 = atomic_fetch_min_explicit(tint_symbol, x_19, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicMin_278235(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..564e8b7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,81 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMin_278235 "atomicMin_278235"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %24 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMin_278235 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %20 = OpAtomicSMin %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %20
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %24
+%local_invocation_index = OpFunctionParameter %uint
+         %27 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicMin_278235
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %34 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %36 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %24
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %43 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1_param_1
+         %46 = OpFunctionCall %void %compute_main_inner_1 %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..e55849b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicMin_278235() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  let x_15 : i32 = atomicMin(&(arg_0), x_19);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicMin_278235();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm
new file mode 100644
index 0000000..784a014
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMin_69d383 "atomicMin_69d383"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMin_69d383 = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+         %14 = OpAtomicUMin %uint %arg_0 %uint_2 %uint_0 %18
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicMin_69d383
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..b5b3a4a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicMin_69d383() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicMin(arg_0, arg_1);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicMin_69d383();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..64c8150
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicMin_69d383() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedMin(arg_0, arg_1, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicMin_69d383();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..6336751
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicMin_69d383(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_14 = atomic_fetch_min_explicit(tint_symbol, x_18, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicMin_69d383(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..8c0bfde
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,79 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicMin_69d383 "atomicMin_69d383"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicMin_69d383 = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpLoad %uint %arg_1
+         %18 = OpAtomicUMin %uint %arg_0 %uint_2 %uint_0 %17
+               OpStore %res %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicMin_69d383
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..9a15df0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicMin/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicMin_69d383() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_14 : u32 = atomicMin(&(arg_0), x_18);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicMin_69d383();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm
new file mode 100644
index 0000000..978e4ad
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicOr_8d96a0 "atomicOr_8d96a0"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicOr_8d96a0 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+         %13 = OpAtomicOr %int %19 %uint_1 %uint_0 %20
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicOr_8d96a0
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicOr_8d96a0
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..dd17274
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicOr_8d96a0() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicOr(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicOr_8d96a0() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicOr(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..1daeac4
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicOr(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedOr(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicOr_8d96a0() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicOr(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..a0bf71f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicOr_8d96a0(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  int const x_13 = atomic_fetch_or_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicOr_8d96a0(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicOr_8d96a0(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..a5753a7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicOr_8d96a0 "atomicOr_8d96a0"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicOr_8d96a0 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %14 = OpLoad %int %arg_1
+         %21 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %15 = OpAtomicOr %int %21 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicOr_8d96a0
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicOr_8d96a0
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..5c96c73
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicOr_8d96a0() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  let x_13 : i32 = atomicOr(&(sb_rw.arg_0), x_20);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicOr_8d96a0();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm
new file mode 100644
index 0000000..c38fc52
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicOr_5e95d4 "atomicOr_5e95d4"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicOr_5e95d4 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+         %13 = OpAtomicOr %uint %17 %uint_1 %uint_0 %18
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicOr_5e95d4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicOr_5e95d4
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..ba78059
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicOr_5e95d4() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicOr(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicOr_5e95d4() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicOr(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..2268f30
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicOr(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedOr(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicOr_5e95d4() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicOr(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..fa54565
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicOr_5e95d4(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_13 = atomic_fetch_or_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicOr_5e95d4(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicOr_5e95d4(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..2d2a230
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicOr_5e95d4 "atomicOr_5e95d4"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicOr_5e95d4 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %14 = OpLoad %uint %arg_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %15 = OpAtomicOr %uint %19 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicOr_5e95d4
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicOr_5e95d4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..8dae20d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicOr_5e95d4() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_13 : u32 = atomicOr(&(sb_rw.arg_0), x_18);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicOr_5e95d4();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm
new file mode 100644
index 0000000..6a5541c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicOr_d09248 "atomicOr_d09248"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicOr_d09248 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %15 = OpAtomicOr %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicOr_d09248
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..b045e75
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicOr_d09248() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicOr(arg_0, arg_1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicOr_d09248();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..a4a9b58
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicOr_d09248() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedOr(arg_0, arg_1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicOr_d09248();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..e262929
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicOr_d09248(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  int const x_15 = atomic_fetch_or_explicit(tint_symbol, x_19, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicOr_d09248(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..f2cdcca
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,81 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicOr_d09248 "atomicOr_d09248"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %24 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicOr_d09248 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %20 = OpAtomicOr %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %20
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %24
+%local_invocation_index = OpFunctionParameter %uint
+         %27 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicOr_d09248
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %34 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %36 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %24
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %43 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1_param_1
+         %46 = OpFunctionCall %void %compute_main_inner_1 %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..f0329eb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicOr_d09248() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  let x_15 : i32 = atomicOr(&(arg_0), x_19);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicOr_d09248();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm
new file mode 100644
index 0000000..9db7191
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicOr_5e3d61 "atomicOr_5e3d61"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicOr_5e3d61 = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+         %14 = OpAtomicOr %uint %arg_0 %uint_2 %uint_0 %18
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicOr_5e3d61
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..2894885
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicOr_5e3d61() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicOr(arg_0, arg_1);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicOr_5e3d61();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..7a4ad50
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicOr_5e3d61() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedOr(arg_0, arg_1, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicOr_5e3d61();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..4078be6
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicOr_5e3d61(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_14 = atomic_fetch_or_explicit(tint_symbol, x_18, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicOr_5e3d61(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..437d7bf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,79 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicOr_5e3d61 "atomicOr_5e3d61"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicOr_5e3d61 = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpLoad %uint %arg_1
+         %18 = OpAtomicOr %uint %arg_0 %uint_2 %uint_0 %17
+               OpStore %res %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicOr_5e3d61
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..781ca2e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicOr/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicOr_5e3d61() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_14 : u32 = atomicOr(&(arg_0), x_18);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicOr_5e3d61();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm
new file mode 100644
index 0000000..5d03e4e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm
@@ -0,0 +1,54 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 27
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicStore_d1e9a6 "atomicStore_d1e9a6"
+               OpName %arg_1 "arg_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicStore_d1e9a6 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+               OpAtomicStore %19 %uint_1 %uint_0 %20
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicStore_d1e9a6
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %atomicStore_d1e9a6
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..cf63c29
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicStore_d1e9a6() {
+  int arg_1 = 0;
+  arg_1 = 1;
+  atomicExchange(sb_rw.arg_0, arg_1);
+  return;
+}
+
+void fragment_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicStore_d1e9a6() {
+  int arg_1 = 0;
+  arg_1 = 1;
+  atomicExchange(sb_rw.arg_0, arg_1);
+  return;
+}
+
+void compute_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..4151049
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,35 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+void tint_atomicStore(RWByteAddressBuffer buffer, uint offset, int value) {
+  int ignored;
+  buffer.InterlockedExchange(offset, value, ignored);
+}
+
+
+void atomicStore_d1e9a6() {
+  int arg_1 = 0;
+  arg_1 = 1;
+  tint_atomicStore(sb_rw, 0u, arg_1);
+  return;
+}
+
+void fragment_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..4aa0544
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicStore_d1e9a6(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  atomic_store_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicStore_d1e9a6(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicStore_d1e9a6(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..77ab9b0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,67 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicStore_d1e9a6 "atomicStore_d1e9a6"
+               OpName %arg_1 "arg_1"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicStore_d1e9a6 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %arg_1 %int_1
+         %13 = OpLoad %int %arg_1
+         %20 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+               OpAtomicStore %20 %uint_1 %uint_0 %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicStore_d1e9a6
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %atomicStore_d1e9a6
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %31 = OpLabel
+         %32 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..9db31ae
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,37 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicStore_d1e9a6() {
+  var arg_1 : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  atomicStore(&(sb_rw.arg_0), x_20);
+  return;
+}
+
+fn fragment_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicStore_d1e9a6();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm
new file mode 100644
index 0000000..f0d74fe
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm
@@ -0,0 +1,52 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 25
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicStore_cdc29e "atomicStore_cdc29e"
+               OpName %arg_1 "arg_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicStore_cdc29e = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+               OpAtomicStore %17 %uint_1 %uint_0 %18
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %20 = OpLabel
+         %21 = OpFunctionCall %void %atomicStore_cdc29e
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicStore_cdc29e
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..7a8d693
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,68 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicStore_cdc29e() {
+  uint arg_1 = 0u;
+  arg_1 = 1u;
+  atomicExchange(sb_rw.arg_0, arg_1);
+  return;
+}
+
+void fragment_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicStore_cdc29e() {
+  uint arg_1 = 0u;
+  arg_1 = 1u;
+  atomicExchange(sb_rw.arg_0, arg_1);
+  return;
+}
+
+void compute_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..4550cdd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,35 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+void tint_atomicStore(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint ignored;
+  buffer.InterlockedExchange(offset, value, ignored);
+}
+
+
+void atomicStore_cdc29e() {
+  uint arg_1 = 0u;
+  arg_1 = 1u;
+  tint_atomicStore(sb_rw, 0u, arg_1);
+  return;
+}
+
+void fragment_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..21f9746
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.msl
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicStore_cdc29e(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  atomic_store_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicStore_cdc29e(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicStore_cdc29e(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..ababfa4
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,65 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicStore_cdc29e "atomicStore_cdc29e"
+               OpName %arg_1 "arg_1"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicStore_cdc29e = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %arg_1 %uint_1
+         %13 = OpLoad %uint %arg_1
+         %18 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+               OpAtomicStore %18 %uint_1 %uint_0 %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %20 = OpLabel
+         %21 = OpFunctionCall %void %atomicStore_cdc29e
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicStore_cdc29e
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..1095ccc
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,37 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicStore_cdc29e() {
+  var arg_1 : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  atomicStore(&(sb_rw.arg_0), x_18);
+  return;
+}
+
+fn fragment_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicStore_cdc29e();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm
new file mode 100644
index 0000000..a0c7fb7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm
@@ -0,0 +1,54 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicStore_8bea94 "atomicStore_8bea94"
+               OpName %arg_1 "arg_1"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicStore_8bea94 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %19
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicStore_8bea94
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..89e564c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicStore_8bea94() {
+  int arg_1 = 0;
+  arg_1 = 1;
+  atomicExchange(arg_0, arg_1);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicStore_8bea94();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..68704dd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,43 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicStore_8bea94() {
+  int arg_1 = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedExchange(arg_0, arg_1, atomic_result);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicStore_8bea94();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..1e0fd57
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,40 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicStore_8bea94(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  atomic_store_explicit(tint_symbol, x_19, memory_order_relaxed);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicStore_8bea94(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..63e9312
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,77 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 47
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicStore_8bea94 "atomicStore_8bea94"
+               OpName %arg_1 "arg_1"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %23 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicStore_8bea94 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %arg_1 %int_1
+         %18 = OpLoad %int %arg_1
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %23
+%local_invocation_index = OpFunctionParameter %uint
+         %26 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %31 = OpFunctionCall %void %atomicStore_8bea94
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %33 = OpLabel
+         %34 = OpLoad %uint %local_invocation_index_1
+         %35 = OpFunctionCall %void %compute_main_inner %34
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %23
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %38 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %42 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %44 = OpLabel
+         %46 = OpLoad %uint %local_invocation_index_1_param_1
+         %45 = OpFunctionCall %void %compute_main_inner_1 %46
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..0262122
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,30 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicStore_8bea94() {
+  var arg_1 : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  atomicStore(&(arg_0), x_19);
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicStore_8bea94();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm
new file mode 100644
index 0000000..72d2c5f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm
@@ -0,0 +1,53 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicStore_726882 "atomicStore_726882"
+               OpName %arg_1 "arg_1"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %19 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicStore_726882 = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %19
+%local_invocation_index = OpFunctionParameter %uint
+         %22 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %27 = OpFunctionCall %void %atomicStore_726882
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %29 = OpLabel
+         %31 = OpLoad %uint %local_invocation_index_1
+         %30 = OpFunctionCall %void %compute_main_inner %31
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..dcceb31
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,37 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicStore_726882() {
+  uint arg_1 = 0u;
+  arg_1 = 1u;
+  atomicExchange(arg_0, arg_1);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicStore_726882();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..5c41763
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,43 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicStore_726882() {
+  uint arg_1 = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedExchange(arg_0, arg_1, atomic_result);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicStore_726882();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..fd36267
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,40 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicStore_726882(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  atomic_store_explicit(tint_symbol, x_18, memory_order_relaxed);
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicStore_726882(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_31 = *(tint_symbol_2);
+  compute_main_inner(x_31, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..2b50c09
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,75 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 45
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicStore_726882 "atomicStore_726882"
+               OpName %arg_1 "arg_1"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicStore_726882 = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %arg_1 %uint_1
+         %16 = OpLoad %uint %arg_1
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %16
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicStore_726882
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %31 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %33 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %21
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %36 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %40 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %42 = OpLabel
+         %44 = OpLoad %uint %local_invocation_index_1_param_1
+         %43 = OpFunctionCall %void %compute_main_inner_1 %44
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..fa0013a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicStore/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,30 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicStore_726882() {
+  var arg_1 : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  atomicStore(&(arg_0), x_18);
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicStore_726882();
+  return;
+}
+
+fn compute_main_1() {
+  let x_31 : u32 = local_invocation_index_1;
+  compute_main_inner(x_31);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm
new file mode 100644
index 0000000..3bbf26a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicSub_051100 "atomicSub_051100"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicSub_051100 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+         %13 = OpAtomicISub %int %19 %uint_1 %uint_0 %20
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicSub_051100
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicSub_051100
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..fcc4155
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicSub_051100() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAdd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicSub_051100() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAdd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..fca2ec1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicSub(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAdd(offset, -value, original_value);
+  return original_value;
+}
+
+
+void atomicSub_051100() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicSub(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..2a79213
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicSub_051100(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  int const x_13 = atomic_fetch_sub_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicSub_051100(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicSub_051100(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..c40beba
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicSub_051100 "atomicSub_051100"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicSub_051100 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %14 = OpLoad %int %arg_1
+         %21 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %15 = OpAtomicISub %int %21 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicSub_051100
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicSub_051100
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..d47d886
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicSub_051100() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  let x_13 : i32 = atomicSub(&(sb_rw.arg_0), x_20);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicSub_051100();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm
new file mode 100644
index 0000000..4d20c6d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicSub_15bfc9 "atomicSub_15bfc9"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicSub_15bfc9 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+         %13 = OpAtomicISub %uint %17 %uint_1 %uint_0 %18
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicSub_15bfc9
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicSub_15bfc9
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..f91b7f8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicSub_15bfc9() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAdd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicSub_15bfc9() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAdd(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..f036c90
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicSub(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAdd(offset, -value, original_value);
+  return original_value;
+}
+
+
+void atomicSub_15bfc9() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicSub(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..efb4526
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicSub_15bfc9(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_13 = atomic_fetch_sub_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicSub_15bfc9(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicSub_15bfc9(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..6704485
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicSub_15bfc9 "atomicSub_15bfc9"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicSub_15bfc9 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %14 = OpLoad %uint %arg_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %15 = OpAtomicISub %uint %19 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicSub_15bfc9
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicSub_15bfc9
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..bd9a80c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicSub_15bfc9() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_13 : u32 = atomicSub(&(sb_rw.arg_0), x_18);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicSub_15bfc9();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm
new file mode 100644
index 0000000..d96432d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicSub_77883a "atomicSub_77883a"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicSub_77883a = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %15 = OpAtomicISub %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicSub_77883a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..e08bb7b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicSub_77883a() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicAdd(arg_0, arg_1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicSub_77883a();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..988dbad
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicSub_77883a() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedAdd(arg_0, -arg_1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicSub_77883a();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..208e18a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicSub_77883a(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  int const x_15 = atomic_fetch_sub_explicit(tint_symbol, x_19, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicSub_77883a(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..9f3a065
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,81 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicSub_77883a "atomicSub_77883a"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %24 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicSub_77883a = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %20 = OpAtomicISub %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %20
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %24
+%local_invocation_index = OpFunctionParameter %uint
+         %27 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicSub_77883a
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %34 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %36 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %24
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %43 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1_param_1
+         %46 = OpFunctionCall %void %compute_main_inner_1 %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b2781ce
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicSub_77883a() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  let x_15 : i32 = atomicSub(&(arg_0), x_19);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicSub_77883a();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm
new file mode 100644
index 0000000..2c6b98e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicSub_0d26c2 "atomicSub_0d26c2"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicSub_0d26c2 = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+         %14 = OpAtomicISub %uint %arg_0 %uint_2 %uint_0 %18
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicSub_0d26c2
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..a11a53e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicSub_0d26c2() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicAdd(arg_0, arg_1);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicSub_0d26c2();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..eb60c8a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicSub_0d26c2() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedAdd(arg_0, -arg_1, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicSub_0d26c2();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..ec9661d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicSub_0d26c2(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_14 = atomic_fetch_sub_explicit(tint_symbol, x_18, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicSub_0d26c2(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..c1b5f7a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,79 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicSub_0d26c2 "atomicSub_0d26c2"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicSub_0d26c2 = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpLoad %uint %arg_1
+         %18 = OpAtomicISub %uint %arg_0 %uint_2 %uint_0 %17
+               OpStore %res %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicSub_0d26c2
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..77f25f7
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicSub/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicSub_0d26c2() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_14 : u32 = atomicSub(&(arg_0), x_18);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicSub_0d26c2();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm
new file mode 100644
index 0000000..cff5564
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicXor_c1b78c "atomicXor_c1b78c"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicXor_c1b78c = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %20 = OpLoad %int %arg_1
+         %13 = OpAtomicXor %int %19 %uint_1 %uint_0 %20
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicXor_c1b78c
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicXor_c1b78c
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..4953346
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicXor_c1b78c() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicXor(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicXor_c1b78c() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicXor(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..195284c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicXor(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedXor(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicXor_c1b78c() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicXor(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..1e98bee
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicXor_c1b78c(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_20 = arg_1;
+  int const x_13 = atomic_fetch_xor_explicit(&((*(tint_symbol)).arg_0), x_20, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicXor_c1b78c(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicXor_c1b78c(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..74a9caa
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,71 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicXor_c1b78c "atomicXor_c1b78c"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicXor_c1b78c = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %14 = OpLoad %int %arg_1
+         %21 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %15 = OpAtomicXor %int %21 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicXor_c1b78c
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %atomicXor_c1b78c
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %32 = OpLabel
+         %33 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..4325da1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicXor_c1b78c() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_20 : i32 = arg_1;
+  let x_13 : i32 = atomicXor(&(sb_rw.arg_0), x_20);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicXor_c1b78c();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm
new file mode 100644
index 0000000..a16daff
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicXor_54510e "atomicXor_54510e"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicXor_54510e = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %18 = OpLoad %uint %arg_1
+         %13 = OpAtomicXor %uint %17 %uint_1 %uint_0 %18
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicXor_54510e
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicXor_54510e
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..c382bff
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicXor_54510e() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicXor(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicXor_54510e() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicXor(sb_rw.arg_0, arg_1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..36ccd47
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicXor(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedXor(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicXor_54510e() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicXor(sb_rw, 0u, arg_1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..068d4aa
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicXor_54510e(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_13 = atomic_fetch_xor_explicit(&((*(tint_symbol)).arg_0), x_18, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicXor_54510e(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicXor_54510e(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..786b9f60
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,69 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 32
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicXor_54510e "atomicXor_54510e"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicXor_54510e = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %14 = OpLoad %uint %arg_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %15 = OpAtomicXor %uint %19 %uint_1 %uint_0 %14
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicXor_54510e
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %27 = OpLabel
+         %28 = OpFunctionCall %void %atomicXor_54510e
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %30 = OpLabel
+         %31 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..2b3dd0d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,39 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicXor_54510e() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_13 : u32 = atomicXor(&(sb_rw.arg_0), x_18);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicXor_54510e();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm
new file mode 100644
index 0000000..62e0351
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm
@@ -0,0 +1,57 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicXor_75dc95 "atomicXor_75dc95"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicXor_75dc95 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %15 = OpAtomicXor %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicXor_75dc95
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..f568517
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicXor_75dc95() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicXor(arg_0, arg_1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicXor_75dc95();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..2c71c9a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicXor_75dc95() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedXor(arg_0, arg_1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicXor_75dc95();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..840c318
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicXor_75dc95(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_19 = arg_1;
+  int const x_15 = atomic_fetch_xor_explicit(tint_symbol, x_19, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicXor_75dc95(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..2bac01d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.spvasm
@@ -0,0 +1,81 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 48
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicXor_75dc95 "atomicXor_75dc95"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %24 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicXor_75dc95 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpLoad %int %arg_1
+         %20 = OpAtomicXor %int %arg_0 %uint_2 %uint_0 %19
+               OpStore %res %20
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %24
+%local_invocation_index = OpFunctionParameter %uint
+         %27 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %32 = OpFunctionCall %void %atomicXor_75dc95
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %34 = OpLabel
+         %35 = OpLoad %uint %local_invocation_index_1
+         %36 = OpFunctionCall %void %compute_main_inner %35
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %24
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %39 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %43 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %45 = OpLabel
+         %47 = OpLoad %uint %local_invocation_index_1_param_1
+         %46 = OpFunctionCall %void %compute_main_inner_1 %47
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..54490e1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicXor_75dc95() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_19 : i32 = arg_1;
+  let x_15 : i32 = atomicXor(&(arg_0), x_19);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicXor_75dc95();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm
new file mode 100644
index 0000000..ba98e8d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicXor_c8e6be "atomicXor_c8e6be"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicXor_c8e6be = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %18 = OpLoad %uint %arg_1
+         %14 = OpAtomicXor %uint %arg_0 %uint_2 %uint_0 %18
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicXor_c8e6be
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..2c88781
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicXor_c8e6be() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicXor(arg_0, arg_1);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicXor_c8e6be();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..e7ceab2
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicXor_c8e6be() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedXor(arg_0, arg_1, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicXor_c8e6be();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..3eb2509
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicXor_c8e6be(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_18 = arg_1;
+  uint const x_14 = atomic_fetch_xor_explicit(tint_symbol, x_18, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicXor_c8e6be(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..83c9054
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,79 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 46
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicXor_c8e6be "atomicXor_c8e6be"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %22 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicXor_c8e6be = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpLoad %uint %arg_1
+         %18 = OpAtomicXor %uint %arg_0 %uint_2 %uint_0 %17
+               OpStore %res %18
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %22
+%local_invocation_index = OpFunctionParameter %uint
+         %25 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %30 = OpFunctionCall %void %atomicXor_c8e6be
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %32 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %34 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %22
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %37 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %41 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %43 = OpLabel
+         %45 = OpLoad %uint %local_invocation_index_1_param_1
+         %44 = OpFunctionCall %void %compute_main_inner_1 %45
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..c75d808
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/atomicXor/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,32 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicXor_c8e6be() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_18 : u32 = arg_1;
+  let x_14 : u32 = atomicXor(&(arg_0), x_18);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicXor_c8e6be();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm
new file mode 100644
index 0000000..fb43e2c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %13 = OpAtomicIDecrement %int %19 %uint_1 %uint_0
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..167e99c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..abf10b5
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicSub(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAdd(offset, -value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicSub(sb_rw, 0u, 1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..8bbb600
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.msl
@@ -0,0 +1,40 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicAdd_d32fe4(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_13 = atomic_fetch_sub_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_d32fe4(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_d32fe4(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..870704e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,70 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %20 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %14 = OpAtomicISub %int %20 %uint_1 %uint_0 %int_1
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %31 = OpLabel
+         %32 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..1e011b0
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,38 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_d32fe4() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_13 : i32 = atomicSub(&(sb_rw.arg_0), 1i);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm
new file mode 100644
index 0000000..9e693ca
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm
@@ -0,0 +1,54 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %13 = OpAtomicIDecrement %uint %17 %uint_1 %uint_0
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..d9e0515
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..4e6bb86
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicSub(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAdd(offset, -value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicSub(sb_rw, 0u, 1u);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..7fe160f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.msl
@@ -0,0 +1,40 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicAdd_8a199a(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_13 = atomic_fetch_sub_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_8a199a(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_8a199a(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..63b3eeb
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,68 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %18 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %14 = OpAtomicISub %uint %18 %uint_1 %uint_0 %uint_1
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %20 = OpLabel
+         %21 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..f12b29e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,38 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_8a199a() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_13 : u32 = atomicSub(&(sb_rw.arg_0), 1u);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm
new file mode 100644
index 0000000..9f14c53
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %15 = OpAtomicIDecrement %int %arg_0 %uint_2 %uint_0
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..ef3ffb1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicAdd_794055() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicAdd(arg_0, 1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..181873a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicAdd_794055() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedAdd(arg_0, -1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..9f11082
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_794055(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_15 = atomic_fetch_sub_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_794055(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..843bd5a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.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 %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %23 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpAtomicISub %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %19
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %23
+%local_invocation_index = OpFunctionParameter %uint
+         %26 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %31 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %33 = OpLabel
+         %34 = OpLoad %uint %local_invocation_index_1
+         %35 = OpFunctionCall %void %compute_main_inner %34
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %23
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %38 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %42 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %44 = OpLabel
+         %46 = OpLoad %uint %local_invocation_index_1_param_1
+         %45 = OpFunctionCall %void %compute_main_inner_1 %46
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..540501a
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,31 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicAdd_794055() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_15 : i32 = atomicSub(&(arg_0), 1i);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicAdd_794055();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm
new file mode 100644
index 0000000..1d1b5bd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %14 = OpAtomicIDecrement %uint %arg_0 %uint_2 %uint_0
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..b9c2faf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicAdd_d5db1d() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicAdd(arg_0, 1u);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..16aaa1c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicAdd_d5db1d() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedAdd(arg_0, -1u, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..74b7a0d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_d5db1d(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_14 = atomic_fetch_sub_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_d5db1d(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..77041b4
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,78 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 45
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpAtomicISub %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %31 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %33 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %21
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %36 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %40 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %42 = OpLabel
+         %44 = OpLoad %uint %local_invocation_index_1_param_1
+         %43 = OpFunctionCall %void %compute_main_inner_1 %44
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..64dabf4
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicDecrement/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,31 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicAdd_d5db1d() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_14 : u32 = atomicSub(&(arg_0), 1u);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm
new file mode 100644
index 0000000..9bacaec
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 28
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+      %SB_RW = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %12 = OpConstantNull %int
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %12
+        %res = OpVariable %_ptr_Function_int Function %12
+               OpStore %arg_1 %int_1
+         %19 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %13 = OpAtomicIIncrement %int %19 %uint_1 %uint_0
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..167e99c
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  int arg_0;
+} sb_rw;
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_13 = atomicAdd(sb_rw.arg_0, 1);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..9be7a9e
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+int tint_atomicAdd(RWByteAddressBuffer buffer, uint offset, int value) {
+  int original_value = 0;
+  buffer.InterlockedAdd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_d32fe4() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  const int x_13 = tint_atomicAdd(sb_rw, 0u, 1);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.msl
new file mode 100644
index 0000000..acfdf7f
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.msl
@@ -0,0 +1,40 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_int arg_0;
+};
+
+struct SB_RW {
+  int arg_0;
+};
+
+void atomicAdd_d32fe4(device SB_RW_atomic* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_13 = atomic_fetch_add_explicit(&((*(tint_symbol)).arg_0), 1, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_d32fe4(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_d32fe4(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..f206f17
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.spvasm
@@ -0,0 +1,70 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_d32fe4 "atomicAdd_d32fe4"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+        %int = OpTypeInt 32 1
+%SB_RW_atomic = OpTypeStruct %int
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+       %uint = OpTypeInt 32 0
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
+%atomicAdd_d32fe4 = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %9
+        %res = OpVariable %_ptr_Function_int Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %int_1
+         %20 = OpAccessChain %_ptr_StorageBuffer_int %sb_rw %uint_0
+         %14 = OpAtomicIAdd %int %20 %uint_1 %uint_0 %int_1
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %22 = OpLabel
+         %23 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %25 = OpLabel
+         %26 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %28 = OpLabel
+         %29 = OpFunctionCall %void %atomicAdd_d32fe4
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %31 = OpLabel
+         %32 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..da3a03b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_i32.spvasm.expected.wgsl
@@ -0,0 +1,38 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<i32>,
+}
+
+struct SB_RW {
+  arg_0 : i32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_d32fe4() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_13 : i32 = atomicAdd(&(sb_rw.arg_0), 1i);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_d32fe4();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm
new file mode 100644
index 0000000..8aad806
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm
@@ -0,0 +1,54 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 26
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW "SB_RW"
+               OpMemberName %SB_RW 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW Block
+               OpMemberDecorate %SB_RW 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+      %SB_RW = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW = OpTypePointer StorageBuffer %SB_RW
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %12 = OpConstantNull %uint
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %12
+        %res = OpVariable %_ptr_Function_uint Function %12
+               OpStore %arg_1 %uint_1
+         %17 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %13 = OpAtomicIIncrement %uint %17 %uint_1 %uint_0
+               OpStore %res %13
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %21 = OpLabel
+         %22 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %24 = OpLabel
+         %25 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..d9e0515
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.glsl
@@ -0,0 +1,72 @@
+#version 310 es
+precision mediump float;
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+}
+
+void main() {
+  fragment_main();
+  return;
+}
+#version 310 es
+
+struct SB_RW_atomic {
+  uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+layout(binding = 0, std430) buffer SB_RW_atomic_1 {
+  uint arg_0;
+} sb_rw;
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_13 = atomicAdd(sb_rw.arg_0, 1u);
+  res = x_13;
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void compute_main() {
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..442a1ff
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.hlsl
@@ -0,0 +1,38 @@
+RWByteAddressBuffer sb_rw : register(u0, space0);
+
+uint tint_atomicAdd(RWByteAddressBuffer buffer, uint offset, uint value) {
+  uint original_value = 0;
+  buffer.InterlockedAdd(offset, value, original_value);
+  return original_value;
+}
+
+
+void atomicAdd_8a199a() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  const uint x_13 = tint_atomicAdd(sb_rw, 0u, 1u);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+void fragment_main() {
+  fragment_main_1();
+  return;
+}
+
+void compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+[numthreads(1, 1, 1)]
+void compute_main() {
+  compute_main_1();
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.msl
new file mode 100644
index 0000000..b3d0ecd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.msl
@@ -0,0 +1,40 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct SB_RW_atomic {
+  /* 0x0000 */ atomic_uint arg_0;
+};
+
+struct SB_RW {
+  uint arg_0;
+};
+
+void atomicAdd_8a199a(device SB_RW_atomic* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_13 = atomic_fetch_add_explicit(&((*(tint_symbol)).arg_0), 1u, memory_order_relaxed);
+  res = x_13;
+  return;
+}
+
+void fragment_main_1(device SB_RW_atomic* const tint_symbol_1) {
+  atomicAdd_8a199a(tint_symbol_1);
+  return;
+}
+
+fragment void fragment_main(device SB_RW_atomic* tint_symbol_2 [[buffer(0)]]) {
+  fragment_main_1(tint_symbol_2);
+  return;
+}
+
+void compute_main_1(device SB_RW_atomic* const tint_symbol_3) {
+  atomicAdd_8a199a(tint_symbol_3);
+  return;
+}
+
+kernel void compute_main(device SB_RW_atomic* tint_symbol_4 [[buffer(0)]]) {
+  compute_main_1(tint_symbol_4);
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..17ebf7b
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.spvasm
@@ -0,0 +1,68 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 31
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %fragment_main "fragment_main"
+               OpEntryPoint GLCompute %compute_main "compute_main"
+               OpExecutionMode %fragment_main OriginUpperLeft
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %SB_RW_atomic "SB_RW_atomic"
+               OpMemberName %SB_RW_atomic 0 "arg_0"
+               OpName %sb_rw "sb_rw"
+               OpName %atomicAdd_8a199a "atomicAdd_8a199a"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %fragment_main_1 "fragment_main_1"
+               OpName %fragment_main "fragment_main"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main "compute_main"
+               OpDecorate %SB_RW_atomic Block
+               OpMemberDecorate %SB_RW_atomic 0 Offset 0
+               OpDecorate %sb_rw DescriptorSet 0
+               OpDecorate %sb_rw Binding 0
+       %uint = OpTypeInt 32 0
+%SB_RW_atomic = OpTypeStruct %uint
+%_ptr_StorageBuffer_SB_RW_atomic = OpTypePointer StorageBuffer %SB_RW_atomic
+      %sb_rw = OpVariable %_ptr_StorageBuffer_SB_RW_atomic StorageBuffer
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+          %9 = OpConstantNull %uint
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+%atomicAdd_8a199a = OpFunction %void None %5
+          %8 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %9
+        %res = OpVariable %_ptr_Function_uint Function %9
+               OpStore %arg_1 %9
+               OpStore %res %9
+               OpStore %arg_1 %uint_1
+         %18 = OpAccessChain %_ptr_StorageBuffer_uint %sb_rw %uint_0
+         %14 = OpAtomicIAdd %uint %18 %uint_1 %uint_0 %uint_1
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%fragment_main_1 = OpFunction %void None %5
+         %20 = OpLabel
+         %21 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%fragment_main = OpFunction %void None %5
+         %23 = OpLabel
+         %24 = OpFunctionCall %void %fragment_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %5
+         %26 = OpLabel
+         %27 = OpFunctionCall %void %atomicAdd_8a199a
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %5
+         %29 = OpLabel
+         %30 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..61fbe49
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/storage_u32.spvasm.expected.wgsl
@@ -0,0 +1,38 @@
+struct SB_RW_atomic {
+  arg_0 : atomic<u32>,
+}
+
+struct SB_RW {
+  arg_0 : u32,
+}
+
+@group(0) @binding(0) var<storage, read_write> sb_rw : SB_RW_atomic;
+
+fn atomicAdd_8a199a() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_13 : u32 = atomicAdd(&(sb_rw.arg_0), 1u);
+  res = x_13;
+  return;
+}
+
+fn fragment_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@fragment
+fn fragment_main() {
+  fragment_main_1();
+}
+
+fn compute_main_1() {
+  atomicAdd_8a199a();
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main() {
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm
new file mode 100644
index 0000000..2fb3e8d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm
@@ -0,0 +1,56 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 34
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+      %int_1 = OpConstant %int 1
+%_ptr_Function_int = OpTypePointer Function %int
+         %14 = OpConstantNull %int
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %7
+         %10 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %int_1
+         %15 = OpAtomicIIncrement %int %arg_0 %uint_2 %uint_0
+               OpStore %res %15
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %7
+         %31 = OpLabel
+         %33 = OpLoad %uint %local_invocation_index_1
+         %32 = OpFunctionCall %void %compute_main_inner %33
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.glsl
new file mode 100644
index 0000000..ef3ffb1
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared int arg_0;
+void atomicAdd_794055() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int x_15 = atomicAdd(arg_0, 1);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0);
+  barrier();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.hlsl
new file mode 100644
index 0000000..6270b9d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared int arg_0;
+
+void atomicAdd_794055() {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int atomic_result = 0;
+  InterlockedAdd(arg_0, 1, atomic_result);
+  const int x_15 = atomic_result;
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  int atomic_result_1 = 0;
+  InterlockedExchange(arg_0, 0, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_794055();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    int atomic_result_2 = 0;
+    InterlockedExchange(arg_0, 0, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.msl
new file mode 100644
index 0000000..dc059dd
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_794055(threadgroup atomic_int* const tint_symbol) {
+  int arg_1 = 0;
+  int res = 0;
+  arg_1 = 1;
+  int const x_15 = atomic_fetch_add_explicit(tint_symbol, 1, memory_order_relaxed);
+  res = x_15;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_int* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_794055(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_int* const tint_symbol_3) {
+  uint const x_33 = *(tint_symbol_2);
+  compute_main_inner(x_33, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_int* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_int tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.spvasm
new file mode 100644
index 0000000..ed49501
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.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 %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_794055 "atomicAdd_794055"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+      %arg_0 = OpVariable %_ptr_Workgroup_int Workgroup
+       %void = OpTypeVoid
+         %10 = OpTypeFunction %void
+         %14 = OpConstantNull %int
+%_ptr_Function_int = OpTypePointer Function %int
+      %int_1 = OpConstant %int 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %23 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_794055 = OpFunction %void None %10
+         %13 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_int Function %14
+        %res = OpVariable %_ptr_Function_int Function %14
+               OpStore %arg_1 %14
+               OpStore %res %14
+               OpStore %arg_1 %int_1
+         %19 = OpAtomicIAdd %int %arg_0 %uint_2 %uint_0 %int_1
+               OpStore %res %19
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %23
+%local_invocation_index = OpFunctionParameter %uint
+         %26 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %31 = OpFunctionCall %void %atomicAdd_794055
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %10
+         %33 = OpLabel
+         %34 = OpLoad %uint %local_invocation_index_1
+         %35 = OpFunctionCall %void %compute_main_inner %34
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %23
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %38 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %14
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %42 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %10
+         %44 = OpLabel
+         %46 = OpLoad %uint %local_invocation_index_1_param_1
+         %45 = OpFunctionCall %void %compute_main_inner_1 %46
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.wgsl
new file mode 100644
index 0000000..b5076c3
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_i32.spvasm.expected.wgsl
@@ -0,0 +1,31 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<i32>;
+
+fn atomicAdd_794055() {
+  var arg_1 : i32 = 0i;
+  var res : i32 = 0i;
+  arg_1 = 1i;
+  let x_15 : i32 = atomicAdd(&(arg_0), 1i);
+  res = x_15;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0i);
+  workgroupBarrier();
+  atomicAdd_794055();
+  return;
+}
+
+fn compute_main_1() {
+  let x_33 : u32 = local_invocation_index_1;
+  compute_main_inner(x_33);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm
new file mode 100644
index 0000000..48d906d
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm
@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 33
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %6 = OpTypeFunction %void
+     %uint_1 = OpConstant %uint 1
+%_ptr_Function_uint = OpTypePointer Function %uint
+         %13 = OpConstantNull %uint
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %20 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %6
+          %9 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %13
+        %res = OpVariable %_ptr_Function_uint Function %13
+               OpStore %arg_1 %uint_1
+         %14 = OpAtomicIIncrement %uint %arg_0 %uint_2 %uint_0
+               OpStore %res %14
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %20
+%local_invocation_index = OpFunctionParameter %uint
+         %23 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %13
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %28 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %6
+         %30 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %31 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.glsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.glsl
new file mode 100644
index 0000000..b9c2faf
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.glsl
@@ -0,0 +1,39 @@
+#version 310 es
+
+uint local_invocation_index_1 = 0u;
+shared uint arg_0;
+void atomicAdd_d5db1d() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint x_14 = atomicAdd(arg_0, 1u);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  atomicExchange(arg_0, 0u);
+  barrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+void compute_main(uint local_invocation_index_1_param) {
+  {
+    atomicExchange(arg_0, 0u);
+  }
+  barrier();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+void main() {
+  compute_main(gl_LocalInvocationIndex);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.hlsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.hlsl
new file mode 100644
index 0000000..d2a0213
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.hlsl
@@ -0,0 +1,46 @@
+static uint local_invocation_index_1 = 0u;
+groupshared uint arg_0;
+
+void atomicAdd_d5db1d() {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint atomic_result = 0u;
+  InterlockedAdd(arg_0, 1u, atomic_result);
+  const uint x_14 = atomic_result;
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index) {
+  uint atomic_result_1 = 0u;
+  InterlockedExchange(arg_0, 0u, atomic_result_1);
+  GroupMemoryBarrierWithGroupSync();
+  atomicAdd_d5db1d();
+  return;
+}
+
+void compute_main_1() {
+  compute_main_inner(local_invocation_index_1);
+  return;
+}
+
+struct tint_symbol_1 {
+  uint local_invocation_index_1_param : SV_GroupIndex;
+};
+
+void compute_main_inner_1(uint local_invocation_index_1_param) {
+  {
+    uint atomic_result_2 = 0u;
+    InterlockedExchange(arg_0, 0u, atomic_result_2);
+  }
+  GroupMemoryBarrierWithGroupSync();
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}
+
+[numthreads(1, 1, 1)]
+void compute_main(tint_symbol_1 tint_symbol) {
+  compute_main_inner_1(tint_symbol.local_invocation_index_1_param);
+  return;
+}
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.msl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.msl
new file mode 100644
index 0000000..1b33ab8
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.msl
@@ -0,0 +1,41 @@
+#include <metal_stdlib>
+
+using namespace metal;
+void atomicAdd_d5db1d(threadgroup atomic_uint* const tint_symbol) {
+  uint arg_1 = 0u;
+  uint res = 0u;
+  arg_1 = 1u;
+  uint const x_14 = atomic_fetch_add_explicit(tint_symbol, 1u, memory_order_relaxed);
+  res = x_14;
+  return;
+}
+
+void compute_main_inner(uint local_invocation_index, threadgroup atomic_uint* const tint_symbol_1) {
+  atomic_store_explicit(tint_symbol_1, 0u, memory_order_relaxed);
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  atomicAdd_d5db1d(tint_symbol_1);
+  return;
+}
+
+void compute_main_1(thread uint* const tint_symbol_2, threadgroup atomic_uint* const tint_symbol_3) {
+  uint const x_32 = *(tint_symbol_2);
+  compute_main_inner(x_32, tint_symbol_3);
+  return;
+}
+
+void compute_main_inner_1(uint local_invocation_index_1_param, threadgroup atomic_uint* const tint_symbol_4, thread uint* const tint_symbol_5) {
+  {
+    atomic_store_explicit(tint_symbol_4, 0u, memory_order_relaxed);
+  }
+  threadgroup_barrier(mem_flags::mem_threadgroup);
+  *(tint_symbol_5) = local_invocation_index_1_param;
+  compute_main_1(tint_symbol_5, tint_symbol_4);
+}
+
+kernel void compute_main(uint local_invocation_index_1_param [[thread_index_in_threadgroup]]) {
+  threadgroup atomic_uint tint_symbol_6;
+  thread uint tint_symbol_7 = 0u;
+  compute_main_inner_1(local_invocation_index_1_param, &(tint_symbol_6), &(tint_symbol_7));
+  return;
+}
+
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.spvasm b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.spvasm
new file mode 100644
index 0000000..1388894
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.spvasm
@@ -0,0 +1,78 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 45
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %compute_main "compute_main" %local_invocation_index_1_param_1
+               OpExecutionMode %compute_main LocalSize 1 1 1
+               OpName %local_invocation_index_1_param_1 "local_invocation_index_1_param_1"
+               OpName %local_invocation_index_1 "local_invocation_index_1"
+               OpName %arg_0 "arg_0"
+               OpName %atomicAdd_d5db1d "atomicAdd_d5db1d"
+               OpName %arg_1 "arg_1"
+               OpName %res "res"
+               OpName %compute_main_inner "compute_main_inner"
+               OpName %local_invocation_index "local_invocation_index"
+               OpName %compute_main_1 "compute_main_1"
+               OpName %compute_main_inner_1 "compute_main_inner_1"
+               OpName %local_invocation_index_1_param "local_invocation_index_1_param"
+               OpName %compute_main "compute_main"
+               OpDecorate %local_invocation_index_1_param_1 BuiltIn LocalInvocationIndex
+       %uint = OpTypeInt 32 0
+%_ptr_Input_uint = OpTypePointer Input %uint
+%local_invocation_index_1_param_1 = OpVariable %_ptr_Input_uint Input
+%_ptr_Private_uint = OpTypePointer Private %uint
+          %6 = OpConstantNull %uint
+%local_invocation_index_1 = OpVariable %_ptr_Private_uint Private %6
+%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint
+      %arg_0 = OpVariable %_ptr_Workgroup_uint Workgroup
+       %void = OpTypeVoid
+          %9 = OpTypeFunction %void
+%_ptr_Function_uint = OpTypePointer Function %uint
+     %uint_1 = OpConstant %uint 1
+     %uint_2 = OpConstant %uint 2
+     %uint_0 = OpConstant %uint 0
+         %21 = OpTypeFunction %void %uint
+   %uint_264 = OpConstant %uint 264
+%atomicAdd_d5db1d = OpFunction %void None %9
+         %12 = OpLabel
+      %arg_1 = OpVariable %_ptr_Function_uint Function %6
+        %res = OpVariable %_ptr_Function_uint Function %6
+               OpStore %arg_1 %6
+               OpStore %res %6
+               OpStore %arg_1 %uint_1
+         %17 = OpAtomicIAdd %uint %arg_0 %uint_2 %uint_0 %uint_1
+               OpStore %res %17
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner = OpFunction %void None %21
+%local_invocation_index = OpFunctionParameter %uint
+         %24 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+         %29 = OpFunctionCall %void %atomicAdd_d5db1d
+               OpReturn
+               OpFunctionEnd
+%compute_main_1 = OpFunction %void None %9
+         %31 = OpLabel
+         %32 = OpLoad %uint %local_invocation_index_1
+         %33 = OpFunctionCall %void %compute_main_inner %32
+               OpReturn
+               OpFunctionEnd
+%compute_main_inner_1 = OpFunction %void None %21
+%local_invocation_index_1_param = OpFunctionParameter %uint
+         %36 = OpLabel
+               OpAtomicStore %arg_0 %uint_2 %uint_0 %6
+               OpControlBarrier %uint_2 %uint_2 %uint_264
+               OpStore %local_invocation_index_1 %local_invocation_index_1_param
+         %40 = OpFunctionCall %void %compute_main_1
+               OpReturn
+               OpFunctionEnd
+%compute_main = OpFunction %void None %9
+         %42 = OpLabel
+         %44 = OpLoad %uint %local_invocation_index_1_param_1
+         %43 = OpFunctionCall %void %compute_main_inner_1 %44
+               OpReturn
+               OpFunctionEnd
diff --git a/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.wgsl b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.wgsl
new file mode 100644
index 0000000..7faf057
--- /dev/null
+++ b/test/tint/builtins/atomics/from_gen/var/spvAtomicIncrement/workgroup_u32.spvasm.expected.wgsl
@@ -0,0 +1,31 @@
+var<private> local_invocation_index_1 : u32;
+
+var<workgroup> arg_0 : atomic<u32>;
+
+fn atomicAdd_d5db1d() {
+  var arg_1 : u32 = 0u;
+  var res : u32 = 0u;
+  arg_1 = 1u;
+  let x_14 : u32 = atomicAdd(&(arg_0), 1u);
+  res = x_14;
+  return;
+}
+
+fn compute_main_inner(local_invocation_index : u32) {
+  atomicStore(&(arg_0), 0u);
+  workgroupBarrier();
+  atomicAdd_d5db1d();
+  return;
+}
+
+fn compute_main_1() {
+  let x_32 : u32 = local_invocation_index_1;
+  compute_main_inner(x_32);
+  return;
+}
+
+@compute @workgroup_size(1i, 1i, 1i)
+fn compute_main(@builtin(local_invocation_index) local_invocation_index_1_param : u32) {
+  local_invocation_index_1 = local_invocation_index_1_param;
+  compute_main_1();
+}