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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "gtest/gtest-spi.h"
#include "src/tint/ast/test_helper.h" #include "src/tint/ast/test_helper.h"
#include "src/tint/builtin/builtin_value.h"
namespace tint::ast { namespace tint::ast {
namespace { namespace {
@ -20,8 +23,27 @@ namespace {
using BuiltinAttributeTest = TestHelper; using BuiltinAttributeTest = TestHelper;
TEST_F(BuiltinAttributeTest, Creation) { TEST_F(BuiltinAttributeTest, Creation) {
auto* d = create<BuiltinAttribute>(builtin::BuiltinValue::kFragDepth); auto* d = Builtin(builtin::BuiltinValue::kFragDepth);
EXPECT_EQ(builtin::BuiltinValue::kFragDepth, d->builtin); 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 } // namespace

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,32 +26,146 @@ namespace {
const char* kWGSLReservedWords[] = { const char* kWGSLReservedWords[] = {
// Please keep this list sorted // Please keep this list sorted
"array", "as", "asm", "array",
"bf16", "binding", "block", "as",
"bool", "break", "builtin", "asm",
"case", "cast", "compute", "atomic",
"const", "continue", "default", "bf16",
"discard", "do", "else", "binding",
"elseif", "entry_point", "enum", "block",
"f16", "f32", "fallthrough", "bool",
"false", "fn", "for", "break",
"fragment", "i16", "i32", "builtin",
"i64", "i8", "if", "case",
"image", "import", "in", "cast",
"let", "location", "loop", "compute",
"mat2x2", "mat2x3", "mat2x4", "const",
"mat3x2", "mat3x3", "mat3x4", "continue",
"mat4x2", "mat4x3", "mat4x4", "default",
"offset", "out", "override", "discard",
"premerge", "private", "ptr", "do",
"regardless", "return", "set", "else",
"storage", "struct", "switch", "elseif",
"true", "type", "typedef", "entry_point",
"u16", "u32", "u64", "enum",
"u8", "uniform", "uniform_constant", "f16",
"unless", "using", "var", "f32",
"vec2", "vec3", "vec4", "fallthrough",
"vertex", "void", "while", "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", "workgroup",
}; };

View File

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

View File

@ -37,6 +37,7 @@ TINT_END_DISABLE_WARNING(NEWLINE_EOF);
#include "src/tint/program_builder.h" #include "src/tint/program_builder.h"
#include "src/tint/reader/reader.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/entry_point_info.h"
#include "src/tint/reader/spirv/enum_converter.h" #include "src/tint/reader/spirv/enum_converter.h"
#include "src/tint/reader/spirv/namer.h" #include "src/tint/reader/spirv/namer.h"
@ -122,7 +123,6 @@ struct WorkgroupSizeInfo {
/// Parser implementation for SPIR-V. /// Parser implementation for SPIR-V.
class ParserImpl : Reader { class ParserImpl : Reader {
using AttributeList = utils::Vector<const ast::Attribute*, 8>;
using ExpressionList = utils::Vector<const ast::Expression*, 8>; using ExpressionList = utils::Vector<const ast::Expression*, 8>;
public: public:
@ -261,7 +261,7 @@ class ParserImpl : Reader {
/// @returns false when the variable should not be emitted as a variable /// @returns false when the variable should not be emitted as a variable
bool ConvertDecorationsForVariable(uint32_t id, bool ConvertDecorationsForVariable(uint32_t id,
const Type** store_type, const Type** store_type,
AttributeList* attributes, Attributes& attributes,
bool transfer_pipeline_io); bool transfer_pipeline_io);
/// Converts SPIR-V decorations for pipeline IO into AST decorations. /// Converts SPIR-V decorations for pipeline IO into AST decorations.
@ -271,15 +271,15 @@ class ParserImpl : Reader {
/// @returns false if conversion fails /// @returns false if conversion fails
bool ConvertPipelineDecorations(const Type* store_type, bool ConvertPipelineDecorations(const Type* store_type,
const DecorationList& decorations, const DecorationList& decorations,
AttributeList* attributes); Attributes& attributes);
/// Updates the attribute list, placing a non-null location decoration into /// Updates the attribute list, placing a non-null location decoration into
/// the list, replacing an existing one if it exists. Does nothing if the /// the list, replacing an existing one if it exists. Does nothing if the
/// replacement is nullptr. /// replacement is nullptr.
/// Assumes the list contains at most one Location decoration. /// 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 /// @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 /// Converts a SPIR-V struct member decoration into a number of AST
/// decorations. If the decoration is recognized but deliberately dropped, /// decorations. If the decoration is recognized but deliberately dropped,
@ -290,7 +290,7 @@ class ParserImpl : Reader {
/// @param member_ty the type of the member /// @param member_ty the type of the member
/// @param decoration an encoded SPIR-V Decoration /// @param decoration an encoded SPIR-V Decoration
/// @returns the AST decorations /// @returns the AST decorations
AttributeList ConvertMemberDecoration(uint32_t struct_type_id, Attributes ConvertMemberDecoration(uint32_t struct_type_id,
uint32_t member_index, uint32_t member_index,
const Type* member_ty, const Type* member_ty,
const Decoration& decoration); const Decoration& decoration);
@ -433,7 +433,7 @@ class ParserImpl : Reader {
/// @param access the access /// @param access the access
/// @param storage_type the storage type of the variable /// @param storage_type the storage type of the variable
/// @param initializer the variable initializer /// @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 /// @returns a new Variable node, or null in the ignorable variable case and
/// in the error case /// in the error case
const ast::Var* MakeVar(uint32_t id, const ast::Var* MakeVar(uint32_t id,
@ -441,7 +441,7 @@ class ParserImpl : Reader {
builtin::Access access, builtin::Access access,
const Type* storage_type, const Type* storage_type,
const ast::Expression* initializer, const ast::Expression* initializer,
AttributeList decorations); Attributes attributes);
/// Creates an AST 'let' node for a SPIR-V ID, including any attached decorations,. /// Creates an AST 'let' node for a SPIR-V ID, including any attached decorations,.
/// @param id the SPIR-V result ID /// @param id the SPIR-V result ID
@ -454,20 +454,20 @@ class ParserImpl : Reader {
/// @param id the SPIR-V result ID /// @param id the SPIR-V result ID
/// @param type the type of the variable /// @param type the type of the variable
/// @param initializer the variable initializer /// @param initializer the variable initializer
/// @param decorations the variable decorations /// @param attributes the variable attributes
/// @returns the AST 'override' node /// @returns the AST 'override' node
const ast::Override* MakeOverride(uint32_t id, const ast::Override* MakeOverride(uint32_t id,
const Type* type, const Type* type,
const ast::Expression* initializer, const ast::Expression* initializer,
AttributeList decorations); Attributes attributes);
/// Creates an AST parameter node for a SPIR-V ID, including any attached decorations, unless /// Creates an AST parameter node for a SPIR-V ID, including any attached decorations, unless
/// it's an ignorable builtin variable. /// it's an ignorable builtin variable.
/// @param id the SPIR-V result ID /// @param id the SPIR-V result ID
/// @param type the type of the parameter /// @param type the type of the parameter
/// @param decorations the parameter decorations /// @param attributes the parameter attributes
/// @returns the AST parameter node /// @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. /// Returns true if a constant expression can be generated.
/// @param id the SPIR-V ID of the value /// @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 p = parser(std::vector<uint32_t>{});
auto result = p->ConvertMemberDecoration(1, 1, nullptr, {}); 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")); 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 p = parser(std::vector<uint32_t>{});
auto result = p->ConvertMemberDecoration(12, 13, nullptr, {uint32_t(spv::Decoration::Offset)}); 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 " EXPECT_THAT(p->error(), Eq("malformed Offset decoration: expected 1 literal "
"operand, has 0: member 13 of SPIR-V type 12")); "operand, has 0: member 13 of SPIR-V type 12"));
} }
@ -42,7 +42,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_OffsetWithTooManyOperands) {
auto result = auto result =
p->ConvertMemberDecoration(12, 13, nullptr, {uint32_t(spv::Decoration::Offset), 3, 4}); 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 " EXPECT_THAT(p->error(), Eq("malformed Offset decoration: expected 1 literal "
"operand, has 2: member 13 of SPIR-V type 12")); "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 p = parser(std::vector<uint32_t>{});
auto result = p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::Offset), 8}); auto result = p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::Offset), 8});
ASSERT_FALSE(result.IsEmpty()); ASSERT_FALSE(result.list.IsEmpty());
EXPECT_TRUE(result[0]->Is<ast::StructMemberOffsetAttribute>()); EXPECT_TRUE(result.list[0]->Is<ast::StructMemberOffsetAttribute>());
auto* offset_deco = result[0]->As<ast::StructMemberOffsetAttribute>(); auto* offset_deco = result.list[0]->As<ast::StructMemberOffsetAttribute>();
ASSERT_NE(offset_deco, nullptr); ASSERT_NE(offset_deco, nullptr);
ASSERT_TRUE(offset_deco->expr->Is<ast::IntLiteralExpression>()); ASSERT_TRUE(offset_deco->expr->Is<ast::IntLiteralExpression>());
EXPECT_EQ(offset_deco->expr->As<ast::IntLiteralExpression>()->value, 8u); 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); spirv::Matrix matrix(&f32, 2, 2);
auto result = auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 8}); 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()); EXPECT_TRUE(p->error().empty());
} }
@ -78,9 +78,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x2_Stride_Custom) {
spirv::Matrix matrix(&f32, 2, 2); spirv::Matrix matrix(&f32, 2, 2);
auto result = auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16}); p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16});
ASSERT_FALSE(result.IsEmpty()); ASSERT_FALSE(result.list.IsEmpty());
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>()); EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result[0]->As<ast::StrideAttribute>(); auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
ASSERT_NE(stride_deco, nullptr); ASSERT_NE(stride_deco, nullptr);
EXPECT_EQ(stride_deco->stride, 16u); EXPECT_EQ(stride_deco->stride, 16u);
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
@ -93,7 +93,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x4_Stride_Natural) {
spirv::Matrix matrix(&f32, 2, 4); spirv::Matrix matrix(&f32, 2, 4);
auto result = auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 16}); 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()); EXPECT_TRUE(p->error().empty());
} }
@ -104,9 +104,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x4_Stride_Custom) {
spirv::Matrix matrix(&f32, 2, 4); spirv::Matrix matrix(&f32, 2, 4);
auto result = auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 64}); p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 64});
ASSERT_FALSE(result.IsEmpty()); ASSERT_FALSE(result.list.IsEmpty());
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>()); EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result[0]->As<ast::StrideAttribute>(); auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
ASSERT_NE(stride_deco, nullptr); ASSERT_NE(stride_deco, nullptr);
EXPECT_EQ(stride_deco->stride, 64u); EXPECT_EQ(stride_deco->stride, 64u);
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
@ -119,9 +119,9 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_Matrix2x3_Stride_Custom) {
spirv::Matrix matrix(&f32, 2, 3); spirv::Matrix matrix(&f32, 2, 3);
auto result = auto result =
p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 32}); p->ConvertMemberDecoration(1, 1, &matrix, {uint32_t(spv::Decoration::MatrixStride), 32});
ASSERT_FALSE(result.IsEmpty()); ASSERT_FALSE(result.list.IsEmpty());
EXPECT_TRUE(result[0]->Is<ast::StrideAttribute>()); EXPECT_TRUE(result.list[0]->Is<ast::StrideAttribute>());
auto* stride_deco = result[0]->As<ast::StrideAttribute>(); auto* stride_deco = result.list[0]->As<ast::StrideAttribute>();
ASSERT_NE(stride_deco, nullptr); ASSERT_NE(stride_deco, nullptr);
EXPECT_EQ(stride_deco->stride, 32u); EXPECT_EQ(stride_deco->stride, 32u);
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
@ -135,7 +135,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_RelaxedPrecision) {
auto result = auto result =
p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::RelaxedPrecision)}); p->ConvertMemberDecoration(1, 1, nullptr, {uint32_t(spv::Decoration::RelaxedPrecision)});
EXPECT_TRUE(result.IsEmpty()); EXPECT_TRUE(result.list.IsEmpty());
EXPECT_TRUE(p->error().empty()); EXPECT_TRUE(p->error().empty());
} }
@ -143,7 +143,7 @@ TEST_F(SpvParserTest, ConvertMemberDecoration_UnhandledDecoration) {
auto p = parser(std::vector<uint32_t>{}); auto p = parser(std::vector<uint32_t>{});
auto result = p->ConvertMemberDecoration(12, 13, nullptr, {12345678}); 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 " EXPECT_THAT(p->error(), Eq("unhandled member decoration: 12345678 on member "
"13 of SPIR-V type 12")); "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 auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : i32; const std::string expected = R"(var<private> x_1 : i32;
var<private> position : vec4<f32>; var<private> position_1 : vec4<f32>;
fn main_1() { fn main_1() {
let x_2 : i32 = x_1; let x_2 : i32 = x_1;
@ -2870,14 +2870,14 @@ fn main_1() {
struct main_out { struct main_out {
@builtin(position) @builtin(position)
position_1 : vec4<f32>, position_1_1 : vec4<f32>,
} }
@vertex @vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out { fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = bitcast<i32>(x_1_param); x_1 = bitcast<i32>(x_1_param);
main_1(); main_1();
return main_out(position); return main_out(position_1);
} }
)"; )";
EXPECT_EQ(module_str, expected) << module_str; 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 auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : i32; const std::string expected = R"(var<private> x_1 : i32;
var<private> position : vec4<f32>; var<private> position_1 : vec4<f32>;
fn main_1() { fn main_1() {
let x_14 : ptr<private, i32> = &(x_1); let x_14 : ptr<private, i32> = &(x_1);
@ -2908,14 +2908,14 @@ fn main_1() {
struct main_out { struct main_out {
@builtin(position) @builtin(position)
position_1 : vec4<f32>, position_1_1 : vec4<f32>,
} }
@vertex @vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out { fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = bitcast<i32>(x_1_param); x_1 = bitcast<i32>(x_1_param);
main_1(); main_1();
return main_out(position); return main_out(position_1);
} }
)"; )";
EXPECT_EQ(module_str, expected) << module_str; 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 auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : i32; const std::string expected = R"(var<private> x_1 : i32;
var<private> position : vec4<f32>; var<private> position_1 : vec4<f32>;
fn main_1() { fn main_1() {
let x_2 : i32 = x_1; let x_2 : i32 = x_1;
@ -2945,14 +2945,14 @@ fn main_1() {
struct main_out { struct main_out {
@builtin(position) @builtin(position)
position_1 : vec4<f32>, position_1_1 : vec4<f32>,
} }
@vertex @vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out { fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = bitcast<i32>(x_1_param); x_1 = bitcast<i32>(x_1_param);
main_1(); main_1();
return main_out(position); return main_out(position_1);
} }
)"; )";
EXPECT_EQ(module_str, expected) << module_str; 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 auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : u32; const std::string expected = R"(var<private> x_1 : u32;
var<private> position : vec4<f32>; var<private> position_1 : vec4<f32>;
fn main_1() { fn main_1() {
let x_2 : u32 = x_1; let x_2 : u32 = x_1;
@ -3004,14 +3004,14 @@ fn main_1() {
struct main_out { struct main_out {
@builtin(position) @builtin(position)
position_1 : vec4<f32>, position_1_1 : vec4<f32>,
} }
@vertex @vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out { fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = x_1_param; x_1 = x_1_param;
main_1(); main_1();
return main_out(position); return main_out(position_1);
} }
)"; )";
EXPECT_EQ(module_str, expected); 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 auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : u32; const std::string expected = R"(var<private> x_1 : u32;
var<private> position : vec4<f32>; var<private> position_1 : vec4<f32>;
fn main_1() { fn main_1() {
let x_14 : ptr<private, u32> = &(x_1); let x_14 : ptr<private, u32> = &(x_1);
@ -3042,14 +3042,14 @@ fn main_1() {
struct main_out { struct main_out {
@builtin(position) @builtin(position)
position_1 : vec4<f32>, position_1_1 : vec4<f32>,
} }
@vertex @vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out { fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = x_1_param; x_1 = x_1_param;
main_1(); main_1();
return main_out(position); return main_out(position_1);
} }
)"; )";
EXPECT_EQ(module_str, expected); 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 auto module_str = test::ToString(p->program());
const std::string expected = R"(var<private> x_1 : u32; const std::string expected = R"(var<private> x_1 : u32;
var<private> position : vec4<f32>; var<private> position_1 : vec4<f32>;
fn main_1() { fn main_1() {
let x_2 : u32 = x_1; let x_2 : u32 = x_1;
@ -3079,14 +3079,14 @@ fn main_1() {
struct main_out { struct main_out {
@builtin(position) @builtin(position)
position_1 : vec4<f32>, position_1_1 : vec4<f32>,
} }
@vertex @vertex
fn main(@builtin(instance_index) x_1_param : u32) -> main_out { fn main(@builtin(instance_index) x_1_param : u32) -> main_out {
x_1 = x_1_param; x_1 = x_1_param;
main_1(); main_1();
return main_out(position); return main_out(position_1);
} }
)"; )";
EXPECT_EQ(module_str, expected); EXPECT_EQ(module_str, expected);

View File

@ -1125,23 +1125,6 @@ Expect<builtin::InterpolationType> ParserImpl::expect_interpolation_type_name()
builtin::kInterpolationTypeStrings); 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 // compound_statement
// : attribute* BRACE_LEFT statement* BRACE_RIGHT // : attribute* BRACE_LEFT statement* BRACE_RIGHT
Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement(std::string_view use) { Expect<ast::BlockStatement*> ParserImpl::expect_compound_statement(std::string_view use) {
@ -3020,7 +3003,7 @@ Maybe<const ast::Attribute*> ParserImpl::attribute() {
if (t == "builtin") { if (t == "builtin") {
return expect_paren_block("builtin attribute", [&]() -> Result { return expect_paren_block("builtin attribute", [&]() -> Result {
auto builtin = expect_builtin(); auto builtin = expect_expression("builtin name");
if (builtin.errored) { if (builtin.errored) {
return Failure::kErrored; return Failure::kErrored;
} }

View File

@ -462,10 +462,6 @@ class ParserImpl {
/// value type name. /// value type name.
/// @returns the parsed type name /// @returns the parsed type name
Expect<builtin::InterpolationType> expect_interpolation_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. /// Parses a `compound_statement` grammar element, erroring on parse failure.
/// @param use a description of what was being parsed if an error was raised /// @param use a description of what was being parsed if an error was raised
/// @returns the parsed statements /// @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) { TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBindingMissingLParen) {
EXPECT("@binding 1) var i : i32;", EXPECT("@binding 1) var i : i32;",
R"(test.wgsl:1:10 error: expected '(' for binding attribute 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; auto attrs_0 = e.value[0]->attributes;
ASSERT_EQ(attrs_0.Length(), 1u); ASSERT_EQ(attrs_0.Length(), 1u);
EXPECT_TRUE(attrs_0[0]->Is<ast::BuiltinAttribute>()); 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.line, 1u);
ASSERT_EQ(e.value[0]->source.range.begin.column, 20u); 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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/ast/test_helper.h"
#include "src/tint/reader/wgsl/parser_impl_test_helper.h" #include "src/tint/reader/wgsl/parser_impl_test_helper.h"
namespace tint::reader::wgsl { namespace tint::reader::wgsl {
@ -38,7 +39,8 @@ TEST_F(ParserImplTest, AttributeList_Parses) {
EXPECT_EQ(exp->value, 4u); EXPECT_EQ(exp->value, 4u);
ASSERT_TRUE(attr_1->Is<ast::BuiltinAttribute>()); 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) { TEST_F(ParserImplTest, AttributeList_Invalid) {
@ -51,27 +53,5 @@ TEST_F(ParserImplTest, AttributeList_Invalid) {
EXPECT_EQ(p->error(), R"(1:2: expected attribute)"); 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
} // namespace tint::reader::wgsl } // namespace tint::reader::wgsl

View File

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

View File

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

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
#include "src/tint/ast/call_statement.h" #include "src/tint/ast/call_statement.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/resolver/resolver_test_helper.h" #include "src/tint/resolver/resolver_test_helper.h"
using namespace tint::number_suffixes; // NOLINT using namespace tint::number_suffixes; // NOLINT
@ -145,7 +146,7 @@ TEST_P(ResolverBuiltinsStageTest, All_input) {
EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(r()->Resolve()) << r()->error();
} else { } else {
std::stringstream err; 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"; err << " cannot be used in input of " << params.stage << " pipeline stage";
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), err.str()); EXPECT_EQ(r()->error(), err.str());
@ -178,9 +179,9 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInput_Fail) {
Location(0_a), Location(0_a),
}); });
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), EXPECT_EQ(
"12:34 error: builtin(frag_depth) cannot be used in input of " r()->error(),
"fragment pipeline stage"); "12:34 error: @builtin(frag_depth) cannot be used in input of fragment pipeline stage");
} }
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) { TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) {
@ -214,7 +215,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), 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" "fragment pipeline stage\n"
"note: while analyzing entry point 'fragShader'"); "note: while analyzing entry point 'fragShader'");
} }
@ -272,7 +273,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_Struct_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
@ -288,7 +289,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
@ -317,7 +318,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
@ -346,7 +347,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
@ -361,7 +362,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
@ -387,7 +388,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
Location(0_a), Location(0_a),
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
@ -416,7 +417,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
@ -442,7 +443,7 @@ TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
Location(0_a), Location(0_a),
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
@ -468,7 +469,7 @@ TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
Location(0_a), Location(0_a),
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
@ -487,7 +488,7 @@ TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
Builtin(Source{{12, 34}}, builtin::BuiltinValue::kFragDepth), Builtin(Source{{12, 34}}, builtin::BuiltinValue::kFragDepth),
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
@ -512,7 +513,7 @@ TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
Builtin(builtin::BuiltinValue::kPosition), Builtin(builtin::BuiltinValue::kPosition),
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
@ -537,7 +538,7 @@ TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
Builtin(builtin::BuiltinValue::kPosition), Builtin(builtin::BuiltinValue::kPosition),
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltin_Pass) {
@ -657,7 +658,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_WorkGroupIdNotVec3U32) {
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), 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>'"); "'vec3<u32>'");
} }
@ -672,7 +673,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_NumWorkgroupsNotVec3U32) {
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), 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>'"); "'vec3<u32>'");
} }
@ -687,7 +688,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_GlobalInvocationNotVec3U32
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), 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>'"); "'vec3<u32>'");
} }
@ -703,7 +704,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationIndexNotU32
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), 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'"); "'u32'");
} }
@ -718,7 +719,7 @@ TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationNotVec3U32)
EXPECT_FALSE(r()->Resolve()); EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(), 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>'"); "'vec3<u32>'");
} }
@ -785,7 +786,7 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsNotBool_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
@ -814,7 +815,50 @@ TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
}); });
EXPECT_FALSE(r()->Resolve()); 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) { TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) {

View File

@ -55,6 +55,7 @@
#include "src/tint/ast/while_statement.h" #include "src/tint/ast/while_statement.h"
#include "src/tint/ast/workgroup_attribute.h" #include "src/tint/ast/workgroup_attribute.h"
#include "src/tint/builtin/builtin.h" #include "src/tint/builtin/builtin.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/scope_stack.h" #include "src/tint/scope_stack.h"
#include "src/tint/sem/builtin.h" #include "src/tint/sem/builtin.h"
#include "src/tint/symbol_table.h" #include "src/tint/symbol_table.h"
@ -424,6 +425,10 @@ class DependencyScanner {
TraverseValueExpression(binding->expr); TraverseValueExpression(binding->expr);
return true; return true;
}, },
[&](const ast::BuiltinAttribute* builtin) {
TraverseExpression(builtin->builtin, "builtin", "references");
return true;
},
[&](const ast::GroupAttribute* group) { [&](const ast::GroupAttribute* group) {
TraverseValueExpression(group->expr); TraverseValueExpression(group->expr);
return true; return true;
@ -480,6 +485,11 @@ class DependencyScanner {
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(builtin_ty)); graph_.resolved_identifiers.Add(from, ResolvedIdentifier(builtin_ty));
return; 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); if (auto addr = builtin::ParseAddressSpace(s);
addr != builtin::AddressSpace::kUndefined) { addr != builtin::AddressSpace::kUndefined) {
graph_.resolved_identifiers.Add(from, ResolvedIdentifier(addr)); 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) { if (auto builtin_ty = BuiltinType(); builtin_ty != builtin::Builtin::kUndefined) {
return "builtin type '" + utils::ToString(builtin_ty) + "'"; 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) { if (auto access = Access(); access != builtin::Access::kUndefined) {
return "access '" + utils::ToString(access) + "'"; return "access '" + utils::ToString(access) + "'";
} }

View File

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

View File

@ -1500,6 +1500,91 @@ INSTANTIATE_TEST_SUITE_P(Functions,
} // namespace resolve_to_address_space } // 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 // Resolve to builtin::TexelFormat tests
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1686,6 +1771,7 @@ TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
Param(Sym(), T, Param(Sym(), T,
utils::Vector{ utils::Vector{
Location(V), // Parameter attributes Location(V), // Parameter attributes
Builtin(V),
}), }),
}, },
T, // Return type T, // Return type

View File

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

View File

@ -27,6 +27,7 @@ enum class Def {
kAddressSpace, kAddressSpace,
kBuiltinFunction, kBuiltinFunction,
kBuiltinType, kBuiltinType,
kBuiltinValue,
kFunction, kFunction,
kStruct, kStruct,
kTexelFormat, kTexelFormat,
@ -44,6 +45,8 @@ std::ostream& operator<<(std::ostream& out, Def def) {
return out << "Def::kBuiltinFunction"; return out << "Def::kBuiltinFunction";
case Def::kBuiltinType: case Def::kBuiltinType:
return out << "Def::kBuiltinType"; return out << "Def::kBuiltinType";
case Def::kBuiltinValue:
return out << "Def::kBuiltinValue";
case Def::kFunction: case Def::kFunction:
return out << "Def::kFunction"; return out << "Def::kFunction";
case Def::kStruct: case Def::kStruct:
@ -62,6 +65,7 @@ enum class Use {
kAccess, kAccess,
kAddressSpace, kAddressSpace,
kBinaryOp, kBinaryOp,
kBuiltinValue,
kCallExpr, kCallExpr,
kCallStmt, kCallStmt,
kFunctionReturnType, kFunctionReturnType,
@ -80,6 +84,8 @@ std::ostream& operator<<(std::ostream& out, Use use) {
return out << "Use::kAddressSpace"; return out << "Use::kAddressSpace";
case Use::kBinaryOp: case Use::kBinaryOp:
return out << "Use::kBinaryOp"; return out << "Use::kBinaryOp";
case Use::kBuiltinValue:
return out << "Use::kBuiltinValue";
case Use::kCallExpr: case Use::kCallExpr:
return out << "Use::kCallExpr"; return out << "Use::kCallExpr";
case Use::kCallStmt: case Use::kCallStmt:
@ -156,6 +162,16 @@ TEST_P(ResolverExpressionKindTest, Test) {
}; };
break; 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: { case Def::kFunction: {
auto* fn = Func(kDefSource, "FUNCTION", utils::Empty, ty.i32(), Return(1_i)); auto* fn = Func(kDefSource, "FUNCTION", utils::Empty, ty.i32(), Return(1_i));
sym = Sym("FUNCTION"); sym = Sym("FUNCTION");
@ -233,6 +249,10 @@ TEST_P(ResolverExpressionKindTest, Test) {
case Use::kBinaryOp: case Use::kBinaryOp:
GlobalVar("v", builtin::AddressSpace::kPrivate, Mul(1_a, expr)); GlobalVar("v", builtin::AddressSpace::kPrivate, Mul(1_a, expr));
break; 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: case Use::kFunctionReturnType:
Func("f", utils::Empty, ty(expr), Return(Call(sym))); Func("f", utils::Empty, ty(expr), Return(Call(sym)));
break; break;
@ -271,6 +291,8 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kAccess, Use::kAddressSpace, {Def::kAccess, Use::kAddressSpace,
R"(5:6 error: cannot use access 'write' as address space)"}, 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::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::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::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)"}, {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::kAddressSpace, kPass},
{Def::kAddressSpace, Use::kBinaryOp, {Def::kAddressSpace, Use::kBinaryOp,
R"(5:6 error: cannot use address space 'workgroup' as value)"}, 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, {Def::kAddressSpace, Use::kCallExpr,
R"(5:6 error: cannot use address space 'workgroup' as call target)"}, R"(5:6 error: cannot use address space 'workgroup' as call target)"},
{Def::kAddressSpace, Use::kCallStmt, {Def::kAddressSpace, Use::kCallStmt,
@ -309,6 +333,8 @@ INSTANTIATE_TEST_SUITE_P(
R"(7:8 error: missing '(' for builtin function call)"}, R"(7:8 error: missing '(' for builtin function call)"},
{Def::kBuiltinFunction, Use::kBinaryOp, {Def::kBuiltinFunction, Use::kBinaryOp,
R"(7:8 error: missing '(' for builtin function call)"}, 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::kCallStmt, kPass},
{Def::kBuiltinFunction, Use::kFunctionReturnType, {Def::kBuiltinFunction, Use::kFunctionReturnType,
R"(7:8 error: missing '(' for builtin function call)"}, R"(7:8 error: missing '(' for builtin function call)"},
@ -329,6 +355,8 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kBuiltinType, Use::kBinaryOp, {Def::kBuiltinType, Use::kBinaryOp,
R"(5:6 error: cannot use type 'vec4<f32>' as value R"(5:6 error: cannot use type 'vec4<f32>' as value
7:8 note: are you missing '()' for type initializer?)"}, 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::kCallExpr, kPass},
{Def::kBuiltinType, Use::kFunctionReturnType, kPass}, {Def::kBuiltinType, Use::kFunctionReturnType, kPass},
{Def::kBuiltinType, Use::kMemberType, 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 R"(5:6 error: cannot use type 'vec4<f32>' as value
7:8 note: are you missing '()' for type initializer?)"}, 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 {Def::kFunction, Use::kAccess, R"(5:6 error: cannot use function 'FUNCTION' as access
1:2 note: function 'FUNCTION' declared here)"}, 1:2 note: function 'FUNCTION' declared here)"},
{Def::kFunction, Use::kAddressSpace, {Def::kFunction, Use::kAddressSpace,
R"(5:6 error: cannot use function 'FUNCTION' as address space R"(5:6 error: cannot use function 'FUNCTION' as address space
1:2 note: function 'FUNCTION' declared here)"}, 1:2 note: function 'FUNCTION' declared here)"},
{Def::kFunction, Use::kBinaryOp, R"(5:6 error: cannot use function 'FUNCTION' as value {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)"}, 1:2 note: function 'FUNCTION' declared here)"},
{Def::kFunction, Use::kCallExpr, kPass}, {Def::kFunction, Use::kCallExpr, kPass},
{Def::kFunction, Use::kCallStmt, 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 {Def::kStruct, Use::kBinaryOp, R"(5:6 error: cannot use type 'STRUCT' as value
7:8 note: are you missing '()' for type initializer? 7:8 note: are you missing '()' for type initializer?
1:2 note: struct 'STRUCT' declared here)"}, 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::kFunctionReturnType, kPass},
{Def::kStruct, Use::kMemberType, kPass}, {Def::kStruct, Use::kMemberType, kPass},
{Def::kStruct, Use::kTexelFormat, R"(5:6 error: cannot use type 'STRUCT' as texel format)"}, {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)"}, R"(5:6 error: cannot use texel format 'rgba8unorm' as address space)"},
{Def::kTexelFormat, Use::kBinaryOp, {Def::kTexelFormat, Use::kBinaryOp,
R"(5:6 error: cannot use texel format 'rgba8unorm' as value)"}, 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, {Def::kTexelFormat, Use::kCallExpr,
R"(5:6 error: cannot use texel format 'rgba8unorm' as call target)"}, R"(5:6 error: cannot use texel format 'rgba8unorm' as call target)"},
{Def::kTexelFormat, Use::kCallStmt, {Def::kTexelFormat, Use::kCallStmt,
@ -416,6 +475,8 @@ INSTANTIATE_TEST_SUITE_P(
{Def::kTypeAlias, Use::kBinaryOp, {Def::kTypeAlias, Use::kBinaryOp,
R"(5:6 error: cannot use type 'i32' as value R"(5:6 error: cannot use type 'i32' as value
7:8 note: are you missing '()' for type initializer?)"}, 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::kCallExpr, kPass},
{Def::kTypeAlias, Use::kFunctionReturnType, kPass}, {Def::kTypeAlias, Use::kFunctionReturnType, kPass},
{Def::kTypeAlias, Use::kMemberType, 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 R"(5:6 error: cannot use const 'VARIABLE' as address space
1:2 note: const 'VARIABLE' declared here)"}, 1:2 note: const 'VARIABLE' declared here)"},
{Def::kVariable, Use::kBinaryOp, kPass}, {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, {Def::kVariable, Use::kCallStmt,
R"(5:6 error: cannot use const 'VARIABLE' as call target R"(5:6 error: cannot use const 'VARIABLE' as call target
1:2 note: const 'VARIABLE' declared here)"}, 1:2 note: const 'VARIABLE' declared here)"},

View File

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

View File

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

View File

@ -34,6 +34,7 @@
#include "src/tint/ast/unary_op_expression.h" #include "src/tint/ast/unary_op_expression.h"
#include "src/tint/ast/variable_decl_statement.h" #include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/ast/workgroup_attribute.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/resolver/resolver_test_helper.h"
#include "src/tint/sem/call.h" #include "src/tint/sem/call.h"
#include "src/tint/sem/function.h" #include "src/tint/sem/function.h"

View File

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

View File

@ -17,6 +17,7 @@
#include <string> #include <string>
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/diagnostic/diagnostic.h" #include "src/tint/diagnostic/diagnostic.h"
#include "src/tint/program_builder.h" #include "src/tint/program_builder.h"
#include "src/tint/resolver/dependency_graph.h" #include "src/tint/resolver/dependency_graph.h"
@ -120,8 +121,23 @@ class SemHelper {
/// @param expr the semantic node /// @param expr the semantic node
/// @returns nullptr if @p expr is nullptr, or @p expr cast to /// @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. /// 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 { sem::BuiltinEnumExpression<builtin::TexelFormat>* AsTexelFormat(sem::Expression* expr) const {
if (TINT_LIKELY(expr)) { if (TINT_LIKELY(expr)) {
auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::TexelFormat>>(); auto* enum_expr = expr->As<sem::BuiltinEnumExpression<builtin::TexelFormat>>();

View File

@ -16,6 +16,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "src/tint/ast/stage_attribute.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/resolver/resolver_test_helper.h"
#include "src/tint/sem/struct.h" #include "src/tint/sem/struct.h"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -33,58 +33,22 @@ TINT_INSTANTIATE_TYPEINFO(tint::transform::ClampFragDepth);
namespace tint::transform { namespace tint::transform {
namespace { /// PIMPL state for the transform
struct ClampFragDepth::State {
bool ContainsFragDepth(utils::VectorRef<const ast::Attribute*> attributes) { /// The source program
for (auto* attribute : attributes) { const Program* const src;
if (auto* builtin_attribute = attribute->As<ast::BuiltinAttribute>()) { /// The target program builder
if (builtin_attribute->builtin == builtin::BuiltinValue::kFragDepth) { ProgramBuilder b{};
return true; /// 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
return false; const SymbolTable& sym = src->Symbols();
}
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;
}
}
}
return false;
}
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;
}
}
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};
/// 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. // Abort on any use of push constants in the module.
for (auto* global : src->AST().GlobalVariables()) { for (auto* global : src->AST().GlobalVariables()) {
if (auto* var = global->As<ast::Var>()) { if (auto* var = global->As<ast::Var>()) {
@ -98,13 +62,10 @@ Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&,
} }
} }
if (!ShouldRun(src)) { if (!ShouldRun()) {
return SkipTransform; return SkipTransform;
} }
auto& sem = src->Sem();
auto& sym = src->Symbols();
// At least one entry-point needs clamping. Add the following to the module: // At least one entry-point needs clamping. Add the following to the module:
// //
// enable chromium_experimental_push_constant; // enable chromium_experimental_push_constant;
@ -138,7 +99,8 @@ Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&,
// The symbol is the name of the helper function to apply the depth clamping. // The symbol is the name of the helper function to apply the depth clamping.
Symbol returns_frag_depth_as_struct_helper; Symbol returns_frag_depth_as_struct_helper;
// Map of io struct to helper function to return the structure with the depth clamping applied. // 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; utils::Hashmap<const ast::Struct*, Symbol, 4u> io_structs_clamp_helpers;
// Register a callback that will be called for each visted AST function. // Register a callback that will be called for each visted AST function.
@ -155,7 +117,7 @@ Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&,
return ctx.CloneWithoutTransform(fn); return ctx.CloneWithoutTransform(fn);
} }
if (ReturnsFragDepthInStruct(sem, fn)) { if (ReturnsFragDepthInStruct(fn)) {
// At most once per I/O struct, add the conversion function: // At most once per I/O struct, add the conversion function:
// //
// fn clamp_frag_depth_S(s : S) -> S { // fn clamp_frag_depth_S(s : S) -> S {
@ -197,8 +159,8 @@ Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&,
return b.Return(stmt->source, b.Call(base_fn_sym, ctx.Clone(stmt->value))); return b.Return(stmt->source, b.Call(base_fn_sym, ctx.Clone(stmt->value)));
} }
if (returns_frag_depth_as_struct_helper.IsValid()) { if (returns_frag_depth_as_struct_helper.IsValid()) {
return b.Return(stmt->source, return b.Return(stmt->source, b.Call(returns_frag_depth_as_struct_helper,
b.Call(returns_frag_depth_as_struct_helper, ctx.Clone(stmt->value))); ctx.Clone(stmt->value)));
} }
return nullptr; return nullptr;
}); });
@ -207,4 +169,60 @@ Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&,
return Program(std::move(b)); 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;
}
/// @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;
}
/// @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);
}
/// @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;
}
};
ClampFragDepth::ClampFragDepth() = default;
ClampFragDepth::~ClampFragDepth() = default;
Transform::ApplyResult ClampFragDepth::Apply(const Program* src, const DataMap&, DataMap&) const {
return State{src}.Run();
}
} // namespace tint::transform } // namespace tint::transform

View File

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

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@
#include <vector> #include <vector>
#include "src/tint/ast/workgroup_attribute.h" #include "src/tint/ast/workgroup_attribute.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program_builder.h" #include "src/tint/program_builder.h"
#include "src/tint/sem/function.h" #include "src/tint/sem/function.h"
#include "src/tint/sem/variable.h" #include "src/tint/sem/variable.h"
@ -159,8 +160,9 @@ struct ZeroInitWorkgroupMemory::State {
// parameter // parameter
std::function<const ast::Expression*()> local_index; std::function<const ast::Expression*()> local_index;
for (auto* param : fn->params) { for (auto* param : fn->params) {
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) { if (auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(param->attributes)) {
if (builtin->builtin == builtin::BuiltinValue::kLocalInvocationIndex) { auto builtin = sem.Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
local_index = [=] { return b.Expr(ctx.Clone(param->name->symbol)); }; local_index = [=] { return b.Expr(ctx.Clone(param->name->symbol)); };
break; break;
} }
@ -168,9 +170,10 @@ struct ZeroInitWorkgroupMemory::State {
if (auto* str = sem.Get(param)->Type()->As<sem::Struct>()) { if (auto* str = sem.Get(param)->Type()->As<sem::Struct>()) {
for (auto* member : str->Members()) { for (auto* member : str->Members()) {
if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>( if (auto* builtin_attr = ast::GetAttribute<ast::BuiltinAttribute>(
member->Declaration()->attributes)) { member->Declaration()->attributes)) {
if (builtin->builtin == builtin::BuiltinValue::kLocalInvocationIndex) { auto builtin = sem.Get(builtin_attr)->Value();
if (builtin == builtin::BuiltinValue::kLocalInvocationIndex) {
local_index = [=] { local_index = [=] {
auto* param_expr = b.Expr(ctx.Clone(param->name->symbol)); auto* param_expr = b.Expr(ctx.Clone(param->name->symbol));
auto* member_name = ctx.Clone(member->Declaration()->name); auto* member_name = ctx.Clone(member->Declaration()->name);
@ -184,10 +187,9 @@ struct ZeroInitWorkgroupMemory::State {
} }
if (!local_index) { if (!local_index) {
// No existing local index parameter. Append one to the entry point. // No existing local index parameter. Append one to the entry point.
auto* param = b.Param(b.Symbols().New("local_invocation_index"), b.ty.u32(), auto param_name = b.Symbols().New("local_invocation_index");
utils::Vector{ auto* local_invocation_index = b.Builtin(builtin::BuiltinValue::kLocalInvocationIndex);
b.Builtin(builtin::BuiltinValue::kLocalInvocationIndex), auto* param = b.Param(param_name, b.ty.u32(), utils::Vector{local_invocation_index});
});
ctx.InsertBack(fn->params, param); ctx.InsertBack(fn->params, param);
local_index = [=] { return b.Expr(param->name->symbol); }; 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 /// @returns an read-only iterator to the beginning of the set
Iterator begin() { Iterator begin() const {
auto it = Iterator{set, -1}; auto it = Iterator{set, -1};
++it; // Move to first set bit ++it; // Move to first set bit
return it; return it;
} }
/// @returns an iterator to the beginning of the set /// @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: private:
static constexpr uint64_t Bit(Enum value) { 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) { bool GeneratorImpl::EmitIOVariable(const sem::GlobalVariable* var) {
auto* decl = var->Declaration(); 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 // Use of gl_SampleID requires the GL_OES_sample_variables extension
if (RequiresOESSampleVariables(b->builtin)) { if (RequiresOESSampleVariables(builtin)) {
requires_oes_sample_variables_ = true; requires_oes_sample_variables_ = true;
} }
// Do not emit builtin (gl_) variables. // Do not emit builtin (gl_) variables.

View File

@ -32,6 +32,7 @@
#include "src/tint/ast/return_statement.h" #include "src/tint/ast/return_statement.h"
#include "src/tint/ast/switch_statement.h" #include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.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/program_builder.h"
#include "src/tint/scope_stack.h" #include "src/tint/scope_stack.h"
#include "src/tint/transform/decompose_memory_access.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 { } else {
TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute"; TINT_ICE(Writer, diagnostics_) << "invalid use of location attribute";
} }
} else if (auto* builtin = attr->As<ast::BuiltinAttribute>()) { } else if (auto* builtin_attr = attr->As<ast::BuiltinAttribute>()) {
auto name = builtin_to_attribute(builtin->builtin); auto builtin = program_->Sem().Get(builtin_attr)->Value();
auto name = builtin_to_attribute(builtin);
if (name.empty()) { if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unsupported builtin"); diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
return false; return false;

View File

@ -31,6 +31,7 @@
#include "src/tint/ast/return_statement.h" #include "src/tint/ast/return_statement.h"
#include "src/tint/ast/switch_statement.h" #include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.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/program_builder.h"
#include "src/tint/scope_stack.h" #include "src/tint/scope_stack.h"
#include "src/tint/sem/binding_point.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; auto& attrs = param->attributes;
bool builtin_found = false; bool builtin_found = false;
for (auto* attr : attrs) { for (auto* attr : attrs) {
auto* builtin = attr->As<ast::BuiltinAttribute>(); auto* builtin_attr = attr->As<ast::BuiltinAttribute>();
if (!builtin) { if (!builtin_attr) {
continue; continue;
} }
auto builtin = program_->Sem().Get(builtin_attr)->Value();
builtin_found = true; builtin_found = true;
auto name = builtin_to_attribute(builtin->builtin); auto name = builtin_to_attribute(builtin);
if (name.empty()) { if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unknown builtin"); diagnostics_.add_error(diag::System::Writer, "unknown builtin");
return false; return false;
@ -2854,8 +2855,9 @@ bool GeneratorImpl::EmitStructType(TextBuffer* b, const sem::Struct* str) {
for (auto* attr : decl->attributes) { for (auto* attr : decl->attributes) {
bool ok = Switch( bool ok = Switch(
attr, attr,
[&](const ast::BuiltinAttribute* builtin) { [&](const ast::BuiltinAttribute* builtin_attr) {
auto name = builtin_to_attribute(builtin->builtin); auto builtin = program_->Sem().Get(builtin_attr)->Value();
auto name = builtin_to_attribute(builtin);
if (name.empty()) { if (name.empty()) {
diagnostics_.add_error(diag::System::Writer, "unknown builtin"); diagnostics_.add_error(diag::System::Writer, "unknown builtin");
return false; return false;

View File

@ -36,6 +36,7 @@
#include "src/tint/ast/return_statement.h" #include "src/tint/ast/return_statement.h"
#include "src/tint/ast/switch_statement.h" #include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.h" #include "src/tint/ast/unary_op_expression.h"
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/program.h" #include "src/tint/program.h"
#include "src/tint/scope_stack.h" #include "src/tint/scope_stack.h"
#include "src/tint/sem/struct.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())}); Operand(wgsize[0].value()), Operand(wgsize[1].value()), Operand(wgsize[2].value())});
} }
for (auto builtin : func_sem->TransitivelyReferencedBuiltinVariables()) { for (auto it : func_sem->TransitivelyReferencedBuiltinVariables()) {
if (builtin.second->builtin == builtin::BuiltinValue::kFragDepth) { auto builtin = builder_.Sem().Get(it.second)->Value();
if (builtin == builtin::BuiltinValue::kFragDepth) {
push_execution_mode(spv::Op::OpExecutionMode, push_execution_mode(spv::Op::OpExecutionMode,
{Operand(id), U32Operand(SpvExecutionModeDepthReplacing)}); {Operand(id), U32Operand(SpvExecutionModeDepthReplacing)});
} }
@ -837,10 +838,11 @@ bool Builder::GenerateGlobalVariable(const ast::Variable* v) {
for (auto* attr : v->attributes) { for (auto* attr : v->attributes) {
bool ok = Switch( bool ok = Switch(
attr, attr,
[&](const ast::BuiltinAttribute* builtin) { [&](const ast::BuiltinAttribute* builtin_attr) {
auto builtin = builder_.Sem().Get(builtin_attr)->Value();
push_annot(spv::Op::OpDecorate, push_annot(spv::Op::OpDecorate,
{Operand(var_id), U32Operand(SpvDecorationBuiltIn), {Operand(var_id), U32Operand(SpvDecorationBuiltIn),
U32Operand(ConvertBuiltin(builtin->builtin, sem->AddressSpace()))}); U32Operand(ConvertBuiltin(builtin, sem->AddressSpace()))});
return true; return true;
}, },
[&](const ast::LocationAttribute*) { [&](const ast::LocationAttribute*) {

View File

@ -33,6 +33,7 @@
#include "src/tint/ast/switch_statement.h" #include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.h" #include "src/tint/ast/unary_op_expression.h"
#include "src/tint/ast/variable_decl_statement.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/program_builder.h"
#include "src/tint/scope_stack.h" #include "src/tint/scope_stack.h"
#include "src/tint/sem/builtin.h" #include "src/tint/sem/builtin.h"

View File

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

View File

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

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include "src/tint/builtin/builtin_value.h"
#include "src/tint/type/depth_texture.h" #include "src/tint/type/depth_texture.h"
#include "src/tint/type/multisampled_texture.h" #include "src/tint/type/multisampled_texture.h"
#include "src/tint/type/sampled_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) { cbuffer cbuffer_x_14 : register(b2, space2) {
uint4 x_14[17]; uint4 x_14[17];
}; };
@ -18,13 +18,13 @@ float4x4 tint_symbol_4(uint4 buffer[17], uint offset) {
void main_1() { void main_1() {
float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f); float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f);
float3 p = float3(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); q = float4(x_13.x, x_13.y, x_13.z, 1.0f);
const float4 x_21 = q; const float4 x_21 = q;
p = float3(x_21.x, x_21.y, x_21.z); p = float3(x_21.x, x_21.y, x_21.z);
const float x_27 = p.x; const float x_27 = p.x;
const float x_41 = asfloat(x_14[13].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); const float x_49 = asfloat(x_14[4].x);
p.x = (x_27 + sin(((x_41 * x_45) + x_49))); p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
const float x_55 = p.y; const float x_55 = p.y;
@ -45,7 +45,7 @@ struct main_out {
float2 vUV_1; float2 vUV_1;
}; };
struct tint_symbol_1 { struct tint_symbol_1 {
float3 position_param : TEXCOORD0; float3 position_1_param : TEXCOORD0;
float3 normal_param : TEXCOORD1; float3 normal_param : TEXCOORD1;
float2 uv_param : TEXCOORD2; float2 uv_param : TEXCOORD2;
}; };
@ -54,8 +54,8 @@ struct tint_symbol_2 {
float4 gl_Position : SV_Position; float4 gl_Position : SV_Position;
}; };
main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param) { main_out main_inner(float3 position_1_param, float2 uv_param, float3 normal_param) {
position = position_param; position_1 = position_1_param;
uv = uv_param; uv = uv_param;
normal = normal_param; normal = normal_param;
main_1(); 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) { 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; tint_symbol_2 wrapper_result = (tint_symbol_2)0;
wrapper_result.gl_Position = inner_result.gl_Position; wrapper_result.gl_Position = inner_result.gl_Position;
wrapper_result.vUV_1 = inner_result.vUV_1; 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) { cbuffer cbuffer_x_14 : register(b2, space2) {
uint4 x_14[17]; uint4 x_14[17];
}; };
@ -18,13 +18,13 @@ float4x4 tint_symbol_4(uint4 buffer[17], uint offset) {
void main_1() { void main_1() {
float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f); float4 q = float4(0.0f, 0.0f, 0.0f, 0.0f);
float3 p = float3(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); q = float4(x_13.x, x_13.y, x_13.z, 1.0f);
const float4 x_21 = q; const float4 x_21 = q;
p = float3(x_21.x, x_21.y, x_21.z); p = float3(x_21.x, x_21.y, x_21.z);
const float x_27 = p.x; const float x_27 = p.x;
const float x_41 = asfloat(x_14[13].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); const float x_49 = asfloat(x_14[4].x);
p.x = (x_27 + sin(((x_41 * x_45) + x_49))); p.x = (x_27 + sin(((x_41 * x_45) + x_49)));
const float x_55 = p.y; const float x_55 = p.y;
@ -45,7 +45,7 @@ struct main_out {
float2 vUV_1; float2 vUV_1;
}; };
struct tint_symbol_1 { struct tint_symbol_1 {
float3 position_param : TEXCOORD0; float3 position_1_param : TEXCOORD0;
float3 normal_param : TEXCOORD1; float3 normal_param : TEXCOORD1;
float2 uv_param : TEXCOORD2; float2 uv_param : TEXCOORD2;
}; };
@ -54,8 +54,8 @@ struct tint_symbol_2 {
float4 gl_Position : SV_Position; float4 gl_Position : SV_Position;
}; };
main_out main_inner(float3 position_param, float2 uv_param, float3 normal_param) { main_out main_inner(float3 position_1_param, float2 uv_param, float3 normal_param) {
position = position_param; position_1 = position_1_param;
uv = uv_param; uv = uv_param;
normal = normal_param; normal = normal_param;
main_1(); 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) { 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; tint_symbol_2 wrapper_result = (tint_symbol_2)0;
wrapper_result.gl_Position = inner_result.gl_Position; wrapper_result.gl_Position = inner_result.gl_Position;
wrapper_result.vUV_1 = inner_result.vUV_1; wrapper_result.vUV_1 = inner_result.vUV_1;

View File

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

View File

@ -58,7 +58,7 @@ struct main_out {
}; };
struct tint_symbol_2 { struct tint_symbol_2 {
float3 position_param [[attribute(0)]]; float3 position_1_param [[attribute(0)]];
float3 normal_param [[attribute(1)]]; float3 normal_param [[attribute(1)]];
float2 uv_param [[attribute(2)]]; float2 uv_param [[attribute(2)]];
}; };
@ -68,9 +68,9 @@ struct tint_symbol_3 {
float4 gl_Position [[position]]; 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; 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_11) = uv_param;
tint_symbol_12 = normal_param; tint_symbol_12 = normal_param;
main_1(tint_symbol_10, tint_symbol_13, tint_symbol_14, tint_symbol_11, tint_symbol_15); 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 float2 tint_symbol_17 = 0.0f;
thread float4 tint_symbol_19 = 0.0f; thread float4 tint_symbol_19 = 0.0f;
thread float2 tint_symbol_20 = 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 = {}; tint_symbol_3 wrapper_result = {};
wrapper_result.gl_Position = inner_result.gl_Position; wrapper_result.gl_Position = inner_result.gl_Position;
wrapper_result.vUV_1 = inner_result.vUV_1; wrapper_result.vUV_1 = inner_result.vUV_1;

View File

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

View File

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

View File

@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
const uint x_25 = idx; const uint x_25 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
const uint x_25 = idx; const uint x_25 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

@ -10,9 +10,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
uint x_25 = idx; uint x_25 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

@ -22,9 +22,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % select(rhs, 1u, (rhs == 0u))); 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
uint const x_25 = idx; uint const x_25 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

@ -17,7 +17,7 @@
OpName %lhs_0 "lhs" OpName %lhs_0 "lhs"
OpName %rhs_0 "rhs" OpName %rhs_0 "rhs"
OpName %compute_main_inner "compute_main_inner" 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 %idx "idx"
OpName %compute_main_1 "compute_main_1" OpName %compute_main_1 "compute_main_1"
OpName %compute_main_inner_1 "compute_main_inner_1" OpName %compute_main_inner_1 "compute_main_inner_1"
@ -75,11 +75,11 @@
OpReturnValue %30 OpReturnValue %30
OpFunctionEnd OpFunctionEnd
%compute_main_inner = OpFunction %void None %31 %compute_main_inner = OpFunction %void None %31
%local_invocation_index = OpFunctionParameter %uint %local_invocation_index_2 = OpFunctionParameter %uint
%35 = OpLabel %35 = OpLabel
%idx = OpVariable %_ptr_Function_uint Function %6 %idx = OpVariable %_ptr_Function_uint Function %6
OpStore %idx %6 OpStore %idx %6
OpStore %idx %local_invocation_index OpStore %idx %local_invocation_index_2
OpBranch %38 OpBranch %38
%38 = OpLabel %38 = OpLabel
OpLoopMerge %39 %40 None 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>; 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; var idx : u32 = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
loop { loop {
let x_25 : u32 = idx; let x_25 : u32 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

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

View File

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

View File

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

View File

@ -14,9 +14,9 @@ struct tint_array {
T elements[N]; 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
uint const x_21 = idx; uint const x_21 = idx;
if (!((x_21 < 4u))) { if (!((x_21 < 4u))) {

View File

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

View File

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

View File

@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
const uint x_25 = idx; const uint x_25 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

@ -9,9 +9,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
const uint x_25 = idx; const uint x_25 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

@ -10,9 +10,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % ((rhs == 0u) ? 1u : 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
uint x_25 = idx; uint x_25 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

@ -22,9 +22,9 @@ uint tint_mod(uint lhs, uint rhs) {
return (lhs % select(rhs, 1u, (rhs == 0u))); 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
uint const x_25 = idx; uint const x_25 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

@ -17,7 +17,7 @@
OpName %lhs_0 "lhs" OpName %lhs_0 "lhs"
OpName %rhs_0 "rhs" OpName %rhs_0 "rhs"
OpName %compute_main_inner "compute_main_inner" 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 %idx "idx"
OpName %compute_main_1 "compute_main_1" OpName %compute_main_1 "compute_main_1"
OpName %compute_main_inner_1 "compute_main_inner_1" OpName %compute_main_inner_1 "compute_main_inner_1"
@ -75,11 +75,11 @@
OpReturnValue %30 OpReturnValue %30
OpFunctionEnd OpFunctionEnd
%compute_main_inner = OpFunction %void None %31 %compute_main_inner = OpFunction %void None %31
%local_invocation_index = OpFunctionParameter %uint %local_invocation_index_2 = OpFunctionParameter %uint
%35 = OpLabel %35 = OpLabel
%idx = OpVariable %_ptr_Function_uint Function %6 %idx = OpVariable %_ptr_Function_uint Function %6
OpStore %idx %6 OpStore %idx %6
OpStore %idx %local_invocation_index OpStore %idx %local_invocation_index_2
OpBranch %38 OpBranch %38
%38 = OpLabel %38 = OpLabel
OpLoopMerge %39 %40 None 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>; 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; var idx : u32 = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
loop { loop {
let x_25 : u32 = idx; let x_25 : u32 = idx;
if (!((x_25 < 6u))) { if (!((x_25 < 6u))) {

View File

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

View File

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

View File

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

View File

@ -26,9 +26,9 @@ struct S {
uint y; 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; uint idx = 0u;
idx = local_invocation_index; idx = local_invocation_index_2;
while (true) { while (true) {
uint const x_23 = idx; uint const x_23 = idx;
if (!((x_23 < 10u))) { if (!((x_23 < 10u))) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,7 @@ struct S {
uint b; 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; (*(tint_symbol)).x = 0;
atomic_store_explicit(&((*(tint_symbol)).a), 0u, memory_order_relaxed); atomic_store_explicit(&((*(tint_symbol)).a), 0u, memory_order_relaxed);
atomic_store_explicit(&((*(tint_symbol)).b), 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