tint: Resolve @builtin() args as expressions

This CL makes the builtin argument resolve as a shadowable enumerator
expression.

Bug: tint:1841
Bug: tint:1845
Change-Id: I2aaa2c63025752f25c113cf2fb38f4d91c2c16c2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/120680
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
This commit is contained in:
Ben Clayton 2023-02-21 17:33:54 +00:00 committed by Dawn LUCI CQ
parent 21571709a2
commit 4d3ff9711e
448 changed files with 2044 additions and 1536 deletions

View File

@ -825,6 +825,7 @@ libtint_source_set("libtint_reader_src") {
libtint_source_set("libtint_spv_reader_src") {
sources = [
"reader/spirv/attributes.h",
"reader/spirv/construct.cc",
"reader/spirv/construct.h",
"reader/spirv/entry_point_info.cc",
@ -916,6 +917,7 @@ libtint_source_set("libtint_spv_writer_src") {
deps = [
":libtint_ast_src",
":libtint_base_src",
":libtint_builtins_src",
":libtint_constant_src",
":libtint_program_src",
":libtint_sem_src",
@ -981,6 +983,7 @@ libtint_source_set("libtint_msl_writer_src") {
deps = [
":libtint_ast_src",
":libtint_base_src",
":libtint_builtins_src",
":libtint_constant_src",
":libtint_program_src",
":libtint_sem_src",
@ -1001,6 +1004,7 @@ libtint_source_set("libtint_hlsl_writer_src") {
deps = [
":libtint_ast_src",
":libtint_base_src",
":libtint_builtins_src",
":libtint_constant_src",
":libtint_program_src",
":libtint_sem_src",
@ -1740,6 +1744,7 @@ if (tint_build_unittests) {
deps = [
":libtint_base_src",
":libtint_builtins_src",
":libtint_unittests_ast_helper",
":libtint_wgsl_reader_src",
]
@ -1781,6 +1786,7 @@ if (tint_build_unittests) {
]
deps = [
":libtint_builtins_src",
":libtint_wgsl_writer_src",
":tint_unittests_ast_src",
]

View File

@ -575,6 +575,7 @@ endif()
if(${TINT_BUILD_SPV_READER})
list(APPEND TINT_LIB_SRCS
reader/spirv/attributes.h
reader/spirv/construct.h
reader/spirv/construct.cc
reader/spirv/entry_point_info.h

View File

@ -25,8 +25,10 @@ namespace tint::ast {
BuiltinAttribute::BuiltinAttribute(ProgramID pid,
NodeID nid,
const Source& src,
builtin::BuiltinValue b)
: Base(pid, nid, src), builtin(b) {}
const Expression* b)
: Base(pid, nid, src), builtin(b) {
TINT_ASSERT_PROGRAM_IDS_EQUAL(AST, b, program_id);
}
BuiltinAttribute::~BuiltinAttribute() = default;
@ -37,7 +39,8 @@ std::string BuiltinAttribute::Name() const {
const BuiltinAttribute* BuiltinAttribute::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering
auto src = ctx->Clone(source);
return ctx->dst->create<BuiltinAttribute>(src, builtin);
auto b = ctx->Clone(builtin);
return ctx->dst->create<BuiltinAttribute>(src, b);
}
} // namespace tint::ast

View File

@ -18,7 +18,11 @@
#include <string>
#include "src/tint/ast/attribute.h"
#include "src/tint/builtin/builtin_value.h"
// Forward declarations
namespace tint::ast {
class Expression;
}
namespace tint::ast {
@ -30,7 +34,7 @@ class BuiltinAttribute final : public Castable<BuiltinAttribute, Attribute> {
/// @param nid the unique node identifier
/// @param src the source of this node
/// @param builtin the builtin value
BuiltinAttribute(ProgramID pid, NodeID nid, const Source& src, builtin::BuiltinValue builtin);
BuiltinAttribute(ProgramID pid, NodeID nid, const Source& src, const Expression* builtin);
~BuiltinAttribute() override;
/// @returns the WGSL name for the attribute
@ -43,7 +47,7 @@ class BuiltinAttribute final : public Castable<BuiltinAttribute, Attribute> {
const BuiltinAttribute* Clone(CloneContext* ctx) const override;
/// The builtin value
const builtin::BuiltinValue builtin;
const Expression* const builtin;
};
} // namespace tint::ast

View File

@ -12,7 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/tint/ast/test_helper.h"
#include "src/tint/builtin/builtin_value.h"
namespace tint::ast {
namespace {
@ -20,8 +23,27 @@ namespace {
using BuiltinAttributeTest = TestHelper;
TEST_F(BuiltinAttributeTest, Creation) {
auto* d = create<BuiltinAttribute>(builtin::BuiltinValue::kFragDepth);
EXPECT_EQ(builtin::BuiltinValue::kFragDepth, d->builtin);
auto* d = Builtin(builtin::BuiltinValue::kFragDepth);
CheckIdentifier(Symbols(), d->builtin, "frag_depth");
}
TEST_F(BuiltinAttributeTest, Assert_Null_Builtin) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b;
b.Builtin(nullptr);
},
"internal compiler error");
}
TEST_F(BuiltinAttributeTest, Assert_DifferentProgramID_Builtin) {
EXPECT_FATAL_FAILURE(
{
ProgramBuilder b1;
ProgramBuilder b2;
b1.Builtin(b2.Expr("bang"));
},
"internal compiler error");
}
} // namespace

View File

@ -16,6 +16,7 @@
#include "src/tint/ast/id_attribute.h"
#include "src/tint/ast/test_helper.h"
#include "src/tint/builtin/builtin_value.h"
using namespace tint::number_suffixes; // NOLINT

View File

@ -28,6 +28,9 @@ namespace tint::builtin {
/// @param str the string to parse
/// @returns the parsed enum, or BuiltinValue::kUndefined if the string could not be parsed.
BuiltinValue ParseBuiltinValue(std::string_view str) {
if (str == "__point_size") {
return BuiltinValue::kPointSize;
}
if (str == "frag_depth") {
return BuiltinValue::kFragDepth;
}
@ -71,6 +74,8 @@ std::ostream& operator<<(std::ostream& out, BuiltinValue value) {
switch (value) {
case BuiltinValue::kUndefined:
return out << "undefined";
case BuiltinValue::kPointSize:
return out << "__point_size";
case BuiltinValue::kFragDepth:
return out << "frag_depth";
case BuiltinValue::kFrontFacing:
@ -85,8 +90,6 @@ std::ostream& operator<<(std::ostream& out, BuiltinValue value) {
return out << "local_invocation_index";
case BuiltinValue::kNumWorkgroups:
return out << "num_workgroups";
case BuiltinValue::kPointSize:
return out << "point_size";
case BuiltinValue::kPosition:
return out << "position";
case BuiltinValue::kSampleIndex:

View File

@ -30,6 +30,7 @@ namespace tint::builtin {
/// Builtin value defined with `@builtin(<name>)`.
enum class BuiltinValue {
kUndefined,
kPointSize,
kFragDepth,
kFrontFacing,
kGlobalInvocationId,
@ -37,7 +38,6 @@ enum class BuiltinValue {
kLocalInvocationId,
kLocalInvocationIndex,
kNumWorkgroups,
kPointSize, // Tint-internal enum entry - not parsed
kPosition,
kSampleIndex,
kSampleMask,
@ -56,12 +56,11 @@ std::ostream& operator<<(std::ostream& out, BuiltinValue value);
BuiltinValue ParseBuiltinValue(std::string_view str);
constexpr const char* kBuiltinValueStrings[] = {
"frag_depth", "front_facing",
"global_invocation_id", "instance_index",
"local_invocation_id", "local_invocation_index",
"num_workgroups", "position",
"sample_index", "sample_mask",
"vertex_index", "workgroup_id",
"__point_size", "frag_depth", "front_facing",
"global_invocation_id", "instance_index", "local_invocation_id",
"local_invocation_index", "num_workgroups", "position",
"sample_index", "sample_mask", "vertex_index",
"workgroup_id",
};
} // namespace tint::builtin

View File

@ -31,90 +31,97 @@ namespace {
void BuiltinValueParser(::benchmark::State& state) {
const char* kStrings[] = {
"fragdeccth",
"flaget3",
"fVag_depth",
"_ccpoint_siz",
"_3poi_ile",
"__poiVt_size",
"__point_size",
"1_point_size",
"__pointJsqze",
"__lloint_siz77",
"frqqgppepHHh",
"fv_dcpt",
"frabGdeth",
"frag_depth",
"frag1depth",
"fraJqqepth",
"fra7ll_depth",
"fonHHpp_facing",
"fron_facg",
"frGnt_fbcin",
"frag_veiith",
"fr8g_depWWh",
"Mragxxepth",
"ggroXtfacing",
"Vot_fuciXg",
"front_fac3ng",
"front_facing",
"front_facvnii",
"frWWnt_faci8g",
"fxxonM_facig",
"gXobalgginvocationid",
"goVal_uvocatioX_id",
"global_in3ocation_id",
"front_fEcing",
"fronPPfaTTing",
"ddroxxtfacing",
"global_invocatio44_id",
"global_invocaSSioVV_id",
"22loRal_invoRtion_id",
"global_invocation_id",
"global_invocation_iE",
"TTobal_invocationPPid",
"globdd_invocatioxx_id",
"instance44index",
"instaVVce_SSndex",
"Rnstane_ind2Rx",
"globalFinvoction_id",
"gloal_invocation_id",
"RRlHOOaV_invoction_id",
"instance_ydex",
"instGlr77cnn_index",
"instan04e_index",
"instance_index",
"inFtanceind9x",
"insance_index",
"inRRancV_OOHdex",
"local_nvocytion_id",
"llGcnnl_inv77catirrn_id",
"local_invoca4i00n_id",
"insacoo_inex",
"izstane_index",
"nippance_in11ex",
"local_invXXcation_id",
"lIIcal_i5599ocation_inn",
"HHrrcal_inSSocation_Yaa",
"local_invocation_id",
"loool_nvocaton_id",
"local_inozztion_id",
"p11cal_invocatiiin_i",
"local_invocation_iXXdex",
"local_invnnIIati99n55index",
"localYirrHHocaationSSindex",
"lokkal_invocatini",
"jocal_invocRRongid",
"local_inocatbon_i",
"local_injocation_index",
"local_invocatio_index",
"locl_invocqtion_ndex",
"local_invocation_index",
"lkkal_invHcation_idx",
"gRcal_invocatioj_inex",
"lcal_invcbtion_index",
"localNNinvocaton_index",
"local_invocatin_ivvdx",
"locl_invocatioQQ_index",
"num_workgffus",
"num_workgroujs",
"num_worgroups",
"nuq_orkgoups",
"num_wrkgNNwoup8",
"num_workgroups",
"nm_workgroNNps",
"um_workgrovps",
"nQQm_orkgroups",
"posftrn",
"pojition",
"poswNN82n",
"numworkgroups",
"num_workrrroups",
"num_worGgroups",
"pFFsition",
"pEiio",
"prrsitio",
"position",
"positon",
"porrition",
"pGsition",
"sample_inFFex",
"samleinex",
"sample_indrr",
"sition",
"poJJDtin",
"poi8i",
"smpke11nde",
"samle_index",
"saple_Jndex",
"sample_index",
"sample_iex",
"DaplX_JJndex",
"8amleinde",
"saplekmak",
"samle_mask",
"saJple_mak",
"cample_index",
"sample_indOx",
"savvKKl___inttex",
"sam8le_xx5k",
"sampqq__msk",
"sampleqmask",
"sample_mask",
"sample_cask",
"sample_maOk",
"__attpvve_KKask",
"vrtex5inxxe8",
"v__rex_qFdex",
"veqqtex_idex",
"33amOe_mas66",
"samoott6QQmask",
"66mple_mask",
"verzzx_in6Oxx",
"vertex_yyndex",
"vetxHHZnZex",
"vertex_index",
"veOtx_33nde66",
"v6ootex_indttQx",
"ver66ex_inex",
"worzzroup6Oxd",
"workgroyyp_id",
"wokrHHZpZid",
"vWWteq_in44ex",
"vrtex_OOndex",
"hrteYooindx",
"wogroup_i",
"wokgrouF_id",
"worgrwup_id",
"workgroup_id",
"wWWkgqoup44id",
"wrkgroOOp_id",
"hrkgYooup_d",
"workGKou_if",
"worKKgrouq_id",
"w3rkgrommp_id",
};
for (auto _ : state) {
for (auto* str : kStrings) {

View File

@ -43,6 +43,7 @@ inline std::ostream& operator<<(std::ostream& out, Case c) {
}
static constexpr Case kValidCases[] = {
{"__point_size", BuiltinValue::kPointSize},
{"frag_depth", BuiltinValue::kFragDepth},
{"front_facing", BuiltinValue::kFrontFacing},
{"global_invocation_id", BuiltinValue::kGlobalInvocationId},
@ -58,42 +59,45 @@ static constexpr Case kValidCases[] = {
};
static constexpr Case kInvalidCases[] = {
{"fragdeccth", BuiltinValue::kUndefined},
{"flaget3", BuiltinValue::kUndefined},
{"fVag_depth", BuiltinValue::kUndefined},
{"1ront_facing", BuiltinValue::kUndefined},
{"front_fJcqng", BuiltinValue::kUndefined},
{"frllnt_facin77", BuiltinValue::kUndefined},
{"global_invoqqtionppHid", BuiltinValue::kUndefined},
{"clvbal_inocaionid", BuiltinValue::kUndefined},
{"global_Gvocation_id", BuiltinValue::kUndefined},
{"invtance_iniiex", BuiltinValue::kUndefined},
{"8nstanceWWindex", BuiltinValue::kUndefined},
{"insxxanceindex", BuiltinValue::kUndefined},
{"lXcal_invoation_igg", BuiltinValue::kUndefined},
{"Xocal_nvocatin_Vd", BuiltinValue::kUndefined},
{"local_invoca3ion_id", BuiltinValue::kUndefined},
{"local_invocation_indeE", BuiltinValue::kUndefined},
{"loTTal_invPPcatin_index", BuiltinValue::kUndefined},
{"loal_invocadxxion_index", BuiltinValue::kUndefined},
{"num_work44roups", BuiltinValue::kUndefined},
{"num_wVVrkgSSoups", BuiltinValue::kUndefined},
{"Rum_wokgrou2Rs", BuiltinValue::kUndefined},
{"oFi9ion", BuiltinValue::kUndefined},
{"postion", BuiltinValue::kUndefined},
{"ROOoHiiVn", BuiltinValue::kUndefined},
{"samply_inde", BuiltinValue::kUndefined},
{"snrrmpl77l_indGx", BuiltinValue::kUndefined},
{"00ample4index", BuiltinValue::kUndefined},
{"smoo_mask", BuiltinValue::kUndefined},
{"sampzemask", BuiltinValue::kUndefined},
{"ppaplii1_mas", BuiltinValue::kUndefined},
{"vertex_iXXdex", BuiltinValue::kUndefined},
{"5nnertex_99IIdex", BuiltinValue::kUndefined},
{"verYeaaHHrrndeSS", BuiltinValue::kUndefined},
{"workkgHo_i", BuiltinValue::kUndefined},
{"worRgoupjid", BuiltinValue::kUndefined},
{"wrkgrupbid", BuiltinValue::kUndefined},
{"_ccpoint_siz", BuiltinValue::kUndefined},
{"_3poi_ile", BuiltinValue::kUndefined},
{"__poiVt_size", BuiltinValue::kUndefined},
{"frag1depth", BuiltinValue::kUndefined},
{"fraJqqepth", BuiltinValue::kUndefined},
{"fra7ll_depth", BuiltinValue::kUndefined},
{"fonHHpp_facing", BuiltinValue::kUndefined},
{"fron_facg", BuiltinValue::kUndefined},
{"frGnt_fbcin", BuiltinValue::kUndefined},
{"glvbal_iinvocation_id", BuiltinValue::kUndefined},
{"gl8bal_invocation_WWd", BuiltinValue::kUndefined},
{"Mlobal_invocaton_xxd", BuiltinValue::kUndefined},
{"isXance_indegg", BuiltinValue::kUndefined},
{"insanc_iXVex", BuiltinValue::kUndefined},
{"instance_in3ex", BuiltinValue::kUndefined},
{"local_Envocation_id", BuiltinValue::kUndefined},
{"localiPPvocatioTT_id", BuiltinValue::kUndefined},
{"localxxnvocationddid", BuiltinValue::kUndefined},
{"loca44_invocation_index", BuiltinValue::kUndefined},
{"local_invocSStionVVindex", BuiltinValue::kUndefined},
{"locRR_invocat22n_index", BuiltinValue::kUndefined},
{"nuF_workrou9s", BuiltinValue::kUndefined},
{"numworkgroups", BuiltinValue::kUndefined},
{"nuRRworVgOOHups", BuiltinValue::kUndefined},
{"posytio", BuiltinValue::kUndefined},
{"77orritllnon", BuiltinValue::kUndefined},
{"04osition", BuiltinValue::kUndefined},
{"smpe_oonde", BuiltinValue::kUndefined},
{"smpl_inzzex", BuiltinValue::kUndefined},
{"saiip11eindep", BuiltinValue::kUndefined},
{"sample_XXask", BuiltinValue::kUndefined},
{"samII99l55_mask", BuiltinValue::kUndefined},
{"samaale_SSrHHYk", BuiltinValue::kUndefined},
{"verkkeH_de", BuiltinValue::kUndefined},
{"verRg_injex", BuiltinValue::kUndefined},
{"vrtexinbex", BuiltinValue::kUndefined},
{"workjroup_id", BuiltinValue::kUndefined},
{"wrkgroup_id", BuiltinValue::kUndefined},
{"qorkgro_id", BuiltinValue::kUndefined},
};
using BuiltinValueParseTest = testing::TestWithParam<Case>;

View File

@ -28,7 +28,9 @@
#include "src/tint/ast/module.h"
#include "src/tint/ast/override.h"
#include "src/tint/ast/var.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/builtin/extension.h"
#include "src/tint/sem/builtin_enum_expression.h"
#include "src/tint/sem/call.h"
#include "src/tint/sem/function.h"
#include "src/tint/sem/module.h"
@ -698,11 +700,10 @@ bool Inspector::ContainsBuiltin(builtin::BuiltinValue builtin,
// Base case: check for builtin
auto* builtin_declaration = ast::GetAttribute<ast::BuiltinAttribute>(attributes);
if (!builtin_declaration || builtin_declaration->builtin != builtin) {
if (!builtin_declaration) {
return false;
}
return true;
return program_->Sem().Get(builtin_declaration)->Value() == builtin;
}
std::vector<ResourceBinding> Inspector::GetStorageBufferResourceBindingsImpl(

View File

@ -25,6 +25,7 @@
#include "tint/override_id.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/inspector/entry_point.h"
#include "src/tint/inspector/resource_binding.h"
#include "src/tint/inspector/scalar.h"

View File

@ -37,7 +37,7 @@ enum builtin_value {
num_workgroups
sample_index
sample_mask
@internal point_size
__point_size
}
// https://gpuweb.github.io/gpuweb/wgsl/#filterable-triggering-rules

View File

@ -3445,15 +3445,17 @@ class ProgramBuilder {
/// @param source the source information
/// @param builtin the builtin value
/// @returns the builtin attribute pointer
const ast::BuiltinAttribute* Builtin(const Source& source, builtin::BuiltinValue builtin) {
return create<ast::BuiltinAttribute>(source, builtin);
template <typename BUILTIN>
const ast::BuiltinAttribute* Builtin(const Source& source, BUILTIN&& builtin) {
return create<ast::BuiltinAttribute>(source, Expr(std::forward<BUILTIN>(builtin)));
}
/// Creates an ast::BuiltinAttribute
/// @param builtin the builtin value
/// @returns the builtin attribute pointer
const ast::BuiltinAttribute* Builtin(builtin::BuiltinValue builtin) {
return create<ast::BuiltinAttribute>(source_, builtin);
template <typename BUILTIN>
const ast::BuiltinAttribute* Builtin(BUILTIN&& builtin) {
return create<ast::BuiltinAttribute>(source_, Expr(std::forward<BUILTIN>(builtin)));
}
/// Creates an ast::InterpolateAttribute

View File

@ -0,0 +1,81 @@
// 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_READER_SPIRV_ATTRIBUTES_H_
#define SRC_TINT_READER_SPIRV_ATTRIBUTES_H_
#include "src/tint/ast/attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/utils/enum_set.h"
#include "src/tint/utils/vector.h"
namespace tint::reader::spirv {
/// Attributes holds a vector of ast::Attribute pointers, and a enum-set of flags used to hold
/// additional metadata.
struct Attributes {
/// Flags used by #flags.
enum class Flags {
kHasBuiltinSampleMask,
};
/// Adds the attributes and flags of @p other to this.
/// @param other the other Attributes to combine into this
void Add(const Attributes& other) {
for (auto* attr : other.list) {
list.Push(attr);
}
for (auto flag : other.flags) {
flags.Add(flag);
}
}
/// Adds the attribute @p attr to the list of attributes
/// @param attr the attribute to add to this
void Add(const ast::Attribute* attr) { list.Push(attr); }
/// Adds the builtin to the attribute list, also marking any necessary flags
/// @param builder the program builder
/// @param source the source of the builtin attribute
/// @param builtin the builtin attribute to add
void Add(ProgramBuilder& builder, const Source& source, builtin::BuiltinValue builtin) {
Add(builder.Builtin(source, builtin));
if (builtin == builtin::BuiltinValue::kSampleMask) {
flags.Add(Flags::kHasBuiltinSampleMask);
}
}
/// @returns true if the attribute list contains an attribute with the type `T`.
template <typename T>
bool Has() const {
return ast::HasAttribute<T>(list);
}
/// @returns the attribute with type `T` in the list, or nullptr if no attribute of the given
/// type exists in list.
template <typename T>
const T* Get() const {
return ast::GetAttribute<T>(list);
}
/// The attributes
utils::Vector<const ast::Attribute*, 8> list;
/// The additional metadata flags
utils::EnumSet<Flags> flags;
};
} // namespace tint::reader::spirv
#endif // SRC_TINT_READER_SPIRV_ATTRIBUTES_H_

View File

@ -756,15 +756,6 @@ struct LoopStatementBuilder final : public Castable<LoopStatementBuilder, Statem
mutable const ast::BlockStatement* continuing = nullptr;
};
/// @param decos a list of parsed decorations
/// @returns true if the decorations include a SampleMask builtin
bool HasBuiltinSampleMask(utils::VectorRef<const ast::Attribute*> decos) {
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(decos)) {
return builtin->builtin == builtin::BuiltinValue::kSampleMask;
}
return false;
}
} // namespace
BlockInfo::BlockInfo(const spvtools::opt::BasicBlock& bb) : basic_block(&bb), id(bb.id()) {}
@ -956,7 +947,7 @@ bool FunctionEmitter::Emit() {
}
builder_.Func(decl.source, decl.name, std::move(decl.params),
decl.return_type->Build(builder_), body, std::move(decl.attributes));
decl.return_type->Build(builder_), body, std::move(decl.attributes.list));
}
if (ep_info_ && !ep_info_->inner_name.empty()) {
@ -994,12 +985,12 @@ const ast::BlockStatement* FunctionEmitter::MakeFunctionBody() {
bool FunctionEmitter::EmitPipelineInput(std::string var_name,
const Type* var_type,
AttributeList* attrs,
utils::Vector<int, 8> index_prefix,
const Type* tip_type,
const Type* forced_param_type,
ParameterList* params,
StatementList* statements) {
Attributes& attrs,
ParameterList& params,
StatementList& statements) {
// TODO(dneto): Handle structs where the locations are annotated on members.
tip_type = tip_type->UnwrapAlias();
if (auto* ref_type = tip_type->As<Reference>()) {
@ -1015,8 +1006,8 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
for (int col = 0; col < num_columns; col++) {
index_prefix.Back() = col;
if (!EmitPipelineInput(var_name, var_type, attrs, index_prefix, vec_ty,
forced_param_type, params, statements)) {
if (!EmitPipelineInput(var_name, var_type, index_prefix, vec_ty, forced_param_type,
attrs, params, statements)) {
return false;
}
}
@ -1030,8 +1021,8 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
const Type* elem_ty = array_type->type;
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
index_prefix.Back() = i;
if (!EmitPipelineInput(var_name, var_type, attrs, index_prefix, elem_ty,
forced_param_type, params, statements)) {
if (!EmitPipelineInput(var_name, var_type, index_prefix, elem_ty, forced_param_type,
attrs, params, statements)) {
return false;
}
}
@ -1042,38 +1033,36 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
index_prefix.Push(0);
for (size_t i = 0; i < members.size(); ++i) {
index_prefix.Back() = static_cast<int>(i);
AttributeList member_attrs(*attrs);
Attributes member_attrs(attrs);
if (!parser_impl_.ConvertPipelineDecorations(
struct_type,
parser_impl_.GetMemberPipelineDecorations(*struct_type,
static_cast<int>(i)),
&member_attrs)) {
member_attrs)) {
return false;
}
if (!EmitPipelineInput(var_name, var_type, &member_attrs, index_prefix, members[i],
forced_param_type, params, statements)) {
if (!EmitPipelineInput(var_name, var_type, index_prefix, members[i],
forced_param_type, member_attrs, params, statements)) {
return false;
}
// Copy the location as updated by nested expansion of the member.
parser_impl_.SetLocation(attrs,
ast::GetAttribute<ast::LocationAttribute>(member_attrs));
parser_impl_.SetLocation(attrs, member_attrs.Get<ast::LocationAttribute>());
}
return success();
},
[&](Default) {
const bool is_builtin = ast::HasAttribute<ast::BuiltinAttribute>(*attrs);
const bool is_builtin = attrs.Has<ast::BuiltinAttribute>();
const Type* param_type = is_builtin ? forced_param_type : tip_type;
const auto param_name = namer_.MakeDerivedName(var_name + "_param");
// Create the parameter.
// TODO(dneto): Note: If the parameter has non-location decorations,
// then those decoration AST nodes will be reused between multiple
// elements of a matrix, array, or structure. Normally that's
// disallowed but currently the SPIR-V reader will make duplicates when
// the entire AST is cloned at the top level of the SPIR-V reader flow.
// TODO(dneto): Note: If the parameter has non-location decorations, then those
// decoration AST nodes will be reused between multiple elements of a matrix, array, or
// structure. Normally that's disallowed but currently the SPIR-V reader will make
// duplicates when the entire AST is cloned at the top level of the SPIR-V reader flow.
// Consider rewriting this to avoid this node-sharing.
params->Push(builder_.Param(param_name, param_type->Build(builder_), *attrs));
params.Push(builder_.Param(param_name, param_type->Build(builder_), attrs.list));
// Add a body statement to copy the parameter to the corresponding
// private variable.
@ -1101,27 +1090,25 @@ bool FunctionEmitter::EmitPipelineInput(std::string var_name,
}
if (is_builtin && (tip_type != forced_param_type)) {
// The parameter will have the WGSL type, but we need bitcast to
// the variable store type.
// The parameter will have the WGSL type, but we need bitcast to the variable store
// type.
param_value = builder_.Bitcast(tip_type->Build(builder_), param_value);
}
statements->Push(builder_.Assign(store_dest, param_value));
statements.Push(builder_.Assign(store_dest, param_value));
// Increment the location attribute, in case more parameters will
// follow.
// Increment the location attribute, in case more parameters will follow.
IncrementLocation(attrs);
return success();
});
}
void FunctionEmitter::IncrementLocation(AttributeList* attributes) {
for (auto*& attr : *attributes) {
void FunctionEmitter::IncrementLocation(Attributes& attributes) {
for (auto*& attr : attributes.list) {
if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
// Replace this location attribute with a new one with one higher index.
// The old one doesn't leak because it's kept in the builder's AST node
// list.
// The old one doesn't leak because it's kept in the builder's AST node list.
attr = builder_.Location(
loc_attr->source, AInt(loc_attr->expr->As<ast::IntLiteralExpression>()->value + 1));
}
@ -1130,12 +1117,12 @@ void FunctionEmitter::IncrementLocation(AttributeList* attributes) {
bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
const Type* var_type,
AttributeList* decos,
utils::Vector<int, 8> index_prefix,
const Type* tip_type,
const Type* forced_member_type,
StructMemberList* return_members,
ExpressionList* return_exprs) {
Attributes& attrs,
StructMemberList& return_members,
ExpressionList& return_exprs) {
tip_type = tip_type->UnwrapAlias();
if (auto* ref_type = tip_type->As<Reference>()) {
tip_type = ref_type->type;
@ -1150,8 +1137,8 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
const Type* vec_ty = ty_.Vector(matrix_type->type, matrix_type->rows);
for (int col = 0; col < num_columns; col++) {
index_prefix.Back() = col;
if (!EmitPipelineOutput(var_name, var_type, std::move(decos), index_prefix, vec_ty,
forced_member_type, return_members, return_exprs)) {
if (!EmitPipelineOutput(var_name, var_type, index_prefix, vec_ty,
forced_member_type, attrs, return_members, return_exprs)) {
return false;
}
}
@ -1165,8 +1152,8 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
const Type* elem_ty = array_type->type;
for (int i = 0; i < static_cast<int>(array_type->size); i++) {
index_prefix.Back() = i;
if (!EmitPipelineOutput(var_name, var_type, std::move(decos), index_prefix, elem_ty,
forced_member_type, return_members, return_exprs)) {
if (!EmitPipelineOutput(var_name, var_type, index_prefix, elem_ty,
forced_member_type, attrs, return_members, return_exprs)) {
return false;
}
}
@ -1177,39 +1164,37 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
index_prefix.Push(0);
for (int i = 0; i < static_cast<int>(members.size()); ++i) {
index_prefix.Back() = i;
AttributeList member_attrs(*decos);
Attributes member_attrs(attrs);
if (!parser_impl_.ConvertPipelineDecorations(
struct_type, parser_impl_.GetMemberPipelineDecorations(*struct_type, i),
&member_attrs)) {
member_attrs)) {
return false;
}
if (!EmitPipelineOutput(var_name, var_type, &member_attrs, index_prefix,
if (!EmitPipelineOutput(var_name, var_type, index_prefix,
members[static_cast<size_t>(i)], forced_member_type,
return_members, return_exprs)) {
member_attrs, return_members, return_exprs)) {
return false;
}
// Copy the location as updated by nested expansion of the member.
parser_impl_.SetLocation(decos,
ast::GetAttribute<ast::LocationAttribute>(member_attrs));
parser_impl_.SetLocation(attrs, member_attrs.Get<ast::LocationAttribute>());
}
return success();
},
[&](Default) {
const bool is_builtin = ast::HasAttribute<ast::BuiltinAttribute>(*decos);
const bool is_builtin = attrs.Has<ast::BuiltinAttribute>();
const Type* member_type = is_builtin ? forced_member_type : tip_type;
// Derive the member name directly from the variable name. They can't
// collide.
const auto member_name = namer_.MakeDerivedName(var_name);
// Create the member.
// TODO(dneto): Note: If the parameter has non-location decorations,
// then those decoration AST nodes will be reused between multiple
// elements of a matrix, array, or structure. Normally that's
// disallowed but currently the SPIR-V reader will make duplicates when
// the entire AST is cloned at the top level of the SPIR-V reader flow.
// TODO(dneto): Note: If the parameter has non-location decorations, then those
// decoration AST nodes will be reused between multiple elements of a matrix, array, or
// structure. Normally that's disallowed but currently the SPIR-V reader will make
// duplicates when the entire AST is cloned at the top level of the SPIR-V reader flow.
// Consider rewriting this to avoid this node-sharing.
return_members->Push(
builder_.Member(member_name, member_type->Build(builder_), *decos));
return_members.Push(
builder_.Member(member_name, member_type->Build(builder_), attrs.list));
// Create an expression to evaluate the part of the variable indexed by
// the index_prefix.
@ -1240,11 +1225,10 @@ bool FunctionEmitter::EmitPipelineOutput(std::string var_name,
// the variable store type.
load_source = builder_.Bitcast(forced_member_type->Build(builder_), load_source);
}
return_exprs->Push(load_source);
return_exprs.Push(load_source);
// Increment the location attribute, in case more parameters will
// follow.
IncrementLocation(decos);
// Increment the location attribute, in case more parameters will follow.
IncrementLocation(attrs);
return success();
});
@ -1270,8 +1254,8 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
TINT_ASSERT(Reader, opcode(var) == spv::Op::OpVariable);
auto* store_type = GetVariableStoreType(*var);
auto* forced_param_type = store_type;
AttributeList param_decos;
if (!parser_impl_.ConvertDecorationsForVariable(var_id, &forced_param_type, &param_decos,
Attributes param_attrs;
if (!parser_impl_.ConvertDecorationsForVariable(var_id, &forced_param_type, param_attrs,
true)) {
// This occurs, and is not an error, for the PointSize builtin.
if (!success()) {
@ -1287,18 +1271,17 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
const auto var_name = namer_.GetName(var_id);
bool ok = true;
if (HasBuiltinSampleMask(param_decos)) {
if (param_attrs.flags.Contains(Attributes::Flags::kHasBuiltinSampleMask)) {
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a scalar.
// Use the first element only.
auto* sample_mask_array_type = store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
TINT_ASSERT(Reader, sample_mask_array_type);
ok = EmitPipelineInput(var_name, store_type, &param_decos, {0},
sample_mask_array_type->type, forced_param_type, &decl.params,
&stmts);
ok = EmitPipelineInput(var_name, store_type, {0}, sample_mask_array_type->type,
forced_param_type, param_attrs, decl.params, stmts);
} else {
// The normal path.
ok = EmitPipelineInput(var_name, store_type, &param_decos, {}, store_type,
forced_param_type, &decl.params, &stmts);
ok = EmitPipelineInput(var_name, store_type, {}, store_type, forced_param_type,
param_attrs, decl.params, stmts);
}
if (!ok) {
return false;
@ -1330,12 +1313,12 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
// The SPIR-V gl_PerVertex variable has already been remapped to
// a gl_Position variable. Substitute the type.
const Type* param_type = ty_.Vector(ty_.F32(), 4);
AttributeList out_decos{
create<ast::BuiltinAttribute>(source, builtin::BuiltinValue::kPosition)};
const auto var_name = namer_.GetName(var_id);
return_members.Push(
builder_.Member(var_name, param_type->Build(builder_), out_decos));
builder_.Member(var_name, param_type->Build(builder_),
utils::Vector{
builder_.Builtin(source, builtin::BuiltinValue::kPosition),
}));
return_exprs.Push(builder_.Expr(var_name));
} else {
@ -1344,9 +1327,9 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
TINT_ASSERT(Reader, opcode(var) == spv::Op::OpVariable);
const Type* store_type = GetVariableStoreType(*var);
const Type* forced_member_type = store_type;
AttributeList out_decos;
Attributes out_attrs;
if (!parser_impl_.ConvertDecorationsForVariable(var_id, &forced_member_type,
&out_decos, true)) {
out_attrs, true)) {
// This occurs, and is not an error, for the PointSize builtin.
if (!success()) {
// But exit early if an error was logged.
@ -1357,19 +1340,20 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
const auto var_name = namer_.GetName(var_id);
bool ok = true;
if (HasBuiltinSampleMask(out_decos)) {
if (out_attrs.flags.Contains(Attributes::Flags::kHasBuiltinSampleMask)) {
// In Vulkan SPIR-V, the sample mask is an array. In WGSL it's a
// scalar. Use the first element only.
auto* sample_mask_array_type =
store_type->UnwrapRef()->UnwrapAlias()->As<Array>();
TINT_ASSERT(Reader, sample_mask_array_type);
ok = EmitPipelineOutput(var_name, store_type, &out_decos, {0},
sample_mask_array_type->type, forced_member_type,
&return_members, &return_exprs);
ok = EmitPipelineOutput(var_name, store_type, {0}, sample_mask_array_type->type,
forced_member_type, out_attrs, return_members,
return_exprs);
} else {
// The normal path.
ok = EmitPipelineOutput(var_name, store_type, &out_decos, {}, store_type,
forced_member_type, &return_members, &return_exprs);
ok =
EmitPipelineOutput(var_name, store_type, {}, store_type, forced_member_type,
out_attrs, return_members, return_exprs);
}
if (!ok) {
return false;
@ -1384,7 +1368,7 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
} else {
// Create and register the result type.
auto* str = create<ast::Struct>(Source{}, builder_.Ident(return_struct_sym),
return_members, AttributeList{});
return_members, utils::Empty);
parser_impl_.AddTypeDecl(return_struct_sym, str);
return_type = builder_.ty.Of(str);
@ -1394,8 +1378,9 @@ bool FunctionEmitter::EmitEntryPointAsWrapper() {
}
}
AttributeList fn_attrs;
fn_attrs.Push(create<ast::StageAttribute>(source, ep_info_->stage));
utils::Vector<const ast::Attribute*, 2> fn_attrs{
create<ast::StageAttribute>(source, ep_info_->stage),
};
if (ep_info_->stage == ast::PipelineStage::kCompute) {
auto& size = ep_info_->workgroup_size;
@ -1442,7 +1427,7 @@ bool FunctionEmitter::ParseFunctionDeclaration(FunctionDeclaration* decl) {
: parser_impl_.ConvertType(param->type_id());
if (type != nullptr) {
auto* ast_param = parser_impl_.MakeParameter(param->result_id(), type, AttributeList{});
auto* ast_param = parser_impl_.MakeParameter(param->result_id(), type, Attributes{});
// Parameters are treated as const declarations.
ast_params.Push(ast_param);
// The value is accessible by name.
@ -1458,7 +1443,7 @@ bool FunctionEmitter::ParseFunctionDeclaration(FunctionDeclaration* decl) {
decl->name = name;
decl->params = std::move(ast_params);
decl->return_type = ret_ty;
decl->attributes.Clear();
decl->attributes = {};
return success();
}
@ -2523,7 +2508,7 @@ bool FunctionEmitter::EmitFunctionVariables() {
}
auto* var = parser_impl_.MakeVar(inst.result_id(), builtin::AddressSpace::kUndefined,
builtin::Access::kUndefined, var_store_type, initializer,
AttributeList{});
Attributes{});
auto* var_decl_stmt = create<ast::VariableDeclStatement>(Source{}, var);
AddStatement(var_decl_stmt);
auto* var_type = ty_.Reference(var_store_type, builtin::AddressSpace::kUndefined);
@ -3370,7 +3355,7 @@ bool FunctionEmitter::EmitStatementsInBasicBlock(const BlockInfo& block_info,
AddStatement(create<ast::VariableDeclStatement>(
Source{},
parser_impl_.MakeVar(id, builtin::AddressSpace::kUndefined, builtin::Access::kUndefined,
store_type, nullptr, AttributeList{})));
store_type, nullptr, Attributes{})));
auto* type = ty_.Reference(store_type, builtin::AddressSpace::kUndefined);
identifier_types_.emplace(id, type);
}

View File

@ -25,6 +25,7 @@
#include <vector>
#include "src/tint/program_builder.h"
#include "src/tint/reader/spirv/attributes.h"
#include "src/tint/reader/spirv/construct.h"
#include "src/tint/reader/spirv/parser_impl.h"
@ -420,7 +421,6 @@ class StatementBuilder : public Castable<StatementBuilder, ast::Statement> {
/// A FunctionEmitter emits a SPIR-V function onto a Tint AST module.
class FunctionEmitter {
using AttributeList = utils::Vector<const ast::Attribute*, 8>;
using StructMemberList = utils::Vector<const ast::StructMember*, 8>;
using ExpressionList = utils::Vector<const ast::Expression*, 8>;
using ParameterList = utils::Vector<const ast::Parameter*, 8>;
@ -473,32 +473,31 @@ class FunctionEmitter {
/// @returns false if emission failed.
bool EmitEntryPointAsWrapper();
/// Creates one or more entry point input parameters corresponding to a
/// part of an input variable. The part of the input variable is specfied
/// by the `index_prefix`, which successively indexes into the variable.
/// Also generates the assignment statements that copy the input parameter
/// to the corresponding part of the variable. Assumes the variable
/// has already been created in the Private address space.
/// Creates one or more entry point input parameters corresponding to a part of an input
/// variable. The part of the input variable is specfied by the `index_prefix`, which
/// successively indexes into the variable. Also generates the assignment statements that copy
/// the input parameter to the corresponding part of the variable. Assumes the variable has
/// already been created in the Private address space.
/// @param var_name The name of the variable
/// @param var_type The store type of the variable
/// @param decos The variable's decorations
/// @param index_prefix Indices stepping into the variable, indicating
/// what part of the variable to populate.
/// @param tip_type The type of the component inside variable, after indexing
/// with the indices in `index_prefix`.
/// @param forced_param_type The type forced by WGSL, if the variable is a
/// builtin, otherwise the same as var_type.
/// @param attributes The variable's attributes
/// @param index_prefix Indices stepping into the variable, indicating what part of the variable
/// to populate.
/// @param tip_type The type of the component inside variable, after indexing with the indices
/// in `index_prefix`.
/// @param forced_param_type The type forced by WGSL, if the variable is a builtin, otherwise
/// the same as var_type.
/// @param params The parameter list where the new parameter is appended.
/// @param statements The statement list where the assignment is appended.
/// @returns false if emission failed
bool EmitPipelineInput(std::string var_name,
const Type* var_type,
AttributeList* decos,
utils::Vector<int, 8> index_prefix,
const Type* tip_type,
const Type* forced_param_type,
ParameterList* params,
StatementList* statements);
Attributes& attributes,
ParameterList& params,
StatementList& statements);
/// Creates one or more struct members from an output variable, and the
/// expressions that compute the value they contribute to the entry point
@ -507,31 +506,31 @@ class FunctionEmitter {
/// Assumes the variable has already been created in the Private address space
/// @param var_name The name of the variable
/// @param var_type The store type of the variable
/// @param decos The variable's decorations
/// @param index_prefix Indices stepping into the variable, indicating what part of the variable
/// to populate.
/// @param tip_type The type of the component inside variable, after indexing with the indices
/// in `index_prefix`.
/// @param forced_member_type The type forced by WGSL, if the variable is a builtin, otherwise
/// the same as var_type.
/// @param attributes The variable's attributes
/// @param return_members The struct member list where the new member is added.
/// @param return_exprs The expression list where the return expression is added.
/// @returns false if emission failed
bool EmitPipelineOutput(std::string var_name,
const Type* var_type,
AttributeList* decos,
utils::Vector<int, 8> index_prefix,
const Type* tip_type,
const Type* forced_member_type,
StructMemberList* return_members,
ExpressionList* return_exprs);
Attributes& attributes,
StructMemberList& return_members,
ExpressionList& return_exprs);
/// Updates the attribute list, replacing an existing Location attribute
/// with another having one higher location value. Does nothing if no
/// location attribute exists.
/// Assumes the list contains at most one Location attribute.
/// @param attributes the attribute list to modify
void IncrementLocation(AttributeList* attributes);
void IncrementLocation(Attributes& attributes);
/// Create an ast::BlockStatement representing the body of the function.
/// This creates the statement stack, which is non-empty for the lifetime
@ -976,7 +975,7 @@ class FunctionEmitter {
/// Function return type
const Type* return_type;
/// Function attributes
AttributeList attributes;
Attributes attributes;
};
/// Parse the function declaration, which comprises the name, parameters, and

View File

@ -26,32 +26,146 @@ namespace {
const char* kWGSLReservedWords[] = {
// Please keep this list sorted
"array", "as", "asm",
"bf16", "binding", "block",
"bool", "break", "builtin",
"case", "cast", "compute",
"const", "continue", "default",
"discard", "do", "else",
"elseif", "entry_point", "enum",
"f16", "f32", "fallthrough",
"false", "fn", "for",
"fragment", "i16", "i32",
"i64", "i8", "if",
"image", "import", "in",
"let", "location", "loop",
"mat2x2", "mat2x3", "mat2x4",
"mat3x2", "mat3x3", "mat3x4",
"mat4x2", "mat4x3", "mat4x4",
"offset", "out", "override",
"premerge", "private", "ptr",
"regardless", "return", "set",
"storage", "struct", "switch",
"true", "type", "typedef",
"u16", "u32", "u64",
"u8", "uniform", "uniform_constant",
"unless", "using", "var",
"vec2", "vec3", "vec4",
"vertex", "void", "while",
"array",
"as",
"asm",
"atomic",
"bf16",
"binding",
"block",
"bool",
"break",
"builtin",
"case",
"cast",
"compute",
"const",
"continue",
"default",
"discard",
"do",
"else",
"elseif",
"entry_point",
"enum",
"f16",
"f32",
"fallthrough",
"false",
"fn",
"for",
"frag_depth",
"fragment",
"front_facing",
"global_invocation_id",
"i16",
"i32",
"i64",
"i8",
"if",
"image",
"import",
"in",
"instance_index",
"let",
"local_invocation_id",
"local_invocation_index",
"location",
"loop",
"mat2x2",
"mat2x2f",
"mat2x2h",
"mat2x3",
"mat2x3f",
"mat2x3h",
"mat2x4",
"mat2x4f",
"mat2x4h",
"mat3x2",
"mat3x2f",
"mat3x2h",
"mat3x3",
"mat3x3f",
"mat3x3h",
"mat3x4",
"mat3x4f",
"mat3x4h",
"mat4x2",
"mat4x2f",
"mat4x2h",
"mat4x3",
"mat4x3f",
"mat4x3h",
"mat4x4",
"mat4x4f",
"mat4x4h",
"num_workgroups",
"offset",
"out",
"override",
"position",
"premerge",
"private",
"ptr",
"regardless",
"return",
"sample_index",
"sample_mask",
"sampler_comparison",
"sampler",
"set",
"storage",
"struct",
"switch",
"texture_1d",
"texture_2d_array",
"texture_2d",
"texture_3d",
"texture_cube_array",
"texture_cube",
"texture_depth_2d_array",
"texture_depth_2d",
"texture_depth_cube_array",
"texture_depth_cube",
"texture_depth_multisampled_2d",
"texture_external",
"texture_multisampled_2d",
"texture_storage_1d",
"texture_storage_2d_array",
"texture_storage_2d",
"texture_storage_3d",
"true",
"type",
"typedef",
"u16",
"u32",
"u64",
"u8",
"uniform_constant",
"uniform",
"unless",
"using",
"var",
"vec2",
"vec2f",
"vec2h",
"vec2i",
"vec2u",
"vec3",
"vec3f",
"vec3h",
"vec3i",
"vec3u",
"vec4",
"vec4f",
"vec4h",
"vec4i",
"vec4u",
"vertex_index",
"vertex",
"void",
"while",
"workgroup_id",
"workgroup",
};

View File

@ -461,49 +461,42 @@ std::string ParserImpl::ShowType(uint32_t type_id) {
return "SPIR-V type " + std::to_string(type_id);
}
ParserImpl::AttributeList ParserImpl::ConvertMemberDecoration(uint32_t struct_type_id,
uint32_t member_index,
const Type* member_ty,
const Decoration& decoration) {
Attributes ParserImpl::ConvertMemberDecoration(uint32_t struct_type_id,
uint32_t member_index,
const Type* member_ty,
const Decoration& decoration) {
if (decoration.empty()) {
Fail() << "malformed SPIR-V decoration: it's empty";
return {};
}
Attributes out;
switch (static_cast<spv::Decoration>(decoration[0])) {
case spv::Decoration::Offset:
case spv::Decoration::Offset: {
if (decoration.size() != 2) {
Fail() << "malformed Offset decoration: expected 1 literal operand, has "
<< decoration.size() - 1 << ": member " << member_index << " of "
<< ShowType(struct_type_id);
return {};
}
return {
builder_.MemberOffset(Source{}, AInt(decoration[1])),
};
case spv::Decoration::NonReadable:
// WGSL doesn't have a member decoration for this. Silently drop it.
return {};
case spv::Decoration::NonWritable:
// WGSL doesn't have a member decoration for this.
return {};
case spv::Decoration::ColMajor:
// WGSL only supports column major matrices.
return {};
case spv::Decoration::RelaxedPrecision:
// WGSL doesn't support relaxed precision.
return {};
out.Add(builder_.MemberOffset(Source{}, AInt(decoration[1])));
break;
}
case spv::Decoration::NonReadable: // WGSL doesn't have a member decoration for this.
case spv::Decoration::NonWritable: // WGSL doesn't have a member decoration for this.
case spv::Decoration::ColMajor: // WGSL only supports column major matrices.
case spv::Decoration::RelaxedPrecision: // WGSL doesn't support relaxed precision.
break;
case spv::Decoration::RowMajor:
Fail() << "WGSL does not support row-major matrices: can't "
"translate member "
<< member_index << " of " << ShowType(struct_type_id);
return {};
break;
case spv::Decoration::MatrixStride: {
if (decoration.size() != 2) {
Fail() << "malformed MatrixStride decoration: expected 1 literal "
"operand, has "
Fail() << "malformed MatrixStride decoration: expected 1 literal operand, has "
<< decoration.size() - 1 << ": member " << member_index << " of "
<< ShowType(struct_type_id);
return {};
break;
}
uint32_t stride = decoration[1];
auto* ty = member_ty->UnwrapAlias();
@ -513,31 +506,30 @@ ParserImpl::AttributeList ParserImpl::ConvertMemberDecoration(uint32_t struct_ty
auto* mat = ty->As<Matrix>();
if (!mat) {
Fail() << "MatrixStride cannot be applied to type " << ty->String();
return {};
break;
}
uint32_t natural_stride = (mat->rows == 2) ? 8 : 16;
if (stride == natural_stride) {
return {}; // Decoration matches the natural stride for the matrix
break; // Decoration matches the natural stride for the matrix
}
if (!member_ty->Is<Matrix>()) {
Fail() << "custom matrix strides not currently supported on array of "
"matrices";
return {};
Fail() << "custom matrix strides not currently supported on array of matrices";
break;
}
return {
create<ast::StrideAttribute>(Source{}, decoration[1]),
builder_.ASTNodes().Create<ast::DisableValidationAttribute>(
builder_.ID(), builder_.AllocateNodeID(),
ast::DisabledValidation::kIgnoreStrideAttribute),
};
}
default:
// TODO(dneto): Support the remaining member decorations.
out.Add(create<ast::StrideAttribute>(Source{}, decoration[1]));
out.Add(builder_.ASTNodes().Create<ast::DisableValidationAttribute>(
builder_.ID(), builder_.AllocateNodeID(),
ast::DisabledValidation::kIgnoreStrideAttribute));
break;
}
default: {
// TODO(dneto): Support the remaining member decorations.
Fail() << "unhandled member decoration: " << decoration[0] << " on member "
<< member_index << " of " << ShowType(struct_type_id);
break;
}
}
Fail() << "unhandled member decoration: " << decoration[0] << " on member " << member_index
<< " of " << ShowType(struct_type_id);
return {};
return out;
}
bool ParserImpl::BuildInternalModule() {
@ -1138,7 +1130,7 @@ const Type* ParserImpl::ConvertType(uint32_t type_id,
}
bool is_non_writable = false;
AttributeList ast_member_decorations;
Attributes ast_member_decorations;
for (auto& decoration : GetDecorationsForMember(type_id, member_index)) {
if (IsPipelineDecoration(decoration)) {
// IO decorations are handled when emitting the entry point.
@ -1149,11 +1141,9 @@ const Type* ParserImpl::ConvertType(uint32_t type_id,
// the members are non-writable.
is_non_writable = true;
} else {
auto decos =
auto attrs =
ConvertMemberDecoration(type_id, member_index, ast_member_ty, decoration);
for (auto* deco : decos) {
ast_member_decorations.Push(deco);
}
ast_member_decorations.Add(attrs);
if (!success_) {
return nullptr;
}
@ -1168,7 +1158,7 @@ const Type* ParserImpl::ConvertType(uint32_t type_id,
const auto member_name = namer_.GetMemberName(type_id, member_index);
auto* ast_struct_member =
builder_.Member(Source{}, member_name, ast_member_ty->Build(builder_),
std::move(ast_member_decorations));
std::move(ast_member_decorations.list));
ast_members.Push(ast_struct_member);
}
@ -1368,7 +1358,7 @@ bool ParserImpl::EmitScalarSpecConstants() {
break;
}
if (ast_type && ast_expr) {
AttributeList spec_id_decos;
Attributes spec_id_attrs;
for (const auto& deco : GetDecorationsFor(inst.result_id())) {
if ((deco.size() == 2) && (deco[0] == uint32_t(spv::Decoration::SpecId))) {
const uint32_t id = deco[1];
@ -1378,12 +1368,12 @@ bool ParserImpl::EmitScalarSpecConstants() {
<< inst.result_id() << " has SpecId " << id;
}
auto* cid = builder_.Id(Source{}, AInt(id));
spec_id_decos.Push(cid);
spec_id_attrs.Add(cid);
break;
}
}
auto* ast_var =
MakeOverride(inst.result_id(), ast_type, ast_expr, std::move(spec_id_decos));
MakeOverride(inst.result_id(), ast_type, ast_expr, std::move(spec_id_attrs));
if (ast_var) {
scalar_spec_constants_.insert(inst.result_id());
}
@ -1506,7 +1496,7 @@ bool ParserImpl::EmitModuleScopeVariables() {
}
auto ast_access = VarAccess(ast_store_type, ast_address_space);
auto* ast_var = MakeVar(var.result_id(), ast_address_space, ast_access, ast_store_type,
ast_initializer, utils::Empty);
ast_initializer, Attributes{});
// TODO(dneto): initializers (a.k.a. initializer expression)
if (ast_var) {
builder_.AST().AddGlobalVariable(ast_var);
@ -1596,7 +1586,7 @@ const ast::Var* ParserImpl::MakeVar(uint32_t id,
builtin::Access access,
const Type* storage_type,
const ast::Expression* initializer,
AttributeList decorations) {
Attributes attrs) {
if (storage_type == nullptr) {
Fail() << "internal error: can't make ast::Variable for null type";
return nullptr;
@ -1608,14 +1598,14 @@ const ast::Var* ParserImpl::MakeVar(uint32_t id,
address_space = builtin::AddressSpace::kUndefined;
}
if (!ConvertDecorationsForVariable(id, &storage_type, &decorations,
if (!ConvertDecorationsForVariable(id, &storage_type, attrs,
address_space != builtin::AddressSpace::kPrivate)) {
return nullptr;
}
auto sym = builder_.Symbols().Register(namer_.Name(id));
return builder_.Var(Source{}, sym, storage_type->Build(builder_), address_space, access,
initializer, decorations);
initializer, std::move(attrs.list));
}
const ast::Let* ParserImpl::MakeLet(uint32_t id,
@ -1628,28 +1618,27 @@ const ast::Let* ParserImpl::MakeLet(uint32_t id,
const ast::Override* ParserImpl::MakeOverride(uint32_t id,
const Type* type,
const ast::Expression* initializer,
AttributeList decorations) {
if (!ConvertDecorationsForVariable(id, &type, &decorations, false)) {
Attributes attrs) {
if (!ConvertDecorationsForVariable(id, &type, attrs, false)) {
return nullptr;
}
auto sym = builder_.Symbols().Register(namer_.Name(id));
return builder_.Override(Source{}, sym, type->Build(builder_), initializer, decorations);
return builder_.Override(Source{}, sym, type->Build(builder_), initializer,
std::move(attrs.list));
}
const ast::Parameter* ParserImpl::MakeParameter(uint32_t id,
const Type* type,
AttributeList decorations) {
if (!ConvertDecorationsForVariable(id, &type, &decorations, false)) {
const ast::Parameter* ParserImpl::MakeParameter(uint32_t id, const Type* type, Attributes attrs) {
if (!ConvertDecorationsForVariable(id, &type, attrs, false)) {
return nullptr;
}
auto sym = builder_.Symbols().Register(namer_.Name(id));
return builder_.Param(Source{}, sym, type->Build(builder_), decorations);
return builder_.Param(Source{}, sym, type->Build(builder_), attrs.list);
}
bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
const Type** store_type,
AttributeList* decorations,
Attributes& attrs,
bool transfer_pipeline_io) {
DecorationList non_builtin_pipeline_decorations;
for (auto& deco : GetDecorationsFor(id)) {
@ -1709,7 +1698,7 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
return false;
}
if (transfer_pipeline_io) {
decorations->Push(create<ast::BuiltinAttribute>(Source{}, ast_builtin));
attrs.Add(builder_, Source{}, ast_builtin);
}
}
if (transfer_pipeline_io && IsPipelineDecoration(deco)) {
@ -1720,19 +1709,18 @@ bool ParserImpl::ConvertDecorationsForVariable(uint32_t id,
return Fail() << "malformed DescriptorSet decoration on ID " << id
<< ": has no operand";
}
decorations->Push(builder_.Group(Source{}, AInt(deco[1])));
attrs.Add(builder_.Group(Source{}, AInt(deco[1])));
}
if (deco[0] == uint32_t(spv::Decoration::Binding)) {
if (deco.size() == 1) {
return Fail() << "malformed Binding decoration on ID " << id << ": has no operand";
}
decorations->Push(builder_.Binding(Source{}, AInt(deco[1])));
attrs.Add(builder_.Binding(Source{}, AInt(deco[1])));
}
}
if (transfer_pipeline_io) {
if (!ConvertPipelineDecorations(*store_type, non_builtin_pipeline_decorations,
decorations)) {
if (!ConvertPipelineDecorations(*store_type, non_builtin_pipeline_decorations, attrs)) {
return false;
}
}
@ -1753,11 +1741,11 @@ DecorationList ParserImpl::GetMemberPipelineDecorations(const Struct& struct_typ
return result;
}
void ParserImpl::SetLocation(AttributeList* attributes, const ast::Attribute* replacement) {
void ParserImpl::SetLocation(Attributes& attributes, const ast::Attribute* replacement) {
if (!replacement) {
return;
}
for (auto*& attribute : *attributes) {
for (auto*& attribute : attributes.list) {
if (attribute->Is<ast::LocationAttribute>()) {
// Replace this location attribute with the replacement.
// The old one doesn't leak because it's kept in the builder's AST node
@ -1767,13 +1755,13 @@ void ParserImpl::SetLocation(AttributeList* attributes, const ast::Attribute* re
}
}
// The list didn't have a location. Add it.
attributes->Push(replacement);
attributes.Add(replacement);
return;
}
bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
const DecorationList& decorations,
AttributeList* attributes) {
Attributes& attributes) {
// Vulkan defaults to perspective-correct interpolation.
builtin::InterpolationType type = builtin::InterpolationType::kPerspective;
builtin::InterpolationSampling sampling = builtin::InterpolationSampling::kUndefined;
@ -1783,8 +1771,8 @@ bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
switch (static_cast<spv::Decoration>(deco[0])) {
case spv::Decoration::Location:
if (deco.size() != 2) {
return Fail() << "malformed Location decoration on ID requires one "
"literal operand";
return Fail()
<< "malformed Location decoration on ID requires one literal operand";
}
SetLocation(attributes, builder_.Location(AInt(deco[1])));
if (store_type->IsIntegerScalarOrVector()) {
@ -1821,8 +1809,7 @@ bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
}
}
if (type == builtin::InterpolationType::kFlat &&
!ast::HasAttribute<ast::LocationAttribute>(*attributes)) {
if (type == builtin::InterpolationType::kFlat && !attributes.Has<ast::LocationAttribute>()) {
// WGSL requires that '@interpolate(flat)' needs to be paired with '@location', however
// SPIR-V requires all fragment shader integer Inputs are 'flat'. If the decorations do not
// contain a spv::Decoration::Location, then make this perspective.
@ -1834,7 +1821,7 @@ bool ParserImpl::ConvertPipelineDecorations(const Type* store_type,
sampling == builtin::InterpolationSampling::kUndefined) {
// This is the default. Don't add a decoration.
} else {
attributes->Push(create<ast::InterpolateAttribute>(type, sampling));
attributes.Add(create<ast::InterpolateAttribute>(type, sampling));
}
return success();

View File

@ -37,6 +37,7 @@ TINT_END_DISABLE_WARNING(NEWLINE_EOF);
#include "src/tint/program_builder.h"
#include "src/tint/reader/reader.h"
#include "src/tint/reader/spirv/attributes.h"
#include "src/tint/reader/spirv/entry_point_info.h"
#include "src/tint/reader/spirv/enum_converter.h"
#include "src/tint/reader/spirv/namer.h"
@ -122,7 +123,6 @@ struct WorkgroupSizeInfo {
/// Parser implementation for SPIR-V.
class ParserImpl : Reader {
using AttributeList = utils::Vector<const ast::Attribute*, 8>;
using ExpressionList = utils::Vector<const ast::Expression*, 8>;
public:
@ -261,7 +261,7 @@ class ParserImpl : Reader {
/// @returns false when the variable should not be emitted as a variable
bool ConvertDecorationsForVariable(uint32_t id,
const Type** store_type,
AttributeList* attributes,
Attributes& attributes,
bool transfer_pipeline_io);
/// Converts SPIR-V decorations for pipeline IO into AST decorations.
@ -271,15 +271,15 @@ class ParserImpl : Reader {
/// @returns false if conversion fails
bool ConvertPipelineDecorations(const Type* store_type,
const DecorationList& decorations,
AttributeList* attributes);
Attributes& attributes);
/// Updates the attribute list, placing a non-null location decoration into
/// the list, replacing an existing one if it exists. Does nothing if the
/// replacement is nullptr.
/// Assumes the list contains at most one Location decoration.
/// @param decos the attribute list to modify
/// @param attributes the attribute list to modify
/// @param replacement the location decoration to place into the list
void SetLocation(AttributeList* decos, const ast::Attribute* replacement);
void SetLocation(Attributes& attributes, const ast::Attribute* replacement);
/// Converts a SPIR-V struct member decoration into a number of AST
/// decorations. If the decoration is recognized but deliberately dropped,
@ -290,10 +290,10 @@ class ParserImpl : Reader {
/// @param member_ty the type of the member
/// @param decoration an encoded SPIR-V Decoration
/// @returns the AST decorations
AttributeList ConvertMemberDecoration(uint32_t struct_type_id,
uint32_t member_index,
const Type* member_ty,
const Decoration& decoration);
Attributes ConvertMemberDecoration(uint32_t struct_type_id,
uint32_t member_index,
const Type* member_ty,
const Decoration& decoration);
/// Returns a string for the given type. If the type ID is invalid,
/// then the resulting string only names the type ID.
@ -433,7 +433,7 @@ class ParserImpl : Reader {
/// @param access the access
/// @param storage_type the storage type of the variable
/// @param initializer the variable initializer
/// @param decorations the variable decorations
/// @param attributes the variable attributes
/// @returns a new Variable node, or null in the ignorable variable case and
/// in the error case
const ast::Var* MakeVar(uint32_t id,
@ -441,7 +441,7 @@ class ParserImpl : Reader {
builtin::Access access,
const Type* storage_type,
const ast::Expression* initializer,
AttributeList decorations);
Attributes attributes);
/// Creates an AST 'let' node for a SPIR-V ID, including any attached decorations,.
/// @param id the SPIR-V result ID
@ -454,20 +454,20 @@ class ParserImpl : Reader {
/// @param id the SPIR-V result ID
/// @param type the type of the variable
/// @param initializer the variable initializer
/// @param decorations the variable decorations
/// @param attributes the variable attributes
/// @returns the AST 'override' node
const ast::Override* MakeOverride(uint32_t id,
const Type* type,
const ast::Expression* initializer,
AttributeList decorations);
Attributes attributes);
/// Creates an AST parameter node for a SPIR-V ID, including any attached decorations, unless
/// it's an ignorable builtin variable.
/// @param id the SPIR-V result ID
/// @param type the type of the parameter
/// @param decorations the parameter decorations
/// @param attributes the parameter attributes
/// @returns the AST parameter node
const ast::Parameter* MakeParameter(uint32_t id, const Type* type, AttributeList decorations);
const ast::Parameter* MakeParameter(uint32_t id, const Type* type, Attributes attributes);
/// Returns true if a constant expression can be generated.
/// @param id the SPIR-V ID of the value

View File

@ -24,7 +24,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_IsEmpty) {
auto p = parser(std::vector<uint32_t>{});
auto result = p->ConvertMemberDecoration(1, 1, nullptr, {});
EXPECT_TRUE(result.IsEmpty());
EXPECT_TRUE(result.list.IsEmpty());
EXPECT_THAT(p->error(), Eq("malformed SPIR-V decoration: it's empty"));
}
@ -32,7 +32,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_OffsetWithoutOperand) {
auto p = parser(std::vector<uint32_t>{});
auto result = p->ConvertMemberDecoration(12, 13, nullptr, {uint32_t(spv::Decoration::Offset)});
EXPECT_TRUE(result.IsEmpty());
EXPECT_TRUE(result.list.IsEmpty());
EXPECT_THAT(p->error(), Eq("malformed Offset decoration: expected 1 literal "
"operand, has 0: member 13 of SPIR-V type 12"));
}
@ -42,7 +42,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_OffsetWithTooManyOperands) {
auto result =
p->ConvertMemberDecoration(12, 13, nullptr, {uint32_t(spv::Decoration::Offset), 3, 4});
EXPECT_TRUE(result.IsEmpty());
EXPECT_TRUE(result.list.IsEmpty());
EXPECT_THAT(p->error(), Eq("malformed Offset decoration: expected 1 literal "
"operand, has 2: member 13 of SPIR-V type 12"));
}
@ -51,9 +51,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Offset) {
auto p = parser(std::vector<uint32_t>{});
auto result = p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::Offset), 8});
ASSERT_FALSE(result.IsEmpty());
EXPECT_TRUE(result[0]->Is<ast::StructMemberOffsetAttribute>());
auto* offset_deco = result[0]->As<ast::StructMemberOffsetAttribute>();
ASSERT_FALSE(result.list.IsEmpty());
EXPECT_TRUE(result.list[0]->Is<ast::StructMemberOffsetAttribute>());
auto* offset_deco = result.list[0]->As<ast::StructMemberOffsetAttribute>();
ASSERT_NE(offset_deco, nullptr);
ASSERT_TRUE(offset_deco->expr->Is<ast::IntLiteralExpression>());
EXPECT_EQ(offset_deco->expr->As<ast::IntLiteralExpression>()->value, 8u);
@ -67,7 +67,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x2_Stride_Natural) {
spirv::Matrix matrix(&f32, 2, 2);
auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 8});
EXPECT_TRUE(result.IsEmpty());
EXPECT_TRUE(result.list.IsEmpty());
EXPECT_TRUE(p->error().empty());
}
@ -78,9 +78,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x2_Stride_Custom) {
spirv::Matrix matrix(&f32, 2, 2);
auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16});
ASSERT_FALSE(result.IsEmpty());
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result[0]->As<ast::StrideAttribute>();
ASSERT_FALSE(result.list.IsEmpty());
EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
ASSERT_NE(stride_deco, nullptr);
EXPECT_EQ(stride_deco->stride, 16u);
EXPECT_TRUE(p->error().empty());
@ -93,7 +93,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x4_Stride_Natural) {
spirv::Matrix matrix(&f32, 2, 4);
auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16});
EXPECT_TRUE(result.IsEmpty());
EXPECT_TRUE(result.list.IsEmpty());
EXPECT_TRUE(p->error().empty());
}
@ -104,9 +104,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x4_Stride_Custom) {
spirv::Matrix matrix(&f32, 2, 4);
auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 64});
ASSERT_FALSE(result.IsEmpty());
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result[0]->As<ast::StrideAttribute>();
ASSERT_FALSE(result.list.IsEmpty());
EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
ASSERT_NE(stride_deco, nullptr);
EXPECT_EQ(stride_deco->stride, 64u);
EXPECT_TRUE(p->error().empty());
@ -119,9 +119,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x3_Stride_Custom) {
spirv::Matrix matrix(&f32, 2, 3);
auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 32});
ASSERT_FALSE(result.IsEmpty());
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result[0]->As<ast::StrideAttribute>();
ASSERT_FALSE(result.list.IsEmpty());
EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
ASSERT_NE(stride_deco, nullptr);
EXPECT_EQ(stride_deco->stride, 32u);
EXPECT_TRUE(p->error().empty());
@ -135,7 +135,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_RelaxedPrecision) {
auto result =
p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::RelaxedPrecision)});
EXPECT_TRUE(result.IsEmpty());
EXPECT_TRUE(result.list.IsEmpty());
EXPECT_TRUE(p->error().empty());
}
@ -143,7 +143,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_UnhandledDecoration) {
auto p = parser(std::vector<uint32_t>{});
auto result = p->ConvertMemberDecoration(12, 13, nullptr, {12345678});
EXPECT_TRUE(result.IsEmpty());
EXPECT_TRUE(result.list.IsEmpty());
EXPECT_THAT(p->error(), Eq("unhandled member decoration: 12345678 on member "
"13 of SPIR-V type 12"));
}

View File

@ -2861,7 +2861,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_Direct) {
const auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : i32;
var<private> position : vec4<f32>;
var<private> position_1 : vec4<f32>;
fn main_1() {
let x_2 : i32 = x_1;
@ -2870,14 +2870,14 @@ fn main_1() {
struct main_out {
@builtin(position)
position_1 : vec4<f32>,
position_1_1 : vec4<f32>,
}
@vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = bitcast<i32>(x_1_param);
main_1();
return main_out(position);
return main_out(position_1);
}
)";
EXPECT_EQ(module_str, expected) << module_str;
@ -2898,7 +2898,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_CopyObject) {
const auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : i32;
var<private> position : vec4<f32>;
var<private> position_1 : vec4<f32>;
fn main_1() {
let x_14 : ptr<private, i32> = &(x_1);
@ -2908,14 +2908,14 @@ fn main_1() {
struct main_out {
@builtin(position)
position_1 : vec4<f32>,
position_1_1 : vec4<f32>,
}
@vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = bitcast<i32>(x_1_param);
main_1();
return main_out(position);
return main_out(position_1);
}
)";
EXPECT_EQ(module_str, expected) << module_str;
@ -2936,7 +2936,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_I32_Load_AccessChain) {
const auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : i32;
var<private> position : vec4<f32>;
var<private> position_1 : vec4<f32>;
fn main_1() {
let x_2 : i32 = x_1;
@ -2945,14 +2945,14 @@ fn main_1() {
struct main_out {
@builtin(position)
position_1 : vec4<f32>,
position_1_1 : vec4<f32>,
}
@vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = bitcast<i32>(x_1_param);
main_1();
return main_out(position);
return main_out(position_1);
}
)";
EXPECT_EQ(module_str, expected) << module_str;
@ -2995,7 +2995,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_Direct) {
const auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : u32;
var<private> position : vec4<f32>;
var<private> position_1 : vec4<f32>;
fn main_1() {
let x_2 : u32 = x_1;
@ -3004,14 +3004,14 @@ fn main_1() {
struct main_out {
@builtin(position)
position_1 : vec4<f32>,
position_1_1 : vec4<f32>,
}
@vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = x_1_param;
main_1();
return main_out(position);
return main_out(position_1);
}
)";
EXPECT_EQ(module_str, expected);
@ -3032,7 +3032,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_CopyObject) {
const auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : u32;
var<private> position : vec4<f32>;
var<private> position_1 : vec4<f32>;
fn main_1() {
let x_14 : ptr<private, u32> = &(x_1);
@ -3042,14 +3042,14 @@ fn main_1() {
struct main_out {
@builtin(position)
position_1 : vec4<f32>,
position_1_1 : vec4<f32>,
}
@vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = x_1_param;
main_1();
return main_out(position);
return main_out(position_1);
}
)";
EXPECT_EQ(module_str, expected);
@ -3070,7 +3070,7 @@ TEST_F(SpvModuleScopeVarParserTest, InstanceIndex_U32_Load_AccessChain) {
const auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : u32;
var<private> position : vec4<f32>;
var<private> position_1 : vec4<f32>;
fn main_1() {
let x_2 : u32 = x_1;
@ -3079,14 +3079,14 @@ fn main_1() {
struct main_out {
@builtin(position)
position_1 : vec4<f32>,
position_1_1 : vec4<f32>,
}
@vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = x_1_param;
main_1();
return main_out(position);
return main_out(position_1);
}
)";
EXPECT_EQ(module_str, expected);

View File

@ -1125,23 +1125,6 @@ Expect<builtin::InterpolationType> ParserImpl::expect_interpolation_type_name()
builtin::kInterpolationTypeStrings);
}
// builtin_value_name
// : frag_depth
// | front_facing
// | global_invocation_id
// | instance_index
// | local_invocation_id
// | local_invocation_index
// | num_workgroups
// | position
// | sample_index
// | sample_mask
// | vertex_index
// | workgroup_id
Expect<builtin::BuiltinValue> ParserImpl::expect_builtin() {
return expect_enum("builtin", builtin::ParseBuiltinValue, builtin::kBuiltinValueStrings);
}
// compound_statement
// : attribute* BRACE_LEFT statement* BRACE_RIGHT
Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement(std::string_view use) {
@ -3020,7 +3003,7 @@ Maybe<const ast::Attribute*> ParserImpl::attribute() {
if (t == "builtin") {
return expect_paren_block("builtin attribute", [&]() -> Result {
auto builtin = expect_builtin();
auto builtin = expect_expression("builtin name");
if (builtin.errored) {
return Failure::kErrored;
}

View File

@ -462,10 +462,6 @@ class ParserImpl {
/// value type name.
/// @returns the parsed type name
Expect<builtin::InterpolationType> expect_interpolation_type_name();
/// Parses a builtin identifier, erroring if the next token does not match a
/// valid builtin name.
/// @returns the parsed builtin.
Expect<builtin::BuiltinValue> expect_builtin();
/// Parses a `compound_statement` grammar element, erroring on parse failure.
/// @param use a description of what was being parsed if an error was raised
/// @returns the parsed statements

View File

@ -969,25 +969,6 @@ TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBuiltinMissingRParen) {
)");
}
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBuiltinInvalidIdentifer) {
EXPECT("@builtin(1) var i : i32;",
R"(test.wgsl:1:10 error: expected builtin
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id'
@builtin(1) var i : i32;
^
)");
}
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBuiltinInvalidValue) {
EXPECT("@builtin(frag_d3pth) var i : i32;",
R"(test.wgsl:1:10 error: expected builtin
Did you mean 'frag_depth'?
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id'
@builtin(frag_d3pth) var i : i32;
^^^^^^^^^^
)");
}
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBindingMissingLParen) {
EXPECT("@binding 1) var i : i32;",
R"(test.wgsl:1:10 error: expected '(' for binding attribute

View File

@ -102,7 +102,8 @@ TEST_F(ParserImplTest, ParamList_Attributes) {
auto attrs_0 = e.value[0]->attributes;
ASSERT_EQ(attrs_0.Length(), 1u);
EXPECT_TRUE(attrs_0[0]->Is<ast::BuiltinAttribute>());
EXPECT_EQ(attrs_0[0]->As<ast::BuiltinAttribute>()->builtin, builtin::BuiltinValue::kPosition);
ast::CheckIdentifier(p->builder().Symbols(), attrs_0[0]->As<ast::BuiltinAttribute>()->builtin,
"position");
ASSERT_EQ(e.value[0]->source.range.begin.line, 1u);
ASSERT_EQ(e.value[0]->source.range.begin.column, 20u);

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/test_helper.h"
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
namespace tint::reader::wgsl {
@ -38,7 +39,8 @@ TEST_F(ParserImplTest, AttributeList_Parses) {
EXPECT_EQ(exp->value, 4u);
ASSERT_TRUE(attr_1->Is<ast::BuiltinAttribute>());
EXPECT_EQ(attr_1->As<ast::BuiltinAttribute>()->builtin, builtin::BuiltinValue::kPosition);
ast::CheckIdentifier(p->builder().Symbols(), attr_1->As<ast::BuiltinAttribute>()->builtin,
"position");
}
TEST_F(ParserImplTest, AttributeList_Invalid) {
@ -51,27 +53,5 @@ TEST_F(ParserImplTest, AttributeList_Invalid) {
EXPECT_EQ(p->error(), R"(1:2: expected attribute)");
}
TEST_F(ParserImplTest, AttributeList_InvalidValue) {
auto p = parser("@builtin(invalid)");
auto attrs = p->attribute_list();
EXPECT_TRUE(p->has_error());
EXPECT_TRUE(attrs.errored);
EXPECT_FALSE(attrs.matched);
EXPECT_TRUE(attrs.value.IsEmpty());
EXPECT_EQ(p->error(), R"(1:10: expected builtin
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
TEST_F(ParserImplTest, AttributeList_InvalidValueSuggest) {
auto p = parser("@builtin(instanceindex)");
auto attrs = p->attribute_list();
EXPECT_TRUE(p->has_error());
EXPECT_TRUE(attrs.errored);
EXPECT_FALSE(attrs.matched);
EXPECT_TRUE(attrs.value.IsEmpty());
EXPECT_EQ(p->error(), R"(1:10: expected builtin
Did you mean 'instance_index'?
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
} // namespace
} // namespace tint::reader::wgsl

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/ast/test_helper.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/reader/wgsl/parser_impl_test_helper.h"
namespace tint::reader::wgsl {
@ -215,20 +217,11 @@ TEST_F(ParserImplTest, Attribute_Location_MissingInvalid) {
EXPECT_EQ(p->error(), "1:10: expected location expression");
}
struct BuiltinData {
const char* input;
builtin::BuiltinValue result;
};
inline std::ostream& operator<<(std::ostream& out, BuiltinData data) {
out << std::string(data.input);
return out;
}
class BuiltinTest : public ParserImplTestWithParam<BuiltinData> {};
class BuiltinTest : public ParserImplTestWithParam<builtin::BuiltinValue> {};
TEST_P(BuiltinTest, Attribute_Builtin) {
auto params = GetParam();
auto p = parser(std::string("builtin(") + params.input + ")");
auto str = utils::ToString(GetParam());
auto p = parser("builtin(" + str + ")");
auto attr = p->attribute();
EXPECT_TRUE(attr.matched);
@ -240,11 +233,11 @@ TEST_P(BuiltinTest, Attribute_Builtin) {
ASSERT_TRUE(var_attr->Is<ast::BuiltinAttribute>());
auto* builtin = var_attr->As<ast::BuiltinAttribute>();
EXPECT_EQ(builtin->builtin, params.result);
ast::CheckIdentifier(p->builder().Symbols(), builtin->builtin, str);
}
TEST_P(BuiltinTest, Attribute_Builtin_TrailingComma) {
auto params = GetParam();
auto p = parser(std::string("builtin(") + params.input + ",)");
auto str = utils::ToString(GetParam());
auto p = parser("builtin(" + str + ",)");
auto attr = p->attribute();
EXPECT_TRUE(attr.matched);
@ -256,24 +249,22 @@ TEST_P(BuiltinTest, Attribute_Builtin_TrailingComma) {
ASSERT_TRUE(var_attr->Is<ast::BuiltinAttribute>());
auto* builtin = var_attr->As<ast::BuiltinAttribute>();
EXPECT_EQ(builtin->builtin, params.result);
ast::CheckIdentifier(p->builder().Symbols(), builtin->builtin, str);
}
INSTANTIATE_TEST_SUITE_P(
ParserImplTest,
BuiltinTest,
testing::Values(BuiltinData{"position", builtin::BuiltinValue::kPosition},
BuiltinData{"vertex_index", builtin::BuiltinValue::kVertexIndex},
BuiltinData{"instance_index", builtin::BuiltinValue::kInstanceIndex},
BuiltinData{"front_facing", builtin::BuiltinValue::kFrontFacing},
BuiltinData{"frag_depth", builtin::BuiltinValue::kFragDepth},
BuiltinData{"local_invocation_id", builtin::BuiltinValue::kLocalInvocationId},
BuiltinData{"local_invocation_index",
builtin::BuiltinValue::kLocalInvocationIndex},
BuiltinData{"global_invocation_id", builtin::BuiltinValue::kGlobalInvocationId},
BuiltinData{"workgroup_id", builtin::BuiltinValue::kWorkgroupId},
BuiltinData{"num_workgroups", builtin::BuiltinValue::kNumWorkgroups},
BuiltinData{"sample_index", builtin::BuiltinValue::kSampleIndex},
BuiltinData{"sample_mask", builtin::BuiltinValue::kSampleMask}));
INSTANTIATE_TEST_SUITE_P(ParserImplTest,
BuiltinTest,
testing::Values(builtin::BuiltinValue::kPosition,
builtin::BuiltinValue::kVertexIndex,
builtin::BuiltinValue::kInstanceIndex,
builtin::BuiltinValue::kFrontFacing,
builtin::BuiltinValue::kFragDepth,
builtin::BuiltinValue::kLocalInvocationId,
builtin::BuiltinValue::kLocalInvocationIndex,
builtin::BuiltinValue::kGlobalInvocationId,
builtin::BuiltinValue::kWorkgroupId,
builtin::BuiltinValue::kNumWorkgroups,
builtin::BuiltinValue::kSampleIndex,
builtin::BuiltinValue::kSampleMask));
TEST_F(ParserImplTest, Attribute_Builtin_MissingLeftParen) {
auto p = parser("builtin position)");
@ -302,42 +293,7 @@ TEST_F(ParserImplTest, Attribute_Builtin_MissingValue) {
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), R"(1:9: expected builtin
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
TEST_F(ParserImplTest, Attribute_Builtin_InvalidValue) {
auto p = parser("builtin(other_thingy)");
auto attr = p->attribute();
EXPECT_FALSE(attr.matched);
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), R"(1:9: expected builtin
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
TEST_F(ParserImplTest, Attribute_Builtin_InvalidValueSuggest) {
auto p = parser("builtin(front_face)");
auto attr = p->attribute();
EXPECT_FALSE(attr.matched);
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), R"(1:9: expected builtin
Did you mean 'front_facing'?
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
TEST_F(ParserImplTest, Attribute_Builtin_MissingInvalid) {
auto p = parser("builtin(3)");
auto attr = p->attribute();
EXPECT_FALSE(attr.matched);
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), R"(1:9: expected builtin
Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
EXPECT_EQ(p->error(), R"(1:9: expected expression for builtin name)");
}
TEST_F(ParserImplTest, Attribute_Interpolate_Flat) {

View File

@ -13,6 +13,7 @@
// limitations under the License.
#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/resolver/resolver.h"
#include "src/tint/resolver/resolver_test_helper.h"
#include "src/tint/transform/add_block_attribute.h"
@ -221,9 +222,9 @@ TEST_P(ComputeShaderParameterAttributeTest, IsValid) {
} else {
EXPECT_FALSE(r()->Resolve());
if (params.kind == AttributeKind::kBuiltin) {
EXPECT_EQ(r()->error(),
"12:34 error: builtin(position) cannot be used in input of "
"compute pipeline stage");
EXPECT_EQ(
r()->error(),
R"(12:34 error: @builtin(position) cannot be used in input of compute pipeline stage)");
} else if (params.kind == AttributeKind::kInterpolate ||
params.kind == AttributeKind::kLocation ||
params.kind == AttributeKind::kInvariant) {
@ -314,9 +315,9 @@ TEST_P(VertexShaderParameterAttributeTest, IsValid) {
} else {
EXPECT_FALSE(r()->Resolve());
if (params.kind == AttributeKind::kBuiltin) {
EXPECT_EQ(r()->error(),
"12:34 error: builtin(position) cannot be used in input of "
"vertex pipeline stage");
EXPECT_EQ(
r()->error(),
R"(12:34 error: @builtin(position) cannot be used in input of vertex pipeline stage)");
} else if (params.kind == AttributeKind::kInvariant) {
EXPECT_EQ(r()->error(),
"12:34 error: invariant attribute must only be applied to a "
@ -362,9 +363,9 @@ TEST_P(ComputeShaderReturnTypeAttributeTest, IsValid) {
} else {
EXPECT_FALSE(r()->Resolve());
if (params.kind == AttributeKind::kBuiltin) {
EXPECT_EQ(r()->error(),
"12:34 error: builtin(position) cannot be used in output of "
"compute pipeline stage");
EXPECT_EQ(
r()->error(),
R"(12:34 error: @builtin(position) cannot be used in output of compute pipeline stage)");
} else if (params.kind == AttributeKind::kInterpolate ||
params.kind == AttributeKind::kLocation ||
params.kind == AttributeKind::kInvariant) {
@ -411,21 +412,20 @@ TEST_P(FragmentShaderReturnTypeAttributeTest, IsValid) {
} else {
EXPECT_FALSE(r()->Resolve());
if (params.kind == AttributeKind::kBuiltin) {
EXPECT_EQ(r()->error(),
"12:34 error: builtin(position) cannot be used in output of "
"fragment pipeline stage");
EXPECT_EQ(
r()->error(),
R"(12:34 error: @builtin(position) cannot be used in output of fragment pipeline stage)");
} else if (params.kind == AttributeKind::kInvariant) {
EXPECT_EQ(r()->error(),
"12:34 error: invariant attribute must only be applied to a "
"position builtin");
EXPECT_EQ(
r()->error(),
R"(12:34 error: invariant attribute must only be applied to a position builtin)");
} else if (params.kind == AttributeKind::kLocation) {
EXPECT_EQ(r()->error(),
"34:56 error: duplicate location attribute\n"
"12:34 note: first attribute declared here");
R"(34:56 error: duplicate location attribute
12:34 note: first attribute declared here)");
} else {
EXPECT_EQ(r()->error(),
"12:34 error: attribute is not valid for entry point return "
"types");
R"(12:34 error: attribute is not valid for entry point return types)");
}
}
}
@ -470,12 +470,11 @@ TEST_P(VertexShaderReturnTypeAttributeTest, IsValid) {
EXPECT_FALSE(r()->Resolve());
if (params.kind == AttributeKind::kLocation) {
EXPECT_EQ(r()->error(),
"34:56 error: multiple entry point IO attributes\n"
"12:34 note: previously consumed location(1)");
R"(34:56 error: multiple entry point IO attributes
12:34 note: previously consumed @location)");
} else {
EXPECT_EQ(r()->error(),
"12:34 error: attribute is not valid for entry point return "
"types");
R"(12:34 error: attribute is not valid for entry point return types)");
}
}
}

View File

@ -13,6 +13,7 @@
// limitations under the License.
#include "src/tint/ast/call_statement.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/resolver/resolver_test_helper.h"
using namespace tint::number_suffixes; // NOLINT
@ -145,7 +146,7 @@ TEST_P(ResolverBuiltinsStageTest, All_input) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
std::stringstream err;
err << "12:34 error: builtin(" << params.builtin << ")";
err << "12:34 error: @builtin(" << params.builtin << ")";
err << " cannot be used in input of " << params.stage << " pipeline stage";
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), err.str());
@ -178,9 +179,9 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInput_Fail) {
Location(0_a),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: builtin(frag_depth) cannot be used in input of "
"fragment pipeline stage");
EXPECT_EQ(
r()->error(),
"12:34 error: @builtin(frag_depth) cannot be used in input of fragment pipeline stage");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) {
@ -214,7 +215,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: builtin(frag_depth) cannot be used in input of "
"12:34 error: @builtin(frag_depth) cannot be used in input of "
"fragment pipeline stage\n"
"note: while analyzing entry point 'fragShader'");
}
@ -272,7 +273,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_Struct_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
@ -288,7 +289,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
@ -317,7 +318,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(frag_depth) must be 'f32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
@ -346,7 +347,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_mask) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
@ -361,7 +362,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_mask) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
@ -387,7 +388,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
Location(0_a),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_mask) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
@ -416,7 +417,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_index) must be 'u32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
@ -442,7 +443,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
Location(0_a),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_index) must be 'u32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(sample_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
@ -468,7 +469,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
Location(0_a),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
@ -487,7 +488,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
Builtin(Source{{12, 34}}, builtin::BuiltinValue::kFragDepth),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(frag_depth) must be 'f32'");
}
TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
@ -512,7 +513,7 @@ TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
Builtin(builtin::BuiltinValue::kPosition),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(vertex_index) must be 'u32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(vertex_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
@ -537,7 +538,7 @@ TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
Builtin(builtin::BuiltinValue::kPosition),
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(instance_index) must be 'u32'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(instance_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltin_Pass) {
@ -657,7 +658,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_WorkGroupIdNotVec3U32) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(workgroup_id) must be "
"12:34 error: store type of @builtin(workgroup_id) must be "
"'vec3<u32>'");
}
@ -672,7 +673,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_NumWorkgroupsNotVec3U32) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(num_workgroups) must be "
"12:34 error: store type of @builtin(num_workgroups) must be "
"'vec3<u32>'");
}
@ -687,7 +688,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_GlobalInvocationNotVec3U32
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(global_invocation_id) must be "
"12:34 error: store type of @builtin(global_invocation_id) must be "
"'vec3<u32>'");
}
@ -703,7 +704,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationIndexNotU32
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(local_invocation_index) must be "
"12:34 error: store type of @builtin(local_invocation_index) must be "
"'u32'");
}
@ -718,7 +719,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationNotVec3U32)
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: store type of builtin(local_invocation_id) must be "
"12:34 error: store type of @builtin(local_invocation_id) must be "
"'vec3<u32>'");
}
@ -785,7 +786,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsNotBool_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(front_facing) must be 'bool'");
}
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
@ -814,7 +815,50 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
EXPECT_EQ(r()->error(), "12:34 error: store type of @builtin(front_facing) must be 'bool'");
}
// TODO(crbug.com/tint/1846): This isn't a validation test, but this sits next to other @builtin
// tests. Clean this up.
TEST_F(ResolverBuiltinsValidationTest, StructMemberAttributeMapsToSemBuiltinEnum) {
// struct S {
// @builtin(front_facing) b : bool;
// };
// @fragment
// fn f(s : S) {}
auto* builtin = Builtin(builtin::BuiltinValue::kFrontFacing);
auto* s = Structure("S", utils::Vector{
Member("f", ty.bool_(), utils::Vector{builtin}),
});
Func("f", utils::Vector{Param("b", ty.Of(s))}, ty.void_(), utils::Empty,
utils::Vector{
Stage(ast::PipelineStage::kFragment),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* builtin_expr = Sem().Get(builtin);
ASSERT_NE(builtin_expr, nullptr);
EXPECT_EQ(builtin_expr->Value(), builtin::BuiltinValue::kFrontFacing);
}
// TODO(crbug.com/tint/1846): This isn't a validation test, but this sits next to other @builtin
// tests. Clean this up.
TEST_F(ResolverBuiltinsValidationTest, ParamAttributeMapsToSemBuiltinEnum) {
// @fragment
// fn f(@builtin(front_facing) b : bool) {}
auto* builtin = Builtin(builtin::BuiltinValue::kFrontFacing);
Func("f", utils::Vector{Param("b", ty.bool_(), utils::Vector{builtin})}, ty.void_(),
utils::Empty,
utils::Vector{
Stage(ast::PipelineStage::kFragment),
});
EXPECT_TRUE(r()->Resolve()) << r()->error();
auto* builtin_expr = Sem().Get(builtin);
ASSERT_NE(builtin_expr, nullptr);
EXPECT_EQ(builtin_expr->Value(), builtin::BuiltinValue::kFrontFacing);
}
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) {

View File

@ -55,6 +55,7 @@
#include "src/tint/ast/while_statement.h"
#include "src/tint/ast/workgroup_attribute.h"
#include "src/tint/builtin/builtin.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/scope_stack.h"
#include "src/tint/sem/builtin.h"
#include "src/tint/symbol_table.h"
@ -424,6 +425,10 @@ class DependencyScanner {
TraverseValueExpression(binding->expr);
return true;
},
[&](const ast::BuiltinAttribute* builtin) {
TraverseExpression(builtin->builtin, "builtin", "references");
return true;
},
[&](const ast::GroupAttribute* group) {
TraverseValueExpression(group->expr);
return true;
@ -480,6 +485,11 @@ class DependencyScanner {
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(builtin_ty));
return;
}
if (auto builtin_val = builtin::ParseBuiltinValue(s);
builtin_val != builtin::BuiltinValue::kUndefined) {
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(builtin_val));
return;
}
if (auto addr = builtin::ParseAddressSpace(s);
addr != builtin::AddressSpace::kUndefined) {
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(addr));
@ -863,6 +873,9 @@ std::string ResolvedIdentifier::String(const SymbolTable& symbols, diag::List& d
if (auto builtin_ty = BuiltinType(); builtin_ty != builtin::Builtin::kUndefined) {
return "builtin type '" + utils::ToString(builtin_ty) + "'";
}
if (auto builtin_val = BuiltinValue(); builtin_val != builtin::BuiltinValue::kUndefined) {
return "builtin value '" + utils::ToString(builtin_val) + "'";
}
if (auto access = Access(); access != builtin::Access::kUndefined) {
return "access '" + utils::ToString(access) + "'";
}

View File

@ -21,6 +21,7 @@
#include "src/tint/ast/module.h"
#include "src/tint/builtin/access.h"
#include "src/tint/builtin/builtin.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/builtin/texel_format.h"
#include "src/tint/diagnostic/diagnostic.h"
#include "src/tint/sem/builtin_type.h"
@ -38,6 +39,7 @@ namespace tint::resolver {
/// - builtin::Access
/// - builtin::AddressSpace
/// - builtin::Builtin
/// - builtin::BuiltinValue
/// - builtin::TexelFormat
class ResolvedIdentifier {
public:
@ -95,8 +97,17 @@ class ResolvedIdentifier {
return builtin::Builtin::kUndefined;
}
/// @return the texel format if the ResolvedIdentifier holds builtin::TexelFormat, otherwise
/// builtin::TexelFormat::kUndefined
/// @return the builtin value if the ResolvedIdentifier holds builtin::BuiltinValue, otherwise
/// builtin::BuiltinValue::kUndefined
builtin::BuiltinValue BuiltinValue() const {
if (auto n = std::get_if<builtin::BuiltinValue>(&value_)) {
return *n;
}
return builtin::BuiltinValue::kUndefined;
}
/// @return the texel format if the ResolvedIdentifier holds type::TexelFormat, otherwise
/// type::TexelFormat::kUndefined
builtin::TexelFormat TexelFormat() const {
if (auto n = std::get_if<builtin::TexelFormat>(&value_)) {
return *n;
@ -133,6 +144,7 @@ class ResolvedIdentifier {
builtin::Access,
builtin::AddressSpace,
builtin::Builtin,
builtin::BuiltinValue,
builtin::TexelFormat>
value_;
};

View File

@ -1500,6 +1500,91 @@ INSTANTIATE_TEST_SUITE_P(Functions,
} // namespace resolve_to_address_space
////////////////////////////////////////////////////////////////////////////////
// Resolve to builtin::BuiltinValue tests
////////////////////////////////////////////////////////////////////////////////
namespace resolve_to_builtin_value {
using ResolverDependencyGraphResolveToBuiltinValue =
ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
TEST_P(ResolverDependencyGraphResolveToBuiltinValue, Resolve) {
const auto use = std::get<0>(GetParam());
const auto name = std::get<1>(GetParam());
const auto symbol = Symbols().New(name);
SymbolTestHelper helper(this);
auto* ident = helper.Add(use, symbol);
helper.Build();
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
EXPECT_EQ(resolved->BuiltinValue(), builtin::ParseBuiltinValue(name))
<< resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinValue, ShadowedByGlobalConst) {
const auto use = std::get<0>(GetParam());
const auto builtin = std::get<1>(GetParam());
const auto symbol = Symbols().New(utils::ToString(builtin));
SymbolTestHelper helper(this);
auto* decl = helper.Add(SymbolDeclKind::GlobalConst, symbol);
auto* ident = helper.Add(use, symbol);
helper.Build();
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinValue, ShadowedByStruct) {
const auto use = std::get<0>(GetParam());
const auto builtin = std::get<1>(GetParam());
const auto symbol = Symbols().New(utils::ToString(builtin));
SymbolTestHelper helper(this);
auto* decl = helper.Add(SymbolDeclKind::Struct, symbol);
auto* ident = helper.Add(use, symbol);
helper.Build();
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
TEST_P(ResolverDependencyGraphResolveToBuiltinValue, ShadowedByFunc) {
const auto use = std::get<0>(GetParam());
const auto builtin = std::get<1>(GetParam());
const auto symbol = Symbols().New(utils::ToString(builtin));
SymbolTestHelper helper(this);
auto* decl = helper.Add(SymbolDeclKind::Function, symbol);
auto* ident = helper.Add(use, symbol);
helper.Build();
auto resolved = Build().resolved_identifiers.Get(ident);
ASSERT_TRUE(resolved);
EXPECT_EQ(resolved->Node(), decl) << resolved->String(Symbols(), Diagnostics());
}
INSTANTIATE_TEST_SUITE_P(Types,
ResolverDependencyGraphResolveToBuiltinValue,
testing::Combine(testing::ValuesIn(kTypeUseKinds),
testing::ValuesIn(builtin::kBuiltinValueStrings)));
INSTANTIATE_TEST_SUITE_P(Values,
ResolverDependencyGraphResolveToBuiltinValue,
testing::Combine(testing::ValuesIn(kValueUseKinds),
testing::ValuesIn(builtin::kBuiltinValueStrings)));
INSTANTIATE_TEST_SUITE_P(Functions,
ResolverDependencyGraphResolveToBuiltinValue,
testing::Combine(testing::ValuesIn(kFuncUseKinds),
testing::ValuesIn(builtin::kBuiltinValueStrings)));
} // namespace resolve_to_builtin_value
////////////////////////////////////////////////////////////////////////////////
// Resolve to builtin::TexelFormat tests
////////////////////////////////////////////////////////////////////////////////
@ -1686,6 +1771,7 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
Param(Sym(), T,
utils::Vector{
Location(V), // Parameter attributes
Builtin(V),
}),
},
T, // Return type

View File

@ -16,6 +16,7 @@
#include "src/tint/ast/location_attribute.h"
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/stage_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/resolver/resolver.h"
#include "src/tint/resolver/resolver_test_helper.h"
@ -94,7 +95,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Missing) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: missing entry point IO attribute on return type");
EXPECT_EQ(r()->error(), R"(12:34 error: missing entry point IO attribute on return type)");
}
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Multiple) {
@ -116,7 +117,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Multiple) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0))");
13:43 note: previously consumed @location)");
}
TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_Valid) {
@ -170,7 +171,7 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_MemberMultipleAttribu
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0)
13:43 note: previously consumed @location
12:34 note: while analyzing entry point 'main')");
}
@ -226,9 +227,8 @@ TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_DuplicateBuiltins) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(12:34 error: builtin(frag_depth) attribute appears multiple times as pipeline output
EXPECT_EQ(r()->error(),
R"(12:34 error: @builtin(frag_depth) appears multiple times as pipeline output
12:34 note: while analyzing entry point 'main')");
}
@ -265,7 +265,7 @@ TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Missing) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "13:43 error: missing entry point IO attribute on parameter");
EXPECT_EQ(r()->error(), R"(13:43 error: missing entry point IO attribute on parameter)");
}
TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Multiple) {
@ -287,7 +287,7 @@ TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Multiple) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0))");
13:43 note: previously consumed @location)");
}
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_Valid) {
@ -341,7 +341,7 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_MemberMultipleAttribut
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0)
13:43 note: previously consumed @location
12:34 note: while analyzing entry point 'main')");
}
@ -396,8 +396,7 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_DuplicateBuiltins) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: builtin(sample_index) attribute appears multiple times as "
"pipeline input");
"12:34 error: @builtin(sample_index) appears multiple times as pipeline input");
}
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateBuiltins) {
@ -432,9 +431,8 @@ TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateBuiltins) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(
r()->error(),
R"(12:34 error: builtin(sample_index) attribute appears multiple times as pipeline input
EXPECT_EQ(r()->error(),
R"(12:34 error: @builtin(sample_index) appears multiple times as pipeline input
12:34 note: while analyzing entry point 'main')");
}
@ -785,10 +783,8 @@ TEST_F(LocationAttributeTests, BadType_Input_bool) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot apply 'location' attribute to declaration of "
"type 'bool'\n"
"34:56 note: 'location' attribute must only be applied to "
"declarations of numeric scalar or numeric vector type");
R"(12:34 error: cannot apply @location to declaration of type 'bool'
34:56 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, BadType_Output_Array) {
@ -808,10 +804,8 @@ TEST_F(LocationAttributeTests, BadType_Output_Array) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot apply 'location' attribute to declaration of "
"type 'array<f32, 2>'\n"
"34:56 note: 'location' attribute must only be applied to "
"declarations of numeric scalar or numeric vector type");
R"(12:34 error: cannot apply @location to declaration of type 'array<f32, 2>'
34:56 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, BadType_Input_Struct) {
@ -838,10 +832,8 @@ TEST_F(LocationAttributeTests, BadType_Input_Struct) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot apply 'location' attribute to declaration of "
"type 'Input'\n"
"13:43 note: 'location' attribute must only be applied to "
"declarations of numeric scalar or numeric vector type");
R"(12:34 error: cannot apply @location to declaration of type 'Input'
13:43 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, BadType_Input_Struct_NestedStruct) {
@ -872,8 +864,8 @@ TEST_F(LocationAttributeTests, BadType_Input_Struct_NestedStruct) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"14:52 error: nested structures cannot be used for entry point IO\n"
"12:34 note: while analyzing entry point 'main'");
R"(14:52 error: nested structures cannot be used for entry point IO
12:34 note: while analyzing entry point 'main')");
}
TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
@ -898,10 +890,8 @@ TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"13:43 error: cannot apply 'location' attribute to declaration of "
"type 'array<f32>'\n"
"note: 'location' attribute must only be applied to declarations "
"of numeric scalar or numeric vector type");
R"(13:43 error: cannot apply @location to declaration of type 'array<f32>'
note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, BadMemberType_Input) {
@ -927,10 +917,8 @@ TEST_F(LocationAttributeTests, BadMemberType_Input) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"34:56 error: cannot apply 'location' attribute to declaration of "
"type 'array<i32>'\n"
"12:34 note: 'location' attribute must only be applied to "
"declarations of numeric scalar or numeric vector type");
R"(34:56 error: cannot apply @location to declaration of type 'array<i32>'
12:34 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, BadMemberType_Output) {
@ -954,10 +942,8 @@ TEST_F(LocationAttributeTests, BadMemberType_Output) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"34:56 error: cannot apply 'location' attribute to declaration of "
"type 'atomic<i32>'\n"
"12:34 note: 'location' attribute must only be applied to "
"declarations of numeric scalar or numeric vector type");
R"(34:56 error: cannot apply @location to declaration of type 'atomic<i32>'
12:34 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, BadMemberType_Unused) {
@ -971,10 +957,8 @@ TEST_F(LocationAttributeTests, BadMemberType_Unused) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"34:56 error: cannot apply 'location' attribute to declaration of "
"type 'mat3x2<f32>'\n"
"12:34 note: 'location' attribute must only be applied to "
"declarations of numeric scalar or numeric vector type");
R"(34:56 error: cannot apply @location to declaration of type 'mat3x2<f32>'
12:34 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, ReturnType_Struct_Valid) {
@ -1026,11 +1010,8 @@ TEST_F(LocationAttributeTests, ReturnType_Struct) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"12:34 error: cannot apply 'location' attribute to declaration of "
"type 'Output'\n"
"13:43 note: 'location' attribute must only be applied to "
"declarations of numeric scalar or numeric vector type");
EXPECT_EQ(r()->error(), R"(12:34 error: cannot apply @location to declaration of type 'Output'
13:43 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, ReturnType_Struct_NestedStruct) {
@ -1059,8 +1040,8 @@ TEST_F(LocationAttributeTests, ReturnType_Struct_NestedStruct) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"14:52 error: nested structures cannot be used for entry point IO\n"
"12:34 note: while analyzing entry point 'main'");
R"(14:52 error: nested structures cannot be used for entry point IO
12:34 note: while analyzing entry point 'main')");
}
TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
@ -1085,10 +1066,8 @@ TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"13:43 error: cannot apply 'location' attribute to declaration of "
"type 'array<f32>'\n"
"12:34 note: 'location' attribute must only be applied to "
"declarations of numeric scalar or numeric vector type");
R"(13:43 error: cannot apply @location to declaration of type 'array<f32>'
12:34 note: @location must only be applied to declarations of numeric scalar or numeric vector type)");
}
TEST_F(LocationAttributeTests, ComputeShaderLocation_Input) {
@ -1105,7 +1084,7 @@ TEST_F(LocationAttributeTests, ComputeShaderLocation_Input) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for compute shader output");
EXPECT_EQ(r()->error(), R"(12:34 error: attribute is not valid for compute shader output)");
}
TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
@ -1120,7 +1099,7 @@ TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for compute shader inputs");
EXPECT_EQ(r()->error(), R"(12:34 error: attribute is not valid for compute shader inputs)");
}
TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Output) {
@ -1186,7 +1165,7 @@ TEST_F(LocationAttributeTests, Duplicate_input) {
});
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), "12:34 error: location(1) attribute appears multiple times");
EXPECT_EQ(r()->error(), R"(12:34 error: @location(1) appears multiple times)");
}
TEST_F(LocationAttributeTests, Duplicate_struct) {
@ -1219,8 +1198,8 @@ TEST_F(LocationAttributeTests, Duplicate_struct) {
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
"34:56 error: location(1) attribute appears multiple times\n"
"12:34 note: while analyzing entry point 'main'");
R"(34:56 error: @location(1) appears multiple times
12:34 note: while analyzing entry point 'main')");
}
} // namespace

View File

@ -27,6 +27,7 @@ enum class Def {
kAddressSpace,
kBuiltinFunction,
kBuiltinType,
kBuiltinValue,
kFunction,
kStruct,
kTexelFormat,
@ -44,6 +45,8 @@ std::ostream& operator<<(std::ostream& out, Def def) {
return out << "Def::kBuiltinFunction";
case Def::kBuiltinType:
return out << "Def::kBuiltinType";
case Def::kBuiltinValue:
return out << "Def::kBuiltinValue";
case Def::kFunction:
return out << "Def::kFunction";
case Def::kStruct:
@ -62,6 +65,7 @@ enum class Use {
kAccess,
kAddressSpace,
kBinaryOp,
kBuiltinValue,
kCallExpr,
kCallStmt,
kFunctionReturnType,
@ -80,6 +84,8 @@ std::ostream& operator<<(std::ostream& out, Use use) {
return out << "Use::kAddressSpace";
case Use::kBinaryOp:
return out << "Use::kBinaryOp";
case Use::kBuiltinValue:
return out << "Use::kBuiltinValue";
case Use::kCallExpr:
return out << "Use::kCallExpr";
case Use::kCallStmt:
@ -156,6 +162,16 @@ TEST_P(ResolverExpressionKindTest, Test) {
};
break;
}
case Def::kBuiltinValue: {
sym = Sym("position");
check_expr = [](const sem::Expression* expr) {
ASSERT_NE(expr, nullptr);
auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::BuiltinValue>>();
ASSERT_NE(enum_expr, nullptr);
EXPECT_EQ(enum_expr->Value(), builtin::BuiltinValue::kPosition);
};
break;
}
case Def::kFunction: {
auto* fn = Func(kDefSource, "FUNCTION", utils::Empty, ty.i32(), Return(1_i));
sym = Sym("FUNCTION");
@ -233,6 +249,10 @@ TEST_P(ResolverExpressionKindTest, Test) {
case Use::kBinaryOp:
GlobalVar("v", builtin::AddressSpace::kPrivate, Mul(1_a, expr));
break;
case Use::kBuiltinValue:
Func("f", utils::Vector{Param("p", ty.vec4<f32>(), utils::Vector{Builtin(expr)})},
ty.void_(), utils::Empty, utils::Vector{Stage(ast::PipelineStage::kFragment)});
break;
case Use::kFunctionReturnType:
Func("f", utils::Empty, ty(expr), Return(Call(sym)));
break;
@ -271,6 +291,8 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kAccess, Use::kAddressSpace,
R"(5:6 error: cannot use access 'write' as address space)"},
{Def::kAccess, Use::kBinaryOp, R"(5:6 error: cannot use access 'write' as value)"},
{Def::kAccess, Use::kBuiltinValue,
R"(5:6 error: cannot use access 'write' as builtin value)"},
{Def::kAccess, Use::kCallExpr, R"(5:6 error: cannot use access 'write' as call target)"},
{Def::kAccess, Use::kCallStmt, R"(5:6 error: cannot use access 'write' as call target)"},
{Def::kAccess, Use::kFunctionReturnType, R"(5:6 error: cannot use access 'write' as type)"},
@ -286,6 +308,8 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kAddressSpace, Use::kAddressSpace, kPass},
{Def::kAddressSpace, Use::kBinaryOp,
R"(5:6 error: cannot use address space 'workgroup' as value)"},
{Def::kAddressSpace, Use::kBuiltinValue,
R"(5:6 error: cannot use address space 'workgroup' as builtin value)"},
{Def::kAddressSpace, Use::kCallExpr,
R"(5:6 error: cannot use address space 'workgroup' as call target)"},
{Def::kAddressSpace, Use::kCallStmt,
@ -309,6 +333,8 @@ INSTANTIATE_TEST_SUITE_P(
R"(7:8 error: missing '(' for builtin function call)"},
{Def::kBuiltinFunction, Use::kBinaryOp,
R"(7:8 error: missing '(' for builtin function call)"},
{Def::kBuiltinFunction, Use::kBuiltinValue,
R"(7:8 error: missing '(' for builtin function call)"},
{Def::kBuiltinFunction, Use::kCallStmt, kPass},
{Def::kBuiltinFunction, Use::kFunctionReturnType,
R"(7:8 error: missing '(' for builtin function call)"},
@ -329,6 +355,8 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kBuiltinType, Use::kBinaryOp,
R"(5:6 error: cannot use type 'vec4<f32>' as value
7:8 note: are you missing '()' for type initializer?)"},
{Def::kBuiltinType, Use::kBuiltinValue,
R"(5:6 error: cannot use type 'vec4<f32>' as builtin value)"},
{Def::kBuiltinType, Use::kCallExpr, kPass},
{Def::kBuiltinType, Use::kFunctionReturnType, kPass},
{Def::kBuiltinType, Use::kMemberType, kPass},
@ -342,12 +370,39 @@ INSTANTIATE_TEST_SUITE_P(
R"(5:6 error: cannot use type 'vec4<f32>' as value
7:8 note: are you missing '()' for type initializer?)"},
{Def::kBuiltinValue, Use::kAccess,
R"(5:6 error: cannot use builtin value 'position' as access)"},
{Def::kBuiltinValue, Use::kAddressSpace,
R"(5:6 error: cannot use builtin value 'position' as address space)"},
{Def::kBuiltinValue, Use::kBinaryOp,
R"(5:6 error: cannot use builtin value 'position' as value)"},
{Def::kBuiltinValue, Use::kBuiltinValue, kPass},
{Def::kBuiltinValue, Use::kCallStmt,
R"(5:6 error: cannot use builtin value 'position' as call target)"},
{Def::kBuiltinValue, Use::kCallExpr,
R"(5:6 error: cannot use builtin value 'position' as call target)"},
{Def::kBuiltinValue, Use::kFunctionReturnType,
R"(5:6 error: cannot use builtin value 'position' as type)"},
{Def::kBuiltinValue, Use::kMemberType,
R"(5:6 error: cannot use builtin value 'position' as type)"},
{Def::kBuiltinValue, Use::kTexelFormat,
R"(5:6 error: cannot use builtin value 'position' as texel format)"},
{Def::kBuiltinValue, Use::kValueExpression,
R"(5:6 error: cannot use builtin value 'position' as value)"},
{Def::kBuiltinValue, Use::kVariableType,
R"(5:6 error: cannot use builtin value 'position' as type)"},
{Def::kBuiltinValue, Use::kUnaryOp,
R"(5:6 error: cannot use builtin value 'position' as value)"},
{Def::kFunction, Use::kAccess, R"(5:6 error: cannot use function 'FUNCTION' as access
1:2 note: function 'FUNCTION' declared here)"},
{Def::kFunction, Use::kAddressSpace,
R"(5:6 error: cannot use function 'FUNCTION' as address space
1:2 note: function 'FUNCTION' declared here)"},
{Def::kFunction, Use::kBinaryOp, R"(5:6 error: cannot use function 'FUNCTION' as value
1:2 note: function 'FUNCTION' declared here)"},
{Def::kFunction, Use::kBuiltinValue,
R"(5:6 error: cannot use function 'FUNCTION' as builtin value
1:2 note: function 'FUNCTION' declared here)"},
{Def::kFunction, Use::kCallExpr, kPass},
{Def::kFunction, Use::kCallStmt, kPass},
@ -375,6 +430,8 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kStruct, Use::kBinaryOp, R"(5:6 error: cannot use type 'STRUCT' as value
7:8 note: are you missing '()' for type initializer?
1:2 note: struct 'STRUCT' declared here)"},
{Def::kStruct, Use::kBuiltinValue,
R"(5:6 error: cannot use type 'STRUCT' as builtin value)"},
{Def::kStruct, Use::kFunctionReturnType, kPass},
{Def::kStruct, Use::kMemberType, kPass},
{Def::kStruct, Use::kTexelFormat, R"(5:6 error: cannot use type 'STRUCT' as texel format)"},
@ -394,6 +451,8 @@ INSTANTIATE_TEST_SUITE_P(
R"(5:6 error: cannot use texel format 'rgba8unorm' as address space)"},
{Def::kTexelFormat, Use::kBinaryOp,
R"(5:6 error: cannot use texel format 'rgba8unorm' as value)"},
{Def::kTexelFormat, Use::kBuiltinValue,
R"(5:6 error: cannot use texel format 'rgba8unorm' as builtin value)"},
{Def::kTexelFormat, Use::kCallExpr,
R"(5:6 error: cannot use texel format 'rgba8unorm' as call target)"},
{Def::kTexelFormat, Use::kCallStmt,
@ -416,6 +475,8 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kTypeAlias, Use::kBinaryOp,
R"(5:6 error: cannot use type 'i32' as value
7:8 note: are you missing '()' for type initializer?)"},
{Def::kTypeAlias, Use::kBuiltinValue,
R"(5:6 error: cannot use type 'i32' as builtin value)"},
{Def::kTypeAlias, Use::kCallExpr, kPass},
{Def::kTypeAlias, Use::kFunctionReturnType, kPass},
{Def::kTypeAlias, Use::kMemberType, kPass},
@ -434,6 +495,9 @@ INSTANTIATE_TEST_SUITE_P(
R"(5:6 error: cannot use const 'VARIABLE' as address space
1:2 note: const 'VARIABLE' declared here)"},
{Def::kVariable, Use::kBinaryOp, kPass},
{Def::kVariable, Use::kBuiltinValue,
R"(5:6 error: cannot use const 'VARIABLE' as builtin value
1:2 note: const 'VARIABLE' declared here)"},
{Def::kVariable, Use::kCallStmt,
R"(5:6 error: cannot use const 'VARIABLE' as call target
1:2 note: const 'VARIABLE' declared here)"},

View File

@ -15,6 +15,7 @@
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/stage_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/resolver/resolver.h"
#include "src/tint/resolver/resolver_test_helper.h"

View File

@ -89,6 +89,7 @@
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::Access>);
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::AddressSpace>);
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::BuiltinValue>);
TINT_INSTANTIATE_TYPEINFO(tint::sem::BuiltinEnumExpression<tint::builtin::TexelFormat>);
namespace tint::resolver {
@ -611,7 +612,9 @@ sem::Parameter* Resolver::Parameter(const ast::Parameter* param, uint32_t index)
};
for (auto* attr : param->attributes) {
Mark(attr);
if (!Attribute(attr)) {
return nullptr;
}
}
if (!validator_.NoDuplicateAttributes(param->attributes)) {
return nullptr;
@ -792,7 +795,9 @@ sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* v) {
}
for (auto* attr : v->attributes) {
Mark(attr);
if (!Attribute(attr)) {
return nullptr;
}
}
if (!validator_.NoDuplicateAttributes(v->attributes)) {
@ -854,11 +859,8 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
validator_.DiagnosticFilters().Push();
TINT_DEFER(validator_.DiagnosticFilters().Pop());
for (auto* attr : decl->attributes) {
Mark(attr);
if (auto* dc = attr->As<ast::DiagnosticAttribute>()) {
if (!DiagnosticControl(dc->control)) {
return nullptr;
}
if (!Attribute(attr)) {
return nullptr;
}
}
if (!validator_.NoDuplicateAttributes(decl->attributes)) {
@ -921,7 +923,9 @@ sem::Function* Resolver::Function(const ast::Function* decl) {
// Determine if the return type has a location
std::optional<uint32_t> return_location;
for (auto* attr : decl->return_type_attributes) {
Mark(attr);
if (!Attribute(attr)) {
return nullptr;
}
if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
auto value = LocationAttribute(loc_attr);
@ -1502,6 +1506,11 @@ sem::BuiltinEnumExpression<builtin::AddressSpace>* Resolver::AddressSpaceExpress
return sem_.AsAddressSpace(Expression(expr));
}
sem::BuiltinEnumExpression<builtin::BuiltinValue>* Resolver::BuiltinValueExpression(
const ast::Expression* expr) {
return sem_.AsBuiltinValue(Expression(expr));
}
sem::BuiltinEnumExpression<builtin::TexelFormat>* Resolver::TexelFormatExpression(
const ast::Expression* expr) {
return sem_.AsTexelFormat(Expression(expr));
@ -2987,6 +2996,11 @@ sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
expr, current_statement_, addr);
}
if (auto builtin = resolved->BuiltinValue(); builtin != builtin::BuiltinValue::kUndefined) {
return builder_->create<sem::BuiltinEnumExpression<builtin::BuiltinValue>>(
expr, current_statement_, builtin);
}
if (auto fmt = resolved->TexelFormat(); fmt != builtin::TexelFormat::kUndefined) {
return builder_->create<sem::BuiltinEnumExpression<builtin::TexelFormat>>(
expr, current_statement_, fmt);
@ -3307,6 +3321,26 @@ sem::ValueExpression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
return sem;
}
bool Resolver::Attribute(const ast::Attribute* attr) {
Mark(attr);
return Switch(
attr, //
[&](const ast::BuiltinAttribute* b) { return BuiltinAttribute(b); },
[&](const ast::DiagnosticAttribute* dc) { return DiagnosticControl(dc->control); },
[&](Default) { return true; });
}
bool Resolver::BuiltinAttribute(const ast::BuiltinAttribute* attr) {
auto* builtin_expr = BuiltinValueExpression(attr->builtin);
if (!builtin_expr) {
return false;
}
// Apply the resolved tint::sem::BuiltinEnumExpression<tint::builtin::BuiltinValue> to the
// attribute.
builder_->Sem().Add(attr, builtin_expr);
return true;
}
bool Resolver::DiagnosticControl(const ast::DiagnosticControl& control) {
Mark(control.rule_name);
@ -3522,7 +3556,9 @@ sem::Struct* Resolver::Structure(const ast::Struct* str) {
bool has_size_attr = false;
std::optional<uint32_t> location;
for (auto* attr : member->attributes) {
Mark(attr);
if (!Attribute(attr)) {
return nullptr;
}
bool ok = Switch(
attr, //
[&](const ast::StructMemberOffsetAttribute* o) {

View File

@ -146,9 +146,15 @@ class Resolver {
const ast::Expression* expr);
/// @returns the call of Expression() cast to a
/// sem::BuiltinEnumExpression<builtin::TexelFormat>. If the sem::Expression is not a
/// sem::BuiltinEnumExpression<builtin::TexelFormat>, then an error diagnostic is raised and
/// sem::BuiltinEnumExpression<builtin::BuiltinValue>. If the sem::Expression is not a
/// sem::BuiltinEnumExpression<builtin::BuiltinValue>, then an error diagnostic is raised and
/// nullptr is returned.
sem::BuiltinEnumExpression<builtin::BuiltinValue>* BuiltinValueExpression(
const ast::Expression* expr);
/// @returns the call of Expression() cast to a sem::BuiltinEnumExpression<type::TexelFormat>.
/// If the sem::Expression is not a sem::BuiltinEnumExpression<type::TexelFormat>, then an error
/// diagnostic is raised and nullptr is returned.
sem::BuiltinEnumExpression<builtin::TexelFormat>* TexelFormatExpression(
const ast::Expression* expr);
@ -290,6 +296,14 @@ class Resolver {
/// current_function_
bool WorkgroupSize(const ast::Function*);
/// Resolves the attribute @p attr
/// @returns true on success, false on failure
bool Attribute(const ast::Attribute* attr);
/// Resolves the `@builtin` attribute @p attr
/// @returns true on success, false on failure
bool BuiltinAttribute(const ast::BuiltinAttribute* attr);
/// @param control the diagnostic control
/// @returns true on success, false on failure
bool DiagnosticControl(const ast::DiagnosticControl& control);

View File

@ -34,6 +34,7 @@
#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/workgroup_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/resolver/resolver_test_helper.h"
#include "src/tint/sem/call.h"
#include "src/tint/sem/function.h"

View File

@ -84,6 +84,11 @@ void SemHelper::ErrorUnexpectedExprKind(const sem::Expression* expr,
std::string(wanted),
addr->Declaration()->source);
},
[&](const sem::BuiltinEnumExpression<builtin::BuiltinValue>* builtin) {
AddError("cannot use builtin value '" + utils::ToString(builtin->Value()) + "' as " +
std::string(wanted),
builtin->Declaration()->source);
},
[&](const sem::BuiltinEnumExpression<builtin::TexelFormat>* fmt) {
AddError("cannot use texel format '" + utils::ToString(fmt->Value()) + "' as " +
std::string(wanted),

View File

@ -17,6 +17,7 @@
#include <string>
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/diagnostic/diagnostic.h"
#include "src/tint/program_builder.h"
#include "src/tint/resolver/dependency_graph.h"
@ -120,8 +121,23 @@ class SemHelper {
/// @param expr the semantic node
/// @returns nullptr if @p expr is nullptr, or @p expr cast to
/// sem::BuiltinEnumExpression<builtin::TexelFormat> if the cast is successful, otherwise an
/// sem::BuiltinEnumExpression<builtin::BuiltinValue> if the cast is successful, otherwise an
/// error diagnostic is raised.
sem::BuiltinEnumExpression<builtin::BuiltinValue>* AsBuiltinValue(sem::Expression* expr) const {
if (TINT_LIKELY(expr)) {
auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::BuiltinValue>>();
if (TINT_LIKELY(enum_expr)) {
return enum_expr;
}
ErrorUnexpectedExprKind(expr, "builtin value");
}
return nullptr;
}
/// @param expr the semantic node
/// @returns nullptr if @p expr is nullptr, or @p expr cast to
/// sem::BuiltinEnumExpression<type::TexelFormat> if the cast is successful, otherwise an error
/// diagnostic is raised.
sem::BuiltinEnumExpression<builtin::TexelFormat>* AsTexelFormat(sem::Expression* expr) const {
if (TINT_LIKELY(expr)) {
auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::TexelFormat>>();

View File

@ -16,6 +16,7 @@
#include "gmock/gmock.h"
#include "src/tint/ast/stage_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/resolver/resolver_test_helper.h"
#include "src/tint/sem/struct.h"

View File

@ -20,6 +20,7 @@
#include <utility>
#include <vector>
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/resolver/dependency_graph.h"
#include "src/tint/scope_stack.h"
@ -1145,11 +1146,12 @@ class UniformityGraph {
const ast::IdentifierExpression* ident,
bool load_rule = false) {
// Helper to check if the entry point attribute of `obj` indicates non-uniformity.
auto has_nonuniform_entry_point_attribute = [](auto* obj) {
auto has_nonuniform_entry_point_attribute = [&](auto* obj) {
// Only the num_workgroups and workgroup_id builtins are uniform.
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(obj->attributes)) {
if (builtin->builtin == builtin::BuiltinValue::kNumWorkgroups ||
builtin->builtin == builtin::BuiltinValue::kWorkgroupId) {
if (auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(obj->attributes)) {
auto builtin = builder_->Sem().Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kNumWorkgroups ||
builtin == builtin::BuiltinValue::kWorkgroupId) {
return false;
}
}

View File

@ -30,6 +30,7 @@
#include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/resolver/resolver_test_helper.h"
#include "src/tint/sem/call.h"
#include "src/tint/sem/function.h"

View File

@ -114,15 +114,11 @@ bool IsValidStorageTextureTexelFormat(builtin::TexelFormat format) {
}
// Helper to stringify a pipeline IO attribute.
std::string attr_to_str(const ast::Attribute* attr,
std::optional<uint32_t> location = std::nullopt) {
std::stringstream str;
if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
str << "builtin(" << builtin->builtin << ")";
} else if (attr->Is<ast::LocationAttribute>()) {
str << "location(" << location.value() << ")";
}
return str.str();
std::string AttrToStr(const ast::Attribute* attr) {
return Switch(
attr, //
[&](const ast::BuiltinAttribute*) { return "@builtin"; },
[&](const ast::LocationAttribute*) { return "@location"; });
}
template <typename CALLBACK>
@ -868,7 +864,8 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
stage_name << stage;
bool is_stage_mismatch = false;
bool is_output = !is_input;
switch (attr->builtin) {
auto builtin = sem_.Get(attr)->Value();
switch (builtin) {
case builtin::BuiltinValue::kPosition:
if (stage != ast::PipelineStage::kNone &&
!((is_input && stage == ast::PipelineStage::kFragment) ||
@ -876,8 +873,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
is_stage_mismatch = true;
}
if (!(type->is_float_vector() && type->As<type::Vector>()->Width() == 4)) {
AddError("store type of " + attr_to_str(attr) + " must be 'vec4<f32>'",
attr->source);
std::stringstream err;
err << "store type of @builtin(" << builtin << ") must be 'vec4<f32>'";
AddError(err.str(), attr->source);
return false;
}
break;
@ -890,8 +888,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
is_stage_mismatch = true;
}
if (!(type->is_unsigned_integer_vector() && type->As<type::Vector>()->Width() == 3)) {
AddError("store type of " + attr_to_str(attr) + " must be 'vec3<u32>'",
attr->source);
std::stringstream err;
err << "store type of @builtin(" << builtin << ") must be 'vec3<u32>'";
AddError(err.str(), attr->source);
return false;
}
break;
@ -901,7 +900,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
is_stage_mismatch = true;
}
if (!type->Is<type::F32>()) {
AddError("store type of " + attr_to_str(attr) + " must be 'f32'", attr->source);
std::stringstream err;
err << "store type of @builtin(" << builtin << ") must be 'f32'";
AddError(err.str(), attr->source);
return false;
}
break;
@ -911,7 +912,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
is_stage_mismatch = true;
}
if (!type->Is<type::Bool>()) {
AddError("store type of " + attr_to_str(attr) + " must be 'bool'", attr->source);
std::stringstream err;
err << "store type of @builtin(" << builtin << ") must be 'bool'";
AddError(err.str(), attr->source);
return false;
}
break;
@ -921,7 +924,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
is_stage_mismatch = true;
}
if (!type->Is<type::U32>()) {
AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
std::stringstream err;
err << "store type of @builtin(" << builtin << ") must be 'u32'";
AddError(err.str(), attr->source);
return false;
}
break;
@ -932,7 +937,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
is_stage_mismatch = true;
}
if (!type->Is<type::U32>()) {
AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
std::stringstream err;
err << "store type of @builtin(" << builtin << ") must be 'u32'";
AddError(err.str(), attr->source);
return false;
}
break;
@ -941,7 +948,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
is_stage_mismatch = true;
}
if (!type->Is<type::U32>()) {
AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
std::stringstream err;
err << "store type of @builtin(" << builtin << ") must be 'u32'";
AddError(err.str(), attr->source);
return false;
}
break;
@ -951,7 +960,9 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
is_stage_mismatch = true;
}
if (!type->Is<type::U32>()) {
AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
std::stringstream err;
err << "store type of @builtin(" << builtin << ") must be 'u32'";
AddError(err.str(), attr->source);
return false;
}
break;
@ -960,9 +971,10 @@ bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
}
if (is_stage_mismatch) {
AddError(attr_to_str(attr) + " cannot be used in " +
(is_input ? "input of " : "output of ") + stage_name.str() + " pipeline stage",
attr->source);
std::stringstream err;
err << "@builtin(" << builtin << ") cannot be used in "
<< (is_input ? "input of " : "output of ") << stage_name.str() << " pipeline stage";
AddError(err.str(), attr->source);
return false;
}
@ -1098,32 +1110,34 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
for (auto* attr : attrs) {
auto is_invalid_compute_shader_attribute = false;
if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
auto builtin = sem_.Get(builtin_attr)->Value();
if (pipeline_io_attribute) {
AddError("multiple entry point IO attributes", attr->source);
AddNote("previously consumed " + attr_to_str(pipeline_io_attribute, location),
AddNote("previously consumed " + AttrToStr(pipeline_io_attribute),
pipeline_io_attribute->source);
return false;
}
pipeline_io_attribute = attr;
if (builtins.Contains(builtin->builtin)) {
AddError(attr_to_str(builtin) +
" attribute appears multiple times as pipeline " +
(param_or_ret == ParamOrRetType::kParameter ? "input" : "output"),
decl->source);
if (builtins.Contains(builtin)) {
std::stringstream err;
err << "@builtin(" << builtin << ") appears multiple times as pipeline "
<< (param_or_ret == ParamOrRetType::kParameter ? "input" : "output");
AddError(err.str(), decl->source);
return false;
}
if (!BuiltinAttribute(builtin, ty, stage,
if (!BuiltinAttribute(builtin_attr, ty, stage,
/* is_input */ param_or_ret == ParamOrRetType::kParameter)) {
return false;
}
builtins.Add(builtin->builtin);
builtins.Add(builtin);
} else if (auto* loc_attr = attr->As<ast::LocationAttribute>()) {
if (pipeline_io_attribute) {
AddError("multiple entry point IO attributes", attr->source);
AddNote("previously consumed " + attr_to_str(pipeline_io_attribute),
AddNote("previously consumed " + AttrToStr(pipeline_io_attribute),
pipeline_io_attribute->source);
return false;
}
@ -1183,16 +1197,16 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
param_or_ret == ParamOrRetType::kReturnType) {
AddError(
"integral user-defined vertex outputs must have a flat "
"interpolation attribute",
"integral user-defined vertex outputs must have a flat interpolation "
"attribute",
source);
return false;
}
if (decl->PipelineStage() == ast::PipelineStage::kFragment &&
param_or_ret == ParamOrRetType::kParameter) {
AddError(
"integral user-defined fragment inputs must have a flat "
"interpolation attribute",
"integral user-defined fragment inputs must have a flat interpolation "
"attribute",
source);
return false;
}
@ -1211,15 +1225,14 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
if (invariant_attribute) {
bool has_position = false;
if (pipeline_io_attribute) {
if (auto* builtin = pipeline_io_attribute->As<ast::BuiltinAttribute>()) {
has_position = (builtin->builtin == builtin::BuiltinValue::kPosition);
if (auto* builtin_attr = pipeline_io_attribute->As<ast::BuiltinAttribute>()) {
auto builtin = sem_.Get(builtin_attr)->Value();
has_position = (builtin == builtin::BuiltinValue::kPosition);
}
}
if (!has_position) {
AddError(
"invariant attribute must only be applied to a position "
"builtin",
invariant_attribute->source);
AddError("invariant attribute must only be applied to a position builtin",
invariant_attribute->source);
return false;
}
}
@ -1280,9 +1293,10 @@ bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage)
// Check module-scope variables, as the SPIR-V sanitizer generates these.
bool found = false;
for (auto* global : func->TransitivelyReferencedGlobals()) {
if (auto* builtin =
if (auto* builtin_attr =
ast::GetAttribute<ast::BuiltinAttribute>(global->Declaration()->attributes)) {
if (builtin->builtin == builtin::BuiltinValue::kPosition) {
auto builtin = sem_.Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kPosition) {
found = true;
break;
}
@ -2120,12 +2134,13 @@ bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) cons
}
return true;
},
[&](const ast::BuiltinAttribute* builtin) {
if (!BuiltinAttribute(builtin, member->Type(), stage,
[&](const ast::BuiltinAttribute* builtin_attr) {
if (!BuiltinAttribute(builtin_attr, member->Type(), stage,
/* is_input */ false)) {
return false;
}
if (builtin->builtin == builtin::BuiltinValue::kPosition) {
auto builtin = sem_.Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kPosition) {
has_position = true;
}
return true;
@ -2208,18 +2223,18 @@ bool Validator::LocationAttribute(const ast::LocationAttribute* loc_attr,
if (!type->is_numeric_scalar_or_vector()) {
std::string invalid_type = sem_.TypeNameOf(type);
AddError("cannot apply 'location' attribute to declaration of type '" + invalid_type + "'",
source);
AddError("cannot apply @location to declaration of type '" + invalid_type + "'", source);
AddNote(
"'location' attribute must only be applied to declarations of numeric scalar or "
"numeric vector type",
"@location must only be applied to declarations of numeric scalar or numeric vector "
"type",
loc_attr->source);
return false;
}
if (!locations.Add(location)) {
AddError(attr_to_str(loc_attr, location) + " attribute appears multiple times",
loc_attr->source);
std::stringstream err;
err << "@location(" << location << ") appears multiple times";
AddError(err.str(), loc_attr->source);
return false;
}

View File

@ -17,6 +17,8 @@
#include <type_traits>
#include "src/tint/sem/builtin_enum_expression.h"
// Forward declarations
namespace tint {
class CastableBase;
@ -25,6 +27,7 @@ namespace tint::ast {
class AccessorExpression;
class BinaryExpression;
class BitcastExpression;
class BuiltinAttribute;
class CallExpression;
class Expression;
class ForLoopStatement;
@ -43,6 +46,9 @@ class Variable;
class WhileStatement;
class UnaryOpExpression;
} // namespace tint::ast
namespace tint::builtin {
enum class BuiltinValue;
}
namespace tint::sem {
class Expression;
class ForLoopStatement;
@ -71,21 +77,22 @@ namespace tint::sem {
/// rules will be used to infer the return type based on the argument type.
struct TypeMappings {
//! @cond Doxygen_Suppress
BuiltinEnumExpression<builtin::BuiltinValue>* operator()(ast::BuiltinAttribute*);
CastableBase* operator()(ast::Node*);
Expression* operator()(ast::Expression*);
ForLoopStatement* operator()(ast::ForLoopStatement*);
Function* operator()(ast::Function*);
IfStatement* operator()(ast::IfStatement*);
CastableBase* operator()(ast::Node*);
GlobalVariable* operator()(ast::Override*);
IfStatement* operator()(ast::IfStatement*);
Statement* operator()(ast::Statement*);
Struct* operator()(ast::Struct*);
StructMember* operator()(ast::StructMember*);
SwitchStatement* operator()(ast::SwitchStatement*);
type::Type* operator()(ast::TypeDecl*);
Expression* operator()(ast::Expression*);
ValueExpression* operator()(ast::AccessorExpression*);
ValueExpression* operator()(ast::CallExpression*);
ValueExpression* operator()(ast::BinaryExpression*);
ValueExpression* operator()(ast::BitcastExpression*);
ValueExpression* operator()(ast::CallExpression*);
ValueExpression* operator()(ast::LiteralExpression*);
ValueExpression* operator()(ast::PhonyExpression*);
ValueExpression* operator()(ast::UnaryOpExpression*);

View File

@ -21,6 +21,7 @@
#include <vector>
#include "src/tint/ast/disable_validation_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/sem/function.h"
#include "src/tint/transform/unshadow.h"
@ -81,46 +82,12 @@ uint32_t BuiltinOrder(builtin::BuiltinValue builtin) {
return 0;
}
/// Comparison function used to reorder struct members such that all members with
/// location attributes appear first (ordered by location slot), followed by
/// those with builtin attributes.
/// @param a a struct member
/// @param b another struct member
/// @returns true if a comes before b
bool StructMemberComparator(const MemberInfo& a, const MemberInfo& b) {
auto* a_loc = ast::GetAttribute<ast::LocationAttribute>(a.member->attributes);
auto* b_loc = ast::GetAttribute<ast::LocationAttribute>(b.member->attributes);
auto* a_blt = ast::GetAttribute<ast::BuiltinAttribute>(a.member->attributes);
auto* b_blt = ast::GetAttribute<ast::BuiltinAttribute>(b.member->attributes);
if (a_loc) {
if (!b_loc) {
// `a` has location attribute and `b` does not: `a` goes first.
return true;
}
// Both have location attributes: smallest goes first.
return a.location < b.location;
} else {
if (b_loc) {
// `b` has location attribute and `a` does not: `b` goes first.
return false;
}
// Both are builtins: order matters for FXC.
return BuiltinOrder(a_blt->builtin) < BuiltinOrder(b_blt->builtin);
}
}
// Returns true if `attr` is a shader IO attribute.
bool IsShaderIOAttribute(const ast::Attribute* attr) {
return attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute, ast::InvariantAttribute,
ast::LocationAttribute>();
}
// Returns true if `attrs` contains a `sample_mask` builtin.
bool HasSampleMask(utils::VectorRef<const ast::Attribute*> attrs) {
auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attrs);
return builtin && builtin->builtin == builtin::BuiltinValue::kSampleMask;
}
} // namespace
/// PIMPL state for the transform
@ -132,7 +99,7 @@ struct CanonicalizeEntryPointIO::State {
/// The type of the output value.
ast::Type type;
/// The shader IO attributes.
utils::Vector<const ast::Attribute*, 2> attributes;
utils::Vector<const ast::Attribute*, 8> attributes;
/// The value itself.
const ast::Expression* value;
/// The output location.
@ -165,6 +132,8 @@ struct CanonicalizeEntryPointIO::State {
utils::Vector<const ast::Statement*, 8> wrapper_body;
/// Input names used by the entrypoint
std::unordered_set<std::string> input_names;
/// A map of cloned attribute to builtin value
utils::Hashmap<const ast::BuiltinAttribute*, builtin::BuiltinValue, 16> builtin_attrs;
/// Constructor
/// @param context the clone context
@ -175,20 +144,64 @@ struct CanonicalizeEntryPointIO::State {
const ast::Function* function)
: ctx(context), cfg(config), func_ast(function), func_sem(ctx.src->Sem().Get(function)) {}
/// Clones the shader IO attributes from `src`.
/// @param src the attributes to clone
/// Clones the attributes from @p in and adds it to @p out. If @p in is a builtin attribute,
/// then builtin_attrs is updated with the builtin information.
/// @param in the attribute to clone
/// @param out the output Attributes
template <size_t N>
void CloneAttribute(const ast::Attribute* in, utils::Vector<const ast::Attribute*, N>& out) {
auto* cloned = ctx.Clone(in);
out.Push(cloned);
if (auto* builtin = in->As<ast::BuiltinAttribute>()) {
builtin_attrs.Add(cloned->As<ast::BuiltinAttribute>(),
ctx.src->Sem().Get(builtin)->Value());
}
}
/// Clones the shader IO attributes from @p in.
/// @param in the attributes to clone
/// @param do_interpolate whether to clone InterpolateAttribute
/// @return the cloned attributes
template <size_t N>
auto CloneShaderIOAttributes(utils::Vector<const ast::Attribute*, N> src, bool do_interpolate) {
utils::Vector<const ast::Attribute*, N> new_attributes;
for (auto* attr : src) {
auto CloneShaderIOAttributes(const utils::Vector<const ast::Attribute*, N> in,
bool do_interpolate) {
utils::Vector<const ast::Attribute*, N> out;
for (auto* attr : in) {
if (IsShaderIOAttribute(attr) &&
(do_interpolate || !attr->template Is<ast::InterpolateAttribute>())) {
new_attributes.Push(ctx.Clone(attr));
CloneAttribute(attr, out);
}
}
return new_attributes;
return out;
}
/// @param attr the input attribute
/// @returns the builtin value of the attribute
builtin::BuiltinValue BuiltinOf(const ast::BuiltinAttribute* attr) {
if (attr->program_id == ctx.dst->ID()) {
// attr belongs to the target program.
// Obtain the builtin value from #builtin_attrs.
if (auto b = builtin_attrs.Get(attr)) {
return *b;
}
} else {
// attr belongs to the source program.
// Obtain the builtin value from the semantic info.
return ctx.src->Sem().Get(attr)->Value();
}
TINT_ICE(Resolver, ctx.dst->Diagnostics())
<< "could not obtain builtin value from attribute";
return builtin::BuiltinValue::kUndefined;
}
/// @param attrs the input attribute list
/// @returns the builtin value if any of the attributes in @p attrs is a builtin attribute,
/// otherwise builtin::BuiltinValue::kUndefined
builtin::BuiltinValue BuiltinOf(utils::VectorRef<const ast::Attribute*> attrs) {
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attrs)) {
return BuiltinOf(builtin);
}
return builtin::BuiltinValue::kUndefined;
}
/// Create or return a symbol for the wrapper function's struct parameter.
@ -204,13 +217,16 @@ struct CanonicalizeEntryPointIO::State {
/// @param name the name of the shader input
/// @param type the type of the shader input
/// @param location the location if provided
/// @param attributes the attributes to apply to the shader input
/// @param attrs the attributes to apply to the shader input
/// @returns an expression which evaluates to the value of the shader input
const ast::Expression* AddInput(std::string name,
const type::Type* type,
std::optional<uint32_t> location,
utils::Vector<const ast::Attribute*, 8> attributes) {
utils::Vector<const ast::Attribute*, 8> attrs) {
auto ast_type = CreateASTTypeFor(ctx, type);
auto builtin_attr = BuiltinOf(attrs);
if (cfg.shader_style == ShaderStyle::kSpirv || cfg.shader_style == ShaderStyle::kGlsl) {
// Vulkan requires that integer user-defined fragment inputs are always decorated with
// `Flat`. See:
@ -219,21 +235,21 @@ struct CanonicalizeEntryPointIO::State {
// required for integers.
if (func_ast->PipelineStage() == ast::PipelineStage::kFragment &&
type->is_integer_scalar_or_vector() &&
!ast::HasAttribute<ast::InterpolateAttribute>(attributes) &&
(ast::HasAttribute<ast::LocationAttribute>(attributes) ||
!ast::HasAttribute<ast::InterpolateAttribute>(attrs) &&
(ast::HasAttribute<ast::LocationAttribute>(attrs) ||
cfg.shader_style == ShaderStyle::kSpirv)) {
attributes.Push(ctx.dst->Interpolate(builtin::InterpolationType::kFlat,
builtin::InterpolationSampling::kUndefined));
attrs.Push(ctx.dst->Interpolate(builtin::InterpolationType::kFlat,
builtin::InterpolationSampling::kUndefined));
}
// Disable validation for use of the `input` address space.
attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
attrs.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
// In GLSL, if it's a builtin, override the name with the
// corresponding gl_ builtin name
auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(attributes);
if (cfg.shader_style == ShaderStyle::kGlsl && builtin) {
name = GLSLBuiltinToString(builtin->builtin, func_ast->PipelineStage(),
if (cfg.shader_style == ShaderStyle::kGlsl &&
builtin_attr != builtin::BuiltinValue::kUndefined) {
name = GLSLBuiltinToString(builtin_attr, func_ast->PipelineStage(),
builtin::AddressSpace::kIn);
}
auto symbol = ctx.dst->Symbols().New(name);
@ -241,32 +257,32 @@ struct CanonicalizeEntryPointIO::State {
// Create the global variable and use its value for the shader input.
const ast::Expression* value = ctx.dst->Expr(symbol);
if (builtin) {
if (builtin_attr != builtin::BuiltinValue::kUndefined) {
if (cfg.shader_style == ShaderStyle::kGlsl) {
value = FromGLSLBuiltin(builtin->builtin, value, ast_type);
} else if (builtin->builtin == builtin::BuiltinValue::kSampleMask) {
value = FromGLSLBuiltin(builtin_attr, value, ast_type);
} else if (builtin_attr == builtin::BuiltinValue::kSampleMask) {
// Vulkan requires the type of a SampleMask builtin to be an array.
// Declare it as array<u32, 1> and then load the first element.
ast_type = ctx.dst->ty.array(ast_type, 1_u);
value = ctx.dst->IndexAccessor(value, 0_i);
}
}
ctx.dst->GlobalVar(symbol, ast_type, builtin::AddressSpace::kIn, std::move(attributes));
ctx.dst->GlobalVar(symbol, ast_type, builtin::AddressSpace::kIn, std::move(attrs));
return value;
} else if (cfg.shader_style == ShaderStyle::kMsl &&
ast::HasAttribute<ast::BuiltinAttribute>(attributes)) {
builtin_attr != builtin::BuiltinValue::kUndefined) {
// If this input is a builtin and we are targeting MSL, then add it to the
// parameter list and pass it directly to the inner function.
Symbol symbol = input_names.emplace(name).second ? ctx.dst->Symbols().Register(name)
: ctx.dst->Symbols().New(name);
wrapper_ep_parameters.Push(ctx.dst->Param(symbol, ast_type, std::move(attributes)));
wrapper_ep_parameters.Push(ctx.dst->Param(symbol, ast_type, std::move(attrs)));
return ctx.dst->Expr(symbol);
} else {
// Otherwise, move it to the new structure member list.
Symbol symbol = input_names.emplace(name).second ? ctx.dst->Symbols().Register(name)
: ctx.dst->Symbols().New(name);
wrapper_struct_param_members.Push(
{ctx.dst->Member(symbol, ast_type, std::move(attributes)), location});
{ctx.dst->Member(symbol, ast_type, std::move(attrs)), location});
return ctx.dst->MemberAccessor(InputStructSymbol(), symbol);
}
}
@ -275,13 +291,14 @@ struct CanonicalizeEntryPointIO::State {
/// @param name the name of the shader output
/// @param type the type of the shader output
/// @param location the location if provided
/// @param attributes the attributes to apply to the shader output
/// @param attrs the attributes to apply to the shader output
/// @param value the value of the shader output
void AddOutput(std::string name,
const type::Type* type,
std::optional<uint32_t> location,
utils::Vector<const ast::Attribute*, 8> attributes,
utils::Vector<const ast::Attribute*, 8> attrs,
const ast::Expression* value) {
auto builtin_attr = BuiltinOf(attrs);
// Vulkan requires that integer user-defined vertex outputs are always decorated with
// `Flat`.
// TODO(crbug.com/tint/1224): Remove this once a flat interpolation attribute is required
@ -289,26 +306,26 @@ struct CanonicalizeEntryPointIO::State {
if (cfg.shader_style == ShaderStyle::kSpirv &&
func_ast->PipelineStage() == ast::PipelineStage::kVertex &&
type->is_integer_scalar_or_vector() &&
ast::HasAttribute<ast::LocationAttribute>(attributes) &&
!ast::HasAttribute<ast::InterpolateAttribute>(attributes)) {
attributes.Push(ctx.dst->Interpolate(builtin::InterpolationType::kFlat,
builtin::InterpolationSampling::kUndefined));
ast::HasAttribute<ast::LocationAttribute>(attrs) &&
!ast::HasAttribute<ast::InterpolateAttribute>(attrs)) {
attrs.Push(ctx.dst->Interpolate(builtin::InterpolationType::kFlat,
builtin::InterpolationSampling::kUndefined));
}
// In GLSL, if it's a builtin, override the name with the
// corresponding gl_ builtin name
if (cfg.shader_style == ShaderStyle::kGlsl) {
if (auto* b = ast::GetAttribute<ast::BuiltinAttribute>(attributes)) {
name = GLSLBuiltinToString(b->builtin, func_ast->PipelineStage(),
if (builtin_attr != builtin::BuiltinValue::kUndefined) {
name = GLSLBuiltinToString(builtin_attr, func_ast->PipelineStage(),
builtin::AddressSpace::kOut);
value = ToGLSLBuiltin(b->builtin, value, type);
value = ToGLSLBuiltin(builtin_attr, value, type);
}
}
OutputValue output;
output.name = name;
output.type = CreateASTTypeFor(ctx, type);
output.attributes = std::move(attributes);
output.attributes = std::move(attrs);
output.value = value;
output.location = location;
wrapper_output_values.Push(output);
@ -322,14 +339,14 @@ struct CanonicalizeEntryPointIO::State {
void ProcessNonStructParameter(const sem::Parameter* param) {
// Do not add interpolation attributes on vertex input
bool do_interpolate = func_ast->PipelineStage() != ast::PipelineStage::kVertex;
// Remove the shader IO attributes from the inner function parameter, and
// attach them to the new object instead.
// Remove the shader IO attributes from the inner function parameter, and attach them to the
// new object instead.
utils::Vector<const ast::Attribute*, 8> attributes;
for (auto* attr : param->Declaration()->attributes) {
if (IsShaderIOAttribute(attr)) {
ctx.Remove(param->Declaration()->attributes, attr);
if ((do_interpolate || !attr->Is<ast::InterpolateAttribute>())) {
attributes.Push(ctx.Clone(attr));
CloneAttribute(attr, attributes);
}
}
}
@ -412,25 +429,28 @@ struct CanonicalizeEntryPointIO::State {
void AddFixedSampleMask() {
// Check the existing output values for a sample mask builtin.
for (auto& outval : wrapper_output_values) {
if (HasSampleMask(outval.attributes)) {
if (BuiltinOf(outval.attributes) == builtin::BuiltinValue::kSampleMask) {
// Combine the authored sample mask with the fixed mask.
outval.value = ctx.dst->And(outval.value, u32(cfg.fixed_sample_mask));
return;
}
}
// No existing sample mask builtin was found, so create a new output value
// using the fixed sample mask.
AddOutput("fixed_sample_mask", ctx.dst->create<type::U32>(), std::nullopt,
{ctx.dst->Builtin(builtin::BuiltinValue::kSampleMask)},
// No existing sample mask builtin was found, so create a new output value using the fixed
// sample mask.
auto* builtin = ctx.dst->Builtin(builtin::BuiltinValue::kSampleMask);
builtin_attrs.Add(builtin, builtin::BuiltinValue::kSampleMask);
AddOutput("fixed_sample_mask", ctx.dst->create<type::U32>(), std::nullopt, {builtin},
ctx.dst->Expr(u32(cfg.fixed_sample_mask)));
}
/// Add a point size builtin to the wrapper function output.
void AddVertexPointSize() {
// Create a new output value and assign it a literal 1.0 value.
AddOutput("vertex_point_size", ctx.dst->create<type::F32>(), std::nullopt,
{ctx.dst->Builtin(builtin::BuiltinValue::kPointSize)}, ctx.dst->Expr(1_f));
auto* builtin = ctx.dst->Builtin(builtin::BuiltinValue::kPointSize);
builtin_attrs.Add(builtin, builtin::BuiltinValue::kPointSize);
AddOutput("vertex_point_size", ctx.dst->create<type::F32>(), std::nullopt, {builtin},
ctx.dst->Expr(1_f));
}
/// Create an expression for gl_Position.[component]
@ -442,11 +462,40 @@ struct CanonicalizeEntryPointIO::State {
return ctx.dst->MemberAccessor(ctx.dst->Expr(pos), c);
}
/// Comparison function used to reorder struct members such that all members with
/// location attributes appear first (ordered by location slot), followed by
/// those with builtin attributes.
/// @param a a struct member
/// @param b another struct member
/// @returns true if a comes before b
bool StructMemberComparator(const MemberInfo& a, const MemberInfo& b) {
auto* a_loc = ast::GetAttribute<ast::LocationAttribute>(a.member->attributes);
auto* b_loc = ast::GetAttribute<ast::LocationAttribute>(b.member->attributes);
auto* a_blt = ast::GetAttribute<ast::BuiltinAttribute>(a.member->attributes);
auto* b_blt = ast::GetAttribute<ast::BuiltinAttribute>(b.member->attributes);
if (a_loc) {
if (!b_loc) {
// `a` has location attribute and `b` does not: `a` goes first.
return true;
}
// Both have location attributes: smallest goes first.
return a.location < b.location;
} else {
if (b_loc) {
// `b` has location attribute and `a` does not: `b` goes first.
return false;
}
// Both are builtins: order matters for FXC.
auto builtin_a = BuiltinOf(a_blt);
auto builtin_b = BuiltinOf(b_blt);
return BuiltinOrder(builtin_a) < BuiltinOrder(builtin_b);
}
}
/// Create the wrapper function's struct parameter and type objects.
void CreateInputStruct() {
// Sort the struct members to satisfy HLSL interfacing matching rules.
std::sort(wrapper_struct_param_members.begin(), wrapper_struct_param_members.end(),
StructMemberComparator);
[&](auto& a, auto& b) { return StructMemberComparator(a, b); });
utils::Vector<const ast::StructMember*, 8> members;
for (auto& mem : wrapper_struct_param_members) {
@ -483,16 +532,17 @@ struct CanonicalizeEntryPointIO::State {
}
member_names.insert(ctx.dst->Symbols().NameFor(name));
wrapper_struct_output_members.Push(
{ctx.dst->Member(name, outval.type, std::move(outval.attributes)),
outval.location});
wrapper_struct_output_members.Push({
ctx.dst->Member(name, outval.type, std::move(outval.attributes)),
outval.location,
});
assignments.Push(
ctx.dst->Assign(ctx.dst->MemberAccessor(wrapper_result, name), outval.value));
}
// Sort the struct members to satisfy HLSL interfacing matching rules.
std::sort(wrapper_struct_output_members.begin(), wrapper_struct_output_members.end(),
StructMemberComparator);
[&](auto& a, auto& b) { return StructMemberComparator(a, b); });
utils::Vector<const ast::StructMember*, 8> members;
for (auto& mem : wrapper_struct_output_members) {
@ -519,14 +569,14 @@ struct CanonicalizeEntryPointIO::State {
void CreateGlobalOutputVariables() {
for (auto& outval : wrapper_output_values) {
// Disable validation for use of the `output` address space.
utils::Vector<const ast::Attribute*, 8> attributes = std::move(outval.attributes);
auto attributes = std::move(outval.attributes);
attributes.Push(ctx.dst->Disable(ast::DisabledValidation::kIgnoreAddressSpace));
// Create the global variable and assign it the output value.
auto name = ctx.dst->Symbols().New(outval.name);
ast::Type type = outval.type;
const ast::Expression* lhs = ctx.dst->Expr(name);
if (HasSampleMask(attributes)) {
if (BuiltinOf(attributes) == builtin::BuiltinValue::kSampleMask) {
// Vulkan requires the type of a SampleMask builtin to be an array.
// Declare it as array<u32, 1> and then store to the first element.
type = ctx.dst->ty.array(type, 1_u);

View File

@ -3163,7 +3163,7 @@ fn vert_main() -> @builtin(position) vec4<f32> {
auto* expect = R"(
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> value : vec4<f32>;
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
fn vert_main_inner() -> vec4<f32> {
return vec4<f32>();
@ -3197,7 +3197,7 @@ fn vert_main() -> @builtin(position) vec4<f32> {
struct tint_symbol {
@builtin(position)
value : vec4<f32>,
@builtin(point_size)
@builtin(__point_size)
vertex_point_size : f32,
}
@ -3238,7 +3238,7 @@ fn vert_main() -> VertOut {
auto* expect = R"(
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> pos_1 : vec4<f32>;
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
struct VertOut {
pos : vec4<f32>,
@ -3279,7 +3279,7 @@ struct VertOut {
auto* expect = R"(
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> pos_1 : vec4<f32>;
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size : f32;
fn vert_main_inner() -> VertOut {
return VertOut();
@ -3325,7 +3325,7 @@ struct VertOut {
struct tint_symbol {
@builtin(position)
pos : vec4<f32>,
@builtin(point_size)
@builtin(__point_size)
vertex_point_size : f32,
}
@ -3367,7 +3367,7 @@ struct VertOut {
struct tint_symbol {
@builtin(position)
pos : vec4<f32>,
@builtin(point_size)
@builtin(__point_size)
vertex_point_size : f32,
}
@ -3432,7 +3432,7 @@ fn vert_main(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_1_1 : vec4<f32>;
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_4 : f32;
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_4 : f32;
var<private> vertex_point_size : f32;
@ -3510,7 +3510,7 @@ struct VertOut {
@builtin(position) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_1_1 : vec4<f32>;
@builtin(point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_4 : f32;
@builtin(__point_size) @internal(disable_validation__ignore_address_space) var<__out> vertex_point_size_4 : f32;
fn vert_main_inner(collide : VertIn1, collide_1 : VertIn2) -> VertOut {
let x = (collide.collide + collide_1.collide);
@ -3601,7 +3601,7 @@ struct tint_symbol_2 {
vertex_point_size : vec4<f32>,
@builtin(position)
vertex_point_size_1 : vec4<f32>,
@builtin(point_size)
@builtin(__point_size)
vertex_point_size_2 : f32,
}
@ -3664,7 +3664,7 @@ struct tint_symbol_2 {
vertex_point_size : vec4<f32>,
@builtin(position)
vertex_point_size_1 : vec4<f32>,
@builtin(point_size)
@builtin(__point_size)
vertex_point_size_2 : f32,
}
@ -3753,7 +3753,7 @@ struct tint_symbol_2 {
vertex_point_size : vec4<f32>,
@builtin(position)
vertex_point_size_1 : vec4<f32>,
@builtin(point_size)
@builtin(__point_size)
vertex_point_size_2 : f32,
}
@ -3816,7 +3816,7 @@ struct tint_symbol_2 {
vertex_point_size : vec4<f32>,
@builtin(position)
vertex_point_size_1 : vec4<f32>,
@builtin(point_size)
@builtin(__point_size)
vertex_point_size_2 : f32,
}

View File

@ -33,178 +33,196 @@ TINT_INSTANTIATE_TYPEINFO(tint::transform::ClampFragDepth);
namespace tint::transform {
namespace {
/// PIMPL state for the transform
struct ClampFragDepth::State {
/// The source program
const Program* const src;
/// The target program builder
ProgramBuilder b{};
/// The clone context
CloneContext ctx = {&b, src, /* auto_clone_symbols */ true};
/// The sem::Info of the program
const sem::Info& sem = src->Sem();
/// The symbols of the program
const SymbolTable& sym = src->Symbols();
bool ContainsFragDepth(utils::VectorRef<const ast::Attribute*> attributes) {
for (auto* attribute : attributes) {
if (auto* builtin_attribute = attribute->As<ast::BuiltinAttribute>()) {
if (builtin_attribute->builtin == builtin::BuiltinValue::kFragDepth) {
/// Runs the transform
/// @returns the new program or SkipTransform if the transform is not required
Transform::ApplyResult Run() {
// Abort on any use of push constants in the module.
for (auto* global : src->AST().GlobalVariables()) {
if (auto* var = global->As<ast::Var>()) {
auto* v = src->Sem().Get(var);
if (TINT_UNLIKELY(v->AddressSpace() == builtin::AddressSpace::kPushConstant)) {
TINT_ICE(Transform, b.Diagnostics())
<< "ClampFragDepth doesn't know how to handle module that already use push "
"constants";
return Program(std::move(b));
}
}
}
if (!ShouldRun()) {
return SkipTransform;
}
// At least one entry-point needs clamping. Add the following to the module:
//
// enable chromium_experimental_push_constant;
//
// struct FragDepthClampArgs {
// min : f32,
// max : f32,
// }
// var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
//
// fn clamp_frag_depth(v : f32) -> f32 {
// return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
// }
b.Enable(builtin::Extension::kChromiumExperimentalPushConstant);
b.Structure(b.Symbols().New("FragDepthClampArgs"),
utils::Vector{b.Member("min", b.ty.f32()), b.Member("max", b.ty.f32())});
auto args_sym = b.Symbols().New("frag_depth_clamp_args");
b.GlobalVar(args_sym, b.ty("FragDepthClampArgs"), builtin::AddressSpace::kPushConstant);
auto base_fn_sym = b.Symbols().New("clamp_frag_depth");
b.Func(base_fn_sym, utils::Vector{b.Param("v", b.ty.f32())}, b.ty.f32(),
utils::Vector{b.Return(b.Call("clamp", "v", b.MemberAccessor(args_sym, "min"),
b.MemberAccessor(args_sym, "max")))});
// If true, the currently cloned function returns frag depth directly as a scalar
bool returns_frag_depth_as_value = false;
// If valid, the currently cloned function returns frag depth in a struct
// The symbol is the name of the helper function to apply the depth clamping.
Symbol returns_frag_depth_as_struct_helper;
// Map of io struct to helper function to return the structure with the depth clamping
// applied.
utils::Hashmap<const ast::Struct*, Symbol, 4u> io_structs_clamp_helpers;
// Register a callback that will be called for each visted AST function.
// This call wraps the cloning of the function's statements, and will assign to
// `returns_frag_depth_as_value` or `returns_frag_depth_as_struct_helper` if the function's
// return value requires depth clamping.
ctx.ReplaceAll([&](const ast::Function* fn) {
if (fn->PipelineStage() != ast::PipelineStage::kFragment) {
return ctx.CloneWithoutTransform(fn);
}
if (ReturnsFragDepthAsValue(fn)) {
TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_value, true);
return ctx.CloneWithoutTransform(fn);
}
if (ReturnsFragDepthInStruct(fn)) {
// At most once per I/O struct, add the conversion function:
//
// fn clamp_frag_depth_S(s : S) -> S {
// return S(s.first, s.second, clamp_frag_depth(s.frag_depth), s.last);
// }
auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()->Declaration();
auto helper = io_structs_clamp_helpers.GetOrCreate(struct_ty, [&] {
auto return_ty = fn->return_type;
auto fn_sym =
b.Symbols().New("clamp_frag_depth_" + sym.NameFor(struct_ty->name->symbol));
utils::Vector<const ast::Expression*, 8u> initializer_args;
for (auto* member : struct_ty->members) {
const ast::Expression* arg =
b.MemberAccessor("s", ctx.Clone(member->name->symbol));
if (ContainsFragDepth(member->attributes)) {
arg = b.Call(base_fn_sym, arg);
}
initializer_args.Push(arg);
}
utils::Vector params{b.Param("s", ctx.Clone(return_ty))};
utils::Vector body{
b.Return(b.Call(ctx.Clone(return_ty), std::move(initializer_args))),
};
b.Func(fn_sym, params, ctx.Clone(return_ty), body);
return fn_sym;
});
TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_struct_helper, helper);
return ctx.CloneWithoutTransform(fn);
}
return ctx.CloneWithoutTransform(fn);
});
// Replace the return statements `return expr` with `return clamp_frag_depth(expr)`.
ctx.ReplaceAll([&](const ast::ReturnStatement* stmt) -> const ast::ReturnStatement* {
if (returns_frag_depth_as_value) {
return b.Return(stmt->source, b.Call(base_fn_sym, ctx.Clone(stmt->value)));
}
if (returns_frag_depth_as_struct_helper.IsValid()) {
return b.Return(stmt->source, b.Call(returns_frag_depth_as_struct_helper,
ctx.Clone(stmt->value)));
}
return nullptr;
});
ctx.Clone();
return Program(std::move(b));
}
private:
/// @returns true if the transform should run
bool ShouldRun() {
for (auto* fn : src->AST().Functions()) {
if (fn->PipelineStage() == ast::PipelineStage::kFragment &&
(ReturnsFragDepthAsValue(fn) || ReturnsFragDepthInStruct(fn))) {
return true;
}
}
return false;
}
return false;
}
bool ReturnsFragDepthAsValue(const ast::Function* fn) {
return ContainsFragDepth(fn->return_type_attributes);
}
bool ReturnsFragDepthInStruct(const sem::Info& sem, const ast::Function* fn) {
if (auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()) {
for (auto* member : struct_ty->Members()) {
if (ContainsFragDepth(member->Declaration()->attributes)) {
return true;
/// @param attrs the attributes to examine
/// @returns true if @p attrs contains a `@builtin(frag_depth)` attribute
bool ContainsFragDepth(utils::VectorRef<const ast::Attribute*> attrs) {
for (auto* attribute : attrs) {
if (auto* builtin_attr = attribute->As<ast::BuiltinAttribute>()) {
auto builtin = sem.Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kFragDepth) {
return true;
}
}
}
return false;
}
return false;
}
/// @param fn the function to examine
/// @returns true if @p fn has a return type with a `@builtin(frag_depth)` attribute
bool ReturnsFragDepthAsValue(const ast::Function* fn) {
return ContainsFragDepth(fn->return_type_attributes);
}
bool ShouldRun(const Program* program) {
auto& sem = program->Sem();
for (auto* fn : program->AST().Functions()) {
if (fn->PipelineStage() == ast::PipelineStage::kFragment &&
(ReturnsFragDepthAsValue(fn) || ReturnsFragDepthInStruct(sem, fn))) {
return true;
/// @param fn the function to examine
/// @returns true if @p fn has a return structure with a `@builtin(frag_depth)` attribute on one
/// of the members
bool ReturnsFragDepthInStruct(const ast::Function* fn) {
if (auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()) {
for (auto* member : struct_ty->Members()) {
if (ContainsFragDepth(member->Declaration()->attributes)) {
return true;
}
}
}
return false;
}
return false;
}
} // anonymous namespace
};
ClampFragDepth::ClampFragDepth() = default;
ClampFragDepth::~ClampFragDepth() = default;
Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&, DataMap&) const {
ProgramBuilder b;
CloneContext ctx{&b, src, /* auto_clone_symbols */ true};
// Abort on any use of push constants in the module.
for (auto* global : src->AST().GlobalVariables()) {
if (auto* var = global->As<ast::Var>()) {
auto* v = src->Sem().Get(var);
if (TINT_UNLIKELY(v->AddressSpace() == builtin::AddressSpace::kPushConstant)) {
TINT_ICE(Transform, b.Diagnostics())
<< "ClampFragDepth doesn't know how to handle module that already use push "
"constants";
return Program(std::move(b));
}
}
}
if (!ShouldRun(src)) {
return SkipTransform;
}
auto& sem = src->Sem();
auto& sym = src->Symbols();
// At least one entry-point needs clamping. Add the following to the module:
//
// enable chromium_experimental_push_constant;
//
// struct FragDepthClampArgs {
// min : f32,
// max : f32,
// }
// var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
//
// fn clamp_frag_depth(v : f32) -> f32 {
// return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
// }
b.Enable(builtin::Extension::kChromiumExperimentalPushConstant);
b.Structure(b.Symbols().New("FragDepthClampArgs"),
utils::Vector{b.Member("min", b.ty.f32()), b.Member("max", b.ty.f32())});
auto args_sym = b.Symbols().New("frag_depth_clamp_args");
b.GlobalVar(args_sym, b.ty("FragDepthClampArgs"), builtin::AddressSpace::kPushConstant);
auto base_fn_sym = b.Symbols().New("clamp_frag_depth");
b.Func(base_fn_sym, utils::Vector{b.Param("v", b.ty.f32())}, b.ty.f32(),
utils::Vector{b.Return(b.Call("clamp", "v", b.MemberAccessor(args_sym, "min"),
b.MemberAccessor(args_sym, "max")))});
// If true, the currently cloned function returns frag depth directly as a scalar
bool returns_frag_depth_as_value = false;
// If valid, the currently cloned function returns frag depth in a struct
// The symbol is the name of the helper function to apply the depth clamping.
Symbol returns_frag_depth_as_struct_helper;
// Map of io struct to helper function to return the structure with the depth clamping applied.
utils::Hashmap<const ast::Struct*, Symbol, 4u> io_structs_clamp_helpers;
// Register a callback that will be called for each visted AST function.
// This call wraps the cloning of the function's statements, and will assign to
// `returns_frag_depth_as_value` or `returns_frag_depth_as_struct_helper` if the function's
// return value requires depth clamping.
ctx.ReplaceAll([&](const ast::Function* fn) {
if (fn->PipelineStage() != ast::PipelineStage::kFragment) {
return ctx.CloneWithoutTransform(fn);
}
if (ReturnsFragDepthAsValue(fn)) {
TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_value, true);
return ctx.CloneWithoutTransform(fn);
}
if (ReturnsFragDepthInStruct(sem, fn)) {
// At most once per I/O struct, add the conversion function:
//
// fn clamp_frag_depth_S(s : S) -> S {
// return S(s.first, s.second, clamp_frag_depth(s.frag_depth), s.last);
// }
auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()->Declaration();
auto helper = io_structs_clamp_helpers.GetOrCreate(struct_ty, [&] {
auto return_ty = fn->return_type;
auto fn_sym =
b.Symbols().New("clamp_frag_depth_" + sym.NameFor(struct_ty->name->symbol));
utils::Vector<const ast::Expression*, 8u> initializer_args;
for (auto* member : struct_ty->members) {
const ast::Expression* arg =
b.MemberAccessor("s", ctx.Clone(member->name->symbol));
if (ContainsFragDepth(member->attributes)) {
arg = b.Call(base_fn_sym, arg);
}
initializer_args.Push(arg);
}
utils::Vector params{b.Param("s", ctx.Clone(return_ty))};
utils::Vector body{
b.Return(b.Call(ctx.Clone(return_ty), std::move(initializer_args))),
};
b.Func(fn_sym, params, ctx.Clone(return_ty), body);
return fn_sym;
});
TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_struct_helper, helper);
return ctx.CloneWithoutTransform(fn);
}
return ctx.CloneWithoutTransform(fn);
});
// Replace the return statements `return expr` with `return clamp_frag_depth(expr)`.
ctx.ReplaceAll([&](const ast::ReturnStatement* stmt) -> const ast::ReturnStatement* {
if (returns_frag_depth_as_value) {
return b.Return(stmt->source, b.Call(base_fn_sym, ctx.Clone(stmt->value)));
}
if (returns_frag_depth_as_struct_helper.IsValid()) {
return b.Return(stmt->source,
b.Call(returns_frag_depth_as_struct_helper, ctx.Clone(stmt->value)));
}
return nullptr;
});
ctx.Clone();
return Program(std::move(b));
return State{src}.Run();
}
} // namespace tint::transform

View File

@ -65,6 +65,9 @@ class ClampFragDepth final : public Castable<ClampFragDepth, Transform> {
ApplyResult Apply(const Program* program,
const DataMap& inputs,
DataMap& outputs) const override;
private:
struct State;
};
} // namespace tint::transform

View File

@ -18,6 +18,7 @@
#include <unordered_map>
#include <utility>
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/sem/function.h"
#include "src/tint/sem/member_accessor_expression.h"
@ -88,7 +89,7 @@ Transform::ApplyResult FirstIndexOffset::Apply(const Program* src,
if (auto* var = node->As<ast::Variable>()) {
for (auto* attr : var->attributes) {
if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
builtin::BuiltinValue builtin = builtin_attr->builtin;
builtin::BuiltinValue builtin = src->Sem().Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kVertexIndex) {
auto* sem_var = ctx.src->Sem().Get(var);
builtin_vars.emplace(sem_var, kFirstVertexName);
@ -105,7 +106,7 @@ Transform::ApplyResult FirstIndexOffset::Apply(const Program* src,
if (auto* member = node->As<ast::StructMember>()) {
for (auto* attr : member->attributes) {
if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
builtin::BuiltinValue builtin = builtin_attr->builtin;
builtin::BuiltinValue builtin = src->Sem().Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kVertexIndex) {
auto* sem_mem = ctx.src->Sem().Get(member);
builtin_members.emplace(sem_mem, kFirstVertexName);

View File

@ -19,6 +19,7 @@
#include <unordered_set>
#include <utility>
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/sem/function.h"
#include "src/tint/transform/canonicalize_entry_point_io.h"
@ -33,7 +34,7 @@ namespace {
bool ShouldRun(const Program* program) {
for (auto* node : program->ASTNodes().Objects()) {
if (auto* attr = node->As<ast::BuiltinAttribute>()) {
if (attr->builtin == builtin::BuiltinValue::kNumWorkgroups) {
if (program->Sem().Get(attr)->Value() == builtin::BuiltinValue::kNumWorkgroups) {
return true;
}
}
@ -100,7 +101,8 @@ Transform::ApplyResult NumWorkgroupsFromUniform::Apply(const Program* src,
for (auto* member : str->Members()) {
auto* builtin =
ast::GetAttribute<ast::BuiltinAttribute>(member->Declaration()->attributes);
if (!builtin || builtin->builtin != builtin::BuiltinValue::kNumWorkgroups) {
if (!builtin ||
src->Sem().Get(builtin)->Value() != builtin::BuiltinValue::kNumWorkgroups) {
continue;
}

View File

@ -20,6 +20,7 @@
#include "src/tint/ast/assignment_statement.h"
#include "src/tint/ast/bitcast_expression.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/sem/variable.h"
#include "src/tint/utils/compiler_macros.h"
@ -773,17 +774,18 @@ struct VertexPulling::State {
}
location_info[sem->Location().value()] = info;
} else {
auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes);
if (TINT_UNLIKELY(!builtin)) {
auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes);
if (TINT_UNLIKELY(!builtin_attr)) {
TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
return;
}
auto builtin = src->Sem().Get(builtin_attr)->Value();
// Check for existing vertex_index and instance_index builtins.
if (builtin->builtin == builtin::BuiltinValue::kVertexIndex) {
if (builtin == builtin::BuiltinValue::kVertexIndex) {
vertex_index_expr = [this, param]() {
return b.Expr(ctx.Clone(param->name->symbol));
};
} else if (builtin->builtin == builtin::BuiltinValue::kInstanceIndex) {
} else if (builtin == builtin::BuiltinValue::kInstanceIndex) {
instance_index_expr = [this, param]() {
return b.Expr(ctx.Clone(param->name->symbol));
};
@ -826,15 +828,16 @@ struct VertexPulling::State {
location_info[sem->Location().value()] = info;
has_locations = true;
} else {
auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(member->attributes);
if (TINT_UNLIKELY(!builtin)) {
auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(member->attributes);
if (TINT_UNLIKELY(!builtin_attr)) {
TINT_ICE(Transform, b.Diagnostics()) << "Invalid entry point parameter";
return;
}
auto builtin = src->Sem().Get(builtin_attr)->Value();
// Check for existing vertex_index and instance_index builtins.
if (builtin->builtin == builtin::BuiltinValue::kVertexIndex) {
if (builtin == builtin::BuiltinValue::kVertexIndex) {
vertex_index_expr = member_expr;
} else if (builtin->builtin == builtin::BuiltinValue::kInstanceIndex) {
} else if (builtin == builtin::BuiltinValue::kInstanceIndex) {
instance_index_expr = member_expr;
}
members_to_clone.Push(member);

View File

@ -21,6 +21,7 @@
#include <vector>
#include "src/tint/ast/workgroup_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/sem/function.h"
#include "src/tint/sem/variable.h"
@ -159,8 +160,9 @@ struct ZeroInitWorkgroupMemory::State {
// parameter
std::function<const ast::Expression*()> local_index;
for (auto* param : fn->params) {
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) {
if (builtin->builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
if (auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) {
auto builtin = sem.Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
local_index = [=] { return b.Expr(ctx.Clone(param->name->symbol)); };
break;
}
@ -168,9 +170,10 @@ struct ZeroInitWorkgroupMemory::State {
if (auto* str = sem.Get(param)->Type()->As<sem::Struct>()) {
for (auto* member : str->Members()) {
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(
if (auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(
member->Declaration()->attributes)) {
if (builtin->builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
auto builtin = sem.Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
local_index = [=] {
auto* param_expr = b.Expr(ctx.Clone(param->name->symbol));
auto* member_name = ctx.Clone(member->Declaration()->name);
@ -184,10 +187,9 @@ struct ZeroInitWorkgroupMemory::State {
}
if (!local_index) {
// No existing local index parameter. Append one to the entry point.
auto* param = b.Param(b.Symbols().New("local_invocation_index"), b.ty.u32(),
utils::Vector{
b.Builtin(builtin::BuiltinValue::kLocalInvocationIndex),
});
auto param_name = b.Symbols().New("local_invocation_index");
auto* local_invocation_index = b.Builtin(builtin::BuiltinValue::kLocalInvocationIndex);
auto* param = b.Param(param_name, b.ty.u32(), utils::Vector{local_invocation_index});
ctx.InsertBack(fn->params, param);
local_index = [=] { return b.Expr(param->name->symbol); };
}

View File

@ -192,14 +192,14 @@ struct EnumSet {
};
/// @returns an read-only iterator to the beginning of the set
Iterator begin() {
Iterator begin() const {
auto it = Iterator{set, -1};
++it; // Move to first set bit
return it;
}
/// @returns an iterator to the beginning of the set
Iterator end() { return Iterator{set, Iterator::kEnd}; }
Iterator end() const { return Iterator{set, Iterator::kEnd}; }
private:
static constexpr uint64_t Bit(Enum value) {

View File

@ -2155,9 +2155,10 @@ bool GeneratorImpl::EmitWorkgroupVariable(const sem::Variable* var) {
bool GeneratorImpl::EmitIOVariable(const sem::GlobalVariable* var) {
auto* decl = var->Declaration();
if (auto* b = ast::GetAttribute<ast::BuiltinAttribute>(decl->attributes)) {
if (auto* attr = ast::GetAttribute<ast::BuiltinAttribute>(decl->attributes)) {
auto builtin = program_->Sem().Get(attr)->Value();
// Use of gl_SampleID requires the GL_OES_sample_variables extension
if (RequiresOESSampleVariables(b->builtin)) {
if (RequiresOESSampleVariables(builtin)) {
requires_oes_sample_variables_ = true;
}
// Do not emit builtin (gl_) variables.

View File

@ -32,6 +32,7 @@
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/scope_stack.h"
#include "src/tint/transform/decompose_memory_access.h"

View File

@ -4210,8 +4210,9 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
} else {
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
}
} else if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
auto name = builtin_to_attribute(builtin->builtin);
} 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;

View File

@ -31,6 +31,7 @@
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/scope_stack.h"
#include "src/tint/sem/binding_point.h"

View File

@ -2077,14 +2077,15 @@ bool GeneratorImpl::EmitEntryPointFunction(const ast::Function* func) {
auto& attrs = param->attributes;
bool builtin_found = false;
for (auto* attr : attrs) {
auto* builtin = attr->As<ast::BuiltinAttribute>();
if (!builtin) {
auto* builtin_attr = attr->As<ast::BuiltinAttribute>();
if (!builtin_attr) {
continue;
}
auto builtin = program_->Sem().Get(builtin_attr)->Value();
builtin_found = true;
auto name = builtin_to_attribute(builtin->builtin);
auto name = builtin_to_attribute(builtin);
if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unknown builtin");
return false;
@ -2854,8 +2855,9 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
for (auto* attr : decl->attributes) {
bool ok = Switch(
attr,
[&](const ast::BuiltinAttribute* builtin) {
auto name = builtin_to_attribute(builtin->builtin);
[&](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;

View File

@ -36,6 +36,7 @@
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program.h"
#include "src/tint/scope_stack.h"
#include "src/tint/sem/struct.h"

View File

@ -548,8 +548,9 @@ bool Builder::GenerateExecutionModes(const ast::Function* func, uint32_t id) {
Operand(wgsize[0].value()), Operand(wgsize[1].value()), Operand(wgsize[2].value())});
}
for (auto builtin : func_sem->TransitivelyReferencedBuiltinVariables()) {
if (builtin.second->builtin == builtin::BuiltinValue::kFragDepth) {
for (auto it : func_sem->TransitivelyReferencedBuiltinVariables()) {
auto builtin = builder_.Sem().Get(it.second)->Value();
if (builtin == builtin::BuiltinValue::kFragDepth) {
push_execution_mode(spv::Op::OpExecutionMode,
{Operand(id), U32Operand(SpvExecutionModeDepthReplacing)});
}
@ -837,10 +838,11 @@ bool Builder::GenerateGlobalVariable(const ast::Variable* v) {
for (auto* attr : v->attributes) {
bool ok = Switch(
attr,
[&](const ast::BuiltinAttribute* builtin) {
[&](const ast::BuiltinAttribute* builtin_attr) {
auto builtin = builder_.Sem().Get(builtin_attr)->Value();
push_annot(spv::Op::OpDecorate,
{Operand(var_id), U32Operand(SpvDecorationBuiltIn),
U32Operand(ConvertBuiltin(builtin->builtin, sem->AddressSpace()))});
U32Operand(ConvertBuiltin(builtin, sem->AddressSpace()))});
return true;
},
[&](const ast::LocationAttribute*) {

View File

@ -33,6 +33,7 @@
#include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h"
#include "src/tint/scope_stack.h"
#include "src/tint/sem/builtin.h"

View File

@ -564,7 +564,11 @@ bool GeneratorImpl::EmitAttributes(std::ostream& out,
return true;
},
[&](const ast::BuiltinAttribute* builtin) {
out << "builtin(" << builtin->builtin << ")";
out << "builtin(";
if (!EmitExpression(out, builtin->builtin)) {
return false;
}
out << ")";
return true;
},
[&](const ast::DiagnosticAttribute* diagnostic) {

View File

@ -15,6 +15,7 @@
#include "src/tint/ast/stage_attribute.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/workgroup_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/writer/wgsl/test_helper.h"
using namespace tint::number_suffixes; // NOLINT

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/type/depth_texture.h"
#include "src/tint/type/multisampled_texture.h"
#include "src/tint/type/sampled_texture.h"

View File

@ -1,4 +1,4 @@
static float3 position = float3(0.0f, 0.0f, 0.0f);
static float3 position_1 = float3(0.0f, 0.0f, 0.0f);
cbuffer cbuffer_x_14 : register(b2, space2) {
uint4 x_14[17];
};
@ -18,13 +18,13 @@ float4x4 tint_symbol_4(uint4 buffer[17], uint offset) {
void main_1() {
float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f);
float3 p = float3(0.0f, 0.0f, 0.0f);
const float3 x_13 = position;
const float3 x_13 = position_1;
q = float4(x_13.x, x_13.y, x_13.z, 1.0f);
const float4 x_21 = q;
p = float3(x_21.x, x_21.y, x_21.z);
const float x_27 = p.x;
const float x_41 = asfloat(x_14[13].x);
const float x_45 = position.y;
const float x_45 = position_1.y;
const float x_49 = asfloat(x_14[4].x);
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
const float x_55 = p.y;
@ -45,7 +45,7 @@ struct main_out {
float2 vUV_1;
};
struct tint_symbol_1 {
float3 position_param : TEXCOORD0;
float3 position_1_param : TEXCOORD0;
float3 normal_param : TEXCOORD1;
float2 uv_param : TEXCOORD2;
};
@ -54,8 +54,8 @@ struct tint_symbol_2 {
float4 gl_Position : SV_Position;
};
main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param) {
position = position_param;
main_out main_inner(float3 position_1_param, float2 uv_param, float3 normal_param) {
position_1 = position_1_param;
uv = uv_param;
normal = normal_param;
main_1();
@ -64,7 +64,7 @@ main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param)
}
tint_symbol_2 main(tint_symbol_1 tint_symbol) {
const main_out inner_result = main_inner(tint_symbol.position_param, tint_symbol.uv_param, tint_symbol.normal_param);
const main_out inner_result = main_inner(tint_symbol.position_1_param, tint_symbol.uv_param, tint_symbol.normal_param);
tint_symbol_2 wrapper_result = (tint_symbol_2)0;
wrapper_result.gl_Position = inner_result.gl_Position;
wrapper_result.vUV_1 = inner_result.vUV_1;

View File

@ -1,4 +1,4 @@
static float3 position = float3(0.0f, 0.0f, 0.0f);
static float3 position_1 = float3(0.0f, 0.0f, 0.0f);
cbuffer cbuffer_x_14 : register(b2, space2) {
uint4 x_14[17];
};
@ -18,13 +18,13 @@ float4x4 tint_symbol_4(uint4 buffer[17], uint offset) {
void main_1() {
float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f);
float3 p = float3(0.0f, 0.0f, 0.0f);
const float3 x_13 = position;
const float3 x_13 = position_1;
q = float4(x_13.x, x_13.y, x_13.z, 1.0f);
const float4 x_21 = q;
p = float3(x_21.x, x_21.y, x_21.z);
const float x_27 = p.x;
const float x_41 = asfloat(x_14[13].x);
const float x_45 = position.y;
const float x_45 = position_1.y;
const float x_49 = asfloat(x_14[4].x);
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
const float x_55 = p.y;
@ -45,7 +45,7 @@ struct main_out {
float2 vUV_1;
};
struct tint_symbol_1 {
float3 position_param : TEXCOORD0;
float3 position_1_param : TEXCOORD0;
float3 normal_param : TEXCOORD1;
float2 uv_param : TEXCOORD2;
};
@ -54,8 +54,8 @@ struct tint_symbol_2 {
float4 gl_Position : SV_Position;
};
main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param) {
position = position_param;
main_out main_inner(float3 position_1_param, float2 uv_param, float3 normal_param) {
position_1 = position_1_param;
uv = uv_param;
normal = normal_param;
main_1();
@ -64,7 +64,7 @@ main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param)
}
tint_symbol_2 main(tint_symbol_1 tint_symbol) {
const main_out inner_result = main_inner(tint_symbol.position_param, tint_symbol.uv_param, tint_symbol.normal_param);
const main_out inner_result = main_inner(tint_symbol.position_1_param, tint_symbol.uv_param, tint_symbol.normal_param);
tint_symbol_2 wrapper_result = (tint_symbol_2)0;
wrapper_result.gl_Position = inner_result.gl_Position;
wrapper_result.vUV_1 = inner_result.vUV_1;

View File

@ -1,6 +1,6 @@
#version 310 es
layout(location = 0) in vec3 position_param_1;
layout(location = 0) in vec3 position_1_param_1;
layout(location = 2) in vec2 uv_param_1;
layout(location = 1) in vec3 normal_param_1;
layout(location = 0) out vec2 vUV_1_1;
@ -21,7 +21,7 @@ struct LeftOver {
strided_arr test[4];
};
vec3 position = vec3(0.0f, 0.0f, 0.0f);
vec3 position_1 = vec3(0.0f, 0.0f, 0.0f);
layout(binding = 2, std140) uniform x_14_block_ubo {
LeftOver inner;
} x_14;
@ -33,13 +33,13 @@ vec4 tint_symbol = vec4(0.0f, 0.0f, 0.0f, 0.0f);
void main_1() {
vec4 q = vec4(0.0f, 0.0f, 0.0f, 0.0f);
vec3 p = vec3(0.0f, 0.0f, 0.0f);
vec3 x_13 = position;
vec3 x_13 = position_1;
q = vec4(x_13.x, x_13.y, x_13.z, 1.0f);
vec4 x_21 = q;
p = vec3(x_21.x, x_21.y, x_21.z);
float x_27 = p.x;
float x_41 = x_14.inner.test[0].el;
float x_45 = position.y;
float x_45 = position_1.y;
float x_49 = x_14.inner.time;
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
float x_55 = p.y;
@ -60,8 +60,8 @@ struct main_out {
vec2 vUV_1;
};
main_out tint_symbol_1(vec3 position_param, vec2 uv_param, vec3 normal_param) {
position = position_param;
main_out tint_symbol_1(vec3 position_1_param, vec2 uv_param, vec3 normal_param) {
position_1 = position_1_param;
uv = uv_param;
normal = normal_param;
main_1();
@ -71,7 +71,7 @@ main_out tint_symbol_1(vec3 position_param, vec2 uv_param, vec3 normal_param) {
void main() {
gl_PointSize = 1.0;
main_out inner_result = tint_symbol_1(position_param_1, uv_param_1, normal_param_1);
main_out inner_result = tint_symbol_1(position_1_param_1, uv_param_1, normal_param_1);
gl_Position = inner_result.tint_symbol;
vUV_1_1 = inner_result.vUV_1;
gl_Position.y = -(gl_Position.y);

View File

@ -58,7 +58,7 @@ struct main_out {
};
struct tint_symbol_2 {
float3 position_param [[attribute(0)]];
float3 position_1_param [[attribute(0)]];
float3 normal_param [[attribute(1)]];
float2 uv_param [[attribute(2)]];
};
@ -68,9 +68,9 @@ struct tint_symbol_3 {
float4 gl_Position [[position]];
};
main_out tint_symbol_inner(float3 position_param, float2 uv_param, float3 normal_param, thread float3* const tint_symbol_10, thread float2* const tint_symbol_11, const constant LeftOver* const tint_symbol_13, thread float4* const tint_symbol_14, thread float2* const tint_symbol_15) {
main_out tint_symbol_inner(float3 position_1_param, float2 uv_param, float3 normal_param, thread float3* const tint_symbol_10, thread float2* const tint_symbol_11, const constant LeftOver* const tint_symbol_13, thread float4* const tint_symbol_14, thread float2* const tint_symbol_15) {
thread float3 tint_symbol_12 = 0.0f;
*(tint_symbol_10) = position_param;
*(tint_symbol_10) = position_1_param;
*(tint_symbol_11) = uv_param;
tint_symbol_12 = normal_param;
main_1(tint_symbol_10, tint_symbol_13, tint_symbol_14, tint_symbol_11, tint_symbol_15);
@ -83,7 +83,7 @@ vertex tint_symbol_3 tint_symbol(const constant LeftOver* tint_symbol_18 [[buffe
thread float2 tint_symbol_17 = 0.0f;
thread float4 tint_symbol_19 = 0.0f;
thread float2 tint_symbol_20 = 0.0f;
main_out const inner_result = tint_symbol_inner(tint_symbol_1.position_param, tint_symbol_1.uv_param, tint_symbol_1.normal_param, &(tint_symbol_16), &(tint_symbol_17), tint_symbol_18, &(tint_symbol_19), &(tint_symbol_20));
main_out const inner_result = tint_symbol_inner(tint_symbol_1.position_1_param, tint_symbol_1.uv_param, tint_symbol_1.normal_param, &(tint_symbol_16), &(tint_symbol_17), tint_symbol_18, &(tint_symbol_19), &(tint_symbol_20));
tint_symbol_3 wrapper_result = {};
wrapper_result.gl_Position = inner_result.gl_Position;
wrapper_result.vUV_1 = inner_result.vUV_1;

View File

@ -6,14 +6,14 @@
OpCapability Shader
%76 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Vertex %main "main" %position_param_1 %uv_param_1 %normal_param_1 %gl_Position_1 %vUV_1_1 %vertex_point_size
OpName %position_param_1 "position_param_1"
OpEntryPoint Vertex %main "main" %position_1_param_1 %uv_param_1 %normal_param_1 %gl_Position_1 %vUV_1_1 %vertex_point_size
OpName %position_1_param_1 "position_1_param_1"
OpName %uv_param_1 "uv_param_1"
OpName %normal_param_1 "normal_param_1"
OpName %gl_Position_1 "gl_Position_1"
OpName %vUV_1_1 "vUV_1_1"
OpName %vertex_point_size "vertex_point_size"
OpName %position "position"
OpName %position_1 "position_1"
OpName %x_14_block "x_14_block"
OpMemberName %x_14_block 0 "inner"
OpName %LeftOver "LeftOver"
@ -35,11 +35,11 @@
OpMemberName %main_out 0 "gl_Position"
OpMemberName %main_out 1 "vUV_1"
OpName %main_inner "main_inner"
OpName %position_param "position_param"
OpName %position_1_param "position_1_param"
OpName %uv_param "uv_param"
OpName %normal_param "normal_param"
OpName %main "main"
OpDecorate %position_param_1 Location 0
OpDecorate %position_1_param_1 Location 0
OpDecorate %uv_param_1 Location 2
OpDecorate %normal_param_1 Location 1
OpDecorate %gl_Position_1 BuiltIn Position
@ -66,7 +66,7 @@
%float = OpTypeFloat 32
%v3float = OpTypeVector %float 3
%_ptr_Input_v3float = OpTypePointer Input %v3float
%position_param_1 = OpVariable %_ptr_Input_v3float Input
%position_1_param_1 = OpVariable %_ptr_Input_v3float Input
%v2float = OpTypeVector %float 2
%_ptr_Input_v2float = OpTypePointer Input %v2float
%uv_param_1 = OpVariable %_ptr_Input_v2float Input
@ -83,7 +83,7 @@
%vertex_point_size = OpVariable %_ptr_Output_float Output %18
%_ptr_Private_v3float = OpTypePointer Private %v3float
%21 = OpConstantNull %v3float
%position = OpVariable %_ptr_Private_v3float Private %21
%position_1 = OpVariable %_ptr_Private_v3float Private %21
%mat4v4float = OpTypeMatrix %v4float 4
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
@ -123,7 +123,7 @@
%42 = OpLabel
%q = OpVariable %_ptr_Function_v4float Function %12
%p = OpVariable %_ptr_Function_v3float Function %21
%47 = OpLoad %v3float %position
%47 = OpLoad %v3float %position_1
%48 = OpCompositeExtract %float %47 0
%49 = OpCompositeExtract %float %47 1
%50 = OpCompositeExtract %float %47 2
@ -139,7 +139,7 @@
%61 = OpLoad %float %60
%66 = OpAccessChain %_ptr_Uniform_float %x_14 %uint_0 %uint_3 %64 %uint_0
%67 = OpLoad %float %66
%70 = OpAccessChain %_ptr_Private_float %position %uint_1
%70 = OpAccessChain %_ptr_Private_float %position_1 %uint_1
%71 = OpLoad %float %70
%72 = OpAccessChain %_ptr_Uniform_float %x_14 %uint_0 %uint_1
%73 = OpLoad %float %72
@ -177,11 +177,11 @@
OpReturn
OpFunctionEnd
%main_inner = OpFunction %main_out None %104
%position_param = OpFunctionParameter %v3float
%position_1_param = OpFunctionParameter %v3float
%uv_param = OpFunctionParameter %v2float
%normal_param = OpFunctionParameter %v3float
%110 = OpLabel
OpStore %position %position_param
OpStore %position_1 %position_1_param
OpStore %uv %uv_param
OpStore %normal %normal_param
%111 = OpFunctionCall %void %main_1
@ -192,7 +192,7 @@
OpFunctionEnd
%main = OpFunction %void None %39
%116 = OpLabel
%118 = OpLoad %v3float %position_param_1
%118 = OpLoad %v3float %position_1_param_1
%119 = OpLoad %v2float %uv_param_1
%120 = OpLoad %v3float %normal_param_1
%117 = OpFunctionCall %main_out %main_inner %118 %119 %120

View File

@ -18,7 +18,7 @@ struct LeftOver {
test : Arr_1,
}
var<private> position : vec3<f32>;
var<private> position_1 : vec3<f32>;
@group(2) @binding(2) var<uniform> x_14 : LeftOver;
@ -33,13 +33,13 @@ var<private> gl_Position : vec4<f32>;
fn main_1() {
var q : vec4<f32>;
var p : vec3<f32>;
let x_13 : vec3<f32> = position;
let x_13 : vec3<f32> = position_1;
q = vec4<f32>(x_13.x, x_13.y, x_13.z, 1.0f);
let x_21 : vec4<f32> = q;
p = vec3<f32>(x_21.x, x_21.y, x_21.z);
let x_27 : f32 = p.x;
let x_41 : f32 = x_14.test[0i].el;
let x_45 : f32 = position.y;
let x_45 : f32 = position_1.y;
let x_49 : f32 = x_14.time;
p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
let x_55 : f32 = p.y;
@ -63,8 +63,8 @@ struct main_out {
}
@vertex
fn main(@location(0) position_param : vec3<f32>, @location(2) uv_param : vec2<f32>, @location(1) normal_param : vec3<f32>) -> main_out {
position = position_param;
fn main(@location(0) position_1_param : vec3<f32>, @location(2) uv_param : vec2<f32>, @location(1) normal_param : vec3<f32>) -> main_out {
position_1 = position_1_param;
uv = uv_param;
normal = normal_param;
main_1();

View File

@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : rhs));
}
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
const uint x_25 = idx;
if (!((x_25 < 6u))) {

View File

@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : rhs));
}
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
const uint x_25 = idx;
if (!((x_25 < 6u))) {

View File

@ -10,9 +10,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : rhs));
}
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
uint x_25 = idx;
if (!((x_25 < 6u))) {

View File

@ -22,9 +22,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % select(rhs, 1u, (rhs == 0u)));
}
void compute_main_inner(uint local_invocation_index, threadgroup tint_array<tint_array<tint_array<atomic_uint, 1>, 2>, 3>* const tint_symbol_3) {
void compute_main_inner(uint local_invocation_index_2, threadgroup tint_array<tint_array<tint_array<atomic_uint, 1>, 2>, 3>* const tint_symbol_3) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
uint const x_25 = idx;
if (!((x_25 < 6u))) {

View File

@ -17,7 +17,7 @@
OpName %lhs_0 "lhs"
OpName %rhs_0 "rhs"
OpName %compute_main_inner "compute_main_inner"
OpName %local_invocation_index "local_invocation_index"
OpName %local_invocation_index_2 "local_invocation_index_2"
OpName %idx "idx"
OpName %compute_main_1 "compute_main_1"
OpName %compute_main_inner_1 "compute_main_inner_1"
@ -75,11 +75,11 @@
OpReturnValue %30
OpFunctionEnd
%compute_main_inner = OpFunction %void None %31
%local_invocation_index = OpFunctionParameter %uint
%local_invocation_index_2 = OpFunctionParameter %uint
%35 = OpLabel
%idx = OpVariable %_ptr_Function_uint Function %6
OpStore %idx %6
OpStore %idx %local_invocation_index
OpStore %idx %local_invocation_index_2
OpBranch %38
%38 = OpLabel
OpLoopMerge %39 %40 None

View File

@ -8,9 +8,9 @@ var<private> local_invocation_index_1 : u32;
var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
fn compute_main_inner(local_invocation_index : u32) {
fn compute_main_inner(local_invocation_index_2 : u32) {
var idx : u32 = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
loop {
let x_25 : u32 = idx;
if (!((x_25 < 6u))) {

View File

@ -1,9 +1,9 @@
static uint local_invocation_index_1 = 0u;
groupshared uint wg[4];
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
const uint x_21 = idx;
if (!((x_21 < 4u))) {

View File

@ -1,9 +1,9 @@
static uint local_invocation_index_1 = 0u;
groupshared uint wg[4];
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
const uint x_21 = idx;
if (!((x_21 < 4u))) {

View File

@ -2,9 +2,9 @@
uint local_invocation_index_1 = 0u;
shared uint wg[4];
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
uint x_21 = idx;
if (!((x_21 < 4u))) {

View File

@ -14,9 +14,9 @@ struct tint_array {
T elements[N];
};
void compute_main_inner(uint local_invocation_index, threadgroup tint_array<atomic_uint, 4>* const tint_symbol) {
void compute_main_inner(uint local_invocation_index_2, threadgroup tint_array<atomic_uint, 4>* const tint_symbol) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
uint const x_21 = idx;
if (!((x_21 < 4u))) {

View File

@ -11,7 +11,7 @@
OpName %local_invocation_index_1 "local_invocation_index_1"
OpName %wg "wg"
OpName %compute_main_inner "compute_main_inner"
OpName %local_invocation_index "local_invocation_index"
OpName %local_invocation_index_2 "local_invocation_index_2"
OpName %idx "idx"
OpName %compute_main_1 "compute_main_1"
OpName %compute_main_inner_1 "compute_main_inner_1"
@ -43,11 +43,11 @@
%int_1 = OpConstant %int 1
%45 = OpTypeFunction %void
%compute_main_inner = OpFunction %void None %11
%local_invocation_index = OpFunctionParameter %uint
%local_invocation_index_2 = OpFunctionParameter %uint
%15 = OpLabel
%idx = OpVariable %_ptr_Function_uint Function %6
OpStore %idx %6
OpStore %idx %local_invocation_index
OpStore %idx %local_invocation_index_2
OpBranch %18
%18 = OpLabel
OpLoopMerge %19 %20 None

View File

@ -4,9 +4,9 @@ var<private> local_invocation_index_1 : u32;
var<workgroup> wg : array<atomic<u32>, 4u>;
fn compute_main_inner(local_invocation_index : u32) {
fn compute_main_inner(local_invocation_index_2 : u32) {
var idx : u32 = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
loop {
let x_21 : u32 = idx;
if (!((x_21 < 4u))) {

View File

@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : rhs));
}
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
const uint x_25 = idx;
if (!((x_25 < 6u))) {

View File

@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : rhs));
}
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
const uint x_25 = idx;
if (!((x_25 < 6u))) {

View File

@ -10,9 +10,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : rhs));
}
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
uint x_25 = idx;
if (!((x_25 < 6u))) {

View File

@ -22,9 +22,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % select(rhs, 1u, (rhs == 0u)));
}
void compute_main_inner(uint local_invocation_index, threadgroup tint_array<tint_array<tint_array<atomic_uint, 1>, 2>, 3>* const tint_symbol_3) {
void compute_main_inner(uint local_invocation_index_2, threadgroup tint_array<tint_array<tint_array<atomic_uint, 1>, 2>, 3>* const tint_symbol_3) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
uint const x_25 = idx;
if (!((x_25 < 6u))) {

View File

@ -17,7 +17,7 @@
OpName %lhs_0 "lhs"
OpName %rhs_0 "rhs"
OpName %compute_main_inner "compute_main_inner"
OpName %local_invocation_index "local_invocation_index"
OpName %local_invocation_index_2 "local_invocation_index_2"
OpName %idx "idx"
OpName %compute_main_1 "compute_main_1"
OpName %compute_main_inner_1 "compute_main_inner_1"
@ -75,11 +75,11 @@
OpReturnValue %30
OpFunctionEnd
%compute_main_inner = OpFunction %void None %31
%local_invocation_index = OpFunctionParameter %uint
%local_invocation_index_2 = OpFunctionParameter %uint
%35 = OpLabel
%idx = OpVariable %_ptr_Function_uint Function %6
OpStore %idx %6
OpStore %idx %local_invocation_index
OpStore %idx %local_invocation_index_2
OpBranch %38
%38 = OpLabel
OpLoopMerge %39 %40 None

View File

@ -8,9 +8,9 @@ var<private> local_invocation_index_1 : u32;
var<workgroup> wg : array<array<array<atomic<u32>, 1u>, 2u>, 3u>;
fn compute_main_inner(local_invocation_index : u32) {
fn compute_main_inner(local_invocation_index_2 : u32) {
var idx : u32 = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
loop {
let x_25 : u32 = idx;
if (!((x_25 < 6u))) {

View File

@ -7,9 +7,9 @@ struct S_atomic {
static uint local_invocation_index_1 = 0u;
groupshared S_atomic wg[10];
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
const uint x_23 = idx;
if (!((x_23 < 10u))) {

View File

@ -7,9 +7,9 @@ struct S_atomic {
static uint local_invocation_index_1 = 0u;
groupshared S_atomic wg[10];
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
const uint x_23 = idx;
if (!((x_23 < 10u))) {

View File

@ -14,9 +14,9 @@ struct S {
uint local_invocation_index_1 = 0u;
shared S_atomic wg[10];
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
uint x_23 = idx;
if (!((x_23 < 10u))) {

View File

@ -26,9 +26,9 @@ struct S {
uint y;
};
void compute_main_inner(uint local_invocation_index, threadgroup tint_array<S_atomic, 10>* const tint_symbol) {
void compute_main_inner(uint local_invocation_index_2, threadgroup tint_array<S_atomic, 10>* const tint_symbol) {
uint idx = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
while (true) {
uint const x_23 = idx;
if (!((x_23 < 10u))) {

View File

@ -15,7 +15,7 @@
OpMemberName %S_atomic 2 "y"
OpName %wg "wg"
OpName %compute_main_inner "compute_main_inner"
OpName %local_invocation_index "local_invocation_index"
OpName %local_invocation_index_2 "local_invocation_index_2"
OpName %idx "idx"
OpName %compute_main_1 "compute_main_1"
OpName %compute_main_inner_1 "compute_main_inner_1"
@ -54,11 +54,11 @@
%int_4 = OpConstant %int 4
%51 = OpTypeFunction %void
%compute_main_inner = OpFunction %void None %13
%local_invocation_index = OpFunctionParameter %uint
%local_invocation_index_2 = OpFunctionParameter %uint
%17 = OpLabel
%idx = OpVariable %_ptr_Function_uint Function %6
OpStore %idx %6
OpStore %idx %local_invocation_index
OpStore %idx %local_invocation_index_2
OpBranch %20
%20 = OpLabel
OpLoopMerge %21 %22 None

View File

@ -22,9 +22,9 @@ var<private> local_invocation_index_1 : u32;
var<workgroup> wg : array<S_atomic, 10u>;
fn compute_main_inner(local_invocation_index : u32) {
fn compute_main_inner(local_invocation_index_2 : u32) {
var idx : u32 = 0u;
idx = local_invocation_index;
idx = local_invocation_index_2;
loop {
let x_23 : u32 = idx;
if (!((x_23 < 10u))) {

View File

@ -7,7 +7,7 @@ struct S_atomic {
static uint local_invocation_index_1 = 0u;
groupshared S_atomic wg;
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
wg.x = 0;
uint atomic_result = 0u;
InterlockedExchange(wg.a, 0u, atomic_result);

View File

@ -7,7 +7,7 @@ struct S_atomic {
static uint local_invocation_index_1 = 0u;
groupshared S_atomic wg;
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
wg.x = 0;
uint atomic_result = 0u;
InterlockedExchange(wg.a, 0u, atomic_result);

View File

@ -14,7 +14,7 @@ struct S {
uint local_invocation_index_1 = 0u;
shared S_atomic wg;
void compute_main_inner(uint local_invocation_index) {
void compute_main_inner(uint local_invocation_index_2) {
wg.x = 0;
atomicExchange(wg.a, 0u);
atomicExchange(wg.b, 0u);

View File

@ -13,7 +13,7 @@ struct S {
uint b;
};
void compute_main_inner(uint local_invocation_index, threadgroup S_atomic* const tint_symbol) {
void compute_main_inner(uint local_invocation_index_2, threadgroup S_atomic* const tint_symbol) {
(*(tint_symbol)).x = 0;
atomic_store_explicit(&((*(tint_symbol)).a), 0u, memory_order_relaxed);
atomic_store_explicit(&((*(tint_symbol)).b), 0u, memory_order_relaxed);

Some files were not shown because too many files have changed in this diff Show More