tint: Add unit test for HasSideEffects for builtins

Change-Id: I7f2546c36a06cd9646ddf39601636954b9da0459
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/98541
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
Antonio Maiorano 2022-08-09 18:18:22 +00:00 committed by Dawn LUCI CQ
parent f7e2de7971
commit 50bc60359e
2 changed files with 238 additions and 5 deletions

View File

@ -19,6 +19,7 @@
#include "src/tint/sem/expression.h"
#include "src/tint/sem/index_accessor_expression.h"
#include "src/tint/sem/member_accessor_expression.h"
#include "src/tint/utils/vector.h"
using namespace tint::number_suffixes; // NOLINT
@ -124,6 +125,228 @@ TEST_F(SideEffectsTest, Call_Builtin_SE) {
EXPECT_TRUE(sem->HasSideEffects());
}
namespace builtin {
struct Case {
const char* name;
utils::Vector<const char*, 3> args;
bool has_side_effects;
ast::PipelineStage pipeline_stage;
};
static Case C(const char* name,
utils::VectorRef<const char*> args,
bool has_side_effects,
ast::PipelineStage stage = ast::PipelineStage::kFragment) {
Case c;
c.name = name;
c.args = std::move(args);
c.has_side_effects = has_side_effects;
c.pipeline_stage = stage;
return c;
}
static std::ostream& operator<<(std::ostream& o, const Case& c) {
o << c.name << "(";
for (size_t i = 0; i < c.args.Length(); ++i) {
o << c.args[i];
if (i + 1 != c.args.Length()) {
o << ", ";
}
}
o << "), ";
o << "has_side_effects = " << c.has_side_effects;
return o;
}
using SideEffectsBuiltinTest = resolver::ResolverTestWithParam<Case>;
TEST_P(SideEffectsBuiltinTest, Test) {
Enable(ast::Extension::kChromiumExperimentalDp4A);
auto& c = GetParam();
uint32_t next_binding = 0;
GlobalVar("f", ty.f32(), ast::StorageClass::kPrivate);
GlobalVar("i", ty.i32(), ast::StorageClass::kPrivate);
GlobalVar("u", ty.u32(), ast::StorageClass::kPrivate);
GlobalVar("b", ty.bool_(), ast::StorageClass::kPrivate);
GlobalVar("vf", ty.vec3<f32>(), ast::StorageClass::kPrivate);
GlobalVar("vf2", ty.vec2<f32>(), ast::StorageClass::kPrivate);
GlobalVar("vi2", ty.vec2<i32>(), ast::StorageClass::kPrivate);
GlobalVar("vf4", ty.vec4<f32>(), ast::StorageClass::kPrivate);
GlobalVar("vb", ty.vec3<bool>(), ast::StorageClass::kPrivate);
GlobalVar("m", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
GlobalVar("arr", ty.array<f32, 10>(), ast::StorageClass::kPrivate);
GlobalVar("storage_arr", ty.array<f32>(), ast::StorageClass::kStorage,
GroupAndBinding(0, next_binding++));
GlobalVar("a", ty.atomic(ty.i32()), ast::StorageClass::kStorage, ast::Access::kReadWrite,
GroupAndBinding(0, next_binding++));
if (c.pipeline_stage != ast::PipelineStage::kCompute) {
GlobalVar("t2d", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(0, next_binding++));
GlobalVar("tdepth2d", ty.depth_texture(ast::TextureDimension::k2d),
GroupAndBinding(0, next_binding++));
GlobalVar("t2d_arr", ty.sampled_texture(ast::TextureDimension::k2dArray, ty.f32()),
GroupAndBinding(0, next_binding++));
GlobalVar("t2d_multi", ty.multisampled_texture(ast::TextureDimension::k2d, ty.f32()),
GroupAndBinding(0, next_binding++));
GlobalVar("tstorage2d",
ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
ast::Access::kWrite),
GroupAndBinding(0, next_binding++));
GlobalVar("s2d", ty.sampler(ast::SamplerKind::kSampler),
GroupAndBinding(0, next_binding++));
GlobalVar("scomp", ty.sampler(ast::SamplerKind::kComparisonSampler),
GroupAndBinding(0, next_binding++));
}
utils::Vector<const ast::Statement*, 4> stmts;
stmts.Push(Decl(Let("pstorage_arr", nullptr, AddressOf("storage_arr"))));
stmts.Push(Decl(Let("pa", nullptr, AddressOf("a"))));
utils::Vector<const ast::Expression*, 5> args;
for (auto& a : c.args) {
args.Push(Expr(a));
}
auto* expr = Call(c.name, args);
utils::Vector<const ast::Attribute*, 2> attrs;
attrs.Push(create<ast::StageAttribute>(c.pipeline_stage));
if (c.pipeline_stage == ast::PipelineStage::kCompute) {
attrs.Push(WorkgroupSize(Expr(1_u)));
}
stmts.Push(create<ast::CallStatement>(expr));
Func("func", utils::Empty, ty.void_(), stmts, attrs);
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
ASSERT_NE(sem, nullptr);
EXPECT_TRUE(sem->Is<sem::Call>());
EXPECT_EQ(c.has_side_effects, sem->HasSideEffects());
}
INSTANTIATE_TEST_SUITE_P(
SideEffectsTest_Builtins,
SideEffectsBuiltinTest,
testing::ValuesIn(std::vector<Case>{
// No side-effect builts
C("abs", utils::Vector{"f"}, false), //
C("acos", utils::Vector{"f"}, false), //
C("acosh", utils::Vector{"f"}, false), //
C("all", utils::Vector{"vb"}, false), //
C("any", utils::Vector{"vb"}, false), //
C("arrayLength", utils::Vector{"pstorage_arr"}, false), //
C("asin", utils::Vector{"f"}, false), //
C("asinh", utils::Vector{"f"}, false), //
C("atan", utils::Vector{"f"}, false), //
C("atan2", utils::Vector{"f", "f"}, false), //
C("atanh", utils::Vector{"f"}, false), //
C("atomicLoad", utils::Vector{"pa"}, false), //
C("ceil", utils::Vector{"f"}, false), //
C("clamp", utils::Vector{"f", "f", "f"}, false), //
C("cos", utils::Vector{"f"}, false), //
C("cosh", utils::Vector{"f"}, false), //
C("countLeadingZeros", utils::Vector{"i"}, false), //
C("countOneBits", utils::Vector{"i"}, false), //
C("countTrailingZeros", utils::Vector{"i"}, false), //
C("cross", utils::Vector{"vf", "vf"}, false), //
C("degrees", utils::Vector{"f"}, false), //
C("determinant", utils::Vector{"m"}, false), //
C("distance", utils::Vector{"f", "f"}, false), //
C("dot", utils::Vector{"vf", "vf"}, false), //
C("dot4I8Packed", utils::Vector{"u", "u"}, false), //
C("dot4U8Packed", utils::Vector{"u", "u"}, false), //
C("exp", utils::Vector{"f"}, false), //
C("exp2", utils::Vector{"f"}, false), //
C("extractBits", utils::Vector{"i", "u", "u"}, false), //
C("faceForward", utils::Vector{"vf", "vf", "vf"}, false), //
C("firstLeadingBit", utils::Vector{"u"}, false), //
C("firstTrailingBit", utils::Vector{"u"}, false), //
C("floor", utils::Vector{"f"}, false), //
C("fma", utils::Vector{"f", "f", "f"}, false), //
C("fract", utils::Vector{"vf"}, false), //
C("frexp", utils::Vector{"f"}, false), //
C("insertBits", utils::Vector{"i", "i", "u", "u"}, false), //
C("inverseSqrt", utils::Vector{"f"}, false), //
C("ldexp", utils::Vector{"f", "i"}, false), //
C("length", utils::Vector{"vf"}, false), //
C("log", utils::Vector{"f"}, false), //
C("log2", utils::Vector{"f"}, false), //
C("max", utils::Vector{"f", "f"}, false), //
C("min", utils::Vector{"f", "f"}, false), //
C("mix", utils::Vector{"f", "f", "f"}, false), //
C("modf", utils::Vector{"f"}, false), //
C("normalize", utils::Vector{"vf"}, false), //
C("pack2x16float", utils::Vector{"vf2"}, false), //
C("pack2x16snorm", utils::Vector{"vf2"}, false), //
C("pack2x16unorm", utils::Vector{"vf2"}, false), //
C("pack4x8snorm", utils::Vector{"vf4"}, false), //
C("pack4x8unorm", utils::Vector{"vf4"}, false), //
C("pow", utils::Vector{"f", "f"}, false), //
C("radians", utils::Vector{"f"}, false), //
C("reflect", utils::Vector{"vf", "vf"}, false), //
C("refract", utils::Vector{"vf", "vf", "f"}, false), //
C("reverseBits", utils::Vector{"u"}, false), //
C("round", utils::Vector{"f"}, false), //
C("select", utils::Vector{"f", "f", "b"}, false), //
C("sign", utils::Vector{"f"}, false), //
C("sin", utils::Vector{"f"}, false), //
C("sinh", utils::Vector{"f"}, false), //
C("smoothstep", utils::Vector{"f", "f", "f"}, false), //
C("sqrt", utils::Vector{"f"}, false), //
C("step", utils::Vector{"f", "f"}, false), //
C("tan", utils::Vector{"f"}, false), //
C("tanh", utils::Vector{"f"}, false), //
C("textureDimensions", utils::Vector{"t2d"}, false), //
C("textureGather", utils::Vector{"tdepth2d", "s2d", "vf2"}, false), //
C("textureGatherCompare", utils::Vector{"tdepth2d", "scomp", "vf2", "f"}, false), //
C("textureLoad", utils::Vector{"t2d", "vi2", "i"}, false), //
C("textureNumLayers", utils::Vector{"t2d_arr"}, false), //
C("textureNumLevels", utils::Vector{"t2d"}, false), //
C("textureNumSamples", utils::Vector{"t2d_multi"}, false), //
C("textureSampleCompareLevel", utils::Vector{"tdepth2d", "scomp", "vf2", "f"}, false), //
C("textureSampleGrad", utils::Vector{"t2d", "s2d", "vf2", "vf2", "vf2"}, false), //
C("textureSampleLevel", utils::Vector{"t2d", "s2d", "vf2", "f"}, false), //
C("transpose", utils::Vector{"m"}, false), //
C("trunc", utils::Vector{"f"}, false), //
C("unpack2x16float", utils::Vector{"u"}, false), //
C("unpack2x16snorm", utils::Vector{"u"}, false), //
C("unpack2x16unorm", utils::Vector{"u"}, false), //
C("unpack4x8snorm", utils::Vector{"u"}, false), //
C("unpack4x8unorm", utils::Vector{"u"}, false), //
C("storageBarrier", utils::Empty, false, ast::PipelineStage::kCompute), //
C("workgroupBarrier", utils::Empty, false, ast::PipelineStage::kCompute), //
C("textureSample", utils::Vector{"t2d", "s2d", "vf2"}, false), //
C("textureSampleBias", utils::Vector{"t2d", "s2d", "vf2", "f"}, false), //
C("textureSampleCompare", utils::Vector{"tdepth2d", "scomp", "vf2", "f"}, false), //
C("dpdx", utils::Vector{"f"}, false), //
C("dpdxCoarse", utils::Vector{"f"}, false), //
C("dpdxFine", utils::Vector{"f"}, false), //
C("dpdy", utils::Vector{"f"}, false), //
C("dpdyCoarse", utils::Vector{"f"}, false), //
C("dpdyFine", utils::Vector{"f"}, false), //
C("fwidth", utils::Vector{"f"}, false), //
C("fwidthCoarse", utils::Vector{"f"}, false), //
C("fwidthFine", utils::Vector{"f"}, false), //
// Side-effect builtins
C("atomicAdd", utils::Vector{"pa", "i"}, true), //
C("atomicAnd", utils::Vector{"pa", "i"}, true), //
C("atomicCompareExchangeWeak", utils::Vector{"pa", "i", "i"}, true), //
C("atomicExchange", utils::Vector{"pa", "i"}, true), //
C("atomicMax", utils::Vector{"pa", "i"}, true), //
C("atomicMin", utils::Vector{"pa", "i"}, true), //
C("atomicOr", utils::Vector{"pa", "i"}, true), //
C("atomicStore", utils::Vector{"pa", "i"}, true), //
C("atomicSub", utils::Vector{"pa", "i"}, true), //
C("atomicXor", utils::Vector{"pa", "i"}, true), //
C("textureStore", utils::Vector{"tstorage2d", "vi2", "vf4"}, true), //
// Unimplemented builtins
// C("quantizeToF16", utils::Vector{"f"}, false), //
// C("saturate", utils::Vector{"f"}, false), //
}));
} // namespace builtin
TEST_F(SideEffectsTest, Call_Function) {
Func("f", utils::Empty, ty.i32(), utils::Vector{Return(1_i)});
auto* expr = Call("f");

View File

@ -153,11 +153,21 @@ bool Builtin::IsDP4a() const {
}
bool Builtin::HasSideEffects() const {
if (IsAtomic() && type_ != sem::BuiltinType::kAtomicLoad) {
return true;
}
if (type_ == sem::BuiltinType::kTextureStore) {
return true;
switch (type_) {
case sem::BuiltinType::kAtomicAdd:
case sem::BuiltinType::kAtomicAnd:
case sem::BuiltinType::kAtomicCompareExchangeWeak:
case sem::BuiltinType::kAtomicExchange:
case sem::BuiltinType::kAtomicMax:
case sem::BuiltinType::kAtomicMin:
case sem::BuiltinType::kAtomicOr:
case sem::BuiltinType::kAtomicStore:
case sem::BuiltinType::kAtomicSub:
case sem::BuiltinType::kAtomicXor:
case sem::BuiltinType::kTextureStore:
return true;
default:
break;
}
return false;
}