mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-06 13:15:59 +00:00
resolver: Resolve atomic types
Bug: tint:892 Change-Id: Ib595378b3b126a8f6bc4712ece943ba70816cb46 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/54647 Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
parent
cf1613ee35
commit
313e6184e6
@ -591,6 +591,8 @@ if(${TINT_BUILD_TESTS})
|
|||||||
intrinsic_table_test.cc
|
intrinsic_table_test.cc
|
||||||
program_test.cc
|
program_test.cc
|
||||||
resolver/assignment_validation_test.cc
|
resolver/assignment_validation_test.cc
|
||||||
|
resolver/atomics_test.cc
|
||||||
|
resolver/atomics_validation_test.cc
|
||||||
resolver/block_test.cc
|
resolver/block_test.cc
|
||||||
resolver/builtins_validation_test.cc
|
resolver/builtins_validation_test.cc
|
||||||
resolver/call_test.cc
|
resolver/call_test.cc
|
||||||
|
@ -745,6 +745,12 @@ class ProgramBuilder {
|
|||||||
return builder->create<ast::Atomic>(source, type);
|
return builder->create<ast::Atomic>(source, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @param type the type of the atomic
|
||||||
|
/// @return the atomic to `type`
|
||||||
|
ast::Atomic* atomic(ast::Type* type) const {
|
||||||
|
return builder->create<ast::Atomic>(type);
|
||||||
|
}
|
||||||
|
|
||||||
/// @return the atomic to type `T`
|
/// @return the atomic to type `T`
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ast::Atomic* atomic() const {
|
ast::Atomic* atomic() const {
|
||||||
@ -1079,6 +1085,19 @@ class ProgramBuilder {
|
|||||||
type, ExprList(std::forward<ARGS>(args)...));
|
type, ExprList(std::forward<ARGS>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @param source the source information
|
||||||
|
/// @param type the type to construct
|
||||||
|
/// @param args the arguments for the constructor
|
||||||
|
/// @return an `ast::TypeConstructorExpression` of `type` constructed with the
|
||||||
|
/// values `args`.
|
||||||
|
template <typename... ARGS>
|
||||||
|
ast::TypeConstructorExpression* Construct(const Source& source,
|
||||||
|
ast::Type* type,
|
||||||
|
ARGS&&... args) {
|
||||||
|
return create<ast::TypeConstructorExpression>(
|
||||||
|
source, type, ExprList(std::forward<ARGS>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
/// @param args the arguments for the vector constructor
|
/// @param args the arguments for the vector constructor
|
||||||
/// @return an `ast::TypeConstructorExpression` of a 2-element vector of type
|
/// @return an `ast::TypeConstructorExpression` of a 2-element vector of type
|
||||||
/// `T`, constructed with the values `args`.
|
/// `T`, constructed with the values `args`.
|
||||||
|
74
src/resolver/atomics_test.cc
Normal file
74
src/resolver/atomics_test.cc
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2021 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/ast/struct_block_decoration.h"
|
||||||
|
#include "src/resolver/resolver.h"
|
||||||
|
#include "src/resolver/resolver_test_helper.h"
|
||||||
|
#include "src/sem/atomic_type.h"
|
||||||
|
#include "src/sem/reference_type.h"
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace resolver {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct ResolverAtomicTest : public resolver::TestHelper,
|
||||||
|
public testing::Test {};
|
||||||
|
|
||||||
|
TEST_F(ResolverAtomicTest, GlobalWorkgroupI32) {
|
||||||
|
auto* g = Global("a", ty.atomic(Source{{12, 34}}, ty.i32()),
|
||||||
|
ast::StorageClass::kWorkgroup);
|
||||||
|
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
|
||||||
|
auto* atomic = TypeOf(g)->UnwrapRef()->As<sem::Atomic>();
|
||||||
|
ASSERT_NE(atomic, nullptr);
|
||||||
|
EXPECT_TRUE(atomic->Type()->Is<sem::I32>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverAtomicTest, GlobalWorkgroupU32) {
|
||||||
|
auto* g = Global("a", ty.atomic(Source{{12, 34}}, ty.u32()),
|
||||||
|
ast::StorageClass::kWorkgroup);
|
||||||
|
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
|
||||||
|
auto* atomic = TypeOf(g)->UnwrapRef()->As<sem::Atomic>();
|
||||||
|
ASSERT_NE(atomic, nullptr);
|
||||||
|
EXPECT_TRUE(atomic->Type()->Is<sem::U32>());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverAtomicTest, GlobalStorageStruct) {
|
||||||
|
auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))},
|
||||||
|
{create<ast::StructBlockDecoration>()});
|
||||||
|
auto* g = Global("g", ty.Of(s), ast::StorageClass::kStorage,
|
||||||
|
ast::Access::kReadWrite,
|
||||||
|
ast::DecorationList{
|
||||||
|
create<ast::BindingDecoration>(0),
|
||||||
|
create<ast::GroupDecoration>(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
|
||||||
|
auto* str = TypeOf(g)->UnwrapRef()->As<sem::Struct>();
|
||||||
|
ASSERT_NE(str, nullptr);
|
||||||
|
ASSERT_EQ(str->Members().size(), 1u);
|
||||||
|
auto* atomic = str->Members()[0]->Type()->As<sem::Atomic>();
|
||||||
|
ASSERT_NE(atomic, nullptr);
|
||||||
|
ASSERT_TRUE(atomic->Type()->Is<sem::I32>());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace resolver
|
||||||
|
} // namespace tint
|
77
src/resolver/atomics_validation_test.cc
Normal file
77
src/resolver/atomics_validation_test.cc
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2021 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/resolver/resolver.h"
|
||||||
|
#include "src/resolver/resolver_test_helper.h"
|
||||||
|
#include "src/sem/atomic_type.h"
|
||||||
|
#include "src/sem/reference_type.h"
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
|
namespace tint {
|
||||||
|
namespace resolver {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct ResolverAtomicValidationTest : public resolver::TestHelper,
|
||||||
|
public testing::Test {};
|
||||||
|
|
||||||
|
TEST_F(ResolverAtomicValidationTest, GlobalOfInvalidType) {
|
||||||
|
Global("a", ty.atomic(ty.f32(Source{{12, 34}})),
|
||||||
|
ast::StorageClass::kWorkgroup);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), "12:34 error: atomic only supports i32 or u32 types");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverAtomicValidationTest, GlobalOfInvalidStorageClass) {
|
||||||
|
Global("a", ty.atomic(Source{{12, 34}}, ty.i32()),
|
||||||
|
ast::StorageClass::kPrivate);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), "12:34 error: atomic var requires workgroup storage");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverAtomicValidationTest, GlobalPrivateStruct) {
|
||||||
|
auto* s =
|
||||||
|
Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
|
||||||
|
Global("g", ty.Of(s), ast::StorageClass::kPrivate);
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: atomic types can only be used in storage classes "
|
||||||
|
"workgroup or storage, but was used by storage class private");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(crbug.com/tint/901): Validate that storage buffer access mode is
|
||||||
|
// read_write.
|
||||||
|
|
||||||
|
TEST_F(ResolverAtomicValidationTest, Local) {
|
||||||
|
WrapInFunction(Var("a", ty.atomic(Source{{12, 34}}, ty.i32())));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: cannot declare an atomic var in a function scope");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverAtomicValidationTest, NoAtomicExpr) {
|
||||||
|
WrapInFunction(Construct(Source{{12, 34}}, ty.atomic<u32>()));
|
||||||
|
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(),
|
||||||
|
"12:34 error: an expression must not evaluate to an atomic type");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace resolver
|
||||||
|
} // namespace tint
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "src/resolver/resolver_test_helper.h"
|
#include "src/resolver/resolver_test_helper.h"
|
||||||
|
#include "src/sem/atomic_type.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace resolver {
|
namespace resolver {
|
||||||
@ -92,6 +93,11 @@ TEST_F(ResolverIsHostShareable, Pointer) {
|
|||||||
EXPECT_FALSE(r()->IsHostShareable(ptr));
|
EXPECT_FALSE(r()->IsHostShareable(ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverIsHostShareable, Atomic) {
|
||||||
|
EXPECT_TRUE(r()->IsHostShareable(create<sem::Atomic>(create<sem::I32>())));
|
||||||
|
EXPECT_TRUE(r()->IsHostShareable(create<sem::Atomic>(create<sem::U32>())));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ResolverIsHostShareable, ArraySizedOfHostShareable) {
|
TEST_F(ResolverIsHostShareable, ArraySizedOfHostShareable) {
|
||||||
auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, true);
|
auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, true);
|
||||||
EXPECT_TRUE(r()->IsHostShareable(arr));
|
EXPECT_TRUE(r()->IsHostShareable(arr));
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "src/resolver/resolver_test_helper.h"
|
#include "src/resolver/resolver_test_helper.h"
|
||||||
|
#include "src/sem/atomic_type.h"
|
||||||
|
|
||||||
namespace tint {
|
namespace tint {
|
||||||
namespace resolver {
|
namespace resolver {
|
||||||
@ -67,6 +68,11 @@ TEST_F(ResolverIsStorableTest, Pointer) {
|
|||||||
EXPECT_FALSE(r()->IsStorable(ptr));
|
EXPECT_FALSE(r()->IsStorable(ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ResolverIsStorableTest, Atomic) {
|
||||||
|
EXPECT_TRUE(r()->IsStorable(create<sem::Atomic>(create<sem::I32>())));
|
||||||
|
EXPECT_TRUE(r()->IsStorable(create<sem::Atomic>(create<sem::U32>())));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) {
|
TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) {
|
||||||
auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, true);
|
auto* arr = create<sem::Array>(create<sem::I32>(), 5, 4, 20, 4, true);
|
||||||
EXPECT_TRUE(r()->IsStorable(arr));
|
EXPECT_TRUE(r()->IsStorable(arr));
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "src/ast/vector.h"
|
#include "src/ast/vector.h"
|
||||||
#include "src/ast/workgroup_decoration.h"
|
#include "src/ast/workgroup_decoration.h"
|
||||||
#include "src/sem/array.h"
|
#include "src/sem/array.h"
|
||||||
|
#include "src/sem/atomic_type.h"
|
||||||
#include "src/sem/call.h"
|
#include "src/sem/call.h"
|
||||||
#include "src/sem/depth_texture_type.h"
|
#include "src/sem/depth_texture_type.h"
|
||||||
#include "src/sem/function.h"
|
#include "src/sem/function.h"
|
||||||
@ -166,19 +167,14 @@ bool Resolver::Resolve() {
|
|||||||
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
|
// https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
|
||||||
bool Resolver::IsPlain(const sem::Type* type) {
|
bool Resolver::IsPlain(const sem::Type* type) {
|
||||||
if (type->is_scalar() || type->Is<sem::Vector>() || type->Is<sem::Matrix>() ||
|
return type->is_scalar() || type->Is<sem::Atomic>() ||
|
||||||
type->Is<sem::Array>() || type->Is<sem::Struct>()) {
|
type->Is<sem::Vector>() || type->Is<sem::Matrix>() ||
|
||||||
return true;
|
type->Is<sem::Array>() || type->Is<sem::Struct>();
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
|
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
|
||||||
bool Resolver::IsStorable(const sem::Type* type) {
|
bool Resolver::IsStorable(const sem::Type* type) {
|
||||||
if (IsPlain(type) || type->Is<sem::Texture>() || type->Is<sem::Sampler>()) {
|
return IsPlain(type) || type->Is<sem::Texture>() || type->Is<sem::Sampler>();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
|
// https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
|
||||||
@ -203,6 +199,9 @@ bool Resolver::IsHostShareable(const sem::Type* type) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (auto* atomic = type->As<sem::Atomic>()) {
|
||||||
|
return IsHostShareable(atomic->Type());
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,6 +236,9 @@ bool Resolver::ResolveInternal() {
|
|||||||
if (!ValidatePipelineStages()) {
|
if (!ValidatePipelineStages()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!ValidateAtomicUses()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
@ -290,6 +292,16 @@ sem::Type* Resolver::Type(const ast::Type* ty) {
|
|||||||
if (auto* t = ty->As<ast::Array>()) {
|
if (auto* t = ty->As<ast::Array>()) {
|
||||||
return Array(t);
|
return Array(t);
|
||||||
}
|
}
|
||||||
|
if (auto* t = ty->As<ast::Atomic>()) {
|
||||||
|
if (auto* el = Type(t->type())) {
|
||||||
|
auto* a = builder_->create<sem::Atomic>(const_cast<sem::Type*>(el));
|
||||||
|
if (!ValidateAtomic(t, a)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
if (auto* t = ty->As<ast::Pointer>()) {
|
if (auto* t = ty->As<ast::Pointer>()) {
|
||||||
if (auto* el = Type(t->type())) {
|
if (auto* el = Type(t->type())) {
|
||||||
auto access = t->access();
|
auto access = t->access();
|
||||||
@ -356,6 +368,45 @@ sem::Type* Resolver::Type(const ast::Type* ty) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Resolver::ValidateAtomic(const ast::Atomic* a, const sem::Atomic* s) {
|
||||||
|
// https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
|
||||||
|
// T must be either u32 or i32.
|
||||||
|
if (!s->Type()->IsAnyOf<sem::U32, sem::I32>()) {
|
||||||
|
diagnostics_.add_error("atomic only supports i32 or u32 types",
|
||||||
|
a->type()->source());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Resolver::ValidateAtomicUses() {
|
||||||
|
// https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
|
||||||
|
// Atomic types may only be instantiated by variables in the workgroup storage
|
||||||
|
// class or by storage buffer variables with a read_write access mode.
|
||||||
|
for (auto sm : atomic_members_) {
|
||||||
|
auto* structure = sm.structure;
|
||||||
|
for (auto usage : structure->StorageClassUsage()) {
|
||||||
|
if (usage == ast::StorageClass::kWorkgroup) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (usage != ast::StorageClass::kStorage) {
|
||||||
|
// TODO(crbug.com/tint/901): Validate that the access mode is
|
||||||
|
// read_write.
|
||||||
|
auto* member = structure->Members()[sm.index];
|
||||||
|
diagnostics_.add_error(
|
||||||
|
"atomic types can only be used in storage classes workgroup or "
|
||||||
|
"storage, but was used by storage class " +
|
||||||
|
std::string(ast::str(usage)),
|
||||||
|
member->Declaration()->type()->source());
|
||||||
|
// TODO(crbug.com/tint/901): Add note pointing at where the usage came
|
||||||
|
// from.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Resolver::ValidateStorageTexture(const ast::StorageTexture* t) {
|
bool Resolver::ValidateStorageTexture(const ast::StorageTexture* t) {
|
||||||
switch (t->access()) {
|
switch (t->access()) {
|
||||||
case ast::Access::kUndefined:
|
case ast::Access::kUndefined:
|
||||||
@ -816,6 +867,26 @@ bool Resolver::ValidateVariable(const VariableInfo* info) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
|
||||||
|
// Atomic types may only be instantiated by variables in the workgroup storage
|
||||||
|
// class or by storage buffer variables with a read_write access mode.
|
||||||
|
if (info->type->UnwrapRef()->Is<sem::Atomic>()) {
|
||||||
|
if (info->kind != VariableKind::kGlobal) {
|
||||||
|
// Neither storage nor workgroup storage classes can be used in function
|
||||||
|
// scopes.
|
||||||
|
diagnostics_.add_error("cannot declare an atomic var in a function scope",
|
||||||
|
info->declaration->type()->source());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (info->storage_class != ast::StorageClass::kWorkgroup) {
|
||||||
|
// Storage buffers require a structure, so just check for workgroup
|
||||||
|
// storage here.
|
||||||
|
diagnostics_.add_error("atomic var requires workgroup storage",
|
||||||
|
info->declaration->type()->source());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1620,36 +1691,42 @@ bool Resolver::Expression(ast::Expression* expr) {
|
|||||||
return true; // Already resolved
|
return true; // Already resolved
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto* a = expr->As<ast::ArrayAccessorExpression>()) {
|
bool ok = false;
|
||||||
return ArrayAccessor(a);
|
if (auto* array = expr->As<ast::ArrayAccessorExpression>()) {
|
||||||
}
|
ok = ArrayAccessor(array);
|
||||||
if (auto* b = expr->As<ast::BinaryExpression>()) {
|
} else if (auto* bin_op = expr->As<ast::BinaryExpression>()) {
|
||||||
return Binary(b);
|
ok = Binary(bin_op);
|
||||||
}
|
} else if (auto* bitcast = expr->As<ast::BitcastExpression>()) {
|
||||||
if (auto* b = expr->As<ast::BitcastExpression>()) {
|
ok = Bitcast(bitcast);
|
||||||
return Bitcast(b);
|
} else if (auto* call = expr->As<ast::CallExpression>()) {
|
||||||
}
|
ok = Call(call);
|
||||||
if (auto* c = expr->As<ast::CallExpression>()) {
|
} else if (auto* ctor = expr->As<ast::ConstructorExpression>()) {
|
||||||
return Call(c);
|
ok = Constructor(ctor);
|
||||||
}
|
} else if (auto* ident = expr->As<ast::IdentifierExpression>()) {
|
||||||
if (auto* c = expr->As<ast::ConstructorExpression>()) {
|
ok = Identifier(ident);
|
||||||
return Constructor(c);
|
} else if (auto* member = expr->As<ast::MemberAccessorExpression>()) {
|
||||||
}
|
ok = MemberAccessor(member);
|
||||||
if (auto* i = expr->As<ast::IdentifierExpression>()) {
|
} else if (auto* unary = expr->As<ast::UnaryOpExpression>()) {
|
||||||
return Identifier(i);
|
ok = UnaryOp(unary);
|
||||||
}
|
} else {
|
||||||
if (auto* m = expr->As<ast::MemberAccessorExpression>()) {
|
|
||||||
return MemberAccessor(m);
|
|
||||||
}
|
|
||||||
if (auto* u = expr->As<ast::UnaryOpExpression>()) {
|
|
||||||
return UnaryOp(u);
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnostics_.add_error("unknown expression for type determination",
|
diagnostics_.add_error("unknown expression for type determination",
|
||||||
expr->source());
|
expr->source());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto* ty = TypeOf(expr);
|
||||||
|
if (ty->Is<sem::Atomic>()) {
|
||||||
|
diagnostics_.add_error("an expression must not evaluate to an atomic type",
|
||||||
|
expr->source());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
|
bool Resolver::ArrayAccessor(ast::ArrayAccessorExpression* expr) {
|
||||||
Mark(expr->array());
|
Mark(expr->array());
|
||||||
if (!Expression(expr->array())) {
|
if (!Expression(expr->array())) {
|
||||||
@ -2883,7 +2960,8 @@ bool Resolver::DefaultAlignAndSize(const sem::Type* ty,
|
|||||||
align = 4;
|
align = 4;
|
||||||
size = 4;
|
size = 4;
|
||||||
return true;
|
return true;
|
||||||
} else if (auto* vec = ty->As<sem::Vector>()) {
|
}
|
||||||
|
if (auto* vec = ty->As<sem::Vector>()) {
|
||||||
if (vec->size() < 2 || vec->size() > 4) {
|
if (vec->size() < 2 || vec->size() > 4) {
|
||||||
TINT_UNREACHABLE(diagnostics_)
|
TINT_UNREACHABLE(diagnostics_)
|
||||||
<< "Invalid vector size: vec" << vec->size();
|
<< "Invalid vector size: vec" << vec->size();
|
||||||
@ -2892,7 +2970,8 @@ bool Resolver::DefaultAlignAndSize(const sem::Type* ty,
|
|||||||
align = vector_align[vec->size()];
|
align = vector_align[vec->size()];
|
||||||
size = vector_size[vec->size()];
|
size = vector_size[vec->size()];
|
||||||
return true;
|
return true;
|
||||||
} else if (auto* mat = ty->As<sem::Matrix>()) {
|
}
|
||||||
|
if (auto* mat = ty->As<sem::Matrix>()) {
|
||||||
if (mat->columns() < 2 || mat->columns() > 4 || mat->rows() < 2 ||
|
if (mat->columns() < 2 || mat->columns() > 4 || mat->rows() < 2 ||
|
||||||
mat->rows() > 4) {
|
mat->rows() > 4) {
|
||||||
TINT_UNREACHABLE(diagnostics_)
|
TINT_UNREACHABLE(diagnostics_)
|
||||||
@ -2902,15 +2981,20 @@ bool Resolver::DefaultAlignAndSize(const sem::Type* ty,
|
|||||||
align = vector_align[mat->rows()];
|
align = vector_align[mat->rows()];
|
||||||
size = vector_align[mat->rows()] * mat->columns();
|
size = vector_align[mat->rows()] * mat->columns();
|
||||||
return true;
|
return true;
|
||||||
} else if (auto* s = ty->As<sem::Struct>()) {
|
}
|
||||||
|
if (auto* s = ty->As<sem::Struct>()) {
|
||||||
align = s->Align();
|
align = s->Align();
|
||||||
size = s->Size();
|
size = s->Size();
|
||||||
return true;
|
return true;
|
||||||
} else if (auto* a = ty->As<sem::Array>()) {
|
}
|
||||||
|
if (auto* a = ty->As<sem::Array>()) {
|
||||||
align = a->Align();
|
align = a->Align();
|
||||||
size = a->SizeInBytes();
|
size = a->SizeInBytes();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (auto* a = ty->As<sem::Atomic>()) {
|
||||||
|
return DefaultAlignAndSize(a->Type(), align, size);
|
||||||
|
}
|
||||||
TINT_UNREACHABLE(diagnostics_) << "invalid type " << ty->TypeInfo().name;
|
TINT_UNREACHABLE(diagnostics_) << "invalid type " << ty->TypeInfo().name;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3187,8 +3271,16 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
|
|||||||
auto size_no_padding = struct_size;
|
auto size_no_padding = struct_size;
|
||||||
struct_size = utils::RoundUp(struct_align, struct_size);
|
struct_size = utils::RoundUp(struct_align, struct_size);
|
||||||
|
|
||||||
auto* out = builder_->create<sem::Struct>(
|
auto* out = builder_->create<sem::Struct>(str, sem_members, struct_align,
|
||||||
str, std::move(sem_members), struct_align, struct_size, size_no_padding);
|
struct_size, size_no_padding);
|
||||||
|
|
||||||
|
// Keep track of atomic members for validation after all usages have been
|
||||||
|
// determined.
|
||||||
|
for (size_t i = 0; i < sem_members.size(); i++) {
|
||||||
|
if (sem_members[i]->Type()->Is<sem::Atomic>()) {
|
||||||
|
atomic_members_.emplace_back(StructMember{out, i});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ValidateStructure(out)) {
|
if (!ValidateStructure(out)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -52,6 +52,7 @@ class Variable;
|
|||||||
} // namespace ast
|
} // namespace ast
|
||||||
namespace sem {
|
namespace sem {
|
||||||
class Array;
|
class Array;
|
||||||
|
class Atomic;
|
||||||
class Intrinsic;
|
class Intrinsic;
|
||||||
class Statement;
|
class Statement;
|
||||||
} // namespace sem
|
} // namespace sem
|
||||||
@ -196,6 +197,13 @@ class Resolver {
|
|||||||
sem::Type* const sem;
|
sem::Type* const sem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Structure holding a pointer to the sem::Struct and an index to a member of
|
||||||
|
// that structure.
|
||||||
|
struct StructMember {
|
||||||
|
sem::Struct* structure;
|
||||||
|
size_t index;
|
||||||
|
};
|
||||||
|
|
||||||
/// Resolves the program, without creating final the semantic nodes.
|
/// Resolves the program, without creating final the semantic nodes.
|
||||||
/// @returns true on success, false on error
|
/// @returns true on success, false on error
|
||||||
bool ResolveInternal();
|
bool ResolveInternal();
|
||||||
@ -255,6 +263,8 @@ class Resolver {
|
|||||||
uint32_t el_size,
|
uint32_t el_size,
|
||||||
uint32_t el_align,
|
uint32_t el_align,
|
||||||
const Source& source);
|
const Source& source);
|
||||||
|
bool ValidateAtomic(const ast::Atomic* a, const sem::Atomic* s);
|
||||||
|
bool ValidateAtomicUses();
|
||||||
bool ValidateAssignment(const ast::AssignmentStatement* a);
|
bool ValidateAssignment(const ast::AssignmentStatement* a);
|
||||||
bool ValidateCallStatement(ast::CallStatement* stmt);
|
bool ValidateCallStatement(ast::CallStatement* stmt);
|
||||||
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
|
bool ValidateEntryPoint(const ast::Function* func, const FunctionInfo* info);
|
||||||
@ -401,6 +411,7 @@ class Resolver {
|
|||||||
ScopeStack<VariableInfo*> variable_stack_;
|
ScopeStack<VariableInfo*> variable_stack_;
|
||||||
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
std::unordered_map<Symbol, FunctionInfo*> symbol_to_function_;
|
||||||
std::vector<FunctionInfo*> entry_points_;
|
std::vector<FunctionInfo*> entry_points_;
|
||||||
|
std::vector<StructMember> atomic_members_;
|
||||||
std::unordered_map<const ast::Function*, FunctionInfo*> function_to_info_;
|
std::unordered_map<const ast::Function*, FunctionInfo*> function_to_info_;
|
||||||
std::unordered_map<const ast::Variable*, VariableInfo*> variable_to_info_;
|
std::unordered_map<const ast::Variable*, VariableInfo*> variable_to_info_;
|
||||||
std::unordered_map<const ast::CallExpression*, FunctionCallInfo>
|
std::unordered_map<const ast::CallExpression*, FunctionCallInfo>
|
||||||
|
@ -222,6 +222,8 @@ tint_unittests_source_set("tint_unittests_core_src") {
|
|||||||
"../src/program_builder_test.cc",
|
"../src/program_builder_test.cc",
|
||||||
"../src/program_test.cc",
|
"../src/program_test.cc",
|
||||||
"../src/resolver/assignment_validation_test.cc",
|
"../src/resolver/assignment_validation_test.cc",
|
||||||
|
"../src/resolver/atomics_test.cc",
|
||||||
|
"../src/resolver/atomics_validation_test.cc",
|
||||||
"../src/resolver/block_test.cc",
|
"../src/resolver/block_test.cc",
|
||||||
"../src/resolver/builtins_validation_test.cc",
|
"../src/resolver/builtins_validation_test.cc",
|
||||||
"../src/resolver/call_test.cc",
|
"../src/resolver/call_test.cc",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user