tint: Implement sem::Load

The resolver now wraps sem::Expression objects with a sem::Load object
anywhere that the load rule is invoked. sem::Expression provides an
`UnwrapLoad()` method that returns the inner expression (or
passthrough, if no load is present), which is analaguous to
Type::UnwrapRef().

The logic for alias analysis in `RegisterLoadIfNeeded` has been folded
into the new `Resolver::Load` method.

Fixed up many transforms and tests. The only difference in output is
for a single SPIR-V backend test, where some IDs have changed due to
slight re-ordering of when expressions are generated.

There may be further clean-ups possible (e.g. removing unnecessary
calls to `UnwrapRef`, and simplifying places in the SPIR-V writer or
transforms that deal with memory accesses), but these can be addressed
in future patches.

Fixed: tint:1654
Change-Id: I69adecfe9251faae46546b64d0cdc29eea26cd4e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/99706
Commit-Queue: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton
2022-12-17 02:20:04 +00:00
committed by Dawn LUCI CQ
parent 57ca8cffa4
commit 2f9a98870e
39 changed files with 808 additions and 322 deletions

View File

@@ -250,9 +250,10 @@ struct CombineSamplers::State {
const sem::Expression* sampler =
sampler_index != -1 ? call->Arguments()[static_cast<size_t>(sampler_index)]
: nullptr;
auto* texture_var = texture->As<sem::VariableUser>()->Variable();
auto* texture_var = texture->UnwrapLoad()->As<sem::VariableUser>()->Variable();
auto* sampler_var =
sampler ? sampler->As<sem::VariableUser>()->Variable() : nullptr;
sampler ? sampler->UnwrapLoad()->As<sem::VariableUser>()->Variable()
: nullptr;
sem::VariablePair new_pair(texture_var, sampler_var);
for (auto* arg : expr->args) {
auto* type = ctx.src->TypeOf(arg)->UnwrapRef();
@@ -296,12 +297,14 @@ struct CombineSamplers::State {
const sem::Variable* sampler_var = pair.second;
if (auto* param = texture_var->As<sem::Parameter>()) {
const sem::Expression* texture = call->Arguments()[param->Index()];
texture_var = texture->As<sem::VariableUser>()->Variable();
texture_var =
texture->UnwrapLoad()->As<sem::VariableUser>()->Variable();
}
if (sampler_var) {
if (auto* param = sampler_var->As<sem::Parameter>()) {
const sem::Expression* sampler = call->Arguments()[param->Index()];
sampler_var = sampler->As<sem::VariableUser>()->Variable();
sampler_var =
sampler->UnwrapLoad()->As<sem::VariableUser>()->Variable();
}
}
sem::VariablePair new_pair(texture_var, sampler_var);

View File

@@ -126,10 +126,10 @@ struct LoadStoreKey {
/// AtomicKey is the unordered map key to an atomic intrinsic.
struct AtomicKey {
ast::Access const access; // buffer access
ast::Access const access; // buffer access
type::Type const* buf_ty = nullptr; // buffer type
type::Type const* el_ty = nullptr; // element type
sem::BuiltinType const op; // atomic op
sem::BuiltinType const op; // atomic op
bool operator==(const AtomicKey& rhs) const {
return access == rhs.access && buf_ty == rhs.buf_ty && el_ty == rhs.el_ty && op == rhs.op;
}
@@ -881,15 +881,17 @@ Transform::ApplyResult DecomposeMemoryAccess::Apply(const Program* src,
for (auto* node : src->ASTNodes().Objects()) {
if (auto* ident = node->As<ast::IdentifierExpression>()) {
// X
if (auto* var = sem.Get<sem::VariableUser>(ident)) {
if (var->Variable()->AddressSpace() == ast::AddressSpace::kStorage ||
var->Variable()->AddressSpace() == ast::AddressSpace::kUniform) {
// Variable to a storage or uniform buffer
state.AddAccess(ident, {
var,
state.ToOffset(0u),
var->Type()->UnwrapRef(),
});
if (auto* sem_ident = sem.Get(ident)) {
if (auto* var = sem_ident->UnwrapLoad()->As<sem::VariableUser>()) {
if (var->Variable()->AddressSpace() == ast::AddressSpace::kStorage ||
var->Variable()->AddressSpace() == ast::AddressSpace::kUniform) {
// Variable to a storage or uniform buffer
state.AddAccess(ident, {
var,
state.ToOffset(0u),
var->Type()->UnwrapRef(),
});
}
}
}
continue;
@@ -897,7 +899,7 @@ Transform::ApplyResult DecomposeMemoryAccess::Apply(const Program* src,
if (auto* accessor = node->As<ast::MemberAccessorExpression>()) {
// X.Y
auto* accessor_sem = sem.Get(accessor);
auto* accessor_sem = sem.Get(accessor)->UnwrapLoad();
if (auto* swizzle = accessor_sem->As<sem::Swizzle>()) {
if (swizzle->Indices().Length() == 1) {
if (auto access = state.TakeAccess(accessor->structure)) {
@@ -906,7 +908,7 @@ Transform::ApplyResult DecomposeMemoryAccess::Apply(const Program* src,
state.AddAccess(accessor, {
access.var,
state.Add(access.offset, offset),
vec_ty->type()->UnwrapRef(),
vec_ty->type(),
});
}
}
@@ -918,7 +920,7 @@ Transform::ApplyResult DecomposeMemoryAccess::Apply(const Program* src,
state.AddAccess(accessor, {
access.var,
state.Add(access.offset, offset),
member->Type()->UnwrapRef(),
member->Type(),
});
}
}
@@ -933,7 +935,7 @@ Transform::ApplyResult DecomposeMemoryAccess::Apply(const Program* src,
state.AddAccess(accessor, {
access.var,
state.Add(access.offset, offset),
arr->ElemType()->UnwrapRef(),
arr->ElemType(),
});
continue;
}
@@ -942,7 +944,7 @@ Transform::ApplyResult DecomposeMemoryAccess::Apply(const Program* src,
state.AddAccess(accessor, {
access.var,
state.Add(access.offset, offset),
vec_ty->type()->UnwrapRef(),
vec_ty->type(),
});
continue;
}
@@ -1014,6 +1016,7 @@ Transform::ApplyResult DecomposeMemoryAccess::Apply(const Program* src,
// All remaining accesses are loads, transform these into calls to the
// corresponding load function
// TODO(crbug.com/tint/1784): Use `sem::Load`s instead of maintaining `state.expression_order`.
for (auto* expr : state.expression_order) {
auto access_it = state.accesses.find(expr);
if (access_it == state.accesses.end()) {

View File

@@ -167,7 +167,7 @@ Transform::ApplyResult DecomposeStridedMatrix::Apply(const Program* src,
// m = arr_to_mat(ssbo.mat)
std::unordered_map<MatrixInfo, Symbol, MatrixInfo::Hasher> arr_to_mat;
ctx.ReplaceAll([&](const ast::MemberAccessorExpression* expr) -> const ast::Expression* {
if (auto* access = src->Sem().Get<sem::StructMemberAccess>(expr)) {
if (auto* access = src->Sem().Get(expr)->UnwrapLoad()->As<sem::StructMemberAccess>()) {
if (auto info = decomposed.Find(access->Member()->Declaration())) {
auto fn = utils::GetOrCreate(arr_to_mat, *info, [&] {
auto name =

View File

@@ -139,7 +139,7 @@ Transform::ApplyResult FirstIndexOffset::Apply(const Program* src,
// Fix up all references to the builtins with the offsets
ctx.ReplaceAll([=, &ctx](const ast::Expression* expr) -> const ast::Expression* {
if (auto* sem = ctx.src->Sem().Get(expr)) {
if (auto* user = sem->As<sem::VariableUser>()) {
if (auto* user = sem->UnwrapLoad()->As<sem::VariableUser>()) {
auto it = builtin_vars.find(user->Variable());
if (it != builtin_vars.end()) {
return ctx.dst->Add(ctx.CloneWithoutTransform(expr),

View File

@@ -197,7 +197,8 @@ struct MultiplanarExternalTexture::State {
if (builtin && !builtin->Parameters().IsEmpty() &&
builtin->Parameters()[0]->Type()->Is<type::ExternalTexture>() &&
builtin->Type() != sem::BuiltinType::kTextureDimensions) {
if (auto* var_user = sem.Get<sem::VariableUser>(expr->args[0])) {
if (auto* var_user =
sem.Get(expr->args[0])->UnwrapLoad()->As<sem::VariableUser>()) {
auto it = new_binding_symbols.find(var_user->Variable());
if (it == new_binding_symbols.end()) {
// If valid new binding locations were not provided earlier, we would have
@@ -222,7 +223,7 @@ struct MultiplanarExternalTexture::State {
// texture_external parameter. These need to be expanded out to multiple plane
// textures and the texture parameters structure.
for (auto* arg : expr->args) {
if (auto* var_user = sem.Get<sem::VariableUser>(arg)) {
if (auto* var_user = sem.Get(arg)->UnwrapLoad()->As<sem::VariableUser>()) {
// Check if a parameter is a texture_external by trying to find
// it in the transform state.
auto it = new_binding_symbols.find(var_user->Variable());

View File

@@ -74,8 +74,14 @@ struct PackedVec3::State {
// that load a whole packed vector (not a scalar / swizzle of the vector).
utils::Hashset<const sem::Expression*, 16> refs;
for (auto* node : ctx.src->ASTNodes().Objects()) {
auto* sem_node = sem.Get(node);
if (sem_node) {
if (auto* expr = sem_node->As<sem::Expression>()) {
sem_node = expr->UnwrapLoad();
}
}
Switch(
sem.Get(node), //
sem_node, //
[&](const sem::StructMemberAccess* access) {
if (members.Contains(access->Member())) {
// Access to a packed vector member. Seed the expression tracking.
@@ -84,11 +90,11 @@ struct PackedVec3::State {
},
[&](const sem::IndexAccessorExpression* access) {
// Not loading a whole packed vector. Ignore.
refs.Remove(access->Object());
refs.Remove(access->Object()->UnwrapLoad());
},
[&](const sem::Swizzle* access) {
// Not loading a whole packed vector. Ignore.
refs.Remove(access->Object());
refs.Remove(access->Object()->UnwrapLoad());
},
[&](const sem::VariableUser* user) {
auto* v = user->Variable();

View File

@@ -276,7 +276,7 @@ class DecomposeSideEffects::CollectHoistsState : public StateBase {
},
[&](const ast::IdentifierExpression* e) {
if (auto* sem_e = sem.Get(e)) {
if (auto* var_user = sem_e->As<sem::VariableUser>()) {
if (auto* var_user = sem_e->UnwrapLoad()->As<sem::VariableUser>()) {
// Don't hoist constants.
if (var_user->ConstantValue()) {
return false;

View File

@@ -1285,7 +1285,7 @@ Transform::ApplyResult Renamer::Apply(const Program* src,
Switch(
node,
[&](const ast::MemberAccessorExpression* accessor) {
auto* sem = src->Sem().Get(accessor);
auto* sem = src->Sem().Get(accessor)->UnwrapLoad();
if (sem->Is<sem::Swizzle>()) {
preserved_identifiers.Add(accessor->member);
} else if (auto* str_expr = src->Sem().Get(accessor->structure)) {

View File

@@ -93,7 +93,8 @@ fn entry() -> @builtin(position) vec4<f32> {
var v : vec4<f32>;
var rgba : f32;
var xyzw : f32;
return v.zyxw + v.rgab;
var z : f32;
return v.zyxw + v.rgab * v.z;
}
)";
@@ -103,7 +104,8 @@ fn tint_symbol() -> @builtin(position) vec4<f32> {
var tint_symbol_1 : vec4<f32>;
var tint_symbol_2 : f32;
var tint_symbol_3 : f32;
return (tint_symbol_1.zyxw + tint_symbol_1.rgab);
var tint_symbol_4 : f32;
return (tint_symbol_1.zyxw + (tint_symbol_1.rgab * tint_symbol_1.z));
}
)";
@@ -115,10 +117,8 @@ fn tint_symbol() -> @builtin(position) vec4<f32> {
ASSERT_NE(data, nullptr);
Renamer::Data::Remappings expected_remappings = {
{"entry", "tint_symbol"},
{"v", "tint_symbol_1"},
{"rgba", "tint_symbol_2"},
{"xyzw", "tint_symbol_3"},
{"entry", "tint_symbol"}, {"v", "tint_symbol_1"}, {"rgba", "tint_symbol_2"},
{"xyzw", "tint_symbol_3"}, {"z", "tint_symbol_4"},
};
EXPECT_THAT(data->remappings, ContainerEq(expected_remappings));
}

View File

@@ -67,7 +67,7 @@ struct Robustness::State {
/// @return the clamped replacement expression, or nullptr if `expr` should be cloned without
/// changes.
const ast::IndexAccessorExpression* Transform(const ast::IndexAccessorExpression* expr) {
auto* sem = src->Sem().Get(expr)->UnwrapMaterialize()->As<sem::IndexAccessorExpression>();
auto* sem = src->Sem().Get(expr)->Unwrap()->As<sem::IndexAccessorExpression>();
auto* ret_type = sem->Type();
auto* ref = ret_type->As<type::Reference>();
@@ -78,7 +78,7 @@ struct Robustness::State {
// idx return the cloned index expression, as a u32.
auto idx = [&]() -> const ast::Expression* {
auto* i = ctx.Clone(expr->index);
if (sem->Index()->Type()->UnwrapRef()->is_signed_integer_scalar()) {
if (sem->Index()->Type()->is_signed_integer_scalar()) {
return b.Construct(b.ty.u32(), i); // u32(idx)
}
return i;

View File

@@ -164,7 +164,7 @@ struct SpirvAtomic::State {
void ProcessAtomicExpressions() {
for (size_t i = 0; i < atomic_expressions.Length(); i++) {
Switch(
atomic_expressions[i], //
atomic_expressions[i]->UnwrapLoad(), //
[&](const sem::VariableUser* user) {
auto* v = user->Variable()->Declaration();
if (v->type && atomic_variables.emplace(user->Variable()).second) {
@@ -262,7 +262,7 @@ struct SpirvAtomic::State {
}
auto sem_rhs = ctx.src->Sem().Get(assign->rhs);
if (is_ref_to_atomic_var(sem_rhs)) {
if (is_ref_to_atomic_var(sem_rhs->UnwrapLoad())) {
ctx.Replace(assign->rhs, [=] {
auto* rhs = ctx.CloneWithoutTransform(assign->rhs);
return b.Call(sem::str(sem::BuiltinType::kAtomicLoad),
@@ -274,7 +274,7 @@ struct SpirvAtomic::State {
[&](const ast::VariableDeclStatement* decl) {
auto* var = decl->variable;
if (auto* sem_init = ctx.src->Sem().Get(var->initializer)) {
if (is_ref_to_atomic_var(sem_init)) {
if (is_ref_to_atomic_var(sem_init->UnwrapLoad())) {
ctx.Replace(var->initializer, [=] {
auto* rhs = ctx.CloneWithoutTransform(var->initializer);
return b.Call(sem::str(sem::BuiltinType::kAtomicLoad),

View File

@@ -511,7 +511,7 @@ struct Std140::State {
while (true) {
enum class Action { kStop, kContinue, kError };
Action action = Switch(
expr, //
expr->Unwrap(), //
[&](const sem::VariableUser* user) {
if (user->Variable() == access.var) {
// Walked all the way to the root identifier. We're done traversing.

View File

@@ -96,9 +96,11 @@ struct Unshadow::State {
});
ctx.ReplaceAll(
[&](const ast::IdentifierExpression* ident) -> const tint::ast::IdentifierExpression* {
if (auto* user = sem.Get<sem::VariableUser>(ident)) {
if (auto renamed = renamed_to.Find(user->Variable())) {
return b.Expr(*renamed);
if (auto* sem_ident = sem.Get(ident)) {
if (auto* user = sem_ident->UnwrapLoad()->As<sem::VariableUser>()) {
if (auto renamed = renamed_to.Find(user->Variable())) {
return b.Expr(*renamed);
}
}
}
return nullptr;

View File

@@ -760,5 +760,31 @@ type a = i32;
EXPECT_EQ(expect, str(got));
}
TEST_F(UnshadowTest, RenamedVarHasUsers) {
auto* src = R"(
fn F() {
var a : bool;
{
var a : i32;
var b = a + 1;
}
}
)";
auto* expect = R"(
fn F() {
var a : bool;
{
var a_1 : i32;
var b = (a_1 + 1);
}
}
)";
auto got = Run<Unshadow>(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace tint::transform