tint: Add StructMember attributes to sem.

Removes the need to examine AST attributes.

Change-Id: Iaaa6b10fd56baf732057c4c3960c1bfc5bbdeaa6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/129621
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
Ben Clayton 2023-04-27 17:58:25 +00:00 committed by Dawn LUCI CQ
parent 333cea405c
commit 576ba1c493
23 changed files with 223 additions and 211 deletions

View File

@ -747,6 +747,7 @@ libtint_source_set("libtint_builtins_src") {
"builtin/extension.h",
"builtin/function.cc",
"builtin/function.h",
"builtin/interpolation.h",
"builtin/interpolation_sampling.cc",
"builtin/interpolation_sampling.h",
"builtin/interpolation_type.cc",

View File

@ -0,0 +1,33 @@
// Copyright 2023 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.
#ifndef SRC_TINT_BUILTIN_INTERPOLATION_H_
#define SRC_TINT_BUILTIN_INTERPOLATION_H_
#include "src/tint/builtin/interpolation_sampling.h"
#include "src/tint/builtin/interpolation_type.h"
namespace tint::builtin {
/// The values of an `@interpolate` attribute
struct Interpolation {
/// The first argument of a `@interpolate` attribute
builtin::InterpolationType type = builtin::InterpolationType::kUndefined;
/// The second argument of a `@interpolate` attribute
builtin::InterpolationSampling sampling = builtin::InterpolationSampling::kUndefined;
};
} // namespace tint::builtin
#endif // SRC_TINT_BUILTIN_INTERPOLATION_H_

View File

@ -621,8 +621,8 @@ void Inspector::AddEntryPointInOutVariables(std::string name,
// Recurse into members.
for (auto* member : struct_ty->Members()) {
AddEntryPointInOutVariables(name + "." + member->Name().Name(), member->Type(),
member->Declaration()->attributes, member->Location(),
variables);
member->Declaration()->attributes,
member->Attributes().location, variables);
}
return;
}

View File

@ -53,7 +53,7 @@ sem::Struct* BuildStruct(ProgramBuilder& b,
/* offset */ offset,
/* align */ align,
/* size */ size,
/* location */ std::nullopt));
/* attributes */ type::StructMemberAttributes{}));
offset += size;
}
uint32_t size_without_padding = offset;

View File

@ -150,11 +150,12 @@ TEST_F(ResolverInferredTypeTest, InferStruct_Pass) {
auto* member = Member("x", ty.i32());
auto* str = Structure("S", utils::Vector{member});
auto* expected_type = create<sem::Struct>(
str, str->source, str->name->symbol,
utils::Vector{create<sem::StructMember>(member, member->source, member->name->symbol,
create<type::I32>(), 0u, 0u, 0u, 4u, std::nullopt)},
0u, 4u, 4u);
auto* expected_type =
create<sem::Struct>(str, str->source, str->name->symbol,
utils::Vector{create<sem::StructMember>(
member, member->source, member->name->symbol, create<type::I32>(),
0u, 0u, 0u, 4u, type::StructMemberAttributes{})},
0u, 4u, 4u);
auto* ctor_expr = Call(ty.Of(str));

View File

@ -3721,14 +3721,22 @@ bool Resolver::StrideAttribute(const ast::StrideAttribute*) {
return true;
}
bool Resolver::InterpolateAttribute(const ast::InterpolateAttribute* attr) {
if (!InterpolationType(attr->type)) {
return false;
utils::Result<builtin::Interpolation> Resolver::InterpolateAttribute(
const ast::InterpolateAttribute* attr) {
builtin::Interpolation out;
auto* type = InterpolationType(attr->type);
if (!type) {
return utils::Failure;
}
if (attr->sampling && !InterpolationSampling(attr->sampling)) {
return false;
out.type = type->Value();
if (attr->sampling) {
auto* sampling = InterpolationSampling(attr->sampling);
if (!sampling) {
return utils::Failure;
}
out.sampling = sampling->Value();
}
return true;
return out;
}
bool Resolver::InternalAttribute(const ast::InternalAttribute* attr) {
@ -4021,7 +4029,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
bool has_offset_attr = false;
bool has_align_attr = false;
bool has_size_attr = false;
std::optional<uint32_t> location;
type::StructMemberAttributes attributes;
for (auto* attribute : member->attributes) {
Mark(attribute);
bool ok = Switch(
@ -4122,12 +4130,32 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
if (!value) {
return false;
}
location = value.Get();
attributes.location = value.Get();
return true;
},
[&](const ast::BuiltinAttribute* attr) {
auto value = BuiltinAttribute(attr);
if (!value) {
return false;
}
attributes.builtin = value.Get();
return true;
},
[&](const ast::InterpolateAttribute* attr) {
auto value = InterpolateAttribute(attr);
if (!value) {
return false;
}
attributes.interpolation = value.Get();
return true;
},
[&](const ast::InvariantAttribute* attr) {
if (!InvariantAttribute(attr)) {
return false;
}
attributes.invariant = true;
return true;
},
[&](const ast::BuiltinAttribute* attr) -> bool { return BuiltinAttribute(attr); },
[&](const ast::InterpolateAttribute* attr) { return InterpolateAttribute(attr); },
[&](const ast::InvariantAttribute* attr) { return InvariantAttribute(attr); },
[&](const ast::StrideAttribute* attr) {
if (validator_.IsValidationEnabled(
member->attributes, ast::DisabledValidation::kIgnoreStrideAttribute)) {
@ -4163,7 +4191,7 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
auto* sem_member = builder_->create<sem::StructMember>(
member, member->source, member->name->symbol, type,
static_cast<uint32_t>(sem_members.Length()), static_cast<uint32_t>(offset),
static_cast<uint32_t>(align), static_cast<uint32_t>(size), location);
static_cast<uint32_t>(align), static_cast<uint32_t>(size), attributes);
builder_->Sem().Add(member, sem_member);
sem_members.Push(sem_member);

View File

@ -354,7 +354,8 @@ class Resolver {
/// Resolves the `@interpolate` attribute @p attr
/// @returns true on success, false on failure
bool InterpolateAttribute(const ast::InterpolateAttribute* attr);
utils::Result<builtin::Interpolation> InterpolateAttribute(
const ast::InterpolateAttribute* attr);
/// Resolves the internal attribute @p attr
/// @returns true on success, false on failure

View File

@ -187,7 +187,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamLocationSet) {
auto* sem = TypeOf(s)->As<sem::Struct>();
ASSERT_NE(sem, nullptr);
ASSERT_EQ(1u, sem->Members().Length());
EXPECT_EQ(3u, sem->Members()[0]->Location());
EXPECT_EQ(3u, sem->Members()[0]->Attributes().location);
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeViaAlias) {
@ -217,7 +217,7 @@ TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeLocationSet) {
auto* sem = TypeOf(s)->As<sem::Struct>();
ASSERT_NE(sem, nullptr);
ASSERT_EQ(1u, sem->Members().Length());
EXPECT_EQ(3u, sem->Members()[0]->Location());
EXPECT_EQ(3u, sem->Members()[0]->Attributes().location);
}
} // namespace

View File

@ -1215,7 +1215,7 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
if (!validate_entry_point_attributes_inner(
member->Declaration()->attributes, member->Type(), member->Source(),
param_or_ret,
/*is_struct_member*/ true, member->Location())) {
/*is_struct_member*/ true, member->Attributes().location)) {
AddNote("while analyzing entry point '" + decl->name->symbol.Name() + "'",
decl->source);
return false;
@ -2105,9 +2105,9 @@ bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) cons
},
[&](const ast::LocationAttribute* location) {
has_location = true;
TINT_ASSERT(Resolver, member->Location().has_value());
if (!LocationAttribute(location, member->Location().value(), member->Type(),
locations, stage, member->Source())) {
TINT_ASSERT(Resolver, member->Attributes().location.has_value());
if (!LocationAttribute(location, member->Attributes().location.value(),
member->Type(), locations, stage, member->Source())) {
return false;
}
return true;

View File

@ -40,8 +40,8 @@ StructMember::StructMember(const ast::StructMember* declaration,
uint32_t offset,
uint32_t align,
uint32_t size,
std::optional<uint32_t> location)
: Base(source, name, type, index, offset, align, size, location), declaration_(declaration) {}
const type::StructMemberAttributes& attributes)
: Base(source, name, type, index, offset, align, size, attributes), declaration_(declaration) {}
StructMember::~StructMember() = default;

View File

@ -84,7 +84,7 @@ class StructMember final : public utils::Castable<StructMember, type::StructMemb
/// @param offset the byte offset from the base of the structure
/// @param align the byte alignment of the member
/// @param size the byte size of the member
/// @param location the location attribute, if present
/// @param attributes the optional attributes
StructMember(const ast::StructMember* declaration,
tint::Source source,
Symbol name,
@ -93,7 +93,7 @@ class StructMember final : public utils::Castable<StructMember, type::StructMemb
uint32_t offset,
uint32_t align,
uint32_t size,
std::optional<uint32_t> location);
const type::StructMemberAttributes& attributes);
/// Destructor
~StructMember() override;

View File

@ -380,8 +380,8 @@ struct CanonicalizeEntryPointIO::State {
auto attributes =
CloneShaderIOAttributes(member->Declaration()->attributes, do_interpolate);
auto* input_expr =
AddInput(name, member->Type(), member->Location(), std::move(attributes));
auto* input_expr = AddInput(name, member->Type(), member->Attributes().location,
std::move(attributes));
inner_struct_values.Push(input_expr);
}
@ -410,8 +410,8 @@ struct CanonicalizeEntryPointIO::State {
CloneShaderIOAttributes(member->Declaration()->attributes, do_interpolate);
// Extract the original structure member.
AddOutput(name, member->Type(), member->Location(), std::move(attributes),
ctx.dst->MemberAccessor(original_result, name));
AddOutput(name, member->Type(), member->Attributes().location,
std::move(attributes), ctx.dst->MemberAccessor(original_result, name));
}
} else if (!inner_ret_type->Is<type::Void>()) {
auto attributes =

View File

@ -83,6 +83,8 @@ Transform::ApplyResult TruncateInterstageVariables::Apply(const Program* src,
auto* func_sem = sem.Get(func_ast);
auto* str = func_sem->ReturnType()->As<sem::Struct>();
// This transform is run after CanonicalizeEntryPointIO transform,
// So it is guaranteed that entry point inputs are already grouped in a struct.
if (TINT_UNLIKELY(!str)) {
TINT_ICE(Transform, ctx.dst->Diagnostics())
<< "Entrypoint function return type is non-struct.\n"
@ -91,20 +93,14 @@ Transform::ApplyResult TruncateInterstageVariables::Apply(const Program* src,
continue;
}
// This transform is run after CanonicalizeEntryPointIO transform,
// So it is guaranteed that entry point inputs are already grouped in a struct.
const ast::Struct* struct_ty = str->Declaration();
// A prepass to check if any interstage variable locations in the entry point needs
// truncating. If not we don't really need to handle this entry point.
utils::Hashset<const sem::StructMember*, 16u> omit_members;
for (auto* member : struct_ty->members) {
if (ast::GetAttribute<ast::LocationAttribute>(member->attributes)) {
auto* m = sem.Get(member);
uint32_t location = m->Location().value();
if (!data->interstage_locations.test(location)) {
omit_members.Add(m);
for (auto* member : str->Members()) {
if (auto location = member->Attributes().location) {
if (!data->interstage_locations.test(location.value())) {
omit_members.Add(member);
}
}
}

View File

@ -826,8 +826,8 @@ struct VertexPulling::State {
auto* sem = src->Sem().Get(member);
info.type = sem->Type();
TINT_ASSERT(Transform, sem->Location().has_value());
location_info[sem->Location().value()] = info;
TINT_ASSERT(Transform, sem->Attributes().location.has_value());
location_info[sem->Attributes().location.value()] = info;
has_locations = true;
} else {
auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(member->attributes);

View File

@ -179,7 +179,7 @@ StructMember::StructMember(tint::Source source,
uint32_t offset,
uint32_t align,
uint32_t size,
std::optional<uint32_t> location)
const StructMemberAttributes& attributes)
: source_(source),
name_(name),
type_(type),
@ -187,7 +187,7 @@ StructMember::StructMember(tint::Source source,
offset_(offset),
align_(align),
size_(size),
location_(location) {}
attributes_(attributes) {}
StructMember::~StructMember() = default;
@ -195,7 +195,7 @@ StructMember* StructMember::Clone(CloneContext& ctx) const {
auto sym = ctx.dst.st->Register(name_.Name());
auto* ty = type_->Clone(ctx);
return ctx.dst.mgr->Get<StructMember>(source_, sym, ty, index_, offset_, align_, size_,
location_);
attributes_);
}
} // namespace tint::type

View File

@ -22,6 +22,7 @@
#include <unordered_set>
#include "src/tint/builtin/address_space.h"
#include "src/tint/builtin/interpolation.h"
#include "src/tint/symbol.h"
#include "src/tint/type/node.h"
#include "src/tint/type/type.h"
@ -163,6 +164,18 @@ class Struct : public utils::Castable<Struct, Type> {
utils::Vector<const Struct*, 2> concrete_types_;
};
/// Attributes that can be applied to the StructMember
struct StructMemberAttributes {
/// The value of a `@location` attribute
std::optional<uint32_t> location;
/// The value of a `@builtin` attribute
std::optional<builtin::BuiltinValue> builtin;
/// The values of a `@interpolate` attribute
std::optional<builtin::Interpolation> interpolation;
/// True if the member was annotated with `@invariant`
bool invariant = false;
};
/// StructMember holds the type information for structure members.
class StructMember : public utils::Castable<StructMember, Node> {
public:
@ -174,7 +187,7 @@ class StructMember : public utils::Castable<StructMember, Node> {
/// @param offset the byte offset from the base of the structure
/// @param align the byte alignment of the member
/// @param size the byte size of the member
/// @param location the location attribute, if present
/// @param attributes the optional attributes
StructMember(tint::Source source,
Symbol name,
const type::Type* type,
@ -182,7 +195,7 @@ class StructMember : public utils::Castable<StructMember, Node> {
uint32_t offset,
uint32_t align,
uint32_t size,
std::optional<uint32_t> location);
const StructMemberAttributes& attributes);
/// Destructor
~StructMember() override;
@ -215,8 +228,8 @@ class StructMember : public utils::Castable<StructMember, Node> {
/// @returns byte size
uint32_t Size() const { return size_; }
/// @returns the location, if set
std::optional<uint32_t> Location() const { return location_; }
/// @returns the optional attributes
const StructMemberAttributes& Attributes() const { return attributes_; }
/// @param ctx the clone context
/// @returns a clone of this struct member
@ -231,7 +244,7 @@ class StructMember : public utils::Castable<StructMember, Node> {
const uint32_t offset_;
const uint32_t align_;
const uint32_t size_;
const std::optional<uint32_t> location_;
const StructMemberAttributes attributes_;
};
} // namespace tint::type

View File

@ -101,10 +101,8 @@ TEST_F(TypeStructTest, Location) {
auto* sem = p.Sem().Get(st);
ASSERT_EQ(2u, sem->Members().Length());
EXPECT_TRUE(sem->Members()[0]->Location().has_value());
EXPECT_EQ(sem->Members()[0]->Location().value(), 1u);
EXPECT_FALSE(sem->Members()[1]->Location().has_value());
EXPECT_EQ(sem->Members()[0]->Attributes().location, 1u);
EXPECT_FALSE(sem->Members()[1]->Attributes().location.has_value());
}
TEST_F(TypeStructTest, IsConstructable) {
@ -207,12 +205,15 @@ TEST_F(TypeStructTest, HasFixedFootprint) {
}
TEST_F(TypeStructTest, Clone) {
type::StructMemberAttributes attrs_location_2;
attrs_location_2.location = 2;
auto* s = create<Struct>(
Source{}, Sym("my_struct"),
utils::Vector{create<StructMember>(Source{}, Sym("b"), create<Vector>(create<F32>(), 3u),
0u, 0u, 16u, 12u, std::optional<uint32_t>{2}),
0u, 0u, 16u, 12u, attrs_location_2),
create<StructMember>(Source{}, Sym("a"), create<I32>(), 1u, 16u, 4u, 4u,
std::optional<uint32_t>())},
type::StructMemberAttributes{})},
4u /* align */, 8u /* size */, 16u /* size_no_padding */);
ProgramID id;

View File

@ -56,7 +56,7 @@ struct TypeTest : public TestHelper {
/* offset */ 0u,
/* align */ 4u,
/* size */ 4u,
/* location */ std::nullopt),
/* attributes */ type::StructMemberAttributes{}),
},
/* align*/ 4u,
/* size*/ 4u,
@ -72,7 +72,7 @@ struct TypeTest : public TestHelper {
/* offset */ 0u,
/* align */ 4u,
/* size */ 4u,
/* location */ std::nullopt),
/* attributes */ type::StructMemberAttributes{}),
},
/* align*/ 4u,
/* size*/ 4u,
@ -88,7 +88,7 @@ struct TypeTest : public TestHelper {
/* offset */ 0u,
/* align */ 4u,
/* size */ 4u,
/* location */ std::nullopt),
/* attributes */ type::StructMemberAttributes{}),
},
/* align*/ 4u,
/* size*/ 4u,

View File

@ -399,7 +399,7 @@ class GeneratorImpl : public TextGenerator {
/// Handles generating a 'var' declaration
/// @param var the variable to generate
void EmitVar(const ast::Var* var);
/// Handles generating a function-scope 'let' declaration
/// Handles generating a 'let' declaration
/// @param let the variable to generate
void EmitLet(const ast::Let* let);
/// Handles generating a module-scope 'let' declaration

View File

@ -4216,73 +4216,48 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
auto* ty = mem->Type();
auto out = line(b);
std::string pre, post;
if (auto* decl = mem->Declaration()) {
for (auto* attr : decl->attributes) {
if (attr->Is<ast::LocationAttribute>()) {
auto& pipeline_stage_uses = str->PipelineStageUses();
if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
}
auto loc = mem->Location().value();
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
post += " : TEXCOORD" + std::to_string(loc);
} else if (pipeline_stage_uses.count(
type::PipelineStageUsage::kVertexOutput)) {
post += " : TEXCOORD" + std::to_string(loc);
} else if (pipeline_stage_uses.count(
type::PipelineStageUsage::kFragmentInput)) {
post += " : TEXCOORD" + std::to_string(loc);
} else if (TINT_LIKELY(pipeline_stage_uses.count(
type::PipelineStageUsage::kFragmentOutput))) {
post += " : SV_Target" + std::to_string(loc);
} else {
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
}
} else if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
auto builtin = program_->Sem().Get(builtin_attr)->Value();
auto name = builtin_to_attribute(builtin);
if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
return false;
}
post += " : " + name;
} else if (auto* interpolate = attr->As<ast::InterpolateAttribute>()) {
auto& sem = program_->Sem();
auto i_type =
sem.Get<sem::BuiltinEnumExpression<builtin::InterpolationType>>(
interpolate->type)
->Value();
auto& attributes = mem->Attributes();
auto i_smpl = builtin::InterpolationSampling::kUndefined;
if (interpolate->sampling) {
i_smpl =
sem.Get<sem::BuiltinEnumExpression<builtin::InterpolationSampling>>(
interpolate->sampling)
->Value();
}
auto mod = interpolation_to_modifiers(i_type, i_smpl);
if (mod.empty()) {
diagnostics_.add_error(diag::System::Writer,
"unsupported interpolation");
return false;
}
pre += mod;
} else if (attr->Is<ast::InvariantAttribute>()) {
// Note: `precise` is not exactly the same as `invariant`, but is
// stricter and therefore provides the necessary guarantees.
// See discussion here: https://github.com/gpuweb/gpuweb/issues/893
pre += "precise ";
} else if (TINT_UNLIKELY((!attr->IsAnyOf<ast::StructMemberAlignAttribute,
ast::StructMemberOffsetAttribute,
ast::StructMemberSizeAttribute>()))) {
TINT_ICE(Writer, diagnostics_)
<< "unhandled struct member attribute: " << attr->Name();
return false;
}
if (auto location = attributes.location) {
auto& pipeline_stage_uses = str->PipelineStageUses();
if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
}
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
post += " : TEXCOORD" + std::to_string(location.value());
} else if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexOutput)) {
post += " : TEXCOORD" + std::to_string(location.value());
} else if (pipeline_stage_uses.count(type::PipelineStageUsage::kFragmentInput)) {
post += " : TEXCOORD" + std::to_string(location.value());
} else if (TINT_LIKELY(pipeline_stage_uses.count(
type::PipelineStageUsage::kFragmentOutput))) {
post += " : SV_Target" + std::to_string(location.value());
} else {
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
}
}
if (auto builtin = attributes.builtin) {
auto name = builtin_to_attribute(builtin.value());
if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
return false;
}
post += " : " + name;
}
if (auto interpolation = attributes.interpolation) {
auto mod = interpolation_to_modifiers(interpolation->type, interpolation->sampling);
if (mod.empty()) {
diagnostics_.add_error(diag::System::Writer, "unsupported interpolation");
return false;
}
pre += mod;
}
if (attributes.invariant) {
// Note: `precise` is not exactly the same as `invariant`, but is
// stricter and therefore provides the necessary guarantees.
// See discussion here: https://github.com/gpuweb/gpuweb/issues/893
pre += "precise ";
}
out << pre;

View File

@ -470,7 +470,7 @@ class GeneratorImpl : public TextGenerator {
/// @param var the variable to generate
/// @returns true if the variable was emitted
bool EmitVar(const ast::Var* var);
/// Handles generating a function-scope 'let' declaration
/// Handles generating a 'let' declaration
/// @param let the variable to generate
/// @returns true if the variable was emitted
bool EmitLet(const ast::Let* let);

View File

@ -2852,88 +2852,51 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
out << " " << mem_name;
// Emit attributes
if (auto* decl = mem->Declaration()) {
for (auto* attr : decl->attributes) {
bool ok = Switch(
attr,
[&](const ast::BuiltinAttribute* builtin_attr) {
auto builtin = program_->Sem().Get(builtin_attr)->Value();
auto name = builtin_to_attribute(builtin);
if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
return false;
}
out << " [[" << name << "]]";
return true;
},
[&](const ast::LocationAttribute*) {
auto& pipeline_stage_uses = str->PipelineStageUses();
if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
return false;
}
auto& attributes = mem->Attributes();
uint32_t loc = mem->Location().value();
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
out << " [[attribute(" + std::to_string(loc) + ")]]";
} else if (pipeline_stage_uses.count(
type::PipelineStageUsage::kVertexOutput)) {
out << " [[user(locn" + std::to_string(loc) + ")]]";
} else if (pipeline_stage_uses.count(
type::PipelineStageUsage::kFragmentInput)) {
out << " [[user(locn" + std::to_string(loc) + ")]]";
} else if (TINT_LIKELY(pipeline_stage_uses.count(
type::PipelineStageUsage::kFragmentOutput))) {
out << " [[color(" + std::to_string(loc) + ")]]";
} else {
TINT_ICE(Writer, diagnostics_) << "invalid use of location decoration";
return false;
}
return true;
},
[&](const ast::InterpolateAttribute* interpolate) {
auto& sem = program_->Sem();
auto i_type =
sem.Get<sem::BuiltinEnumExpression<builtin::InterpolationType>>(
interpolate->type)
->Value();
auto i_smpl = builtin::InterpolationSampling::kUndefined;
if (interpolate->sampling) {
i_smpl =
sem.Get<sem::BuiltinEnumExpression<builtin::InterpolationSampling>>(
interpolate->sampling)
->Value();
}
auto name = interpolation_to_attribute(i_type, i_smpl);
if (name.empty()) {
diagnostics_.add_error(diag::System::Writer,
"unknown interpolation attribute");
return false;
}
out << " [[" << name << "]]";
return true;
},
[&](const ast::InvariantAttribute*) {
if (invariant_define_name_.empty()) {
invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
}
out << " " << invariant_define_name_;
return true;
},
[&](const ast::StructMemberOffsetAttribute*) { return true; },
[&](const ast::StructMemberAlignAttribute*) { return true; },
[&](const ast::StructMemberSizeAttribute*) { return true; },
[&](Default) {
TINT_ICE(Writer, diagnostics_)
<< "unhandled struct member attribute: " << attr->Name();
return false;
});
if (!ok) {
return false;
}
if (auto builtin = attributes.builtin) {
auto name = builtin_to_attribute(builtin.value());
if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
return false;
}
out << " [[" << name << "]]";
}
if (auto location = attributes.location) {
auto& pipeline_stage_uses = str->PipelineStageUses();
if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
TINT_ICE(Writer, diagnostics_) << "invalid entry point IO struct uses";
return false;
}
if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
out << " [[attribute(" + std::to_string(location.value()) + ")]]";
} else if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexOutput)) {
out << " [[user(locn" + std::to_string(location.value()) + ")]]";
} else if (pipeline_stage_uses.count(type::PipelineStageUsage::kFragmentInput)) {
out << " [[user(locn" + std::to_string(location.value()) + ")]]";
} else if (TINT_LIKELY(
pipeline_stage_uses.count(type::PipelineStageUsage::kFragmentOutput))) {
out << " [[color(" + std::to_string(location.value()) + ")]]";
} else {
TINT_ICE(Writer, diagnostics_) << "invalid use of location decoration";
return false;
}
}
if (auto interpolation = attributes.interpolation) {
auto name = interpolation_to_attribute(interpolation->type, interpolation->sampling);
if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unknown interpolation attribute");
return false;
}
out << " [[" << name << "]]";
}
if (attributes.invariant) {
invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
out << " " << invariant_define_name_;
}
out << ";";

View File

@ -350,7 +350,7 @@ class GeneratorImpl : public TextGenerator {
/// @param var the variable to generate
/// @returns true if the variable was emitted
bool EmitVar(const ast::Var* var);
/// Handles generating a function-scope 'let' declaration
/// Handles generating a 'let' declaration
/// @param let the variable to generate
/// @returns true if the variable was emitted
bool EmitLet(const ast::Let* let);