tint: Support multiple extensions per 'enable'
Fixed: tint:1865
Change-Id: I245bd36b12ff23977c2e69deee27f976db820849
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/123900
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index c8d89e4..11d98bd 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -495,6 +495,7 @@
"ast/discard_statement.h",
"ast/enable.h",
"ast/expression.h",
+ "ast/extension.h",
"ast/float_literal_expression.h",
"ast/for_loop_statement.h",
"ast/function.h",
@@ -581,6 +582,7 @@
"ast/discard_statement.cc",
"ast/enable.cc",
"ast/expression.cc",
+ "ast/extension.cc",
"ast/float_literal_expression.cc",
"ast/for_loop_statement.cc",
"ast/function.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 0c2da7a..07f7aa8 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -134,6 +134,8 @@
ast/enable.h
ast/expression.cc
ast/expression.h
+ ast/extension.cc
+ ast/extension.h
ast/float_literal_expression.cc
ast/float_literal_expression.h
ast/for_loop_statement.cc
diff --git a/src/tint/ast/enable.cc b/src/tint/ast/enable.cc
index 6087a3e..bb2b3b6 100644
--- a/src/tint/ast/enable.cc
+++ b/src/tint/ast/enable.cc
@@ -20,15 +20,29 @@
namespace tint::ast {
-Enable::Enable(ProgramID pid, NodeID nid, const Source& src, builtin::Extension ext)
- : Base(pid, nid, src), extension(ext) {}
+Enable::Enable(ProgramID pid,
+ NodeID nid,
+ const Source& src,
+ utils::VectorRef<const Extension*> exts)
+ : Base(pid, nid, src), extensions(std::move(exts)) {}
Enable::Enable(Enable&&) = default;
Enable::~Enable() = default;
+bool Enable::HasExtension(builtin::Extension ext) const {
+ for (auto* e : extensions) {
+ if (e->name == ext) {
+ return true;
+ }
+ }
+ return false;
+}
+
const Enable* Enable::Clone(CloneContext* ctx) const {
auto src = ctx->Clone(source);
- return ctx->dst->create<Enable>(src, extension);
+ auto exts = ctx->Clone(extensions);
+ return ctx->dst->create<Enable>(src, std::move(exts));
}
+
} // namespace tint::ast
diff --git a/src/tint/ast/enable.h b/src/tint/ast/enable.h
index 0c64df9..d87d12f 100644
--- a/src/tint/ast/enable.h
+++ b/src/tint/ast/enable.h
@@ -19,8 +19,7 @@
#include <utility>
#include <vector>
-#include "src/tint/ast/node.h"
-#include "src/tint/builtin/extension.h"
+#include "src/tint/ast/extension.h"
namespace tint::ast {
@@ -35,21 +34,24 @@
/// @param pid the identifier of the program that owns this node
/// @param nid the unique node identifier
/// @param src the source of this node
- /// @param ext the extension
- Enable(ProgramID pid, NodeID nid, const Source& src, builtin::Extension ext);
+ /// @param exts the extensions being enabled by this directive
+ Enable(ProgramID pid, NodeID nid, const Source& src, utils::VectorRef<const Extension*> exts);
/// Move constructor
Enable(Enable&&);
~Enable() override;
- /// Clones this node and all transitive child nodes using the `CloneContext`
- /// `ctx`.
+ /// @param ext the extension to search for
+ /// @returns true if this Enable lists the given extension
+ bool HasExtension(builtin::Extension ext) const;
+
+ /// Clones this node and all transitive child nodes using the `CloneContext` `ctx`.
/// @param ctx the clone context
/// @return the newly cloned node
const Enable* Clone(CloneContext* ctx) const override;
- /// The extension name
- const builtin::Extension extension;
+ /// The extensions being enabled by this directive
+ const utils::Vector<const Extension*, 4> extensions;
};
} // namespace tint::ast
diff --git a/src/tint/ast/enable_test.cc b/src/tint/ast/enable_test.cc
index 5622207..20105bc 100644
--- a/src/tint/ast/enable_test.cc
+++ b/src/tint/ast/enable_test.cc
@@ -27,7 +27,14 @@
EXPECT_EQ(ext->source.range.begin.column, 2u);
EXPECT_EQ(ext->source.range.end.line, 20u);
EXPECT_EQ(ext->source.range.end.column, 5u);
- EXPECT_EQ(ext->extension, builtin::Extension::kF16);
+ ASSERT_EQ(ext->extensions.Length(), 1u);
+ EXPECT_EQ(ext->extensions[0]->name, builtin::Extension::kF16);
+}
+
+TEST_F(EnableTest, HasExtension) {
+ auto* ext = Enable(Source{{{20, 2}, {20, 5}}}, builtin::Extension::kF16);
+ EXPECT_TRUE(ext->HasExtension(builtin::Extension::kF16));
+ EXPECT_FALSE(ext->HasExtension(builtin::Extension::kChromiumDisableUniformityAnalysis));
}
} // namespace
diff --git a/src/tint/ast/extension.cc b/src/tint/ast/extension.cc
new file mode 100644
index 0000000..a45c704
--- /dev/null
+++ b/src/tint/ast/extension.cc
@@ -0,0 +1,40 @@
+// 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.
+
+#include "src/tint/ast/extension.h"
+
+#include "src/tint/program_builder.h"
+
+//! @cond Doxygen_Suppress
+// Doxygen gets confused with tint::ast::Extension and tint::builtin::Extension
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::Extension);
+
+namespace tint::ast {
+
+Extension::Extension(ProgramID pid, NodeID nid, const Source& src, builtin::Extension ext)
+ : Base(pid, nid, src), name(ext) {}
+
+Extension::Extension(Extension&&) = default;
+
+Extension::~Extension() = default;
+
+const Extension* Extension::Clone(CloneContext* ctx) const {
+ auto src = ctx->Clone(source);
+ return ctx->dst->create<Extension>(src, name);
+}
+
+} // namespace tint::ast
+
+//! @endcond
diff --git a/src/tint/ast/extension.h b/src/tint/ast/extension.h
new file mode 100644
index 0000000..93f3baa
--- /dev/null
+++ b/src/tint/ast/extension.h
@@ -0,0 +1,52 @@
+// 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_AST_EXTENSION_H_
+#define SRC_TINT_AST_EXTENSION_H_
+
+#include "src/tint/ast/node.h"
+#include "src/tint/builtin/extension.h"
+
+namespace tint::ast {
+
+/// An extension used in an "enable" directive. Example:
+/// ```
+/// enable f16;
+/// ```
+class Extension final : public Castable<Extension, Node> {
+ public:
+ /// Create a extension
+ /// @param pid the identifier of the program that owns this node
+ /// @param nid the unique node identifier
+ /// @param src the source of this node
+ /// @param ext the extension
+ Extension(ProgramID pid, NodeID nid, const Source& src, builtin::Extension ext);
+ /// Move constructor
+ Extension(Extension&&);
+
+ ~Extension() override;
+
+ /// Clones this node and all transitive child nodes using the `CloneContext`
+ /// `ctx`.
+ /// @param ctx the clone context
+ /// @return the newly cloned node
+ const Extension* Clone(CloneContext* ctx) const override;
+
+ /// The extension name
+ const builtin::Extension name;
+};
+
+} // namespace tint::ast
+
+#endif // SRC_TINT_AST_EXTENSION_H_
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index 2daf75f..898f0ac 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -755,8 +755,8 @@
auto enable_list = program->AST().Enables();
bool dxc_require_16bit_types = false;
- for (auto enable : enable_list) {
- if (enable->extension == tint::builtin::Extension::kF16) {
+ for (auto* enable : enable_list) {
+ if (enable->HasExtension(tint::builtin::Extension::kF16)) {
dxc_require_16bit_types = true;
break;
}
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index a480356..93e5c58 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -571,8 +571,10 @@
// Ast nodes for enable directive are stored within global declarations list
auto global_decls = program_->AST().GlobalDeclarations();
for (auto* node : global_decls) {
- if (auto* ext = node->As<ast::Enable>()) {
- result.push_back({utils::ToString(ext->extension), ext->source});
+ if (auto* enable = node->As<ast::Enable>()) {
+ for (auto* ext : enable->extensions) {
+ result.push_back({utils::ToString(ext->name), ext->source});
+ }
}
}
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index d5576b8..31f566b 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -2048,20 +2048,22 @@
}
/// Adds the extension to the list of enable directives at the top of the module.
- /// @param ext the extension to enable
+ /// @param extension the extension to enable
/// @return an `ast::Enable` enabling the given extension.
- const ast::Enable* Enable(builtin::Extension ext) {
- auto* enable = create<ast::Enable>(ext);
+ const ast::Enable* Enable(builtin::Extension extension) {
+ auto* ext = create<ast::Extension>(extension);
+ auto* enable = create<ast::Enable>(utils::Vector{ext});
AST().AddEnable(enable);
return enable;
}
/// Adds the extension to the list of enable directives at the top of the module.
/// @param source the enable source
- /// @param ext the extension to enable
+ /// @param extension the extension to enable
/// @return an `ast::Enable` enabling the given extension.
- const ast::Enable* Enable(const Source& source, builtin::Extension ext) {
- auto* enable = create<ast::Enable>(source, ext);
+ const ast::Enable* Enable(const Source& source, builtin::Extension extension) {
+ auto* ext = create<ast::Extension>(source, extension);
+ auto* enable = create<ast::Enable>(source, utils::Vector{ext});
AST().AddEnable(enable);
return enable;
}
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index c1a379e..6864084 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -390,53 +390,52 @@
return decl;
}
-// enable_directive
-// : enable name SEMICLON
+// enable_directive :
+// | 'enable' identifier (COMMA identifier)* COMMA? SEMICOLON
Maybe<Void> ParserImpl::enable_directive() {
- auto decl = sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
+ return sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
+ MultiTokenSource decl_source(this);
if (!match(Token::Type::kEnable)) {
return Failure::kNoMatch;
}
- // Match the extension name.
- auto& t = peek();
- if (handle_error(t)) {
- // The token might itself be an error.
- return Failure::kErrored;
- }
-
- if (t.Is(Token::Type::kParenLeft)) {
+ if (peek_is(Token::Type::kParenLeft)) {
// A common error case is writing `enable(foo);` instead of `enable foo;`.
synchronized_ = false;
- return add_error(t.source(), "enable directives don't take parenthesis");
+ return add_error(peek().source(), "enable directives don't take parenthesis");
}
- auto ext = expect_enum("extension", builtin::ParseExtension, builtin::kExtensionStrings);
- if (ext.errored) {
- return Failure::kErrored;
+ utils::Vector<const ast::Extension*, 4> extensions;
+ while (continue_parsing()) {
+ Source ext_src = peek().source();
+ auto ext =
+ expect_enum("extension", builtin::ParseExtension, builtin::kExtensionStrings);
+ if (ext.errored) {
+ return Failure::kErrored;
+ }
+ extensions.Push(create<ast::Extension>(ext_src, ext.value));
+
+ if (!match(Token::Type::kComma)) {
+ break;
+ }
+ if (peek_is(Token::Type::kSemicolon)) {
+ break;
+ }
}
if (!expect("enable directive", Token::Type::kSemicolon)) {
return Failure::kErrored;
}
- builder_.AST().AddEnable(create<ast::Enable>(t.source(), ext.value));
+
+ builder_.AST().AddEnable(create<ast::Enable>(decl_source.Source(), std::move(extensions)));
return kSuccess;
});
-
- if (decl.errored) {
- return Failure::kErrored;
- }
- if (decl.matched) {
- return kSuccess;
- }
-
- return Failure::kNoMatch;
}
// requires_directive
-// : require identifier (COMMA identifier)? SEMICLON
+// : require identifier (COMMA identifier)* COMMA? SEMICOLON
Maybe<Void> ParserImpl::requires_directive() {
- auto decl = sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
+ return sync(Token::Type::kSemicolon, [&]() -> Maybe<Void> {
if (!match(Token::Type::kRequires)) {
return Failure::kNoMatch;
}
@@ -483,15 +482,6 @@
// conditional.
return add_error(t.source(), "missing feature names in requires directive");
});
-
- if (decl.errored) {
- return Failure::kErrored;
- }
- if (decl.matched) {
- return kSuccess;
- }
-
- return Failure::kNoMatch;
}
// global_decl
diff --git a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
index 7ad39c2..3c63a02 100644
--- a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
@@ -22,7 +22,7 @@
using EnableDirectiveTest = ParserImplTest;
// Test a valid enable directive.
-TEST_F(EnableDirectiveTest, Valid) {
+TEST_F(EnableDirectiveTest, Single) {
auto p = parser("enable f16;");
p->enable_directive();
EXPECT_FALSE(p->has_error()) << p->error();
@@ -30,13 +30,105 @@
auto& ast = program.AST();
ASSERT_EQ(ast.Enables().Length(), 1u);
auto* enable = ast.Enables()[0];
- EXPECT_EQ(enable->extension, builtin::Extension::kF16);
+ EXPECT_EQ(enable->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->source.range.begin.column, 1u);
+ EXPECT_EQ(enable->source.range.end.line, 1u);
+ EXPECT_EQ(enable->source.range.end.column, 12u);
+ ASSERT_EQ(enable->extensions.Length(), 1u);
+ EXPECT_EQ(enable->extensions[0]->name, builtin::Extension::kF16);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.column, 8u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.line, 1u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.column, 11u);
+ ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
+ EXPECT_EQ(ast.GlobalDeclarations()[0], enable);
+}
+
+// Test a valid enable directive.
+TEST_F(EnableDirectiveTest, SingleTrailingComma) {
+ auto p = parser("enable f16, ;");
+ p->enable_directive();
+ EXPECT_FALSE(p->has_error()) << p->error();
+ auto program = p->program();
+ auto& ast = program.AST();
+ ASSERT_EQ(ast.Enables().Length(), 1u);
+ auto* enable = ast.Enables()[0];
+ EXPECT_EQ(enable->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->source.range.begin.column, 1u);
+ EXPECT_EQ(enable->source.range.end.line, 1u);
+ EXPECT_EQ(enable->source.range.end.column, 14u);
+ ASSERT_EQ(enable->extensions.Length(), 1u);
+ EXPECT_EQ(enable->extensions[0]->name, builtin::Extension::kF16);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.column, 8u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.line, 1u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.column, 11u);
+ ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
+ EXPECT_EQ(ast.GlobalDeclarations()[0], enable);
+}
+
+// Test a valid enable directive with multiple extensions.
+TEST_F(EnableDirectiveTest, Multiple) {
+ auto p =
+ parser("enable f16, chromium_disable_uniformity_analysis, chromium_experimental_dp4a;");
+ p->enable_directive();
+ EXPECT_FALSE(p->has_error()) << p->error();
+ auto program = p->program();
+ auto& ast = program.AST();
+ ASSERT_EQ(ast.Enables().Length(), 1u);
+ auto* enable = ast.Enables()[0];
+ ASSERT_EQ(enable->extensions.Length(), 3u);
+ EXPECT_EQ(enable->extensions[0]->name, builtin::Extension::kF16);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.column, 8u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.line, 1u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.column, 11u);
+ EXPECT_EQ(enable->extensions[1]->name, builtin::Extension::kChromiumDisableUniformityAnalysis);
+ EXPECT_EQ(enable->extensions[1]->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->extensions[1]->source.range.begin.column, 13u);
+ EXPECT_EQ(enable->extensions[1]->source.range.end.line, 1u);
+ EXPECT_EQ(enable->extensions[1]->source.range.end.column, 49u);
+ EXPECT_EQ(enable->extensions[2]->name, builtin::Extension::kChromiumExperimentalDp4A);
+ EXPECT_EQ(enable->extensions[2]->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->extensions[2]->source.range.begin.column, 51u);
+ EXPECT_EQ(enable->extensions[2]->source.range.end.line, 1u);
+ EXPECT_EQ(enable->extensions[2]->source.range.end.column, 77u);
+ ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
+ EXPECT_EQ(ast.GlobalDeclarations()[0], enable);
+}
+
+// Test a valid enable directive with multiple extensions.
+TEST_F(EnableDirectiveTest, MultipleTrailingComma) {
+ auto p =
+ parser("enable f16, chromium_disable_uniformity_analysis, chromium_experimental_dp4a,;");
+ p->enable_directive();
+ EXPECT_FALSE(p->has_error()) << p->error();
+ auto program = p->program();
+ auto& ast = program.AST();
+ ASSERT_EQ(ast.Enables().Length(), 1u);
+ auto* enable = ast.Enables()[0];
+ ASSERT_EQ(enable->extensions.Length(), 3u);
+ EXPECT_EQ(enable->extensions[0]->name, builtin::Extension::kF16);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.column, 8u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.line, 1u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.column, 11u);
+ EXPECT_EQ(enable->extensions[1]->name, builtin::Extension::kChromiumDisableUniformityAnalysis);
+ EXPECT_EQ(enable->extensions[1]->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->extensions[1]->source.range.begin.column, 13u);
+ EXPECT_EQ(enable->extensions[1]->source.range.end.line, 1u);
+ EXPECT_EQ(enable->extensions[1]->source.range.end.column, 49u);
+ EXPECT_EQ(enable->extensions[2]->name, builtin::Extension::kChromiumExperimentalDp4A);
+ EXPECT_EQ(enable->extensions[2]->source.range.begin.line, 1u);
+ EXPECT_EQ(enable->extensions[2]->source.range.begin.column, 51u);
+ EXPECT_EQ(enable->extensions[2]->source.range.end.line, 1u);
+ EXPECT_EQ(enable->extensions[2]->source.range.end.column, 77u);
ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
EXPECT_EQ(ast.GlobalDeclarations()[0], enable);
}
// Test multiple enable directives for a same extension.
-TEST_F(EnableDirectiveTest, EnableMultipleTime) {
+TEST_F(EnableDirectiveTest, EnableSameLine) {
auto p = parser(R"(
enable f16;
enable f16;
@@ -48,8 +140,18 @@
ASSERT_EQ(ast.Enables().Length(), 2u);
auto* enable_a = ast.Enables()[0];
auto* enable_b = ast.Enables()[1];
- EXPECT_EQ(enable_a->extension, builtin::Extension::kF16);
- EXPECT_EQ(enable_b->extension, builtin::Extension::kF16);
+ ASSERT_EQ(enable_a->extensions.Length(), 1u);
+ EXPECT_EQ(enable_a->extensions[0]->name, builtin::Extension::kF16);
+ EXPECT_EQ(enable_a->extensions[0]->source.range.begin.line, 2u);
+ EXPECT_EQ(enable_a->extensions[0]->source.range.begin.column, 8u);
+ EXPECT_EQ(enable_a->extensions[0]->source.range.end.line, 2u);
+ EXPECT_EQ(enable_a->extensions[0]->source.range.end.column, 11u);
+ ASSERT_EQ(enable_b->extensions.Length(), 1u);
+ EXPECT_EQ(enable_b->extensions[0]->name, builtin::Extension::kF16);
+ EXPECT_EQ(enable_b->extensions[0]->source.range.begin.line, 3u);
+ EXPECT_EQ(enable_b->extensions[0]->source.range.begin.column, 8u);
+ EXPECT_EQ(enable_b->extensions[0]->source.range.end.line, 3u);
+ EXPECT_EQ(enable_b->extensions[0]->source.range.end.column, 11u);
ASSERT_EQ(ast.GlobalDeclarations().Length(), 2u);
EXPECT_EQ(ast.GlobalDeclarations()[0], enable_a);
EXPECT_EQ(ast.GlobalDeclarations()[1], enable_b);
@@ -169,7 +271,12 @@
// Accept the enable directive although it caused an error
ASSERT_EQ(ast.Enables().Length(), 1u);
auto* enable = ast.Enables()[0];
- EXPECT_EQ(enable->extension, builtin::Extension::kF16);
+ ASSERT_EQ(enable->extensions.Length(), 1u);
+ EXPECT_EQ(enable->extensions[0]->name, builtin::Extension::kF16);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.line, 3u);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.column, 8u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.line, 3u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.column, 11u);
ASSERT_EQ(ast.GlobalDeclarations().Length(), 2u);
EXPECT_EQ(ast.GlobalDeclarations()[1], enable);
}
@@ -189,7 +296,12 @@
// Accept the enable directive although it cause an error
ASSERT_EQ(ast.Enables().Length(), 1u);
auto* enable = ast.Enables()[0];
- EXPECT_EQ(enable->extension, builtin::Extension::kF16);
+ ASSERT_EQ(enable->extensions.Length(), 1u);
+ EXPECT_EQ(enable->extensions[0]->name, builtin::Extension::kF16);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.line, 3u);
+ EXPECT_EQ(enable->extensions[0]->source.range.begin.column, 8u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.line, 3u);
+ EXPECT_EQ(enable->extensions[0]->source.range.end.column, 11u);
ASSERT_EQ(ast.GlobalDeclarations().Length(), 1u);
EXPECT_EQ(ast.GlobalDeclarations()[0], enable);
}
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 48fa4b1..2cd40e9 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -3476,7 +3476,10 @@
}
bool Resolver::Enable(const ast::Enable* enable) {
- enabled_extensions_.Add(enable->extension);
+ for (auto* ext : enable->extensions) {
+ Mark(ext);
+ enabled_extensions_.Add(ext->name);
+ }
return true;
}
diff --git a/src/tint/transform/builtin_polyfill.cc b/src/tint/transform/builtin_polyfill.cc
index c242521..cc770ab 100644
--- a/src/tint/transform/builtin_polyfill.cc
+++ b/src/tint/transform/builtin_polyfill.cc
@@ -47,8 +47,9 @@
State(const Program* program, const Config& config) : src(program), cfg(config) {
has_full_ptr_params = false;
for (auto* enable : src->AST().Enables()) {
- if (enable->extension == builtin::Extension::kChromiumExperimentalFullPtrParameters) {
+ if (enable->HasExtension(builtin::Extension::kChromiumExperimentalFullPtrParameters)) {
has_full_ptr_params = true;
+ break;
}
}
}
diff --git a/src/tint/transform/preserve_padding.cc b/src/tint/transform/preserve_padding.cc
index 59fd786..e8c95e3 100644
--- a/src/tint/transform/preserve_padding.cc
+++ b/src/tint/transform/preserve_padding.cc
@@ -67,8 +67,8 @@
},
[&](const ast::Enable* enable) {
// Check if the full pointer parameters extension is already enabled.
- if (enable->extension ==
- builtin::Extension::kChromiumExperimentalFullPtrParameters) {
+ if (enable->HasExtension(
+ builtin::Extension::kChromiumExperimentalFullPtrParameters)) {
ext_enabled = true;
}
});
diff --git a/src/tint/writer/check_supported_extensions.cc b/src/tint/writer/check_supported_extensions.cc
index bde86a5..88ddcdd 100644
--- a/src/tint/writer/check_supported_extensions.cc
+++ b/src/tint/writer/check_supported_extensions.cc
@@ -33,13 +33,14 @@
}
for (auto* enable : module.Enables()) {
- auto ext = enable->extension;
- if (!set.Contains(ext)) {
- diags.add_error(diag::System::Writer,
- std::string(writer_name) + " backend does not support extension '" +
- utils::ToString(ext) + "'",
- enable->source);
- return false;
+ for (auto* ext : enable->extensions) {
+ if (!set.Contains(ext->name)) {
+ diags.add_error(diag::System::Writer,
+ std::string(writer_name) + " backend does not support extension '" +
+ utils::ToString(ext->name) + "'",
+ ext->source);
+ return false;
+ }
}
}
return true;
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index cdf4a15..311005b 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -347,10 +347,10 @@
return true;
}
-bool GeneratorImpl::RecordExtension(const ast::Enable* ext) {
+bool GeneratorImpl::RecordExtension(const ast::Enable* enable) {
// Deal with extension node here, recording it within the generator for later emition.
- if (ext->extension == builtin::Extension::kF16) {
+ if (enable->HasExtension(builtin::Extension::kF16)) {
requires_f16_extension_ = true;
}
diff --git a/src/tint/writer/spirv/builder_builtin_test.cc b/src/tint/writer/spirv/builder_builtin_test.cc
index 5db867a..280b878 100644
--- a/src/tint/writer/spirv/builder_builtin_test.cc
+++ b/src/tint/writer/spirv/builder_builtin_test.cc
@@ -4154,10 +4154,7 @@
namespace DP4A_builtin_tests {
TEST_F(BuiltinBuilderTest, Call_Dot4I8Packed) {
- auto* ext =
- create<ast::Enable>(Source{Source::Range{Source::Location{10, 2}, Source::Location{10, 5}}},
- builtin::Extension::kChromiumExperimentalDp4A);
- AST().AddEnable(ext);
+ Enable(builtin::Extension::kChromiumExperimentalDp4A);
auto* val1 = Var("val1", ty.u32());
auto* val2 = Var("val2", ty.u32());
@@ -4194,10 +4191,7 @@
}
TEST_F(BuiltinBuilderTest, Call_Dot4U8Packed) {
- auto* ext =
- create<ast::Enable>(Source{Source::Range{Source::Location{10, 2}, Source::Location{10, 5}}},
- builtin::Extension::kChromiumExperimentalDp4A);
- AST().AddEnable(ext);
+ Enable(builtin::Extension::kChromiumExperimentalDp4A);
auto* val1 = Var("val1", ty.u32());
auto* val2 = Var("val2", ty.u32());
diff --git a/src/tint/writer/syntax_tree/generator_impl.cc b/src/tint/writer/syntax_tree/generator_impl.cc
index 87d182e..02e6ff7 100644
--- a/src/tint/writer/syntax_tree/generator_impl.cc
+++ b/src/tint/writer/syntax_tree/generator_impl.cc
@@ -79,7 +79,15 @@
}
bool GeneratorImpl::EmitEnable(const ast::Enable* enable) {
- line() << "Enable [" << enable->extension << "]";
+ auto l = line();
+ l << "Enable [";
+ for (auto* ext : enable->extensions) {
+ if (ext != enable->extensions.Front()) {
+ l << ", ";
+ }
+ l << ext->name;
+ }
+ l << "]";
return true;
}
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 0421616..2b5f474 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -100,7 +100,14 @@
bool GeneratorImpl::EmitEnable(const ast::Enable* enable) {
auto out = line();
- out << "enable " << enable->extension << ";";
+ out << "enable ";
+ for (auto* ext : enable->extensions) {
+ if (ext != enable->extensions.Front()) {
+ out << ", ";
+ }
+ out << ext->name;
+ }
+ out << ";";
return true;
}
diff --git a/test/tint/extensions/parsing/multiple.wgsl b/test/tint/extensions/parsing/multiple.wgsl
new file mode 100644
index 0000000..c5224f0
--- /dev/null
+++ b/test/tint/extensions/parsing/multiple.wgsl
@@ -0,0 +1,6 @@
+enable chromium_experimental_push_constant, f16;
+
+@fragment
+fn main() -> @location(0) vec4<f32> {
+ return vec4<f32>(0.1, 0.2, 0.3, 0.4);
+}
diff --git a/test/tint/extensions/parsing/multiple.wgsl.expected.dxc.hlsl b/test/tint/extensions/parsing/multiple.wgsl.expected.dxc.hlsl
new file mode 100644
index 0000000..02332f3
--- /dev/null
+++ b/test/tint/extensions/parsing/multiple.wgsl.expected.dxc.hlsl
@@ -0,0 +1,14 @@
+struct tint_symbol {
+ float4 value : SV_Target0;
+};
+
+float4 main_inner() {
+ return float4(0.10000000149011611938f, 0.20000000298023223877f, 0.30000001192092895508f, 0.40000000596046447754f);
+}
+
+tint_symbol main() {
+ const float4 inner_result = main_inner();
+ tint_symbol wrapper_result = (tint_symbol)0;
+ wrapper_result.value = inner_result;
+ return wrapper_result;
+}
diff --git a/test/tint/extensions/parsing/multiple.wgsl.expected.fxc.hlsl b/test/tint/extensions/parsing/multiple.wgsl.expected.fxc.hlsl
new file mode 100644
index 0000000..02332f3
--- /dev/null
+++ b/test/tint/extensions/parsing/multiple.wgsl.expected.fxc.hlsl
@@ -0,0 +1,14 @@
+struct tint_symbol {
+ float4 value : SV_Target0;
+};
+
+float4 main_inner() {
+ return float4(0.10000000149011611938f, 0.20000000298023223877f, 0.30000001192092895508f, 0.40000000596046447754f);
+}
+
+tint_symbol main() {
+ const float4 inner_result = main_inner();
+ tint_symbol wrapper_result = (tint_symbol)0;
+ wrapper_result.value = inner_result;
+ return wrapper_result;
+}
diff --git a/test/tint/extensions/parsing/multiple.wgsl.expected.glsl b/test/tint/extensions/parsing/multiple.wgsl.expected.glsl
new file mode 100644
index 0000000..37063fa
--- /dev/null
+++ b/test/tint/extensions/parsing/multiple.wgsl.expected.glsl
@@ -0,0 +1,14 @@
+#version 310 es
+#extension GL_AMD_gpu_shader_half_float : require
+precision highp float;
+
+layout(location = 0) out vec4 value;
+vec4 tint_symbol() {
+ return vec4(0.10000000149011611938f, 0.20000000298023223877f, 0.30000001192092895508f, 0.40000000596046447754f);
+}
+
+void main() {
+ vec4 inner_result = tint_symbol();
+ value = inner_result;
+ return;
+}
diff --git a/test/tint/extensions/parsing/multiple.wgsl.expected.msl b/test/tint/extensions/parsing/multiple.wgsl.expected.msl
new file mode 100644
index 0000000..8eb15e9
--- /dev/null
+++ b/test/tint/extensions/parsing/multiple.wgsl.expected.msl
@@ -0,0 +1,18 @@
+#include <metal_stdlib>
+
+using namespace metal;
+struct tint_symbol_1 {
+ float4 value [[color(0)]];
+};
+
+float4 tint_symbol_inner() {
+ return float4(0.10000000149011611938f, 0.20000000298023223877f, 0.30000001192092895508f, 0.40000000596046447754f);
+}
+
+fragment tint_symbol_1 tint_symbol() {
+ float4 const inner_result = tint_symbol_inner();
+ tint_symbol_1 wrapper_result = {};
+ wrapper_result.value = inner_result;
+ return wrapper_result;
+}
+
diff --git a/test/tint/extensions/parsing/multiple.wgsl.expected.spvasm b/test/tint/extensions/parsing/multiple.wgsl.expected.spvasm
new file mode 100644
index 0000000..3b3eac5
--- /dev/null
+++ b/test/tint/extensions/parsing/multiple.wgsl.expected.spvasm
@@ -0,0 +1,40 @@
+; SPIR-V
+; Version: 1.3
+; Generator: Google Tint Compiler; 0
+; Bound: 19
+; Schema: 0
+ OpCapability Shader
+ OpCapability Float16
+ OpCapability UniformAndStorageBuffer16BitAccess
+ OpCapability StorageBuffer16BitAccess
+ OpCapability StorageInputOutput16
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %main "main" %value
+ OpExecutionMode %main OriginUpperLeft
+ OpName %value "value"
+ OpName %main_inner "main_inner"
+ OpName %main "main"
+ OpDecorate %value Location 0
+ %float = OpTypeFloat 32
+ %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+ %5 = OpConstantNull %v4float
+ %value = OpVariable %_ptr_Output_v4float Output %5
+ %6 = OpTypeFunction %v4float
+%float_0_100000001 = OpConstant %float 0.100000001
+%float_0_200000003 = OpConstant %float 0.200000003
+%float_0_300000012 = OpConstant %float 0.300000012
+%float_0_400000006 = OpConstant %float 0.400000006
+ %13 = OpConstantComposite %v4float %float_0_100000001 %float_0_200000003 %float_0_300000012 %float_0_400000006
+ %void = OpTypeVoid
+ %14 = OpTypeFunction %void
+ %main_inner = OpFunction %v4float None %6
+ %8 = OpLabel
+ OpReturnValue %13
+ OpFunctionEnd
+ %main = OpFunction %void None %14
+ %17 = OpLabel
+ %18 = OpFunctionCall %v4float %main_inner
+ OpStore %value %18
+ OpReturn
+ OpFunctionEnd
diff --git a/test/tint/extensions/parsing/multiple.wgsl.expected.wgsl b/test/tint/extensions/parsing/multiple.wgsl.expected.wgsl
new file mode 100644
index 0000000..448585d
--- /dev/null
+++ b/test/tint/extensions/parsing/multiple.wgsl.expected.wgsl
@@ -0,0 +1,6 @@
+enable chromium_experimental_push_constant, f16;
+
+@fragment
+fn main() -> @location(0) vec4<f32> {
+ return vec4<f32>(0.10000000000000000555, 0.2000000000000000111, 0.2999999999999999889, 0.4000000000000000222);
+}