tint: Fix DemoteToHelper for atomicCmpXchgWeak

We cannot explicitly name the result type of this builtin, so we have
to redeclare it manually.

Fixed: oss-fuzz:53347, oss-fuzz:53343
Change-Id: I23816b8b35eb20ae91472143ab30668b573d65bf
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/110160
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Auto-Submit: James Price <jrprice@google.com>
diff --git a/src/tint/transform/demote_to_helper.cc b/src/tint/transform/demote_to_helper.cc
index f6d2388..cb35c67 100644
--- a/src/tint/transform/demote_to_helper.cc
+++ b/src/tint/transform/demote_to_helper.cc
@@ -14,6 +14,7 @@
 
 #include "src/tint/transform/demote_to_helper.h"
 
+#include <unordered_map>
 #include <unordered_set>
 #include <utility>
 
@@ -24,6 +25,7 @@
 #include "src/tint/sem/reference.h"
 #include "src/tint/sem/statement.h"
 #include "src/tint/transform/utils/hoist_to_decl_before.h"
+#include "src/tint/utils/map.h"
 
 TINT_INSTANTIATE_TYPEINFO(tint::transform::DemoteToHelper);
 
@@ -102,6 +104,7 @@
     // Mask all writes to host-visible memory using the discarded flag.
     // We also insert a discard statement before all return statements in entry points for shaders
     // that discard.
+    std::unordered_map<const sem::Type*, Symbol> atomic_cmpxchg_result_types;
     for (auto* node : src->ASTNodes().Objects()) {
         Switch(
             node,
@@ -174,11 +177,51 @@
                         //   }
                         //   let y = x + tmp;
                         auto result = b.Sym();
-                        auto result_decl =
-                            b.Decl(b.Var(result, CreateASTTypeFor(ctx, sem_call->Type())));
-                        auto* masked_call =
-                            b.If(b.Not(flag),
-                                 b.Block(b.Assign(result, ctx.CloneWithoutTransform(call))));
+                        const ast::Type* result_ty = nullptr;
+                        const ast::Statement* masked_call = nullptr;
+                        if (builtin->Type() == sem::BuiltinType::kAtomicCompareExchangeWeak) {
+                            // Special case for atomicCompareExchangeWeak as we cannot name its
+                            // result type. We have to declare an equivalent struct and copy the
+                            // original member values over to it.
+
+                            // Declare a struct to hold the result values.
+                            auto* result_struct = sem_call->Type()->As<sem::Struct>();
+                            auto* atomic_ty = result_struct->Members()[0]->Type();
+                            result_ty = b.ty.type_name(
+                                utils::GetOrCreate(atomic_cmpxchg_result_types, atomic_ty, [&]() {
+                                    auto name = b.Sym();
+                                    b.Structure(
+                                        name,
+                                        utils::Vector{
+                                            b.Member("old_value", CreateASTTypeFor(ctx, atomic_ty)),
+                                            b.Member("exchanged", b.ty.bool_()),
+                                        });
+                                    return name;
+                                }));
+
+                            // Generate the masked call and member-wise copy:
+                            //   if (!tint_discarded) {
+                            //     let tmp_result = atomicCompareExchangeWeak(&p, 1, 2);
+                            //     result.exchanged = tmp_result.exchanged;
+                            //     result.old_value = tmp_result.old_value;
+                            //   }
+                            auto tmp_result = b.Sym();
+                            masked_call =
+                                b.If(b.Not(flag),
+                                     b.Block(utils::Vector{
+                                         b.Decl(b.Let(tmp_result, ctx.CloneWithoutTransform(call))),
+                                         b.Assign(b.MemberAccessor(result, "old_value"),
+                                                  b.MemberAccessor(tmp_result, "old_value")),
+                                         b.Assign(b.MemberAccessor(result, "exchanged"),
+                                                  b.MemberAccessor(tmp_result, "exchanged")),
+                                     }));
+                        } else {
+                            result_ty = CreateASTTypeFor(ctx, sem_call->Type());
+                            masked_call =
+                                b.If(b.Not(flag),
+                                     b.Block(b.Assign(result, ctx.CloneWithoutTransform(call))));
+                        }
+                        auto* result_decl = b.Decl(b.Var(result, result_ty));
                         hoist_to_decl_before.Prepare(sem_call);
                         hoist_to_decl_before.InsertBefore(stmt, result_decl);
                         hoist_to_decl_before.InsertBefore(stmt, masked_call);