Import Tint changes from Dawn
Changes:
- 7d34de88f1752c1119f33a444b5383c8510e78e5 tint/test-runner: Split expectations for FXC and DXC by Ben Clayton <bclayton@google.com>
- a089376daf17ed16c2b527d26aa41338c1ad60f8 Context aware address space parsing. by dan sinclair <dsinclair@chromium.org>
- 87254ff58e954cf6daa9f8f851d4fa5604cadf60 Update error messages to say initializer by dan sinclair <dsinclair@chromium.org>
- 05288f6cff05391449877104b338a15ae0bbe81f Rename paren_rhs_stmt to paren_expression. by dan sinclair <dsinclair@chromium.org>
- 256f1116b836aa88d8704e7f4ee2f6b32ed44dff Add transform to substitute overrides with const expressi... by dan sinclair <dsinclair@chromium.org>
GitOrigin-RevId: 7d34de88f1752c1119f33a444b5383c8510e78e5
Change-Id: I8b57174088889a4de2ceea5033b3b74e1a91fa1a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/96984
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/include/tint/tint.h b/include/tint/tint.h
index 42ceda4..a6ab6ca 100644
--- a/include/tint/tint.h
+++ b/include/tint/tint.h
@@ -31,6 +31,7 @@
#include "src/tint/transform/renamer.h"
#include "src/tint/transform/robustness.h"
#include "src/tint/transform/single_entry_point.h"
+#include "src/tint/transform/substitute_override.h"
#include "src/tint/transform/vertex_pulling.h"
#include "src/tint/writer/flatten_bindings.h"
#include "src/tint/writer/writer.h"
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 03f27e5..3290694 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -287,9 +287,9 @@
"ast/module.h",
"ast/multisampled_texture.cc",
"ast/multisampled_texture.h",
- "ast/node_id.h",
"ast/node.cc",
"ast/node.h",
+ "ast/node_id.h",
"ast/override.cc",
"ast/override.h",
"ast/parameter.cc",
@@ -529,6 +529,8 @@
"transform/single_entry_point.h",
"transform/spirv_atomic.cc",
"transform/spirv_atomic.h",
+ "transform/substitute_override.cc",
+ "transform/substitute_override.h",
"transform/transform.cc",
"transform/transform.h",
"transform/unshadow.cc",
@@ -1034,6 +1036,7 @@
"ast/module_clone_test.cc",
"ast/module_test.cc",
"ast/multisampled_texture_test.cc",
+ "ast/override_test.cc",
"ast/phony_expression_test.cc",
"ast/pointer_test.cc",
"ast/return_statement_test.cc",
@@ -1197,6 +1200,7 @@
"transform/simplify_pointers_test.cc",
"transform/single_entry_point_test.cc",
"transform/spirv_atomic_test.cc",
+ "transform/substitute_override_test.cc",
"transform/test_helper.h",
"transform/transform_test.cc",
"transform/unshadow_test.cc",
@@ -1365,7 +1369,7 @@
"reader/wgsl/parser_impl_loop_stmt_test.cc",
"reader/wgsl/parser_impl_multiplicative_expression_test.cc",
"reader/wgsl/parser_impl_param_list_test.cc",
- "reader/wgsl/parser_impl_paren_rhs_stmt_test.cc",
+ "reader/wgsl/parser_impl_paren_expression_test.cc",
"reader/wgsl/parser_impl_pipeline_stage_test.cc",
"reader/wgsl/parser_impl_primary_expression_test.cc",
"reader/wgsl/parser_impl_relational_expression_test.cc",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 4eb97a2..8f8f19c 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -441,6 +441,8 @@
transform/single_entry_point.h
transform/spirv_atomic.cc
transform/spirv_atomic.h
+ transform/substitute_override.cc
+ transform/substitute_override.h
transform/transform.cc
transform/transform.h
transform/unshadow.cc
@@ -730,6 +732,7 @@
ast/module_clone_test.cc
ast/module_test.cc
ast/multisampled_texture_test.cc
+ ast/override_test.cc
ast/phony_expression_test.cc
ast/pointer_test.cc
ast/return_statement_test.cc
@@ -963,7 +966,7 @@
reader/wgsl/parser_impl_loop_stmt_test.cc
reader/wgsl/parser_impl_multiplicative_expression_test.cc
reader/wgsl/parser_impl_param_list_test.cc
- reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
+ reader/wgsl/parser_impl_paren_expression_test.cc
reader/wgsl/parser_impl_pipeline_stage_test.cc
reader/wgsl/parser_impl_primary_expression_test.cc
reader/wgsl/parser_impl_relational_expression_test.cc
@@ -1109,6 +1112,7 @@
transform/simplify_pointers_test.cc
transform/single_entry_point_test.cc
transform/spirv_atomic_test.cc
+ transform/substitute_override_test.cc
transform/test_helper.h
transform/unshadow_test.cc
transform/unwind_discard_functions_test.cc
diff --git a/src/tint/ast/override.cc b/src/tint/ast/override.cc
index b7ee064..2876920 100644
--- a/src/tint/ast/override.cc
+++ b/src/tint/ast/override.cc
@@ -46,4 +46,11 @@
return ctx->dst->create<Override>(src, sym, ty, ctor, attrs);
}
+std::string Override::Identifier(const SymbolTable& symbols) const {
+ if (auto* id = ast::GetAttribute<ast::IdAttribute>(attributes)) {
+ return std::to_string(id->value);
+ }
+ return symbols.NameFor(symbol);
+}
+
} // namespace tint::ast
diff --git a/src/tint/ast/override.h b/src/tint/ast/override.h
index e97f14c..00c36fc 100644
--- a/src/tint/ast/override.h
+++ b/src/tint/ast/override.h
@@ -15,6 +15,8 @@
#ifndef SRC_TINT_AST_OVERRIDE_H_
#define SRC_TINT_AST_OVERRIDE_H_
+#include <string>
+
#include "src/tint/ast/variable.h"
namespace tint::ast {
@@ -60,6 +62,12 @@
/// @param ctx the clone context
/// @return the newly cloned node
const Override* Clone(CloneContext* ctx) const override;
+
+ /// @param symbols the symbol table to retrieve the name from
+ /// @returns the identifier string for the override. If the override has
+ /// an ID attribute, the string is the id-stringified. Otherwise, the ID
+ /// is the symbol.
+ std::string Identifier(const SymbolTable& symbols) const;
};
} // namespace tint::ast
diff --git a/src/tint/ast/override_test.cc b/src/tint/ast/override_test.cc
new file mode 100644
index 0000000..b4179e5
--- /dev/null
+++ b/src/tint/ast/override_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2022 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/override.h"
+
+#include "src/tint/ast/test_helper.h"
+
+namespace tint::ast {
+namespace {
+
+using OverrideTest = TestHelper;
+
+TEST_F(OverrideTest, Identifier_NoId) {
+ auto* o = Override("o", nullptr, Expr(f32(1.0)));
+ EXPECT_EQ(std::string("o"), o->Identifier(Symbols()));
+}
+
+TEST_F(OverrideTest, Identifier_WithId) {
+ auto* o = Override("o", nullptr, Expr(f32(1.0)), {Id(4u)});
+ EXPECT_EQ(std::string("4"), o->Identifier(Symbols()));
+}
+
+} // namespace
+} // namespace tint::ast
diff --git a/src/tint/cmd/main.cc b/src/tint/cmd/main.cc
index f6029ac..b948e69 100644
--- a/src/tint/cmd/main.cc
+++ b/src/tint/cmd/main.cc
@@ -20,6 +20,7 @@
#include <optional>
#include <sstream>
#include <string>
+#include <unordered_map>
#include <vector>
#if TINT_BUILD_GLSL_WRITER
@@ -66,6 +67,7 @@
struct Options {
bool show_help = false;
+ bool verbose = false;
std::string input_filename;
std::string output_file = "-"; // Default to stdout
@@ -83,10 +85,10 @@
std::vector<std::string> transforms;
- bool use_fxc = false;
+ std::string fxc_path;
std::string dxc_path;
std::string xcrun_path;
- std::vector<std::string> overrides;
+ std::unordered_map<std::string, double> overrides;
std::optional<tint::sem::BindingPoint> hlsl_root_constant_binding_point;
};
@@ -119,14 +121,15 @@
used for num_workgroups in HLSL. If not specified, then
default to binding 0 of the largest used group plus 1,
or group 0 if no resource bound.
- --validate -- Validates the generated shader
- --fxc -- Ask to validate HLSL output using FXC instead of DXC.
- When specified, automatically enables --validate
+ --validate -- Validates the generated shader with all available validators
+ --fxc -- Path to FXC dll, used to validate HLSL output.
+ When specified, automatically enables HLSL validation with FXC
--dxc -- Path to DXC executable, used to validate HLSL output.
- When specified, automatically enables --validate
+ When specified, automatically enables HLSL validation with DXC
--xcrun -- Path to xcrun executable, used to validate MSL output.
- When specified, automatically enables --validate
- --overrides -- Pipeline overrides as NAME=VALUE, comma-separated.)";
+ When specified, automatically enables MSL validation
+ --overrides -- Override values as IDENTIFIER=VALUE, comma-separated.
+)";
Format parse_format(const std::string& fmt) {
(void)fmt;
@@ -215,18 +218,26 @@
return Format::kNone;
}
-std::vector<std::string> split_on_comma(std::string list) {
+std::vector<std::string> split_on_char(std::string list, char c) {
std::vector<std::string> res;
std::stringstream str(list);
while (str.good()) {
std::string substr;
- getline(str, substr, ',');
+ getline(str, substr, c);
res.push_back(substr);
}
return res;
}
+std::vector<std::string> split_on_comma(std::string list) {
+ return split_on_char(list, ',');
+}
+
+std::vector<std::string> split_on_equal(std::string list) {
+ return split_on_char(list, '=');
+}
+
std::optional<uint64_t> parse_unsigned_number(std::string number) {
for (char c : number) {
if (!std::isdigit(c)) {
@@ -387,6 +398,8 @@
} else if (arg == "-h" || arg == "--help") {
opts->show_help = true;
+ } else if (arg == "-v" || arg == "--verbose") {
+ opts->verbose = true;
} else if (arg == "--transform") {
++i;
if (i >= args.size()) {
@@ -405,8 +418,12 @@
} else if (arg == "--validate") {
opts->validate = true;
} else if (arg == "--fxc") {
- opts->validate = true;
- opts->use_fxc = true;
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for " << arg << std::endl;
+ return false;
+ }
+ opts->fxc_path = args[i];
} else if (arg == "--dxc") {
++i;
if (i >= args.size()) {
@@ -414,7 +431,6 @@
return false;
}
opts->dxc_path = args[i];
- opts->validate = true;
} else if (arg == "--xcrun") {
++i;
if (i >= args.size()) {
@@ -429,7 +445,10 @@
std::cerr << "Missing value for " << arg << std::endl;
return false;
}
- opts->overrides = split_on_comma(args[i]);
+ for (const auto& o : split_on_comma(args[i])) {
+ auto parts = split_on_equal(o);
+ opts->overrides.insert({parts[0], std::stod(parts[1])});
+ }
} else if (arg == "--hlsl-root-constant-binding-point") {
++i;
if (i >= args.size()) {
@@ -788,30 +807,71 @@
return false;
}
- if (options.validate) {
- tint::val::Result res;
- if (options.use_fxc) {
-#ifdef _WIN32
- res = tint::val::HlslUsingFXC(result.hlsl, result.entry_points, options.overrides);
-#else
- res.failed = true;
- res.output = "FXC can only be used on Windows. Sorry :X";
-#endif // _WIN32
- } else {
+ // If --fxc or --dxc was passed, then we must explicitly find and validate with that respective
+ // compiler.
+ const bool must_validate_dxc = !options.dxc_path.empty();
+ const bool must_validate_fxc = !options.fxc_path.empty();
+ if (options.validate || must_validate_dxc || must_validate_fxc) {
+ tint::val::Result dxc_res;
+ bool dxc_found = false;
+ if (options.validate || must_validate_dxc) {
auto dxc =
tint::utils::Command::LookPath(options.dxc_path.empty() ? "dxc" : options.dxc_path);
if (dxc.Found()) {
- res = tint::val::HlslUsingDXC(dxc.Path(), result.hlsl, result.entry_points,
- options.overrides);
- } else {
- res.failed = true;
- res.output = "DXC executable not found. Cannot validate";
+ dxc_found = true;
+ dxc_res = tint::val::HlslUsingDXC(dxc.Path(), result.hlsl, result.entry_points);
+ } else if (must_validate_dxc) {
+ // DXC was explicitly requested. Error if it could not be found.
+ dxc_res.failed = true;
+ dxc_res.output =
+ "DXC executable '" + options.dxc_path + "' not found. Cannot validate";
}
}
- if (res.failed) {
- std::cerr << res.output << std::endl;
+
+ tint::val::Result fxc_res;
+ bool fxc_found = false;
+ if (options.validate || must_validate_fxc) {
+ auto fxc = tint::utils::Command::LookPath(
+ options.fxc_path.empty() ? tint::val::kFxcDLLName : options.fxc_path);
+
+#ifdef _WIN32
+ if (fxc.Found()) {
+ fxc_found = true;
+ fxc_res = tint::val::HlslUsingFXC(fxc.Path(), result.hlsl, result.entry_points);
+ } else if (must_validate_fxc) {
+ // FXC was explicitly requested. Error if it could not be found.
+ fxc_res.failed = true;
+ fxc_res.output = "FXC DLL '" + options.fxc_path + "' not found. Cannot validate";
+ }
+#else
+ if (must_validate_dxc) {
+ fxc_res.failed = true;
+ fxc_res.output = "FXC can only be used on Windows.";
+ }
+#endif // _WIN32
+ }
+
+ if (fxc_res.failed) {
+ std::cerr << "FXC validation failure:" << std::endl << fxc_res.output << std::endl;
+ }
+ if (dxc_res.failed) {
+ std::cerr << "DXC validation failure:" << std::endl << dxc_res.output << std::endl;
+ }
+ if (fxc_res.failed || dxc_res.failed) {
return false;
}
+ if (!fxc_found && !dxc_found) {
+ std::cerr << "Couldn't find FXC or DXC. Cannot validate" << std::endl;
+ return false;
+ }
+ if (options.verbose) {
+ if (fxc_found && !fxc_res.failed) {
+ std::cout << "Passed FXC validation" << std::endl;
+ }
+ if (dxc_found && !dxc_res.failed) {
+ std::cout << "Passed DXC validation" << std::endl;
+ }
+ }
}
return true;
@@ -947,6 +1007,14 @@
tint::transform::DataMap&) { m.Add<tint::transform::Renamer>(); }},
{"robustness", [](tint::transform::Manager& m,
tint::transform::DataMap&) { m.Add<tint::transform::Robustness>(); }},
+ {"substitute_override",
+ [&](tint::transform::Manager& m, tint::transform::DataMap& i) {
+ tint::transform::SubstituteOverride::Config cfg;
+ cfg.map = options.overrides;
+
+ i.Add<tint::transform::SubstituteOverride::Config>(cfg);
+ m.Add<tint::transform::SubstituteOverride>();
+ }},
};
auto transform_names = [&] {
std::stringstream names;
@@ -1078,6 +1146,17 @@
tint::transform::Manager transform_manager;
tint::transform::DataMap transform_inputs;
+
+ // If overrides are provided, add the SubstituteOverride transform.
+ if (!options.overrides.empty()) {
+ for (auto& t : transforms) {
+ if (t.name == std::string("substitute_override")) {
+ t.make(transform_manager, transform_inputs);
+ break;
+ }
+ }
+ }
+
for (const auto& name : options.transforms) {
// TODO(dsinclair): The vertex pulling transform requires setup code to
// be run that needs user input. Should we find a way to support that here
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index ade723c..22eedda 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -1127,9 +1127,6 @@
if (str == "for") {
return {Token::Type::kFor, source, "for"};
}
- if (str == "function") {
- return {Token::Type::kFunction, source, "function"};
- }
if (str == "i32") {
return {Token::Type::kI32, source, "i32"};
}
@@ -1175,9 +1172,6 @@
if (str == "override") {
return {Token::Type::kOverride, source, "override"};
}
- if (str == "private") {
- return {Token::Type::kPrivate, source, "private"};
- }
if (str == "ptr") {
return {Token::Type::kPtr, source, "ptr"};
}
@@ -1190,9 +1184,6 @@
if (str == "sampler_comparison") {
return {Token::Type::kComparisonSampler, source, "sampler_comparison"};
}
- if (str == "storage_buffer" || str == "storage") {
- return {Token::Type::kStorage, source, "storage"};
- }
if (str == "struct") {
return {Token::Type::kStruct, source, "struct"};
}
@@ -1259,9 +1250,6 @@
if (str == "u32") {
return {Token::Type::kU32, source, "u32"};
}
- if (str == "uniform") {
- return {Token::Type::kUniform, source, "uniform"};
- }
if (str == "var") {
return {Token::Type::kVar, source, "var"};
}
@@ -1277,9 +1265,6 @@
if (str == "while") {
return {Token::Type::kWhile, source, "while"};
}
- if (str == "workgroup") {
- return {Token::Type::kWorkgroup, source, "workgroup"};
- }
return {};
}
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index bf32a52..799f9c2 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -941,7 +941,6 @@
TokenData{"false", Token::Type::kFalse},
TokenData{"fn", Token::Type::kFn},
TokenData{"for", Token::Type::kFor},
- TokenData{"function", Token::Type::kFunction},
TokenData{"i32", Token::Type::kI32},
TokenData{"if", Token::Type::kIf},
TokenData{"import", Token::Type::kImport},
@@ -957,13 +956,10 @@
TokenData{"mat4x3", Token::Type::kMat4x3},
TokenData{"mat4x4", Token::Type::kMat4x4},
TokenData{"override", Token::Type::kOverride},
- TokenData{"private", Token::Type::kPrivate},
TokenData{"ptr", Token::Type::kPtr},
TokenData{"return", Token::Type::kReturn},
TokenData{"sampler", Token::Type::kSampler},
TokenData{"sampler_comparison", Token::Type::kComparisonSampler},
- TokenData{"storage", Token::Type::kStorage},
- TokenData{"storage_buffer", Token::Type::kStorage},
TokenData{"struct", Token::Type::kStruct},
TokenData{"switch", Token::Type::kSwitch},
TokenData{"texture_1d", Token::Type::kTextureSampled1d},
@@ -986,13 +982,11 @@
TokenData{"true", Token::Type::kTrue},
TokenData{"type", Token::Type::kType},
TokenData{"u32", Token::Type::kU32},
- TokenData{"uniform", Token::Type::kUniform},
TokenData{"var", Token::Type::kVar},
TokenData{"vec2", Token::Type::kVec2},
TokenData{"vec3", Token::Type::kVec3},
TokenData{"vec4", Token::Type::kVec4},
- TokenData{"while", Token::Type::kWhile},
- TokenData{"workgroup", Token::Type::kWorkgroup}));
+ TokenData{"while", Token::Type::kWhile}));
} // namespace
} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index a490a5c..6ca78d6 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -562,7 +562,7 @@
decl->type, // type
decl->storage_class, // storage class
decl->access, // access control
- initalizer, // constructor
+ initalizer, // initializer
std::move(attrs)); // attributes
}
@@ -619,20 +619,20 @@
return create<ast::Const>(decl->source, // source
builder_.Symbols().Register(decl->name), // symbol
decl->type, // type
- initializer, // constructor
+ initializer, // initializer
std::move(attrs)); // attributes
}
if (is_overridable) {
return create<ast::Override>(decl->source, // source
builder_.Symbols().Register(decl->name), // symbol
decl->type, // type
- initializer, // constructor
+ initializer, // initializer
std::move(attrs)); // attributes
}
return create<ast::Const>(decl->source, // source
builder_.Symbols().Register(decl->name), // symbol
decl->type, // type
- initializer, // constructor
+ initializer, // initializer
std::move(attrs)); // attributes
}
@@ -1271,34 +1271,31 @@
return builder_.ty.mat(make_source_range_from(t.source()), subtype, columns, rows);
}
-// storage_class
-// : INPUT
-// | OUTPUT
-// | UNIFORM
-// | WORKGROUP
-// | STORAGE
-// | PRIVATE
-// | FUNCTION
Expect<ast::StorageClass> ParserImpl::expect_storage_class(std::string_view use) {
auto source = peek().source();
+ auto ident = expect_ident("storage class");
+ if (ident.errored) {
+ return Failure::kErrored;
+ }
- if (match(Token::Type::kUniform)) {
+ auto name = ident.value;
+ if (name == "uniform") {
return {ast::StorageClass::kUniform, source};
}
- if (match(Token::Type::kWorkgroup)) {
+ if (name == "workgroup") {
return {ast::StorageClass::kWorkgroup, source};
}
- if (match(Token::Type::kStorage)) {
+ if (name == "storage" || name == "storage_buffer") {
return {ast::StorageClass::kStorage, source};
}
- if (match(Token::Type::kPrivate)) {
+ if (name == "private") {
return {ast::StorageClass::kPrivate, source};
}
- if (match(Token::Type::kFunction)) {
+ if (name == "function") {
return {ast::StorageClass::kFunction, source};
}
@@ -1567,9 +1564,9 @@
});
}
-// paren_rhs_stmt
+// paren_expression
// : PAREN_LEFT logical_or_expression PAREN_RIGHT
-Expect<const ast::Expression*> ParserImpl::expect_paren_rhs_stmt() {
+Expect<const ast::Expression*> ParserImpl::expect_paren_expression() {
return expect_paren_block("", [&]() -> Expect<const ast::Expression*> {
auto expr = logical_or_expression();
if (expr.errored) {
@@ -1803,18 +1800,18 @@
return Failure::kErrored;
}
- auto constructor = logical_or_expression();
- if (constructor.errored) {
+ auto initializer = logical_or_expression();
+ if (initializer.errored) {
return Failure::kErrored;
}
- if (!constructor.matched) {
- return add_error(peek(), "missing constructor for 'const' declaration");
+ if (!initializer.matched) {
+ return add_error(peek(), "missing initializer for 'const' declaration");
}
auto* const_ = create<ast::Const>(decl->source, // source
builder_.Symbols().Register(decl->name), // symbol
decl->type, // type
- constructor.value, // constructor
+ initializer.value, // initializer
ast::AttributeList{}); // attributes
return create<ast::VariableDeclStatement>(decl->source, const_);
@@ -1831,18 +1828,18 @@
return Failure::kErrored;
}
- auto constructor = logical_or_expression();
- if (constructor.errored) {
+ auto initializer = logical_or_expression();
+ if (initializer.errored) {
return Failure::kErrored;
}
- if (!constructor.matched) {
- return add_error(peek(), "missing constructor for 'let' declaration");
+ if (!initializer.matched) {
+ return add_error(peek(), "missing initializer for 'let' declaration");
}
auto* let = create<ast::Let>(decl->source, // source
builder_.Symbols().Register(decl->name), // symbol
decl->type, // type
- constructor.value, // constructor
+ initializer.value, // initializer
ast::AttributeList{}); // attributes
return create<ast::VariableDeclStatement>(decl->source, let);
@@ -1856,17 +1853,17 @@
return Failure::kNoMatch;
}
- const ast::Expression* constructor = nullptr;
+ const ast::Expression* initializer = nullptr;
if (match(Token::Type::kEqual)) {
- auto constructor_expr = logical_or_expression();
- if (constructor_expr.errored) {
+ auto initializer_expr = logical_or_expression();
+ if (initializer_expr.errored) {
return Failure::kErrored;
}
- if (!constructor_expr.matched) {
- return add_error(peek(), "missing constructor for 'var' declaration");
+ if (!initializer_expr.matched) {
+ return add_error(peek(), "missing initializer for 'var' declaration");
}
- constructor = constructor_expr.value;
+ initializer = initializer_expr.value;
}
auto* var = create<ast::Var>(decl->source, // source
@@ -1874,7 +1871,7 @@
decl->type, // type
decl->storage_class, // storage class
decl->access, // access control
- constructor, // constructor
+ initializer, // initializer
ast::AttributeList{}); // attributes
return create<ast::VariableDeclStatement>(var->source, var);
@@ -1963,7 +1960,7 @@
}
// switch_stmt
-// : SWITCH paren_rhs_stmt BRACKET_LEFT switch_body+ BRACKET_RIGHT
+// : SWITCH paren_expression BRACKET_LEFT switch_body+ BRACKET_RIGHT
Maybe<const ast::SwitchStatement*> ParserImpl::switch_stmt() {
Source source;
if (!match(Token::Type::kSwitch, &source)) {
@@ -2325,8 +2322,8 @@
// : IDENT argument_expression_list?
// | type_decl argument_expression_list
// | const_literal
-// | paren_rhs_stmt
-// | BITCAST LESS_THAN type_decl GREATER_THAN paren_rhs_stmt
+// | paren_expression
+// | BITCAST LESS_THAN type_decl GREATER_THAN paren_expression
Maybe<const ast::Expression*> ParserImpl::primary_expression() {
auto t = peek();
auto source = t.source();
@@ -2340,7 +2337,7 @@
}
if (t.Is(Token::Type::kParenLeft)) {
- auto paren = expect_paren_rhs_stmt();
+ auto paren = expect_paren_expression();
if (paren.errored) {
return Failure::kErrored;
}
@@ -2356,7 +2353,7 @@
return Failure::kErrored;
}
- auto params = expect_paren_rhs_stmt();
+ auto params = expect_paren_expression();
if (params.errored) {
return Failure::kErrored;
}
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index 5857454..a9c0423 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -476,9 +476,9 @@
/// Parses a `body_stmt` grammar element, erroring on parse failure.
/// @returns the parsed statements
Expect<ast::BlockStatement*> expect_body_stmt();
- /// Parses a `paren_rhs_stmt` grammar element, erroring on parse failure.
+ /// Parses a `paren_expression` grammar element, erroring on parse failure.
/// @returns the parsed element or nullptr
- Expect<const ast::Expression*> expect_paren_rhs_stmt();
+ Expect<const ast::Expression*> expect_paren_expression();
/// Parses a `statements` grammar element
/// @returns the statements parsed
Expect<ast::StatementList> expect_statements();
diff --git a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
index 48969a5..453e549 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -220,7 +220,7 @@
TEST_F(ParserImplErrorTest, ConstVarStmtMissingConstructor) {
EXPECT("fn f() { let a : i32 = >; }",
- R"(test.wgsl:1:24 error: missing constructor for 'let' declaration
+ R"(test.wgsl:1:24 error: missing initializer for 'let' declaration
fn f() { let a : i32 = >; }
^
)");
@@ -1284,7 +1284,7 @@
TEST_F(ParserImplErrorTest, VarStmtInvalidAssignment) {
EXPECT("fn f() { var a : u32 = >; }",
- R"(test.wgsl:1:24 error: missing constructor for 'var' declaration
+ R"(test.wgsl:1:24 error: missing initializer for 'var' declaration
fn f() { var a : u32 = >; }
^
)");
diff --git a/src/tint/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_paren_expression_test.cc
similarity index 89%
rename from src/tint/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
rename to src/tint/reader/wgsl/parser_impl_paren_expression_test.cc
index dd5a978..e6bb223 100644
--- a/src/tint/reader/wgsl/parser_impl_paren_rhs_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_paren_expression_test.cc
@@ -19,7 +19,7 @@
TEST_F(ParserImplTest, ParenRhsStmt) {
auto p = parser("(a + b)");
- auto e = p->expect_paren_rhs_stmt();
+ auto e = p->expect_paren_expression();
ASSERT_FALSE(p->has_error()) << p->error();
ASSERT_FALSE(e.errored);
ASSERT_NE(e.value, nullptr);
@@ -28,7 +28,7 @@
TEST_F(ParserImplTest, ParenRhsStmt_MissingLeftParen) {
auto p = parser("true)");
- auto e = p->expect_paren_rhs_stmt();
+ auto e = p->expect_paren_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
@@ -37,7 +37,7 @@
TEST_F(ParserImplTest, ParenRhsStmt_MissingRightParen) {
auto p = parser("(true");
- auto e = p->expect_paren_rhs_stmt();
+ auto e = p->expect_paren_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
@@ -46,7 +46,7 @@
TEST_F(ParserImplTest, ParenRhsStmt_InvalidExpression) {
auto p = parser("(if (a() {})");
- auto e = p->expect_paren_rhs_stmt();
+ auto e = p->expect_paren_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
@@ -55,7 +55,7 @@
TEST_F(ParserImplTest, ParenRhsStmt_MissingExpression) {
auto p = parser("()");
- auto e = p->expect_paren_rhs_stmt();
+ auto e = p->expect_paren_expression();
ASSERT_TRUE(p->has_error());
ASSERT_TRUE(e.errored);
ASSERT_EQ(e.value, nullptr);
diff --git a/src/tint/reader/wgsl/parser_impl_statement_test.cc b/src/tint/reader/wgsl/parser_impl_statement_test.cc
index 8190829..677daa0 100644
--- a/src/tint/reader/wgsl/parser_impl_statement_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_statement_test.cc
@@ -114,7 +114,7 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:14: missing constructor for 'var' declaration");
+ EXPECT_EQ(p->error(), "1:14: missing initializer for 'var' declaration");
}
TEST_F(ParserImplTest, Statement_Variable_MissingSemicolon) {
diff --git a/src/tint/reader/wgsl/parser_impl_storage_class_test.cc b/src/tint/reader/wgsl/parser_impl_storage_class_test.cc
index 4abbe76..deb87fc 100644
--- a/src/tint/reader/wgsl/parser_impl_storage_class_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_storage_class_test.cc
@@ -56,10 +56,6 @@
EXPECT_EQ(sc.errored, true);
EXPECT_TRUE(p->has_error());
EXPECT_EQ(p->error(), "1:1: invalid storage class for test");
-
- auto t = p->next();
- EXPECT_TRUE(t.IsIdentifier());
- EXPECT_EQ(t.to_str(), "not");
}
} // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
index bc2bfe5..5459af6 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_test.cc
@@ -272,7 +272,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:5: invalid storage class for ptr declaration");
+ ASSERT_EQ(p->error(), "1:5: expected identifier for storage class");
}
TEST_F(ParserImplTest, TypeDecl_Ptr_MissingType) {
@@ -302,7 +302,7 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:5: invalid storage class for ptr declaration");
+ ASSERT_EQ(p->error(), "1:5: expected identifier for storage class");
}
TEST_F(ParserImplTest, TypeDecl_Ptr_BadStorageClass) {
diff --git a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
index de63f92..aefd2b5 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
@@ -73,7 +73,7 @@
EXPECT_TRUE(p->has_error());
EXPECT_TRUE(sc.errored);
EXPECT_FALSE(sc.matched);
- EXPECT_EQ(p->error(), "1:2: invalid storage class for variable declaration");
+ EXPECT_EQ(p->error(), "1:2: expected identifier for storage class");
}
TEST_F(ParserImplTest, VariableQualifier_MissingLessThan) {
@@ -84,7 +84,7 @@
EXPECT_FALSE(sc.matched);
auto t = p->next();
- ASSERT_TRUE(t.Is(Token::Type::kPrivate));
+ ASSERT_TRUE(t.Is(Token::Type::kIdentifier));
}
TEST_F(ParserImplTest, VariableQualifier_MissingLessThan_AfterSC) {
@@ -95,7 +95,7 @@
EXPECT_FALSE(sc.matched);
auto t = p->next();
- ASSERT_TRUE(t.Is(Token::Type::kPrivate));
+ ASSERT_TRUE(t.Is(Token::Type::kIdentifier));
}
TEST_F(ParserImplTest, VariableQualifier_MissingGreaterThan) {
diff --git a/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
index 9fdc503..21ab6a8 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_stmt_test.cc
@@ -63,7 +63,7 @@
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:15: missing constructor for 'var' declaration");
+ EXPECT_EQ(p->error(), "1:15: missing initializer for 'var' declaration");
}
TEST_F(ParserImplTest, VariableStmt_VariableDecl_ArrayInit) {
@@ -170,7 +170,7 @@
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:14: missing constructor for 'let' declaration");
+ EXPECT_EQ(p->error(), "1:14: missing initializer for 'let' declaration");
}
TEST_F(ParserImplTest, VariableStmt_Let_InvalidConstructor) {
@@ -180,7 +180,7 @@
EXPECT_TRUE(e.errored);
EXPECT_EQ(e.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:15: missing constructor for 'let' declaration");
+ EXPECT_EQ(p->error(), "1:15: missing initializer for 'let' declaration");
}
} // namespace
diff --git a/src/tint/reader/wgsl/token.cc b/src/tint/reader/wgsl/token.cc
index c6a06c9..44255d3 100644
--- a/src/tint/reader/wgsl/token.cc
+++ b/src/tint/reader/wgsl/token.cc
@@ -167,8 +167,6 @@
return "fn";
case Token::Type::kFor:
return "for";
- case Token::Type::kFunction:
- return "function";
case Token::Type::kI32:
return "i32";
case Token::Type::kIf:
@@ -199,8 +197,6 @@
return "mat4x4";
case Token::Type::kOverride:
return "override";
- case Token::Type::kPrivate:
- return "private";
case Token::Type::kPtr:
return "ptr";
case Token::Type::kReturn:
@@ -209,8 +205,6 @@
return "sampler";
case Token::Type::kComparisonSampler:
return "sampler_comparison";
- case Token::Type::kStorage:
- return "storage";
case Token::Type::kStruct:
return "struct";
case Token::Type::kSwitch:
@@ -255,8 +249,6 @@
return "type";
case Token::Type::kU32:
return "u32";
- case Token::Type::kUniform:
- return "uniform";
case Token::Type::kVar:
return "var";
case Token::Type::kVec2:
@@ -267,8 +259,6 @@
return "vec4";
case Token::Type::kWhile:
return "while";
- case Token::Type::kWorkgroup:
- return "workgroup";
}
return "<unknown>";
diff --git a/src/tint/reader/wgsl/token.h b/src/tint/reader/wgsl/token.h
index df78a5b..473588d 100644
--- a/src/tint/reader/wgsl/token.h
+++ b/src/tint/reader/wgsl/token.h
@@ -177,8 +177,6 @@
kFn,
// A 'for'
kFor,
- /// A 'function'
- kFunction,
/// A 'i32'
kI32,
/// A 'if'
@@ -209,8 +207,6 @@
kMat4x4,
/// A 'override'
kOverride,
- /// A 'private'
- kPrivate,
/// A 'ptr'
kPtr,
/// A 'return'
@@ -219,8 +215,6 @@
kSampler,
/// A 'sampler_comparison'
kComparisonSampler,
- /// A 'storage'
- kStorage,
/// A 'struct'
kStruct,
/// A 'switch'
@@ -265,8 +259,6 @@
kType,
/// A 'u32'
kU32,
- /// A 'uniform'
- kUniform,
/// A 'var'
kVar,
/// A 'vec2'
@@ -277,8 +269,6 @@
kVec4,
/// A 'while'
kWhile,
- /// A 'workgroup'
- kWorkgroup,
};
/// Converts a token type to a name
diff --git a/src/tint/transform/substitute_override.cc b/src/tint/transform/substitute_override.cc
new file mode 100644
index 0000000..aa7abff
--- /dev/null
+++ b/src/tint/transform/substitute_override.cc
@@ -0,0 +1,96 @@
+// Copyright 2022 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/transform/substitute_override.h"
+
+#include <functional>
+
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/variable.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::SubstituteOverride);
+TINT_INSTANTIATE_TYPEINFO(tint::transform::SubstituteOverride::Config);
+
+namespace tint::transform {
+
+SubstituteOverride::SubstituteOverride() = default;
+
+SubstituteOverride::~SubstituteOverride() = default;
+
+bool SubstituteOverride::ShouldRun(const Program* program, const DataMap&) const {
+ for (auto* node : program->AST().GlobalVariables()) {
+ if (node->Is<ast::Override>()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void SubstituteOverride::Run(CloneContext& ctx, const DataMap& config, DataMap&) const {
+ const auto* data = config.Get<Config>();
+ if (!data) {
+ ctx.dst->Diagnostics().add_error(diag::System::Transform,
+ "Missing override substitution data");
+ return;
+ }
+
+ ctx.ReplaceAll([&](const ast::Override* w) -> const ast::Const* {
+ auto ident = w->Identifier(ctx.src->Symbols());
+
+ auto src = ctx.Clone(w->source);
+ auto sym = ctx.Clone(w->symbol);
+ auto* ty = ctx.Clone(w->type);
+
+ // No replacement provided, just clone the override node as a const.
+ auto iter = data->map.find(ident);
+ if (iter == data->map.end()) {
+ if (!w->constructor) {
+ ctx.dst->Diagnostics().add_error(
+ diag::System::Transform,
+ "Initializer not provided for override, and override not overridden.");
+ return nullptr;
+ }
+ return ctx.dst->Const(src, sym, ty, ctx.Clone(w->constructor));
+ }
+
+ auto value = iter->second;
+ auto* ctor = Switch(
+ ctx.src->Sem().Get(w)->Type(),
+ [&](const sem::Bool*) { return ctx.dst->Expr(!std::equal_to<double>()(value, 0.0)); },
+ [&](const sem::I32*) { return ctx.dst->Expr(i32(value)); },
+ [&](const sem::U32*) { return ctx.dst->Expr(u32(value)); },
+ [&](const sem::F32*) { return ctx.dst->Expr(f32(value)); },
+ [&](const sem::F16*) { return ctx.dst->Expr(f16(value)); });
+
+ if (!ctor) {
+ ctx.dst->Diagnostics().add_error(diag::System::Transform,
+ "Failed to create override expression");
+ return nullptr;
+ }
+
+ return ctx.dst->Const(src, sym, ty, ctor);
+ });
+
+ ctx.Clone();
+}
+
+SubstituteOverride::Config::Config() = default;
+
+SubstituteOverride::Config::Config(const Config&) = default;
+
+SubstituteOverride::Config::~Config() = default;
+
+SubstituteOverride::Config& SubstituteOverride::Config::operator=(const Config&) = default;
+
+} // namespace tint::transform
diff --git a/src/tint/transform/substitute_override.h b/src/tint/transform/substitute_override.h
new file mode 100644
index 0000000..ab4b38a
--- /dev/null
+++ b/src/tint/transform/substitute_override.h
@@ -0,0 +1,89 @@
+// Copyright 2022 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_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
+#define SRC_TINT_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
+
+#include <string>
+#include <unordered_map>
+
+#include "src/tint/transform/transform.h"
+
+namespace tint::transform {
+
+/// A transform that replaces overrides with the constant values provided.
+///
+/// # Example
+/// ```
+/// override width: f32;
+/// @id(1) override height: i32 = 4;
+/// override depth = 1i;
+/// ```
+///
+/// When transformed with `width` -> 1, `1` -> 22, `depth` -> 42
+///
+/// ```
+/// const width: f32 = 1f;
+/// const height: i32 = 22i;
+/// const depth = 42i;
+/// ```
+///
+/// @see crbug.com/tint/1582
+class SubstituteOverride final : public Castable<SubstituteOverride, Transform> {
+ public:
+ /// Configuration options for the transform
+ struct Config final : public Castable<Config, Data> {
+ /// Constructor
+ Config();
+
+ /// Copy constructor
+ Config(const Config&);
+
+ /// Destructor
+ ~Config() override;
+
+ /// Assignment operator
+ /// @returns this Config
+ Config& operator=(const Config&);
+
+ /// The map of override name (either the identifier or id) to value.
+ /// The value is always a double coming into the transform and will be
+ /// converted to the correct type through and initializer.
+ std::unordered_map<std::string, double> map;
+ };
+
+ /// Constructor
+ SubstituteOverride();
+
+ /// Destructor
+ ~SubstituteOverride() override;
+
+ /// @param program the program to inspect
+ /// @param data optional extra transform-specific input data
+ /// @returns true if this transform should be run for the given program
+ bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
+
+ protected:
+ /// Runs the transform using the CloneContext built for transforming a
+ /// program. Run() is responsible for calling Clone() on the CloneContext.
+ /// @param ctx the CloneContext primed with the input program and
+ /// ProgramBuilder
+ /// @param inputs optional extra transform-specific input data
+ /// @param outputs optional extra transform-specific output data
+ void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
+};
+
+} // namespace tint::transform
+
+#endif // SRC_TINT_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
diff --git a/src/tint/transform/substitute_override_test.cc b/src/tint/transform/substitute_override_test.cc
new file mode 100644
index 0000000..70ac675
--- /dev/null
+++ b/src/tint/transform/substitute_override_test.cc
@@ -0,0 +1,243 @@
+// Copyright 2022 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/transform/substitute_override.h"
+
+#include "src/tint/transform/test_helper.h"
+
+namespace tint::transform {
+namespace {
+
+using SubstituteOverrideTest = TransformTest;
+
+TEST_F(SubstituteOverrideTest, Error_NoData) {
+ auto* src = R"(
+override width: i32;
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ auto* expect = "error: Missing override substitution data";
+
+ DataMap data;
+ auto got = Run<SubstituteOverride>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SubstituteOverrideTest, Error_NoOverrideValue) {
+ auto* src = R"(
+override width: i32;
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ auto* expect = "error: Initializer not provided for override, and override not overridden.";
+
+ SubstituteOverride::Config cfg;
+ DataMap data;
+ data.Add<SubstituteOverride::Config>(cfg);
+
+ auto got = Run<SubstituteOverride>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SubstituteOverrideTest, Module_NoOverrides) {
+ auto* src = R"(
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ auto* expect = R"(
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ SubstituteOverride::Config cfg;
+
+ DataMap data;
+ data.Add<SubstituteOverride::Config>(cfg);
+ auto got = Run<SubstituteOverride>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SubstituteOverrideTest, Identifier) {
+ auto* src = R"(
+override i_width: i32;
+override i_height = 1i;
+
+override f_width: f32;
+override f_height = 1.f;
+
+// TODO(crbug.com/tint/1473)
+// override h_width: f16;
+// override h_height = 1.h;
+
+override b_width: bool;
+override b_height = true;
+
+override o_width = 2i;
+
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ auto* expect = R"(
+const i_width : i32 = 42i;
+
+const i_height = 11i;
+
+const f_width : f32 = 22.299999237f;
+
+const f_height = 12.399999619f;
+
+const b_width : bool = true;
+
+const b_height = false;
+
+const o_width = 2i;
+
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ SubstituteOverride::Config cfg;
+ cfg.map.insert({"i_width", 42.0});
+ cfg.map.insert({"i_height", 11.0});
+ cfg.map.insert({"f_width", 22.3});
+ cfg.map.insert({"f_height", 12.4});
+ cfg.map.insert({"h_width", 9.4});
+ cfg.map.insert({"h_height", 3.4});
+ cfg.map.insert({"b_width", 1.0});
+ cfg.map.insert({"b_height", 0.0});
+
+ DataMap data;
+ data.Add<SubstituteOverride::Config>(cfg);
+ auto got = Run<SubstituteOverride>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SubstituteOverrideTest, Id) {
+ auto* src = R"(
+enable f16;
+
+@id(0) override i_width: i32;
+@id(10) override i_height = 1i;
+
+@id(1) override f_width: f32;
+@id(9) override f_height = 1.f;
+
+// TODO(crbug.com/tint/1473)
+// @id(2) override h_width: f16;
+// @id(8) override h_height = 1.h;
+
+@id(3) override b_width: bool;
+@id(7) override b_height = true;
+
+@id(5) override o_width = 2i;
+
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ auto* expect = R"(
+enable f16;
+
+const i_width : i32 = 42i;
+
+const i_height = 11i;
+
+const f_width : f32 = 22.299999237f;
+
+const f_height = 12.399999619f;
+
+const b_width : bool = true;
+
+const b_height = false;
+
+const o_width = 2i;
+
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ SubstituteOverride::Config cfg;
+ cfg.map.insert({"0", 42.0});
+ cfg.map.insert({"10", 11.0});
+ cfg.map.insert({"1", 22.3});
+ cfg.map.insert({"9", 12.4});
+ cfg.map.insert({"2", 9.4});
+ cfg.map.insert({"8", 3.4});
+ cfg.map.insert({"3", 1.0});
+ cfg.map.insert({"7", 0.0});
+ // No effect because an @id is set for o_width
+ cfg.map.insert({"o_width", 13});
+
+ DataMap data;
+ data.Add<SubstituteOverride::Config>(cfg);
+ auto got = Run<SubstituteOverride>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(SubstituteOverrideTest, Identifier_Expression) {
+ auto* src = R"(
+override i_height = ~2i;
+
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ auto* expect = R"(
+const i_height = 11i;
+
+@vertex
+fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+}
+)";
+
+ SubstituteOverride::Config cfg;
+ cfg.map.insert({"i_height", 11.0});
+
+ DataMap data;
+ data.Add<SubstituteOverride::Config>(cfg);
+ auto got = Run<SubstituteOverride>(src, data);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+} // namespace
+} // namespace tint::transform
diff --git a/src/tint/utils/io/command_windows.cc b/src/tint/utils/io/command_windows.cc
index f953a85..36c39c6 100644
--- a/src/tint/utils/io/command_windows.cc
+++ b/src/tint/utils/io/command_windows.cc
@@ -16,9 +16,12 @@
#define WIN32_LEAN_AND_MEAN 1
#include <Windows.h>
+#include <dbghelp.h>
#include <sstream>
#include <string>
+#include "src/tint/utils/defer.h"
+
namespace tint::utils {
namespace {
@@ -97,9 +100,34 @@
Handle write;
};
+/// Queries whether the file at the given path is an executable or DLL.
bool ExecutableExists(const std::string& path) {
- DWORD type = 0;
- return GetBinaryTypeA(path.c_str(), &type);
+ auto file = Handle(CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_READONLY, NULL));
+ if (!file) {
+ return false;
+ }
+
+ auto map = Handle(CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL));
+ if (map == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ void* addr_header = MapViewOfFileEx(map, FILE_MAP_READ, 0, 0, 0, NULL);
+
+ // Dynamically obtain the address of, and call ImageNtHeader. This is done to avoid tint.exe
+ // needing to statically link Dbghelp.lib.
+ static auto* dbg_help = LoadLibraryA("Dbghelp.dll"); // Leaks, but who cares?
+ if (dbg_help) {
+ if (FARPROC proc = GetProcAddress(dbg_help, "ImageNtHeader")) {
+ using ImageNtHeaderPtr = decltype(&ImageNtHeader);
+ auto* image_nt_header = reinterpret_cast<ImageNtHeaderPtr>(proc)(addr_header);
+ return image_nt_header != nullptr;
+ }
+ }
+
+ // Couldn't call ImageNtHeader, assume it is executable
+ return false;
}
std::string FindExecutable(const std::string& name) {
@@ -187,7 +215,8 @@
&si, // Pointer to STARTUPINFO structure
&pi)) { // Pointer to PROCESS_INFORMATION structure
Output out;
- out.err = "Command::Exec() CreateProcess() failed";
+ out.err = "Command::Exec() CreateProcess('" + args.str() + "') failed";
+ out.error_code = 1;
return out;
}
diff --git a/src/tint/val/hlsl.cc b/src/tint/val/hlsl.cc
index 49c3850..a2998d8 100644
--- a/src/tint/val/hlsl.cc
+++ b/src/tint/val/hlsl.cc
@@ -30,8 +30,7 @@
Result HlslUsingDXC(const std::string& dxc_path,
const std::string& source,
- const EntryPointList& entry_points,
- const std::vector<std::string>& overrides) {
+ const EntryPointList& entry_points) {
Result result;
auto dxc = utils::Command(dxc_path);
@@ -70,13 +69,7 @@
"/Zpr " // D3DCOMPILE_PACK_MATRIX_ROW_MAJOR
"/Gis"; // D3DCOMPILE_IEEE_STRICTNESS
- std::string defs;
- defs.reserve(overrides.size() * 20);
- for (auto& o : overrides) {
- defs += "/D" + o + " ";
- }
-
- auto res = dxc(profile, "-E " + ep.first, compileFlags, file.Path(), defs);
+ auto res = dxc(profile, "-E " + ep.first, compileFlags, file.Path());
if (!res.out.empty()) {
if (!result.output.empty()) {
result.output += "\n";
@@ -102,15 +95,15 @@
}
#ifdef _WIN32
-Result HlslUsingFXC(const std::string& source,
- const EntryPointList& entry_points,
- const std::vector<std::string>& overrides) {
+Result HlslUsingFXC(const std::string& fxc_path,
+ const std::string& source,
+ const EntryPointList& entry_points) {
Result result;
// This library leaks if an error happens in this function, but it is ok
// because it is loaded at most once, and the executables using HlslUsingFXC
// are short-lived.
- HMODULE fxcLib = LoadLibraryA("d3dcompiler_47.dll");
+ HMODULE fxcLib = LoadLibraryA(fxc_path.c_str());
if (fxcLib == nullptr) {
result.output = "Couldn't load FXC";
result.failed = true;
@@ -148,26 +141,12 @@
UINT compileFlags = D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_PACK_MATRIX_ROW_MAJOR |
D3DCOMPILE_IEEE_STRICTNESS;
- auto overrides_copy = overrides; // Copy so that we can replace '=' with '\0'
- std::vector<D3D_SHADER_MACRO> macros;
- macros.reserve(overrides_copy.size() * 2);
- for (auto& o : overrides_copy) {
- if (auto sep = o.find_first_of('='); sep != std::string::npos) {
- // Replace '=' with '\0' so we can point directly into the allocated string buffer
- o[sep] = '\0';
- macros.push_back(D3D_SHADER_MACRO{&o[0], &o[sep + 1]});
- } else {
- macros.emplace_back(D3D_SHADER_MACRO{o.c_str(), NULL});
- }
- }
- macros.emplace_back(D3D_SHADER_MACRO{NULL, NULL});
-
ComPtr<ID3DBlob> compiledShader;
ComPtr<ID3DBlob> errors;
HRESULT cr = d3dCompile(source.c_str(), // pSrcData
source.length(), // SrcDataSize
nullptr, // pSourceName
- macros.data(), // pDefines
+ nullptr, // pDefines
nullptr, // pInclude
ep.first.c_str(), // pEntrypoint
profile, // pTarget
diff --git a/src/tint/val/val.h b/src/tint/val/val.h
index c869efb..dae6fca 100644
--- a/src/tint/val/val.h
+++ b/src/tint/val/val.h
@@ -30,6 +30,9 @@
using EntryPointList = std::vector<std::pair<std::string, ast::PipelineStage>>;
+/// Name of the FXC compiler DLL
+static constexpr const char kFxcDLLName[] = "d3dcompiler_47.dll";
+
/// The return structure of Validate()
struct Result {
/// True if validation passed
@@ -43,23 +46,21 @@
/// @param dxc_path path to DXC
/// @param source the generated HLSL source
/// @param entry_points the list of entry points to validate
-/// @param overrides optional list of pipeline overrides
/// @return the result of the compile
Result HlslUsingDXC(const std::string& dxc_path,
const std::string& source,
- const EntryPointList& entry_points,
- const std::vector<std::string>& overrides);
+ const EntryPointList& entry_points);
#ifdef _WIN32
/// Hlsl attempts to compile the shader with FXC, verifying that the shader
/// compiles successfully.
+/// @param fxc_path path to the FXC DLL
/// @param source the generated HLSL source
/// @param entry_points the list of entry points to validate
-/// @param overrides optional list of pipeline overrides
/// @return the result of the compile
-Result HlslUsingFXC(const std::string& source,
- const EntryPointList& entry_points,
- const std::vector<std::string>& overrides);
+Result HlslUsingFXC(const std::string& fxc_path,
+ const std::string& source,
+ const EntryPointList& entry_points);
#endif // _WIN32
/// Msl attempts to compile the shader with the Metal Shader Compiler,