Allow sem::GlobalVariable to hold a location.

During some transforms we may move an entry point parameter to
a global variable. This means the global may now have a Location
attached where it isn't permitted by the WGSL spec.

This CL adds a `location` to the GlobalVariable sem value and
populates it in the resolver.

Bug: tint:1633
Change-Id: I684f715fe52d39a0f890fe76c627c1ae543fc746
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/101462
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
dan sinclair 2022-09-07 20:05:54 +00:00 committed by Dawn LUCI CQ
parent 5881e735f9
commit 23cf74c30e
6 changed files with 47 additions and 15 deletions

View File

@ -698,12 +698,12 @@ using VariableAttributeTest = TestWithParams;
TEST_P(VariableAttributeTest, IsValid) { TEST_P(VariableAttributeTest, IsValid) {
auto& params = GetParam(); auto& params = GetParam();
auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
auto* attr = attrs[0];
if (IsBindingAttribute(params.kind)) { if (IsBindingAttribute(params.kind)) {
GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler), GlobalVar("a", ty.sampler(ast::SamplerKind::kSampler), attrs);
createAttributes(Source{{12, 34}}, *this, params.kind));
} else { } else {
GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, GlobalVar("a", ty.f32(), ast::StorageClass::kPrivate, attrs);
createAttributes(Source{{12, 34}}, *this, params.kind));
} }
if (params.should_pass) { if (params.should_pass) {
@ -711,7 +711,8 @@ TEST_P(VariableAttributeTest, IsValid) {
} else { } else {
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
if (!IsBindingAttribute(params.kind)) { if (!IsBindingAttribute(params.kind)) {
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for module-scope 'var'"); EXPECT_EQ(r()->error(), "12:34 error: attribute '" + attr->Name() +
"' is not valid for module-scope 'var'");
} }
} }
} }

View File

@ -382,7 +382,8 @@ sem::Variable* Resolver::Let(const ast::Let* v, bool is_global) {
if (is_global) { if (is_global) {
sem = builder_->create<sem::GlobalVariable>( sem = builder_->create<sem::GlobalVariable>(
v, ty, sem::EvaluationStage::kRuntime, ast::StorageClass::kNone, v, ty, sem::EvaluationStage::kRuntime, ast::StorageClass::kNone,
ast::Access::kUndefined, /* constant_value */ nullptr, sem::BindingPoint{}); ast::Access::kUndefined, /* constant_value */ nullptr, sem::BindingPoint{},
std::nullopt);
} else { } else {
sem = builder_->create<sem::LocalVariable>(v, ty, sem::EvaluationStage::kRuntime, sem = builder_->create<sem::LocalVariable>(v, ty, sem::EvaluationStage::kRuntime,
ast::StorageClass::kNone, ast::StorageClass::kNone,
@ -437,7 +438,7 @@ sem::Variable* Resolver::Override(const ast::Override* v) {
auto* sem = builder_->create<sem::GlobalVariable>( auto* sem = builder_->create<sem::GlobalVariable>(
v, ty, sem::EvaluationStage::kOverride, ast::StorageClass::kNone, ast::Access::kUndefined, v, ty, sem::EvaluationStage::kOverride, ast::StorageClass::kNone, ast::Access::kUndefined,
/* constant_value */ nullptr, sem::BindingPoint{}); /* constant_value */ nullptr, sem::BindingPoint{}, std::nullopt);
sem->SetConstructor(rhs); sem->SetConstructor(rhs);
if (auto* id_attr = ast::GetAttribute<ast::IdAttribute>(v->attributes)) { if (auto* id_attr = ast::GetAttribute<ast::IdAttribute>(v->attributes)) {
@ -521,7 +522,7 @@ sem::Variable* Resolver::Const(const ast::Const* c, bool is_global) {
auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>( auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
c, ty, sem::EvaluationStage::kConstant, ast::StorageClass::kNone, c, ty, sem::EvaluationStage::kConstant, ast::StorageClass::kNone,
ast::Access::kUndefined, value, sem::BindingPoint{})) ast::Access::kUndefined, value, sem::BindingPoint{}, std::nullopt))
: static_cast<sem::Variable*>(builder_->create<sem::LocalVariable>( : static_cast<sem::Variable*>(builder_->create<sem::LocalVariable>(
c, ty, sem::EvaluationStage::kConstant, ast::StorageClass::kNone, c, ty, sem::EvaluationStage::kConstant, ast::StorageClass::kNone,
ast::Access::kUndefined, current_statement_, value)); ast::Access::kUndefined, current_statement_, value));
@ -636,9 +637,15 @@ sem::Variable* Resolver::Var(const ast::Var* var, bool is_global) {
} }
binding_point = {group, binding}; binding_point = {group, binding};
} }
sem = builder_->create<sem::GlobalVariable>(var, var_ty, sem::EvaluationStage::kRuntime,
storage_class, access, std::optional<uint32_t> location;
/* constant_value */ nullptr, binding_point); if (auto* attr = ast::GetAttribute<ast::LocationAttribute>(var->attributes)) {
location = attr->value;
}
sem = builder_->create<sem::GlobalVariable>(
var, var_ty, sem::EvaluationStage::kRuntime, storage_class, access,
/* constant_value */ nullptr, binding_point, location);
} else { } else {
sem = builder_->create<sem::LocalVariable>(var, var_ty, sem::EvaluationStage::kRuntime, sem = builder_->create<sem::LocalVariable>(var, var_ty, sem::EvaluationStage::kRuntime,

View File

@ -806,6 +806,18 @@ TEST_F(ResolverTest, Function_Parameters_Locations) {
EXPECT_EQ(1u, func_sem->Parameters()[2]->Location()); EXPECT_EQ(1u, func_sem->Parameters()[2]->Location());
} }
TEST_F(ResolverTest, Function_GlobalVariable_Location) {
auto* var = GlobalVar(
"my_vec", ty.vec4<f32>(), ast::StorageClass::kIn,
utils::Vector{Location(3), Disable(ast::DisabledValidation::kIgnoreStorageClass)});
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* sem = Sem().Get<sem::GlobalVariable>(var);
ASSERT_NE(sem, nullptr);
EXPECT_EQ(3u, sem->Location());
}
TEST_F(ResolverTest, Function_RegisterInputOutputVariables) { TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
auto* s = Structure("S", utils::Vector{Member("m", ty.u32())}); auto* s = Structure("S", utils::Vector{Member("m", ty.u32())});

View File

@ -601,7 +601,8 @@ bool Validator::GlobalVariable(
if (!attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute, if (!attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
ast::InternalAttribute>() && ast::InternalAttribute>() &&
(!is_shader_io_attribute || !has_io_storage_class)) { (!is_shader_io_attribute || !has_io_storage_class)) {
AddError("attribute is not valid for module-scope 'var'", attr->source); AddError("attribute '" + attr->Name() + "' is not valid for module-scope 'var'",
attr->source);
return false; return false;
} }
} }

View File

@ -61,9 +61,11 @@ GlobalVariable::GlobalVariable(const ast::Variable* declaration,
ast::StorageClass storage_class, ast::StorageClass storage_class,
ast::Access access, ast::Access access,
const Constant* constant_value, const Constant* constant_value,
sem::BindingPoint binding_point) sem::BindingPoint binding_point,
std::optional<uint32_t> location)
: Base(declaration, type, stage, storage_class, access, constant_value), : Base(declaration, type, stage, storage_class, access, constant_value),
binding_point_(binding_point) {} binding_point_(binding_point),
location_(location) {}
GlobalVariable::~GlobalVariable() = default; GlobalVariable::~GlobalVariable() = default;

View File

@ -153,13 +153,18 @@ class GlobalVariable final : public Castable<GlobalVariable, Variable> {
/// @param access the variable access control type /// @param access the variable access control type
/// @param constant_value the constant value for the variable. May be null /// @param constant_value the constant value for the variable. May be null
/// @param binding_point the optional resource binding point of the variable /// @param binding_point the optional resource binding point of the variable
/// @param location the location value if provided
///
/// Note, a GlobalVariable generally doesn't have a `location` in WGSL, as it isn't allowed by
/// the spec. The location maybe attached by transforms such as CanonicalizeEntryPointIO.
GlobalVariable(const ast::Variable* declaration, GlobalVariable(const ast::Variable* declaration,
const sem::Type* type, const sem::Type* type,
EvaluationStage stage, EvaluationStage stage,
ast::StorageClass storage_class, ast::StorageClass storage_class,
ast::Access access, ast::Access access,
const Constant* constant_value, const Constant* constant_value,
sem::BindingPoint binding_point = {}); sem::BindingPoint binding_point = {},
std::optional<uint32_t> location = std::nullopt);
/// Destructor /// Destructor
~GlobalVariable() override; ~GlobalVariable() override;
@ -173,10 +178,14 @@ class GlobalVariable final : public Castable<GlobalVariable, Variable> {
/// @returns the pipeline constant ID associated with the variable /// @returns the pipeline constant ID associated with the variable
tint::OverrideId OverrideId() const { return override_id_; } tint::OverrideId OverrideId() const { return override_id_; }
/// @returns the location value for the parameter, if set
std::optional<uint32_t> Location() const { return location_; }
private: private:
const sem::BindingPoint binding_point_; const sem::BindingPoint binding_point_;
tint::OverrideId override_id_; tint::OverrideId override_id_;
std::optional<uint32_t> location_;
}; };
/// Parameter is a function parameter /// Parameter is a function parameter