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

@@ -804,6 +804,37 @@ TEST_F(ResolverAliasAnalysisTest, Write_MemberAccessor) {
12:34 note: aliases with another argument passed here)");
}
TEST_F(ResolverAliasAnalysisTest, Read_MultiComponentSwizzle) {
// fn f2(p1 : ptr<function, vec4<f32>, p2 : ptr<function, vec4<f32>) {
// _ = (*p2).zy;
// *p1 = vec4<f32>();
// }
// fn f1() {
// var v : vec4<f32>;
// f2(&v, &v);
// }
Structure("S", utils::Vector{Member("a", ty.i32())});
Func("f2",
utils::Vector{
Param("p1", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction)),
Param("p2", ty.pointer(ty.vec4<f32>(), ast::AddressSpace::kFunction)),
},
ty.void_(),
utils::Vector{
Assign(Phony(), MemberAccessor(Deref("p2"), "zy")),
Assign(Deref("p1"), Construct(ty.vec4<f32>())),
});
Func("f1", utils::Empty, ty.void_(),
utils::Vector{
Decl(Var("v", ty.vec4<f32>())),
CallStmt(
Call("f2", AddressOf(Source{{12, 34}}, "v"), AddressOf(Source{{56, 76}}, "v"))),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
EXPECT_EQ(r()->error(), R"(56:76 warning: invalid aliased pointer argument
12:34 note: aliases with another argument passed here)");
}
TEST_F(ResolverAliasAnalysisTest, SinglePointerReadWrite) {
// Test that we can both read and write from a single pointer parameter.
//

View File

@@ -43,7 +43,7 @@ TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic_Ref) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -58,7 +58,7 @@ TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions_Dynamic_Ref) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -73,7 +73,7 @@ TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic) {
EXPECT_TRUE(r()->Resolve());
EXPECT_EQ(r()->error(), "");
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -108,13 +108,10 @@ TEST_F(ResolverIndexAccessorTest, Matrix) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(acc), nullptr);
ASSERT_TRUE(TypeOf(acc)->Is<type::Reference>());
ASSERT_TRUE(TypeOf(acc)->Is<type::Vector>());
EXPECT_EQ(TypeOf(acc)->As<type::Vector>()->Width(), 3u);
auto* ref = TypeOf(acc)->As<type::Reference>();
ASSERT_TRUE(ref->StoreType()->Is<type::Vector>());
EXPECT_EQ(ref->StoreType()->As<type::Vector>()->Width(), 3u);
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -129,12 +126,9 @@ TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(acc), nullptr);
ASSERT_TRUE(TypeOf(acc)->Is<type::Reference>());
EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
auto* ref = TypeOf(acc)->As<type::Reference>();
EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -157,7 +151,7 @@ TEST_F(ResolverIndexAccessorTest, Vector_Dynamic_Ref) {
EXPECT_TRUE(r()->Resolve());
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -181,12 +175,9 @@ TEST_F(ResolverIndexAccessorTest, Vector) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(acc), nullptr);
ASSERT_TRUE(TypeOf(acc)->Is<type::Reference>());
EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
auto* ref = TypeOf(acc)->As<type::Reference>();
EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -197,12 +188,9 @@ TEST_F(ResolverIndexAccessorTest, Array_Literal_i32) {
auto* acc = IndexAccessor("my_var", 2_i);
WrapInFunction(acc);
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(acc), nullptr);
auto* ref = TypeOf(acc)->As<type::Reference>();
ASSERT_NE(ref, nullptr);
EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -213,12 +201,9 @@ TEST_F(ResolverIndexAccessorTest, Array_Literal_u32) {
auto* acc = IndexAccessor("my_var", 2_u);
WrapInFunction(acc);
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(acc), nullptr);
auto* ref = TypeOf(acc)->As<type::Reference>();
ASSERT_NE(ref, nullptr);
EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -229,12 +214,9 @@ TEST_F(ResolverIndexAccessorTest, Array_Literal_AInt) {
auto* acc = IndexAccessor("my_var", 2_a);
WrapInFunction(acc);
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(acc), nullptr);
auto* ref = TypeOf(acc)->As<type::Reference>();
ASSERT_NE(ref, nullptr);
EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -251,12 +233,9 @@ TEST_F(ResolverIndexAccessorTest, Alias_Array) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(acc), nullptr);
ASSERT_TRUE(TypeOf(acc)->Is<type::Reference>());
EXPECT_TRUE(TypeOf(acc)->Is<type::F32>());
auto* ref = TypeOf(acc)->As<type::Reference>();
EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -292,7 +271,7 @@ TEST_F(ResolverIndexAccessorTest, Array_Dynamic_I32) {
EXPECT_TRUE(r()->Resolve());
EXPECT_EQ(r()->error(), "");
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -325,7 +304,7 @@ TEST_F(ResolverIndexAccessorTest, Array_Literal_I32) {
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);
@@ -346,7 +325,7 @@ TEST_F(ResolverIndexAccessorTest, Expr_Deref_FuncGoodParent) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto idx_sem = Sem().Get<sem::IndexAccessorExpression>(acc);
auto idx_sem = Sem().Get(acc)->UnwrapLoad()->As<sem::IndexAccessorExpression>();
ASSERT_NE(idx_sem, nullptr);
EXPECT_EQ(idx_sem->Index()->Declaration(), acc->index);
EXPECT_EQ(idx_sem->Object()->Declaration(), acc->object);

View File

@@ -181,7 +181,7 @@ TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVarUsedAsVariable
WrapInFunction(Decl(Var("v", use)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get<sem::VariableUser>(use);
auto* sem = Sem().Get(use)->UnwrapLoad()->As<sem::VariableUser>();
ASSERT_NE(sem, nullptr);
EXPECT_EQ(sem->Variable(), Sem().Get(mix));
}

View File

@@ -0,0 +1,370 @@
// 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/resolver/resolver.h"
#include "src/tint/resolver/resolver_test_helper.h"
#include "src/tint/sem/test_helper.h"
#include "src/tint/sem/load.h"
#include "src/tint/type/reference.h"
#include "gmock/gmock.h"
using namespace tint::number_suffixes; // NOLINT
namespace tint::resolver {
namespace {
using ResolverLoadTest = ResolverTest;
TEST_F(ResolverLoadTest, VarInitializer) {
// var ref = 1i;
// var v = ref;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Var("v", ident));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::I32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, LetInitializer) {
// var ref = 1i;
// let l = ref;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Let("l", ident));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::I32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, Assignment) {
// var ref = 1i;
// var v : i32;
// v = ref;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Var("v", ty.i32()), //
Assign("v", ident));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::I32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, CompoundAssignment) {
// var ref = 1i;
// var v : i32;
// v += ref;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Var("v", ty.i32()), //
CompoundAssign("v", ident, ast::BinaryOp::kAdd));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::I32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, UnaryOp) {
// var ref = 1i;
// var v = -ref;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Var("v", Negation(ident)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::I32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, UnaryOp_NoLoad) {
// var ref = 1i;
// let v = &ref;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Let("v", AddressOf(ident)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* var_user = Sem().Get<sem::VariableUser>(ident);
ASSERT_NE(var_user, nullptr);
EXPECT_TRUE(var_user->Type()->Is<type::Reference>());
EXPECT_TRUE(var_user->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, BinaryOp) {
// var ref = 1i;
// var v = ref * 1i;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Var("v", Mul(ident, 1_i)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::I32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, Index) {
// var ref = 1i;
// var v = array<i32, 3>(1i, 2i, 3i)[ref];
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
IndexAccessor(array<i32, 3>(1_i, 2_i, 3_i), ident));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::I32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, MultiComponentSwizzle) {
// var ref = vec4(1);
// var v = ref.xyz;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Construct(ty.vec4<i32>(), 1_i)), //
Var("v", MemberAccessor(ident, "xyz")));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::Vector>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Vector>());
}
TEST_F(ResolverLoadTest, Bitcast) {
// var ref = 1f;
// var v = bitcast<i32>(ref);
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_f)), //
Bitcast<i32>(ident));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::F32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
}
TEST_F(ResolverLoadTest, BuiltinArg) {
// var ref = 1f;
// var v = abs(ref);
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_f)), //
Call("abs", ident));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::F32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
}
TEST_F(ResolverLoadTest, FunctionArg) {
// fn f(x : f32) {}
// var ref = 1f;
// f(ref);
Func("f", utils::Vector{Param("x", ty.f32())}, ty.void_(), utils::Empty);
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_f)), //
CallStmt(Call("f", ident)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::F32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
}
TEST_F(ResolverLoadTest, FunctionArg_Handles) {
// @group(0) @binding(0) var t : texture_2d<f32>;
// @group(0) @binding(1) var s : sampler;
// fn f(tp : texture_2d<f32>, sp : sampler) -> vec4<f32> {
// return textureSampleLevel(tp, sp, vec2(), 0);
// }
// f(t, s);
GlobalVar("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
utils::Vector{Group(0_a), Binding(0_a)});
GlobalVar("s", ty.sampler(ast::SamplerKind::kSampler), utils::Vector{Group(0_a), Binding(1_a)});
Func("f",
utils::Vector{
Param("tp", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32())),
Param("sp", ty.sampler(ast::SamplerKind::kSampler)),
},
ty.vec4<f32>(),
utils::Vector{
Return(Call("textureSampleLevel", "tp", "sp", Construct(ty.vec2<f32>()), 0_a)),
});
auto* t_ident = Expr("t");
auto* s_ident = Expr("s");
WrapInFunction(CallStmt(Call("f", t_ident, s_ident)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
{
auto* load = Sem().Get<sem::Load>(t_ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::SampledTexture>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::SampledTexture>());
}
{
auto* load = Sem().Get<sem::Load>(s_ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::Sampler>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Sampler>());
}
}
TEST_F(ResolverLoadTest, FunctionReturn) {
// var ref = 1f;
// return ref;
auto* ident = Expr("ref");
Func("f", utils::Empty, ty.f32(),
utils::Vector{
Decl(Var("ref", Expr(1_f))),
Return(ident),
});
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::F32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::F32>());
}
TEST_F(ResolverLoadTest, IfCond) {
// var ref = false;
// if (ref) {}
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(false)), //
If(ident, Block()));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::Bool>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
}
TEST_F(ResolverLoadTest, Switch) {
// var ref = 1i;
// switch (ref) {
// default:
// }
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Switch(ident, DefaultCase()));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::I32>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::I32>());
}
TEST_F(ResolverLoadTest, BreakIfCond) {
// var ref = false;
// loop {
// continuing {
// break if (ref);
// }
// }
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(false)), //
Loop(Block(), Block(BreakIf(ident))));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::Bool>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
}
TEST_F(ResolverLoadTest, ForCond) {
// var ref = false;
// for (; ref; ) {}
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(false)), //
For(nullptr, ident, nullptr, Block()));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::Bool>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
}
TEST_F(ResolverLoadTest, WhileCond) {
// var ref = false;
// while (ref) {}
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(false)), //
While(ident, Block()));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* load = Sem().Get<sem::Load>(ident);
ASSERT_NE(load, nullptr);
EXPECT_TRUE(load->Type()->Is<type::Bool>());
EXPECT_TRUE(load->Reference()->Type()->Is<type::Reference>());
EXPECT_TRUE(load->Reference()->Type()->UnwrapRef()->Is<type::Bool>());
}
TEST_F(ResolverLoadTest, AddressOf) {
// var ref = 1i;
// let l = &ref;
auto* ident = Expr("ref");
WrapInFunction(Var("ref", Expr(1_i)), //
Let("l", AddressOf(ident)));
ASSERT_TRUE(r()->Resolve()) << r()->error();
auto* no_load = Sem().Get(ident);
ASSERT_NE(no_load, nullptr);
EXPECT_TRUE(no_load->Type()->Is<type::Reference>()); // No load
}
} // namespace
} // namespace tint::resolver

View File

@@ -14,6 +14,7 @@
#include "src/tint/resolver/resolver.h"
#include "src/tint/resolver/resolver_test_helper.h"
#include "src/tint/sem/load.h"
#include "src/tint/type/reference.h"
#include "gmock/gmock.h"
@@ -52,8 +53,14 @@ TEST_F(ResolverPtrRefTest, AddressOfThenDeref) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_TRUE(TypeOf(expr)->Is<type::Reference>());
EXPECT_TRUE(TypeOf(expr)->As<type::Reference>()->StoreType()->Is<type::I32>());
auto* load = Sem().Get<sem::Load>(expr);
ASSERT_NE(load, nullptr);
auto* ref = load->Reference();
ASSERT_NE(ref, nullptr);
ASSERT_TRUE(ref->Type()->Is<type::Reference>());
EXPECT_TRUE(ref->Type()->As<type::Reference>()->StoreType()->Is<type::I32>());
}
TEST_F(ResolverPtrRefTest, DefaultPtrAddressSpace) {

View File

@@ -58,6 +58,7 @@
#include "src/tint/sem/function.h"
#include "src/tint/sem/if_statement.h"
#include "src/tint/sem/index_accessor_expression.h"
#include "src/tint/sem/load.h"
#include "src/tint/sem/loop_statement.h"
#include "src/tint/sem/materialize.h"
#include "src/tint/sem/member_accessor_expression.h"
@@ -379,13 +380,11 @@ sem::Variable* Resolver::Let(const ast::Let* v, bool is_global) {
return nullptr;
}
auto* rhs = Materialize(Expression(v->initializer), ty);
auto* rhs = Load(Materialize(Expression(v->initializer), ty));
if (!rhs) {
return nullptr;
}
RegisterLoadIfNeeded(rhs);
// If the variable has no declared type, infer it from the RHS
if (!ty) {
ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
@@ -432,8 +431,11 @@ sem::Variable* Resolver::Override(const ast::Override* v) {
const sem::Expression* rhs = nullptr;
// Does the variable have a initializer?
// Does the variable have an initializer?
if (v->initializer) {
// Note: RHS must be a const or override expression, which excludes references.
// So there's no need to load or unwrap references here.
ExprEvalStageConstraint constraint{sem::EvaluationStage::kOverride, "override initializer"};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
rhs = Materialize(Expression(v->initializer), ty);
@@ -443,7 +445,7 @@ sem::Variable* Resolver::Override(const ast::Override* v) {
// If the variable has no declared type, infer it from the RHS
if (!ty) {
ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
ty = rhs->Type();
}
} else if (!ty) {
AddError("override declaration requires a type or initializer", v->source);
@@ -529,6 +531,9 @@ sem::Variable* Resolver::Const(const ast::Const* c, bool is_global) {
}
}
// Note: RHS must be a const expression, which excludes references.
// So there's no need to load or unwrap references here.
if (ty) {
// If an explicit type was specified, materialize to that type
rhs = Materialize(rhs, ty);
@@ -584,16 +589,14 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
};
TINT_SCOPED_ASSIGNMENT(expr_eval_stage_constraint_, constraint);
rhs = Materialize(Expression(var->initializer), storage_ty);
rhs = Load(Materialize(Expression(var->initializer), storage_ty));
if (!rhs) {
return nullptr;
}
// If the variable has no declared type, infer it from the RHS
if (!storage_ty) {
storage_ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
storage_ty = rhs->Type();
}
RegisterLoadIfNeeded(rhs);
}
if (!storage_ty) {
@@ -1315,7 +1318,7 @@ sem::IfStatement* Resolver::IfStatement(const ast::IfStatement* stmt) {
auto* sem =
builder_->create<sem::IfStatement>(stmt, current_compound_statement_, current_function_);
return StatementScope(stmt, sem, [&] {
auto* cond = Expression(stmt->condition);
auto* cond = Load(Expression(stmt->condition));
if (!cond) {
return false;
}
@@ -1323,8 +1326,6 @@ sem::IfStatement* Resolver::IfStatement(const ast::IfStatement* stmt) {
sem->Behaviors() = cond->Behaviors();
sem->Behaviors().Remove(sem::Behavior::kNext);
RegisterLoadIfNeeded(cond);
Mark(stmt->body);
auto* body = builder_->create<sem::BlockStatement>(stmt->body, current_compound_statement_,
current_function_);
@@ -1412,14 +1413,12 @@ sem::ForLoopStatement* Resolver::ForLoopStatement(const ast::ForLoopStatement* s
}
if (auto* cond_expr = stmt->condition) {
auto* cond = Expression(cond_expr);
auto* cond = Load(Expression(cond_expr));
if (!cond) {
return false;
}
sem->SetCondition(cond);
behaviors.Add(cond->Behaviors());
RegisterLoadIfNeeded(cond);
}
if (auto* continuing = stmt->continuing) {
@@ -1457,15 +1456,13 @@ sem::WhileStatement* Resolver::WhileStatement(const ast::WhileStatement* stmt) {
return StatementScope(stmt, sem, [&] {
auto& behaviors = sem->Behaviors();
auto* cond = Expression(stmt->condition);
auto* cond = Load(Expression(stmt->condition));
if (!cond) {
return false;
}
sem->SetCondition(cond);
behaviors.Add(cond->Behaviors());
RegisterLoadIfNeeded(cond);
Mark(stmt->body);
auto* body = builder_->create<sem::LoopBlockStatement>(
@@ -1592,26 +1589,6 @@ sem::Expression* Resolver::Expression(const ast::Expression* root) {
return nullptr;
}
void Resolver::RegisterLoadIfNeeded(const sem::Expression* expr) {
if (!expr) {
return;
}
if (!expr->Type()->Is<type::Reference>()) {
return;
}
if (!current_function_) {
// There is currently no situation where the Load Rule can be invoked outside of a function.
return;
}
auto& info = alias_analysis_infos_[current_function_];
Switch(
expr->RootIdentifier(),
[&](const sem::GlobalVariable* global) {
info.module_scope_reads.insert({global, expr});
},
[&](const sem::Parameter* param) { info.parameter_reads.insert(param); });
}
void Resolver::RegisterStore(const sem::Expression* expr) {
auto& info = alias_analysis_infos_[current_function_];
Switch(
@@ -1778,6 +1755,33 @@ const type::Type* Resolver::ConcreteType(const type::Type* ty,
});
}
const sem::Expression* Resolver::Load(const sem::Expression* expr) {
if (!expr) {
// Allow for Load(Expression(blah)), where failures pass through Load()
return nullptr;
}
if (!expr->Type()->Is<type::Reference>()) {
// Expression is not a reference type, so cannot be loaded. Just return expr.
return expr;
}
auto* load = builder_->create<sem::Load>(expr, current_statement_);
load->Behaviors() = expr->Behaviors();
builder_->Sem().Replace(expr->Declaration(), load);
// Track the load for the alias analysis.
auto& alias_info = alias_analysis_infos_[current_function_];
Switch(
expr->RootIdentifier(),
[&](const sem::GlobalVariable* global) {
alias_info.module_scope_reads.insert({global, expr});
},
[&](const sem::Parameter* param) { alias_info.parameter_reads.insert(param); });
return load;
}
const sem::Expression* Resolver::Materialize(const sem::Expression* expr,
const type::Type* target_type /* = nullptr */) {
if (!expr) {
@@ -1829,8 +1833,8 @@ const sem::Expression* Resolver::Materialize(const sem::Expression* expr,
}
template <size_t N>
bool Resolver::MaybeMaterializeArguments(utils::Vector<const sem::Expression*, N>& args,
const sem::CallTarget* target) {
bool Resolver::MaybeMaterializeAndLoadArguments(utils::Vector<const sem::Expression*, N>& args,
const sem::CallTarget* target) {
for (size_t i = 0, n = std::min(args.Length(), target->Parameters().Length()); i < n; i++) {
const auto* param_ty = target->Parameters()[i]->Type();
if (ShouldMaterializeArgument(param_ty)) {
@@ -1840,6 +1844,13 @@ bool Resolver::MaybeMaterializeArguments(utils::Vector<const sem::Expression*, N
}
args[i] = materialized;
}
if (!param_ty->Is<type::Reference>()) {
auto* load = Load(args[i]);
if (!load) {
return false;
}
args[i] = load;
}
}
return true;
}
@@ -1875,7 +1886,7 @@ utils::Result<utils::Vector<const constant::Value*, N>> Resolver::ConvertArgumen
}
sem::Expression* Resolver::IndexAccessor(const ast::IndexAccessorExpression* expr) {
auto* idx = Materialize(sem_.Get(expr->index));
auto* idx = Load(Materialize(sem_.Get(expr->index)));
if (!idx) {
return nullptr;
}
@@ -1886,7 +1897,6 @@ sem::Expression* Resolver::IndexAccessor(const ast::IndexAccessorExpression* exp
// vec2(1, 2)[runtime-index]
obj = Materialize(obj);
}
RegisterLoadIfNeeded(idx);
if (!obj) {
return nullptr;
}
@@ -1939,7 +1949,7 @@ sem::Expression* Resolver::IndexAccessor(const ast::IndexAccessorExpression* exp
}
sem::Expression* Resolver::Bitcast(const ast::BitcastExpression* expr) {
auto* inner = Materialize(sem_.Get(expr->expr));
auto* inner = Load(Materialize(sem_.Get(expr->expr)));
if (!inner) {
return nullptr;
}
@@ -1948,8 +1958,6 @@ sem::Expression* Resolver::Bitcast(const ast::BitcastExpression* expr) {
return nullptr;
}
RegisterLoadIfNeeded(inner);
const constant::Value* val = nullptr;
// TODO(crbug.com/tint/1582): short circuit 'expr' once const eval of Bitcast is implemented.
if (auto r = const_eval_.Bitcast(ty, inner)) {
@@ -1990,8 +1998,6 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
args.Push(arg);
args_stage = sem::EarliestStage(args_stage, arg->Stage());
arg_behaviors.Add(arg->Behaviors());
RegisterLoadIfNeeded(arg);
}
arg_behaviors.Remove(sem::Behavior::kNext);
@@ -2008,7 +2014,7 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
if (!ctor_or_conv.target) {
return nullptr;
}
if (!MaybeMaterializeArguments(args, ctor_or_conv.target)) {
if (!MaybeMaterializeAndLoadArguments(args, ctor_or_conv.target)) {
return nullptr;
}
@@ -2037,7 +2043,7 @@ sem::Call* Resolver::Call(const ast::CallExpression* expr) {
// initializer call target.
auto arr_or_str_init = [&](const type::Type* ty,
const sem::CallTarget* call_target) -> sem::Call* {
if (!MaybeMaterializeArguments(args, call_target)) {
if (!MaybeMaterializeAndLoadArguments(args, call_target)) {
return nullptr;
}
@@ -2325,7 +2331,7 @@ sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
}
} else {
// Materialize arguments if the parameter type is not abstract
if (!MaybeMaterializeArguments(args, builtin.sem)) {
if (!MaybeMaterializeAndLoadArguments(args, builtin.sem)) {
return nullptr;
}
}
@@ -2476,14 +2482,17 @@ void Resolver::CollectTextureSamplerPairs(const sem::Builtin* builtin,
if (texture_index == -1) {
TINT_ICE(Resolver, diagnostics_) << "texture builtin without texture parameter";
}
if (auto* user = args[static_cast<size_t>(texture_index)]->As<sem::VariableUser>()) {
if (auto* user =
args[static_cast<size_t>(texture_index)]->UnwrapLoad()->As<sem::VariableUser>()) {
auto* texture = user->Variable();
if (!texture->Type()->UnwrapRef()->Is<type::StorageTexture>()) {
int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
const sem::Variable* sampler =
sampler_index != -1
? args[static_cast<size_t>(sampler_index)]->As<sem::VariableUser>()->Variable()
: nullptr;
const sem::Variable* sampler = sampler_index != -1
? args[static_cast<size_t>(sampler_index)]
->UnwrapLoad()
->As<sem::VariableUser>()
->Variable()
: nullptr;
current_function_->AddTextureSamplerPair(texture, sampler);
}
}
@@ -2497,7 +2506,7 @@ sem::Call* Resolver::FunctionCall(const ast::CallExpression* expr,
auto sym = expr->target.name->symbol;
auto name = builder_->Symbols().NameFor(sym);
if (!MaybeMaterializeArguments(args, target)) {
if (!MaybeMaterializeAndLoadArguments(args, target)) {
return nullptr;
}
@@ -2554,11 +2563,11 @@ void Resolver::CollectTextureSamplerPairs(sem::Function* func,
const sem::Variable* texture = pair.first;
const sem::Variable* sampler = pair.second;
if (auto* param = texture->As<sem::Parameter>()) {
texture = args[param->Index()]->As<sem::VariableUser>()->Variable();
texture = args[param->Index()]->UnwrapLoad()->As<sem::VariableUser>()->Variable();
}
if (sampler) {
if (auto* param = sampler->As<sem::Parameter>()) {
sampler = args[param->Index()]->As<sem::VariableUser>()->Variable();
sampler = args[param->Index()]->UnwrapLoad()->As<sem::VariableUser>()->Variable();
}
}
current_function_->AddTextureSamplerPair(texture, sampler);
@@ -2820,6 +2829,7 @@ sem::Expression* Resolver::MemberAccessor(const ast::MemberAccessorExpression* e
return nullptr;
}
const sem::Expression* obj_expr = object;
if (size == 1) {
// A single element swizzle is just the type of the vector.
ty = vec->type();
@@ -2831,12 +2841,15 @@ sem::Expression* Resolver::MemberAccessor(const ast::MemberAccessorExpression* e
// The vector will have a number of components equal to the length of
// the swizzle.
ty = builder_->create<type::Vector>(vec->type(), static_cast<uint32_t>(size));
// The load rule is invoked before the swizzle, if necessary.
obj_expr = Load(object);
}
auto val = const_eval_.Swizzle(ty, object, swizzle);
if (!val) {
return nullptr;
}
return builder_->create<sem::Swizzle>(expr, ty, current_statement_, val.Get(), object,
return builder_->create<sem::Swizzle>(expr, ty, current_statement_, val.Get(), obj_expr,
std::move(swizzle), has_side_effects, root_ident);
},
@@ -2872,8 +2885,15 @@ sem::Expression* Resolver::Binary(const ast::BinaryExpression* expr) {
}
}
RegisterLoadIfNeeded(lhs);
RegisterLoadIfNeeded(rhs);
// Load arguments if they are references
lhs = Load(lhs);
if (!lhs) {
return nullptr;
}
rhs = Load(rhs);
if (!rhs) {
return nullptr;
}
const constant::Value* value = nullptr;
if (stage == sem::EvaluationStage::kConstant) {
@@ -2975,6 +2995,14 @@ sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
return nullptr;
}
}
// Load expr if it is a reference
expr = Load(expr);
if (!expr) {
return nullptr;
}
stage = expr->Stage();
if (stage == sem::EvaluationStage::kConstant) {
if (op.const_eval_fn) {
if (auto r = (const_eval_.*op.const_eval_fn)(
@@ -2988,7 +3016,6 @@ sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
stage = sem::EvaluationStage::kRuntime;
}
}
RegisterLoadIfNeeded(expr);
break;
}
}
@@ -3437,7 +3464,7 @@ sem::Statement* Resolver::ReturnStatement(const ast::ReturnStatement* stmt) {
const type::Type* value_ty = nullptr;
if (auto* value = stmt->value) {
const auto* expr = Expression(value);
const auto* expr = Load(Expression(value));
if (!expr) {
return false;
}
@@ -3448,9 +3475,8 @@ sem::Statement* Resolver::ReturnStatement(const ast::ReturnStatement* stmt) {
}
}
behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
value_ty = expr->Type()->UnwrapRef();
RegisterLoadIfNeeded(expr);
value_ty = expr->Type();
} else {
value_ty = builder_->create<type::Void>();
}
@@ -3468,15 +3494,13 @@ sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt
return StatementScope(stmt, sem, [&] {
auto& behaviors = sem->Behaviors();
const auto* cond = Expression(stmt->condition);
const auto* cond = Load(Expression(stmt->condition));
if (!cond) {
return false;
}
behaviors = cond->Behaviors() - sem::Behavior::kNext;
RegisterLoadIfNeeded(cond);
auto* cond_ty = cond->Type()->UnwrapRef();
auto* cond_ty = cond->Type();
// Determine the common type across all selectors and the switch expression
// This must materialize to an integer scalar (non-abstract).
@@ -3579,7 +3603,10 @@ sem::Statement* Resolver::AssignmentStatement(const ast::AssignmentStatement* st
}
}
RegisterLoadIfNeeded(rhs);
rhs = Load(rhs);
if (!rhs) {
return false;
}
auto& behaviors = sem->Behaviors();
behaviors = rhs->Behaviors();
@@ -3609,7 +3636,7 @@ sem::Statement* Resolver::BreakIfStatement(const ast::BreakIfStatement* stmt) {
auto* sem = builder_->create<sem::BreakIfStatement>(stmt, current_compound_statement_,
current_function_);
return StatementScope(stmt, sem, [&] {
auto* cond = Expression(stmt->condition);
auto* cond = Load(Expression(stmt->condition));
if (!cond) {
return false;
}
@@ -3617,8 +3644,6 @@ sem::Statement* Resolver::BreakIfStatement(const ast::BreakIfStatement* stmt) {
sem->Behaviors() = cond->Behaviors();
sem->Behaviors().Add(sem::Behavior::kBreak);
RegisterLoadIfNeeded(cond);
return validator_.BreakIfStatement(sem, current_statement_);
});
}
@@ -3645,12 +3670,11 @@ sem::Statement* Resolver::CompoundAssignmentStatement(
return false;
}
auto* rhs = Expression(stmt->rhs);
auto* rhs = Load(Expression(stmt->rhs));
if (!rhs) {
return false;
}
RegisterLoadIfNeeded(rhs);
RegisterStore(lhs);
sem->Behaviors() = rhs->Behaviors() + lhs->Behaviors();
@@ -3705,7 +3729,6 @@ sem::Statement* Resolver::IncrementDecrementStatement(
}
sem->Behaviors() = lhs->Behaviors();
RegisterLoadIfNeeded(lhs);
RegisterStore(lhs);
return validator_.IncrementDecrementStatement(stmt);

View File

@@ -157,10 +157,6 @@ class Resolver {
sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
/// Register a memory load from an expression, to track accesses to root identifiers in order to
/// perform alias analysis.
void RegisterLoadIfNeeded(const sem::Expression* expr);
/// Register a memory store to an expression, to track accesses to root identifiers in order to
/// perform alias analysis.
void RegisterStore(const sem::Expression* expr);
@@ -169,8 +165,11 @@ class Resolver {
/// @returns true is the call arguments are free from aliasing issues, false otherwise.
bool AliasAnalysis(const sem::Call* call);
/// If `expr` is of a reference type, then Load will create and return a sem::Load node wrapping
/// `expr`. If `expr` is not of a reference type, then Load will just return `expr`.
const sem::Expression* Load(const sem::Expression* expr);
/// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
/// If `expr` is of an abstract-numeric type:
/// * Materialize will create and return a sem::Materialize node wrapping `expr`.
/// * The AST -> Sem binding will be updated to point to the new sem::Materialize node.
/// * The sem::Materialize node will have a new concrete type, which will be `target_type` if
@@ -181,15 +180,19 @@ class Resolver {
/// if `expr` has a element type of abstract-float.
/// * The sem::Materialize constant value will be the value of `expr` value-converted to the
/// materialized type.
/// If `expr` is not of an abstract-numeric type, then Materialize() will just return `expr`.
/// If `expr` is nullptr, then Materialize() will also return nullptr.
const sem::Expression* Materialize(const sem::Expression* expr,
const type::Type* target_type = nullptr);
/// Materializes all the arguments in `args` to the parameter types of `target`.
/// For each argument in `args`:
/// * Calls Materialize() passing the argument and the corresponding parameter type.
/// * Calls Load() passing the argument, iff the corresponding parameter type is not a
/// reference type.
/// @returns true on success, false on failure.
template <size_t N>
bool MaybeMaterializeArguments(utils::Vector<const sem::Expression*, N>& args,
const sem::CallTarget* target);
bool MaybeMaterializeAndLoadArguments(utils::Vector<const sem::Expression*, N>& args,
const sem::CallTarget* target);
/// @returns true if an argument of an abstract numeric type, passed to a parameter of type
/// `parameter_ty` should be materialized.

View File

@@ -707,8 +707,7 @@ TEST_F(ResolverTest, Expr_Identifier_GlobalVariable) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(ident), nullptr);
ASSERT_TRUE(TypeOf(ident)->Is<type::Reference>());
EXPECT_TRUE(TypeOf(ident)->UnwrapRef()->Is<type::F32>());
EXPECT_TRUE(TypeOf(ident)->Is<type::F32>());
EXPECT_TRUE(CheckVarUsers(my_var, utils::Vector{ident}));
ASSERT_NE(VarOf(ident), nullptr);
EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
@@ -788,8 +787,7 @@ TEST_F(ResolverTest, Expr_Identifier_FunctionVariable) {
EXPECT_TRUE(TypeOf(my_var_a)->UnwrapRef()->Is<type::F32>());
EXPECT_EQ(StmtOf(my_var_a), assign);
ASSERT_NE(TypeOf(my_var_b), nullptr);
ASSERT_TRUE(TypeOf(my_var_b)->Is<type::Reference>());
EXPECT_TRUE(TypeOf(my_var_b)->UnwrapRef()->Is<type::F32>());
EXPECT_TRUE(TypeOf(my_var_b)->Is<type::F32>());
EXPECT_EQ(StmtOf(my_var_b), assign);
EXPECT_TRUE(CheckVarUsers(var, utils::Vector{my_var_a, my_var_b}));
ASSERT_NE(VarOf(my_var_a), nullptr);
@@ -1250,11 +1248,8 @@ TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(mem), nullptr);
ASSERT_TRUE(TypeOf(mem)->Is<type::Reference>());
auto* ref = TypeOf(mem)->As<type::Reference>();
EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
EXPECT_TRUE(TypeOf(mem)->Is<type::F32>());
auto* sma = Sem().Get(mem)->UnwrapLoad()->As<sem::StructMemberAccess>();
ASSERT_NE(sma, nullptr);
EXPECT_TRUE(sma->Member()->Type()->Is<type::F32>());
EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
@@ -1274,11 +1269,8 @@ TEST_F(ResolverTest, Expr_MemberAccessor_Struct_Alias) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(mem), nullptr);
ASSERT_TRUE(TypeOf(mem)->Is<type::Reference>());
auto* ref = TypeOf(mem)->As<type::Reference>();
EXPECT_TRUE(ref->StoreType()->Is<type::F32>());
auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
EXPECT_TRUE(TypeOf(mem)->Is<type::F32>());
auto* sma = Sem().Get(mem)->UnwrapLoad()->As<sem::StructMemberAccess>();
ASSERT_NE(sma, nullptr);
EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
EXPECT_TRUE(sma->Member()->Type()->Is<type::F32>());
@@ -1300,7 +1292,7 @@ TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle) {
auto* sma = Sem().Get(mem)->As<sem::Swizzle>();
ASSERT_NE(sma, nullptr);
EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
EXPECT_THAT(sma->As<sem::Swizzle>()->Indices(), ElementsAre(0, 2, 1, 3));
EXPECT_THAT(sma->Indices(), ElementsAre(0, 2, 1, 3));
}
TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
@@ -1312,14 +1304,11 @@ TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
ASSERT_NE(TypeOf(mem), nullptr);
ASSERT_TRUE(TypeOf(mem)->Is<type::Reference>());
auto* ref = TypeOf(mem)->As<type::Reference>();
ASSERT_TRUE(ref->StoreType()->Is<type::F32>());
auto* sma = Sem().Get(mem)->As<sem::Swizzle>();
ASSERT_TRUE(TypeOf(mem)->Is<type::F32>());
auto* sma = Sem().Get(mem)->UnwrapLoad()->As<sem::Swizzle>();
ASSERT_NE(sma, nullptr);
EXPECT_EQ(sma->Object()->Declaration(), mem->structure);
EXPECT_THAT(Sem().Get(mem)->As<sem::Swizzle>()->Indices(), ElementsAre(2));
EXPECT_THAT(sma->Indices(), ElementsAre(2));
}
TEST_F(ResolverTest, Expr_Accessor_MultiLevel) {

View File

@@ -83,7 +83,7 @@ class TestHelper : public ProgramBuilder {
/// @return the resolved sem::Variable of the identifier, or nullptr if
/// the expression did not resolve to a variable.
const sem::Variable* VarOf(const ast::Expression* expr) {
auto* sem_ident = Sem().Get(expr);
auto* sem_ident = Sem().Get(expr)->UnwrapLoad();
auto* var_user = sem_ident ? sem_ident->As<sem::VariableUser>() : nullptr;
return var_user ? var_user->Variable() : nullptr;
}

View File

@@ -83,7 +83,7 @@ TEST_F(SideEffectsTest, VariableUser) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
ASSERT_NE(sem, nullptr);
EXPECT_TRUE(sem->Is<sem::VariableUser>());
EXPECT_TRUE(sem->UnwrapLoad()->Is<sem::VariableUser>());
EXPECT_FALSE(sem->HasSideEffects());
}
@@ -438,8 +438,8 @@ TEST_F(SideEffectsTest, MemberAccessor_Vector) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_TRUE(sem->Is<sem::MemberAccessorExpression>());
ASSERT_NE(sem, nullptr);
EXPECT_TRUE(sem->UnwrapLoad()->Is<sem::MemberAccessorExpression>());
EXPECT_FALSE(sem->HasSideEffects());
}
@@ -450,8 +450,8 @@ TEST_F(SideEffectsTest, MemberAccessor_VectorSwizzleNoSE) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_TRUE(sem->Is<sem::Swizzle>());
ASSERT_NE(sem, nullptr);
EXPECT_TRUE(sem->Is<sem::Swizzle>());
EXPECT_FALSE(sem->HasSideEffects());
}
@@ -462,8 +462,8 @@ TEST_F(SideEffectsTest, MemberAccessor_VectorSwizzleSE) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get(expr);
EXPECT_TRUE(sem->Is<sem::Swizzle>());
ASSERT_NE(sem, nullptr);
EXPECT_TRUE(sem->Is<sem::Swizzle>());
EXPECT_TRUE(sem->HasSideEffects());
}

View File

@@ -1035,7 +1035,7 @@ class UniformityGraph {
};
auto name = builder_->Symbols().NameFor(ident->symbol);
auto* sem = sem_.Get(ident)->UnwrapMaterialize()->As<sem::VariableUser>()->Variable();
auto* sem = sem_.Get(ident)->Unwrap()->As<sem::VariableUser>()->Variable();
auto* node = CreateNode(name + "_ident_expr", ident);
return Switch(
sem,
@@ -1203,7 +1203,7 @@ class UniformityGraph {
[&](const ast::IdentifierExpression* i) {
auto name = builder_->Symbols().NameFor(i->symbol);
auto* sem = sem_.Get<sem::VariableUser>(i);
auto* sem = sem_.Get(i)->UnwrapLoad()->As<sem::VariableUser>();
if (sem->Variable()->Is<sem::GlobalVariable>()) {
return std::make_pair(cf, current_function_->may_be_non_uniform);
} else if (auto* local = sem->Variable()->As<sem::LocalVariable>()) {
@@ -1536,7 +1536,7 @@ class UniformityGraph {
Switch(
non_uniform_source->ast,
[&](const ast::IdentifierExpression* ident) {
auto* var = sem_.Get<sem::VariableUser>(ident)->Variable();
auto* var = sem_.Get(ident)->UnwrapLoad()->As<sem::VariableUser>()->Variable();
std::string var_type = get_var_type(var);
diagnostics_.add_note(diag::System::Resolver,
"reading from " + var_type + "'" +

View File

@@ -2205,7 +2205,7 @@ bool Validator::Return(const ast::ReturnStatement* ret,
}
bool Validator::SwitchStatement(const ast::SwitchStatement* s) {
auto* cond_ty = sem_.TypeOf(s->condition)->UnwrapRef();
auto* cond_ty = sem_.TypeOf(s->condition);
if (!cond_ty->is_integer_scalar()) {
AddError("switch statement selector expression must be of a scalar integer type",
s->condition->source);

View File

@@ -249,7 +249,8 @@ TEST_F(ResolverVariableTest, LocalVar_ShadowsGlobalVar) {
ASSERT_NE(local, nullptr);
EXPECT_EQ(local->Shadows(), global);
auto* user_v = Sem().Get<sem::VariableUser>(local->Declaration()->initializer);
auto* user_v =
Sem().Get(local->Declaration()->initializer)->UnwrapLoad()->As<sem::VariableUser>();
ASSERT_NE(user_v, nullptr);
EXPECT_EQ(user_v->Variable(), global);
}
@@ -298,7 +299,8 @@ TEST_F(ResolverVariableTest, LocalVar_ShadowsLocalVar) {
ASSERT_NE(local_y, nullptr);
EXPECT_EQ(local_y->Shadows(), local_x);
auto* user_y = Sem().Get<sem::VariableUser>(local_y->Declaration()->initializer);
auto* user_y =
Sem().Get(local_y->Declaration()->initializer)->UnwrapLoad()->As<sem::VariableUser>();
ASSERT_NE(user_y, nullptr);
EXPECT_EQ(user_y->Variable(), local_x);
}
@@ -563,7 +565,8 @@ TEST_F(ResolverVariableTest, LocalLet_ShadowsGlobalVar) {
ASSERT_NE(local, nullptr);
EXPECT_EQ(local->Shadows(), global);
auto* user = Sem().Get<sem::VariableUser>(local->Declaration()->initializer);
auto* user =
Sem().Get(local->Declaration()->initializer)->UnwrapLoad()->As<sem::VariableUser>();
ASSERT_NE(user, nullptr);
EXPECT_EQ(user->Variable(), global);
}
@@ -612,7 +615,8 @@ TEST_F(ResolverVariableTest, LocalLet_ShadowsLocalVar) {
ASSERT_NE(local_l, nullptr);
EXPECT_EQ(local_l->Shadows(), local_v);
auto* user = Sem().Get<sem::VariableUser>(local_l->Declaration()->initializer);
auto* user =
Sem().Get(local_l->Declaration()->initializer)->UnwrapLoad()->As<sem::VariableUser>();
ASSERT_NE(user, nullptr);
EXPECT_EQ(user->Variable(), local_v);
}