blob: 818297c04340fe771a8cf338f549295631fc793d [file] [log] [blame]
// Copyright 2022 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "src/tint/lang/spirv/reader/ast_lower/atomics.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "src/tint/lang/wgsl/ast/transform/helper_test.h"
#include "src/tint/lang/wgsl/reader/parser/parser.h"
#include "src/tint/lang/wgsl/resolver/resolve.h"
using namespace tint::core::number_suffixes; // NOLINT
namespace tint::spirv::reader {
namespace {
class AtomicsTest : public ast::transform::TransformTest {
public:
ast::transform::Output Run(std::string in) {
auto file = std::make_unique<Source::File>("test", std::move(in));
auto parser = wgsl::reader::Parser(file.get());
parser.Parse();
auto& b = parser.builder();
wgsl::BuiltinFn two_params[] = {
wgsl::BuiltinFn::kAtomicExchange, wgsl::BuiltinFn::kAtomicAdd,
wgsl::BuiltinFn::kAtomicSub, wgsl::BuiltinFn::kAtomicMin,
wgsl::BuiltinFn::kAtomicMax, wgsl::BuiltinFn::kAtomicAnd,
wgsl::BuiltinFn::kAtomicOr, wgsl::BuiltinFn::kAtomicXor,
};
for (auto& a : two_params) {
b.Func(std::string{"stub_"} + wgsl::str(a) + "_u32",
tint::Vector{
b.Param("p0", b.ty.u32()),
b.Param("p1", b.ty.u32()),
},
b.ty.u32(),
tint::Vector{
b.Return(0_u),
},
tint::Vector{
b.ASTNodes().Create<Atomics::Stub>(b.ID(), b.AllocateNodeID(), a),
});
b.Func(std::string{"stub_"} + wgsl::str(a) + "_i32",
tint::Vector{
b.Param("p0", b.ty.i32()),
b.Param("p1", b.ty.i32()),
},
b.ty.i32(),
tint::Vector{
b.Return(0_i),
},
tint::Vector{
b.ASTNodes().Create<Atomics::Stub>(b.ID(), b.AllocateNodeID(), a),
});
}
b.Func("stub_atomicLoad_u32",
tint::Vector{
b.Param("p0", b.ty.u32()),
},
b.ty.u32(),
tint::Vector{
b.Return(0_u),
},
tint::Vector{
b.ASTNodes().Create<Atomics::Stub>(b.ID(), b.AllocateNodeID(),
wgsl::BuiltinFn::kAtomicLoad),
});
b.Func("stub_atomicLoad_i32",
tint::Vector{
b.Param("p0", b.ty.i32()),
},
b.ty.i32(),
tint::Vector{
b.Return(0_i),
},
tint::Vector{
b.ASTNodes().Create<Atomics::Stub>(b.ID(), b.AllocateNodeID(),
wgsl::BuiltinFn::kAtomicLoad),
});
b.Func("stub_atomicStore_u32",
tint::Vector{
b.Param("p0", b.ty.u32()),
b.Param("p1", b.ty.u32()),
},
b.ty.void_(), tint::Empty,
tint::Vector{
b.ASTNodes().Create<Atomics::Stub>(b.ID(), b.AllocateNodeID(),
wgsl::BuiltinFn::kAtomicStore),
});
b.Func("stub_atomicStore_i32",
tint::Vector{
b.Param("p0", b.ty.i32()),
b.Param("p1", b.ty.i32()),
},
b.ty.void_(), tint::Empty,
tint::Vector{
b.ASTNodes().Create<Atomics::Stub>(b.ID(), b.AllocateNodeID(),
wgsl::BuiltinFn::kAtomicStore),
});
b.Func("stub_atomic_compare_exchange_weak_u32",
tint::Vector{
b.Param("p0", b.ty.u32()),
b.Param("p1", b.ty.u32()),
b.Param("p2", b.ty.u32()),
},
b.ty.u32(),
tint::Vector{
b.Return(0_u),
},
tint::Vector{
b.ASTNodes().Create<Atomics::Stub>(b.ID(), b.AllocateNodeID(),
wgsl::BuiltinFn::kAtomicCompareExchangeWeak),
});
b.Func("stub_atomic_compare_exchange_weak_i32",
tint::Vector{b.Param("p0", b.ty.i32()), b.Param("p1", b.ty.i32()),
b.Param("p2", b.ty.i32())},
b.ty.i32(),
tint::Vector{
b.Return(0_i),
},
tint::Vector{
b.ASTNodes().Create<Atomics::Stub>(b.ID(), b.AllocateNodeID(),
wgsl::BuiltinFn::kAtomicCompareExchangeWeak),
});
// Keep this pointer alive after Transform() returns
files_.emplace_back(std::move(file));
return ast::transform::TransformTest::Run<Atomics>(resolver::Resolve(b));
}
private:
std::vector<std::unique_ptr<Source::File>> files_;
};
TEST_F(AtomicsTest, StripUnusedBuiltins) {
auto* src = R"(
fn f() {
}
)";
auto* expect = src;
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, 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(AtomicsTest, ArrayOfU32_ViaDerefPointerIndex) {
auto* src = R"(
var<workgroup> wg : array<u32, 4>;
fn f() {
let p = &wg;
stub_atomicStore_u32((*p)[1], 1u);
}
)";
auto* expect = R"(
var<workgroup> wg : array<atomic<u32>, 4u>;
fn f() {
let p = &(wg);
atomicStore(&((*(p))[1]), 1u);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ArrayOfU32_ViaPointerIndex) {
auto* src = R"(
var<workgroup> wg : array<u32, 4>;
fn f() {
let p = &wg;
stub_atomicStore_u32(p[1], 1u);
}
)";
auto* expect = R"(
var<workgroup> wg : array<atomic<u32>, 4u>;
fn f() {
let p = &(wg);
atomicStore(&(p[1]), 1u);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, 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(AtomicsTest, AliasedArraysOfU32) {
auto* src = R"(
alias A0 = u32;
alias A1 = array<A0, 1>;
alias A2 = array<A1, 2>;
alias A3 = array<A2, 3>;
var<workgroup> wg : A3;
fn f() {
stub_atomicStore_u32(wg[2][1][0], 1u);
}
)";
auto* expect = R"(
alias A0 = u32;
alias A1 = array<A0, 1>;
alias A2 = array<A1, 2>;
alias 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(AtomicsTest, 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(AtomicsTest, FlatStructSingleAtomic_ViaDerefPointerDot) {
auto* src = R"(
struct S {
a : u32,
}
var<workgroup> wg : S;
fn f() {
let p = &wg;
stub_atomicStore_u32((*p).a, 1u);
}
)";
auto* expect = R"(
struct S_atomic {
a : atomic<u32>,
}
struct S {
a : u32,
}
var<workgroup> wg : S_atomic;
fn f() {
let p = &(wg);
atomicStore(&((*(p)).a), 1u);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, FlatStructSingleAtomic_ViaPointerDot) {
auto* src = R"(
struct S {
a : u32,
}
var<workgroup> wg : S;
fn f() {
let p = &wg;
stub_atomicStore_u32(p.a, 1u);
}
)";
auto* expect = R"(
struct S_atomic {
a : atomic<u32>,
}
struct S {
a : u32,
}
var<workgroup> wg : S_atomic;
fn f() {
let p = &(wg);
atomicStore(&(p.a), 1u);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, ArrayOfStruct_ViaDerefPointerIndex) {
auto* src = R"(
struct S {
a : u32,
b : i32,
c : u32,
}
@group(0) @binding(1) var<storage, read_write> arr : array<S>;
fn f() {
let p = &arr;
stub_atomicStore_i32((*p)[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() {
let p = &(arr);
atomicStore(&((*(p))[4].b), 1i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ArrayOfStruct_ViaPointerIndex) {
auto* src = R"(
struct S {
a : u32,
b : i32,
c : u32,
}
@group(0) @binding(1) var<storage, read_write> arr : array<S>;
fn f() {
let p = &arr;
stub_atomicStore_i32(p[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() {
let p = &(arr);
atomicStore(&(p[4].b), 1i);
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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));
}
TEST_F(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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(AtomicsTest, 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));
}
TEST_F(AtomicsTest, ReplaceAssignsAndDecls_Scaler) {
auto* src = R"(
var<workgroup> wg : u32;
fn f() {
stub_atomicAdd_u32(wg, 1u);
wg = 0u;
let a = wg;
var b : u32;
b = wg;
}
)";
auto* expect = R"(
var<workgroup> wg : atomic<u32>;
fn f() {
atomicAdd(&(wg), 1u);
atomicStore(&(wg), 0u);
let a = atomicLoad(&(wg));
var b : u32;
b = atomicLoad(&(wg));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceAssignsAndDecls_Struct) {
auto* src = R"(
struct S {
a : u32,
}
var<workgroup> wg : S;
fn f() {
stub_atomicAdd_u32(wg.a, 1u);
wg.a = 0u;
let a = wg.a;
var b : u32;
b = wg.a;
}
)";
auto* expect = R"(
struct S_atomic {
a : atomic<u32>,
}
struct S {
a : u32,
}
var<workgroup> wg : S_atomic;
fn f() {
atomicAdd(&(wg.a), 1u);
atomicStore(&(wg.a), 0u);
let a = atomicLoad(&(wg.a));
var b : u32;
b = atomicLoad(&(wg.a));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceAssignsAndDecls_NestedStruct) {
auto* src = R"(
struct S0 {
a : u32,
}
struct S1 {
s0 : S0
}
var<workgroup> wg : S1;
fn f() {
stub_atomicAdd_u32(wg.s0.a, 1u);
wg.s0.a = 0u;
let a = wg.s0.a;
var b : u32;
b = wg.s0.a;
}
)";
auto* expect = R"(
struct S0_atomic {
a : atomic<u32>,
}
struct S0 {
a : u32,
}
struct S1_atomic {
s0 : S0_atomic,
}
struct S1 {
s0 : S0,
}
var<workgroup> wg : S1_atomic;
fn f() {
atomicAdd(&(wg.s0.a), 1u);
atomicStore(&(wg.s0.a), 0u);
let a = atomicLoad(&(wg.s0.a));
var b : u32;
b = atomicLoad(&(wg.s0.a));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceAssignsAndDecls_StructMultipleAtomics) {
auto* src = R"(
struct S {
a : u32,
b : u32,
c : u32,
}
var<workgroup> wg : S;
fn f() {
stub_atomicAdd_u32(wg.a, 1u);
stub_atomicAdd_u32(wg.b, 1u);
wg.a = 0u;
let a = wg.a;
var b : u32;
b = wg.a;
wg.b = 0u;
let c = wg.b;
var d : u32;
d = wg.b;
wg.c = 0u;
let e = wg.c;
var f : u32;
f = wg.c;
}
)";
auto* expect = R"(
struct S_atomic {
a : atomic<u32>,
b : atomic<u32>,
c : u32,
}
struct S {
a : u32,
b : u32,
c : u32,
}
var<workgroup> wg : S_atomic;
fn f() {
atomicAdd(&(wg.a), 1u);
atomicAdd(&(wg.b), 1u);
atomicStore(&(wg.a), 0u);
let a = atomicLoad(&(wg.a));
var b : u32;
b = atomicLoad(&(wg.a));
atomicStore(&(wg.b), 0u);
let c = atomicLoad(&(wg.b));
var d : u32;
d = atomicLoad(&(wg.b));
wg.c = 0u;
let e = wg.c;
var f : u32;
f = wg.c;
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceAssignsAndDecls_ArrayOfScalar) {
auto* src = R"(
var<workgroup> wg : array<u32, 4>;
fn f() {
stub_atomicAdd_u32(wg[1], 1u);
wg[1] = 0u;
let a = wg[1];
var b : u32;
b = wg[1];
}
)";
auto* expect = R"(
var<workgroup> wg : array<atomic<u32>, 4u>;
fn f() {
atomicAdd(&(wg[1]), 1u);
atomicStore(&(wg[1]), 0u);
let a = atomicLoad(&(wg[1]));
var b : u32;
b = atomicLoad(&(wg[1]));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceAssignsAndDecls_ArrayOfStruct) {
auto* src = R"(
struct S {
a : u32,
}
var<workgroup> wg : array<S, 4>;
fn f() {
stub_atomicAdd_u32(wg[1].a, 1u);
wg[1].a = 0u;
let a = wg[1].a;
var b : u32;
b = wg[1].a;
}
)";
auto* expect = R"(
struct S_atomic {
a : atomic<u32>,
}
struct S {
a : u32,
}
var<workgroup> wg : array<S_atomic, 4u>;
fn f() {
atomicAdd(&(wg[1].a), 1u);
atomicStore(&(wg[1].a), 0u);
let a = atomicLoad(&(wg[1].a));
var b : u32;
b = atomicLoad(&(wg[1].a));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceAssignsAndDecls_StructOfArray) {
auto* src = R"(
struct S {
a : array<u32>,
}
@group(0) @binding(1) var<storage, read_write> s : S;
fn f() {
stub_atomicAdd_u32(s.a[4], 1u);
s.a[4] = 0u;
let a = s.a[4];
var b : u32;
b = s.a[4];
}
)";
auto* expect = R"(
struct S_atomic {
a : array<atomic<u32>>,
}
struct S {
a : array<u32>,
}
@group(0) @binding(1) var<storage, read_write> s : S_atomic;
fn f() {
atomicAdd(&(s.a[4]), 1u);
atomicStore(&(s.a[4]), 0u);
let a = atomicLoad(&(s.a[4]));
var b : u32;
b = atomicLoad(&(s.a[4]));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceAssignsAndDecls_ViaPtrLet) {
auto* src = R"(
struct S {
i : u32,
}
@group(0) @binding(1) var<storage, read_write> s : S;
fn f() {
let p0 = &(s);
let p1 : ptr<storage, u32, read_write> = &((*(p0)).i);
stub_atomicAdd_u32(*p1, 1u);
*p1 = 0u;
let a = *p1;
var b : u32;
b = *p1;
}
)";
auto* expect = R"(
struct S_atomic {
i : atomic<u32>,
}
struct S {
i : u32,
}
@group(0) @binding(1) var<storage, read_write> s : S_atomic;
fn f() {
let p0 = &(s);
let p1 : ptr<storage, atomic<u32>, read_write> = &((*(p0)).i);
atomicAdd(&(*(p1)), 1u);
atomicStore(&(*(p1)), 0u);
let a = atomicLoad(&(*(p1)));
var b : u32;
b = atomicLoad(&(*(p1)));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceBitcastArgument_Scaler) {
auto* src = R"(
var<workgroup> wg : u32;
fn f() {
stub_atomicAdd_u32(wg, 1u);
wg = 0u;
var b : f32;
b = bitcast<f32>(wg);
}
)";
auto* expect = R"(
var<workgroup> wg : atomic<u32>;
fn f() {
atomicAdd(&(wg), 1u);
atomicStore(&(wg), 0u);
var b : f32;
b = bitcast<f32>(atomicLoad(&(wg)));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(AtomicsTest, ReplaceBitcastArgument_Struct) {
auto* src = R"(
struct S {
a : u32,
}
var<workgroup> wg : S;
fn f() {
stub_atomicAdd_u32(wg.a, 1u);
wg.a = 0u;
var b : f32;
b = bitcast<f32>(wg.a);
}
)";
auto* expect = R"(
struct S_atomic {
a : atomic<u32>,
}
struct S {
a : u32,
}
var<workgroup> wg : S_atomic;
fn f() {
atomicAdd(&(wg.a), 1u);
atomicStore(&(wg.a), 0u);
var b : f32;
b = bitcast<f32>(atomicLoad(&(wg.a)));
}
)";
auto got = Run(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace tint::spirv::reader