Import Tint changes from Dawn
Changes:
- fafeb9a3279b602f59cc7493bb15fe1931fefaee tint/reader/wgsl: Improve errors when parsing access cont... by Ben Clayton <bclayton@google.com>
- bccd87c37a90dca50e82f31a2eb1f055398db656 tint/ast: Generate access.[h|cc] by Ben Clayton <bclayton@google.com>
- a44e3d8bea3f4e4430c04b20695ff0812ac32a52 tint: include T type in converter overload diagnostics by Ben Clayton <bclayton@google.com>
- c889500f6e3e8fa5c23c8235848a2a06a2f5fa31 tint/reader/wgsl: Improve errors when parsing interpolati... by Ben Clayton <bclayton@google.com>
- 542d27d874fb558614f229886e083f9a3119dea2 spirv-reader: support GLSL.std.450 instruction FindILsb by David Neto <dneto@google.com>
- 2ad747dedd99d4f88742295f2760ae35759b2dd9 tint: Fix const eval unary tests by Antonio Maiorano <amaiorano@google.com>
- 75bc93c0df83671adc3a62c172bd33a8be939dad tint: Fix const eval of type conversions by Ben Clayton <bclayton@google.com>
- feb447d9dc271b94017a0ee41ff6ef7f97276cc3 tint/resolver: Formatting by Ben Clayton <bclayton@google.com>
- f9ed9d3a6349f781957fccae7af9bf8d6bd268b4 tint/ast: Generate interpolate_attribute.[h|cc] by Ben Clayton <bclayton@google.com>
- c1af0f5005d04d2db500acba0f093cafe9a95de5 tint/reader/wgsl: Improve errors when parsing address spa... by Ben Clayton <bclayton@google.com>
- 9af5b406bab262d713338f350d3a8544cf762c8d tint/reader/wgsl: Improve errors when parsing texel formats by Ben Clayton <bclayton@google.com>
- ecd7f7ee7fbbf438ff05a0b0ced26983e13fa6f6 tint/reader/wgsl: Improve errors when parsing extensions by Ben Clayton <bclayton@google.com>
- 760eee9e925141186b0e76357dcb08d3d010ebfc tint/reader/wgsl: Improve errors when parsing builtin by Ben Clayton <bclayton@google.com>
- b7aef033fa73937cbc837478d9244999df26257d tint/templates: Generate enum string arrays by Ben Clayton <bclayton@google.com>
- 3e0984afe036c85238cd61e647ccf892894b88d0 tint/program_builder.h: Remove stale comment by Ben Clayton <bclayton@google.com>
- db368f1b9c310873f1194a18deac890f44de81b3 tools/tint/intrinsic: Sort enum values lexicographically by Ben Clayton <bclayton@google.com>
- ae832a2c6ae769593321ae5f8368c948f4727376 tint/reader/wgsl: Minor cleanups by Ben Clayton <bclayton@google.com>
- d04c54e1784c6cafa5c8b3a936c1c682f99de4e4 tint/utils: Add Levenshtein Distance() function by Ben Clayton <bclayton@google.com>
- 3c34d83088f6e90294f63d1191b04d2f1d70429a tint/ast: Add missing 'const' on pointer member by Ben Clayton <bclayton@google.com>
- ab2fa8be87f3cc7a636230b07d61b96f2d9c7d30 tint: Fix vector equality operator by Ben Clayton <bclayton@google.com>
- 8d3d4f6fd11fb179b4c56b782e9e5ef0da1fd4a9 tint/inspector: Reflect whether @builtin(frag_depth) is u... by Corentin Wallez <cwallez@chromium.org>
- 98a7cd11aff1850f953f26fb6db6dc06632b8d63 Add a ClampFragDepth transform. by Corentin Wallez <cwallez@chromium.org>
- 5cbb32d764fb93c7586fb01b48a20fca32b6682b Tint/tranfsorm/test: Refactor and add exhaustive test for... by Zhaoming Jiang <zhaoming.jiang@intel.com>
- 112d7614483a266869cb74891a8bdb9c5b5bb1ee spirv-reader: Track storage class for pointer/ref values by David Neto <dneto@google.com>
GitOrigin-RevId: fafeb9a3279b602f59cc7493bb15fe1931fefaee
Change-Id: I00adc0bca37895f36e239a67b32b04270f606e4a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/105380
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Copybara Prod <copybara-worker-blackhole@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn
index 07c5350..9e39630 100644
--- a/src/tint/BUILD.gn
+++ b/src/tint/BUILD.gn
@@ -482,8 +482,11 @@
"transform/builtin_polyfill.h",
"transform/calculate_array_length.cc",
"transform/calculate_array_length.h",
+ "transform/calculate_array_length.h",
"transform/canonicalize_entry_point_io.cc",
"transform/canonicalize_entry_point_io.h",
+ "transform/clamp_frag_depth.cc",
+ "transform/clamp_frag_depth.h",
"transform/combine_samplers.cc",
"transform/combine_samplers.h",
"transform/decompose_memory_access.cc",
@@ -574,6 +577,7 @@
"utils/map.h",
"utils/math.h",
"utils/scoped_assignment.h",
+ "utils/string.cc",
"utils/string.h",
"utils/unique_allocator.h",
"utils/unique_vector.h",
@@ -1191,6 +1195,7 @@
"transform/builtin_polyfill_test.cc",
"transform/calculate_array_length_test.cc",
"transform/canonicalize_entry_point_io_test.cc",
+ "transform/clamp_frag_depth_test.cc",
"transform/combine_samplers_test.cc",
"transform/decompose_memory_access_test.cc",
"transform/decompose_strided_array_test.cc",
@@ -1214,6 +1219,7 @@
"transform/simplify_pointers_test.cc",
"transform/single_entry_point_test.cc",
"transform/spirv_atomic_test.cc",
+ "transform/std140_exhaustive_test.cc",
"transform/std140_test.cc",
"transform/substitute_override_test.cc",
"transform/test_helper.h",
diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt
index 10b0686..c331ccf 100644
--- a/src/tint/CMakeLists.txt
+++ b/src/tint/CMakeLists.txt
@@ -394,6 +394,8 @@
transform/builtin_polyfill.h
transform/calculate_array_length.cc
transform/calculate_array_length.h
+ transform/clamp_frag_depth.cc
+ transform/clamp_frag_depth.h
transform/canonicalize_entry_point_io.cc
transform/canonicalize_entry_point_io.h
transform/combine_samplers.cc
@@ -484,6 +486,7 @@
utils/map.h
utils/math.h
utils/scoped_assignment.h
+ utils/string.cc
utils/string.h
utils/unique_allocator.h
utils/unique_vector.h
@@ -1105,6 +1108,7 @@
transform/binding_remapper_test.cc
transform/builtin_polyfill_test.cc
transform/calculate_array_length_test.cc
+ transform/clamp_frag_depth_test.cc
transform/canonicalize_entry_point_io_test.cc
transform/combine_samplers_test.cc
transform/decompose_memory_access_test.cc
@@ -1130,6 +1134,7 @@
transform/simplify_pointers_test.cc
transform/single_entry_point_test.cc
transform/spirv_atomic_test.cc
+ transform/std140_exhaustive_test.cc
transform/std140_test.cc
transform/substitute_override_test.cc
transform/test_helper.h
diff --git a/src/tint/ast/access.cc b/src/tint/ast/access.cc
index 642d852..3997715 100644
--- a/src/tint/ast/access.cc
+++ b/src/tint/ast/access.cc
@@ -12,30 +12,46 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+// src/tint/ast/access.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
#include "src/tint/ast/access.h"
namespace tint::ast {
-std::ostream& operator<<(std::ostream& out, Access access) {
- switch (access) {
- case ast::Access::kUndefined: {
- out << "undefined";
- break;
- }
- case ast::Access::kRead: {
- out << "read";
- break;
- }
- case ast::Access::kReadWrite: {
- out << "read_write";
- break;
- }
- case ast::Access::kWrite: {
- out << "write";
- break;
- }
+/// ParseAccess parses a Access from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or Access::kInvalid if the string could not be parsed.
+Access ParseAccess(std::string_view str) {
+ if (str == "read") {
+ return Access::kRead;
}
- return out;
+ if (str == "read_write") {
+ return Access::kReadWrite;
+ }
+ if (str == "write") {
+ return Access::kWrite;
+ }
+ return Access::kInvalid;
+}
+
+std::ostream& operator<<(std::ostream& out, Access value) {
+ switch (value) {
+ case Access::kInvalid:
+ return out << "invalid";
+ case Access::kRead:
+ return out << "read";
+ case Access::kReadWrite:
+ return out << "read_write";
+ case Access::kWrite:
+ return out << "write";
+ }
+ return out << "<unknown>";
}
} // namespace tint::ast
diff --git a/src/tint/ast/access.cc.tmpl b/src/tint/ast/access.cc.tmpl
new file mode 100644
index 0000000..bd52a3b
--- /dev/null
+++ b/src/tint/ast/access.cc.tmpl
@@ -0,0 +1,25 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate access.cc
+
+To update the generated file, run:
+ ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+{{- $enum := (Sem.Enum "access") -}}
+
+#include "src/tint/ast/access.h"
+
+namespace tint::ast {
+
+{{ Eval "ParseEnum" $enum}}
+
+{{ Eval "EnumOStream" $enum}}
+
+} // namespace tint::ast
diff --git a/src/tint/ast/access.h b/src/tint/ast/access.h
index 3714b70..12e1a20 100644
--- a/src/tint/ast/access.h
+++ b/src/tint/ast/access.h
@@ -12,32 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+// src/tint/ast/access.h.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
#ifndef SRC_TINT_AST_ACCESS_H_
#define SRC_TINT_AST_ACCESS_H_
#include <ostream>
-#include <string>
namespace tint::ast {
-/// The access control settings
-enum Access {
- /// Not declared in the source
- kUndefined = 0,
- /// Read only
+/// Address space of a given pointer.
+enum class Access {
+ kInvalid,
kRead,
- /// Write only
- kWrite,
- /// Read write
kReadWrite,
- // Last valid access mode
- kLastValid = kReadWrite,
+ kWrite,
};
/// @param out the std::ostream to write to
-/// @param access the Access
-/// @return the std::ostream so calls can be chained
-std::ostream& operator<<(std::ostream& out, Access access);
+/// @param value the Access
+/// @returns `out` so calls can be chained
+std::ostream& operator<<(std::ostream& out, Access value);
+
+/// ParseAccess parses a Access from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or Access::kInvalid if the string could not be parsed.
+Access ParseAccess(std::string_view str);
+
+constexpr const char* kAccessStrings[] = {
+ "read",
+ "read_write",
+ "write",
+};
} // namespace tint::ast
diff --git a/src/tint/ast/access.h.tmpl b/src/tint/ast/access.h.tmpl
new file mode 100644
index 0000000..ab14fcf
--- /dev/null
+++ b/src/tint/ast/access.h.tmpl
@@ -0,0 +1,29 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate access.h
+
+To update the generated file, run:
+ ./tools/run gen
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+{{- $enum := (Sem.Enum "access") -}}
+
+#ifndef SRC_TINT_AST_ACCESS_H_
+#define SRC_TINT_AST_ACCESS_H_
+
+#include <ostream>
+
+namespace tint::ast {
+
+/// Address space of a given pointer.
+{{ Eval "DeclareEnum" $enum}}
+
+} // namespace tint::ast
+
+#endif // SRC_TINT_AST_ACCESS_H_
diff --git a/src/tint/ast/address_space.cc b/src/tint/ast/address_space.cc
index a55fb87..d8174a2 100644
--- a/src/tint/ast/address_space.cc
+++ b/src/tint/ast/address_space.cc
@@ -34,17 +34,17 @@
if (str == "private") {
return AddressSpace::kPrivate;
}
- if (str == "workgroup") {
- return AddressSpace::kWorkgroup;
- }
- if (str == "uniform") {
- return AddressSpace::kUniform;
+ if (str == "push_constant") {
+ return AddressSpace::kPushConstant;
}
if (str == "storage") {
return AddressSpace::kStorage;
}
- if (str == "push_constant") {
- return AddressSpace::kPushConstant;
+ if (str == "uniform") {
+ return AddressSpace::kUniform;
+ }
+ if (str == "workgroup") {
+ return AddressSpace::kWorkgroup;
}
return AddressSpace::kInvalid;
}
@@ -53,26 +53,26 @@
switch (value) {
case AddressSpace::kInvalid:
return out << "invalid";
- case AddressSpace::kNone:
- return out << "none";
case AddressSpace::kFunction:
return out << "function";
- case AddressSpace::kPrivate:
- return out << "private";
- case AddressSpace::kWorkgroup:
- return out << "workgroup";
- case AddressSpace::kUniform:
- return out << "uniform";
- case AddressSpace::kStorage:
- return out << "storage";
- case AddressSpace::kPushConstant:
- return out << "push_constant";
case AddressSpace::kHandle:
return out << "handle";
case AddressSpace::kIn:
return out << "in";
+ case AddressSpace::kNone:
+ return out << "none";
case AddressSpace::kOut:
return out << "out";
+ case AddressSpace::kPrivate:
+ return out << "private";
+ case AddressSpace::kPushConstant:
+ return out << "push_constant";
+ case AddressSpace::kStorage:
+ return out << "storage";
+ case AddressSpace::kUniform:
+ return out << "uniform";
+ case AddressSpace::kWorkgroup:
+ return out << "workgroup";
}
return out << "<unknown>";
}
diff --git a/src/tint/ast/address_space.h b/src/tint/ast/address_space.h
index 210dc7f..8b96f0f 100644
--- a/src/tint/ast/address_space.h
+++ b/src/tint/ast/address_space.h
@@ -30,16 +30,16 @@
/// Address space of a given pointer.
enum class AddressSpace {
kInvalid,
- kNone, // Tint-internal enum entry - not parsed
kFunction,
- kPrivate,
- kWorkgroup,
- kUniform,
- kStorage,
- kPushConstant,
kHandle, // Tint-internal enum entry - not parsed
kIn, // Tint-internal enum entry - not parsed
+ kNone, // Tint-internal enum entry - not parsed
kOut, // Tint-internal enum entry - not parsed
+ kPrivate,
+ kPushConstant,
+ kStorage,
+ kUniform,
+ kWorkgroup,
};
/// @param out the std::ostream to write to
@@ -52,6 +52,10 @@
/// @returns the parsed enum, or AddressSpace::kInvalid if the string could not be parsed.
AddressSpace ParseAddressSpace(std::string_view str);
+constexpr const char* kAddressSpaceStrings[] = {
+ "function", "private", "push_constant", "storage", "uniform", "workgroup",
+};
+
/// @returns true if the AddressSpace is host-shareable
/// @param address_space the AddressSpace
/// @see https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable
diff --git a/src/tint/ast/address_space_bench.cc b/src/tint/ast/address_space_bench.cc
index 364d015..fb3b0a5 100644
--- a/src/tint/ast/address_space_bench.cc
+++ b/src/tint/ast/address_space_bench.cc
@@ -31,48 +31,15 @@
void AddressSpaceParser(::benchmark::State& state) {
std::array kStrings{
- "fccnctin",
- "ucti3",
- "functVon",
- "function",
- "1unction",
- "unJtqqon",
- "llun77tion",
- "ppqqivtHH",
- "prcv",
- "bivaGe",
- "private",
- "priviive",
- "8WWivate",
- "pxxvate",
- "wXkgrggup",
- "worXVup",
- "3orkgroup",
- "workgroup",
- "workgroEp",
- "woTTPkroup",
- "ddorkroxxp",
- "u44iform",
- "unSSfoVVm",
- "RniR22m",
- "uniform",
- "uFfo9m",
- "uniorm",
- "VOORRHrm",
- "straye",
- "llntrrr77ge",
- "stor4g00",
- "storage",
- "trooe",
- "zzrage",
- "siioppa1",
- "puXXh_constant",
- "pusII9_nn55nstant",
- "YusHH_coaastSSrnt",
- "push_constant",
- "pushonkkHan",
- "jush_consgRt",
- "puh_cobsant",
+ "fccnctin", "ucti3", "functVon", "function", "1unction",
+ "unJtqqon", "llun77tion", "ppqqivtHH", "prcv", "bivaGe",
+ "private", "priviive", "8WWivate", "pxxvate", "pXh_cggnstant",
+ "pX_Vonstanu", "push_consta3t", "push_constant", "push_constanE", "push_TTPnstant",
+ "puxxdh_constan", "s44orage", "stSSraVVe", "RtoR22e", "storage",
+ "sFra9e", "stoage", "VOORRHge", "unfoym", "llnnrrf77rm",
+ "unif4r00", "uniform", "nfoom", "zzform", "uiiippo1",
+ "workgrouXX", "wor55gro99nII", "wrrrkgroSSaHH", "workgroup", "kkrHoup",
+ "jgkrouRR", "wokroub",
};
for (auto _ : state) {
for (auto& str : kStrings) {
diff --git a/src/tint/ast/address_space_test.cc b/src/tint/ast/address_space_test.cc
index 86a8e40..271cf33 100644
--- a/src/tint/ast/address_space_test.cc
+++ b/src/tint/ast/address_space_test.cc
@@ -42,21 +42,24 @@
}
static constexpr Case kValidCases[] = {
- {"function", AddressSpace::kFunction}, {"private", AddressSpace::kPrivate},
- {"workgroup", AddressSpace::kWorkgroup}, {"uniform", AddressSpace::kUniform},
- {"storage", AddressSpace::kStorage}, {"push_constant", AddressSpace::kPushConstant},
+ {"function", AddressSpace::kFunction},
+ {"private", AddressSpace::kPrivate},
+ {"push_constant", AddressSpace::kPushConstant},
+ {"storage", AddressSpace::kStorage},
+ {"uniform", AddressSpace::kUniform},
+ {"workgroup", AddressSpace::kWorkgroup},
};
static constexpr Case kInvalidCases[] = {
- {"fccnctin", AddressSpace::kInvalid}, {"ucti3", AddressSpace::kInvalid},
- {"functVon", AddressSpace::kInvalid}, {"priv1te", AddressSpace::kInvalid},
- {"pqiJate", AddressSpace::kInvalid}, {"privat7ll", AddressSpace::kInvalid},
- {"workroppqHH", AddressSpace::kInvalid}, {"workru", AddressSpace::kInvalid},
- {"wbkgGoup", AddressSpace::kInvalid}, {"unifiivm", AddressSpace::kInvalid},
- {"8WWiform", AddressSpace::kInvalid}, {"uxxform", AddressSpace::kInvalid},
- {"sXraggg", AddressSpace::kInvalid}, {"traXe", AddressSpace::kInvalid},
- {"stor3ge", AddressSpace::kInvalid}, {"push_constanE", AddressSpace::kInvalid},
- {"push_TTPnstant", AddressSpace::kInvalid}, {"puxxdh_constan", AddressSpace::kInvalid},
+ {"fccnctin", AddressSpace::kInvalid}, {"ucti3", AddressSpace::kInvalid},
+ {"functVon", AddressSpace::kInvalid}, {"priv1te", AddressSpace::kInvalid},
+ {"pqiJate", AddressSpace::kInvalid}, {"privat7ll", AddressSpace::kInvalid},
+ {"pqqsh_pponstHnt", AddressSpace::kInvalid}, {"pus_cnstat", AddressSpace::kInvalid},
+ {"bus_Gonstant", AddressSpace::kInvalid}, {"storiive", AddressSpace::kInvalid},
+ {"8WWorage", AddressSpace::kInvalid}, {"sxxrage", AddressSpace::kInvalid},
+ {"uXforgg", AddressSpace::kInvalid}, {"nfoXm", AddressSpace::kInvalid},
+ {"unif3rm", AddressSpace::kInvalid}, {"workgroEp", AddressSpace::kInvalid},
+ {"woTTPkroup", AddressSpace::kInvalid}, {"ddorkroxxp", AddressSpace::kInvalid},
};
using AddressSpaceParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/ast/builtin_value.cc b/src/tint/ast/builtin_value.cc
index e8d6451..d4eaec0 100644
--- a/src/tint/ast/builtin_value.cc
+++ b/src/tint/ast/builtin_value.cc
@@ -28,20 +28,17 @@
/// @param str the string to parse
/// @returns the parsed enum, or BuiltinValue::kInvalid if the string could not be parsed.
BuiltinValue ParseBuiltinValue(std::string_view str) {
- if (str == "position") {
- return BuiltinValue::kPosition;
- }
- if (str == "vertex_index") {
- return BuiltinValue::kVertexIndex;
- }
- if (str == "instance_index") {
- return BuiltinValue::kInstanceIndex;
+ if (str == "frag_depth") {
+ return BuiltinValue::kFragDepth;
}
if (str == "front_facing") {
return BuiltinValue::kFrontFacing;
}
- if (str == "frag_depth") {
- return BuiltinValue::kFragDepth;
+ if (str == "global_invocation_id") {
+ return BuiltinValue::kGlobalInvocationId;
+ }
+ if (str == "instance_index") {
+ return BuiltinValue::kInstanceIndex;
}
if (str == "local_invocation_id") {
return BuiltinValue::kLocalInvocationId;
@@ -49,21 +46,24 @@
if (str == "local_invocation_index") {
return BuiltinValue::kLocalInvocationIndex;
}
- if (str == "global_invocation_id") {
- return BuiltinValue::kGlobalInvocationId;
- }
- if (str == "workgroup_id") {
- return BuiltinValue::kWorkgroupId;
- }
if (str == "num_workgroups") {
return BuiltinValue::kNumWorkgroups;
}
+ if (str == "position") {
+ return BuiltinValue::kPosition;
+ }
if (str == "sample_index") {
return BuiltinValue::kSampleIndex;
}
if (str == "sample_mask") {
return BuiltinValue::kSampleMask;
}
+ if (str == "vertex_index") {
+ return BuiltinValue::kVertexIndex;
+ }
+ if (str == "workgroup_id") {
+ return BuiltinValue::kWorkgroupId;
+ }
return BuiltinValue::kInvalid;
}
@@ -71,32 +71,32 @@
switch (value) {
case BuiltinValue::kInvalid:
return out << "invalid";
- case BuiltinValue::kPosition:
- return out << "position";
- case BuiltinValue::kVertexIndex:
- return out << "vertex_index";
- case BuiltinValue::kInstanceIndex:
- return out << "instance_index";
- case BuiltinValue::kFrontFacing:
- return out << "front_facing";
case BuiltinValue::kFragDepth:
return out << "frag_depth";
+ case BuiltinValue::kFrontFacing:
+ return out << "front_facing";
+ case BuiltinValue::kGlobalInvocationId:
+ return out << "global_invocation_id";
+ case BuiltinValue::kInstanceIndex:
+ return out << "instance_index";
case BuiltinValue::kLocalInvocationId:
return out << "local_invocation_id";
case BuiltinValue::kLocalInvocationIndex:
return out << "local_invocation_index";
- case BuiltinValue::kGlobalInvocationId:
- return out << "global_invocation_id";
- case BuiltinValue::kWorkgroupId:
- return out << "workgroup_id";
case BuiltinValue::kNumWorkgroups:
return out << "num_workgroups";
+ case BuiltinValue::kPointSize:
+ return out << "point_size";
+ case BuiltinValue::kPosition:
+ return out << "position";
case BuiltinValue::kSampleIndex:
return out << "sample_index";
case BuiltinValue::kSampleMask:
return out << "sample_mask";
- case BuiltinValue::kPointSize:
- return out << "point_size";
+ case BuiltinValue::kVertexIndex:
+ return out << "vertex_index";
+ case BuiltinValue::kWorkgroupId:
+ return out << "workgroup_id";
}
return out << "<unknown>";
}
diff --git a/src/tint/ast/builtin_value.h b/src/tint/ast/builtin_value.h
index 0a2c7f0..f7dd660 100644
--- a/src/tint/ast/builtin_value.h
+++ b/src/tint/ast/builtin_value.h
@@ -27,22 +27,22 @@
namespace tint::ast {
-/// Storage class of a given pointer.
+/// Builtin value defined with `@builtin(<name>)`.
enum class BuiltinValue {
kInvalid,
- kPosition,
- kVertexIndex,
- kInstanceIndex,
- kFrontFacing,
kFragDepth,
+ kFrontFacing,
+ kGlobalInvocationId,
+ kInstanceIndex,
kLocalInvocationId,
kLocalInvocationIndex,
- kGlobalInvocationId,
- kWorkgroupId,
kNumWorkgroups,
+ kPointSize, // Tint-internal enum entry - not parsed
+ kPosition,
kSampleIndex,
kSampleMask,
- kPointSize, // Tint-internal enum entry - not parsed
+ kVertexIndex,
+ kWorkgroupId,
};
/// @param out the std::ostream to write to
@@ -55,6 +55,15 @@
/// @returns the parsed enum, or BuiltinValue::kInvalid if the string could not be parsed.
BuiltinValue ParseBuiltinValue(std::string_view str);
+constexpr const char* kBuiltinValueStrings[] = {
+ "frag_depth", "front_facing",
+ "global_invocation_id", "instance_index",
+ "local_invocation_id", "local_invocation_index",
+ "num_workgroups", "position",
+ "sample_index", "sample_mask",
+ "vertex_index", "workgroup_id",
+};
+
} // namespace tint::ast
#endif // SRC_TINT_AST_BUILTIN_VALUE_H_
diff --git a/src/tint/ast/builtin_value.h.tmpl b/src/tint/ast/builtin_value.h.tmpl
index 1985305..ea63d56 100644
--- a/src/tint/ast/builtin_value.h.tmpl
+++ b/src/tint/ast/builtin_value.h.tmpl
@@ -18,7 +18,7 @@
namespace tint::ast {
-/// Storage class of a given pointer.
+/// Builtin value defined with `@builtin(<name>)`.
{{ Eval "DeclareEnum" $enum}}
} // namespace tint::ast
diff --git a/src/tint/ast/builtin_value_bench.cc b/src/tint/ast/builtin_value_bench.cc
index 0a4048c..b323b6b 100644
--- a/src/tint/ast/builtin_value_bench.cc
+++ b/src/tint/ast/builtin_value_bench.cc
@@ -31,90 +31,90 @@
void BuiltinValueParser(::benchmark::State& state) {
std::array kStrings{
- "pccsitin",
- "oiti3",
- "positVon",
- "position",
- "1osition",
- "osJtqqon",
- "llos77tion",
- "vrtHHppx_index",
- "vertx_icx",
- "veGtex_bnde",
- "vertex_index",
- "vertex_inveii",
- "veWWtex_ind8x",
- "vxxrtMx_indx",
- "isXance_indegg",
- "insanc_iXVex",
- "instance_in3ex",
- "instance_index",
- "instancE_index",
- "nsTTance_PPndex",
- "nstancxx_indddx",
- "44ront_facing",
- "fSSont_facinVV",
- "fronR_Racing",
- "front_facing",
- "ron9_faciFg",
- "front_facin",
- "fVonRR_HaOing",
- "fyag_epth",
- "f77ag_nnellrrh",
- "fra400depth",
+ "fragdeccth",
+ "flaget3",
+ "fVag_depth",
"frag_depth",
- "fa_epooh",
- "frg_ezzth",
- "f11a_eppiih",
- "local_invXXcation_id",
- "lIIcal_i5599ocation_inn",
- "HHrrcal_inSSocation_Yaa",
- "local_invocation_id",
- "lokkal_invocatini",
- "jocal_invocRRongid",
- "local_inocatbon_i",
- "local_injocation_index",
- "local_invocatio_index",
- "locl_invocqtion_ndex",
- "local_invocation_index",
- "localNNinvocaton_index",
- "local_invocatin_ivvdx",
- "locl_invocatioQQ_index",
- "globalrnvocaton_iff",
- "global_invocation_jd",
- "NNlbal_wwnvocation82d",
+ "frag1depth",
+ "fraJqqepth",
+ "fra7ll_depth",
+ "fonHHpp_facing",
+ "fron_facg",
+ "frGnt_fbcin",
+ "front_facing",
+ "front_facvnii",
+ "frWWnt_faci8g",
+ "fxxonM_facig",
+ "gXobalgginvocationid",
+ "goVal_uvocatioX_id",
+ "global_in3ocation_id",
"global_invocation_id",
- "global_invocationid",
- "globalrrinvocation_id",
- "globaG_invocation_id",
- "workgroupFFid",
- "worgrupid",
- "workgroup_rr",
- "workgroup_id",
- "workgrouid",
- "DokgXoJJp_id",
- "8orgrup_i",
- "num_wkkr11up",
- "numworkgroups",
- "Ju_workgroups",
+ "global_invocation_iE",
+ "TTobal_invocationPPid",
+ "globdd_invocatioxx_id",
+ "instance44index",
+ "instaVVce_SSndex",
+ "Rnstane_ind2Rx",
+ "instance_index",
+ "inFtanceind9x",
+ "insance_index",
+ "inRRancV_OOHdex",
+ "local_nvocytion_id",
+ "llGcnnl_inv77catirrn_id",
+ "local_invoca4i00n_id",
+ "local_invocation_id",
+ "loool_nvocaton_id",
+ "local_inozztion_id",
+ "p11cal_invocatiiin_i",
+ "local_invocation_iXXdex",
+ "local_invnnIIati99n55index",
+ "localYirrHHocaationSSindex",
+ "local_invocation_index",
+ "lkkal_invHcation_idx",
+ "gRcal_invocatioj_inex",
+ "lcal_invcbtion_index",
+ "num_workgroujs",
+ "num_worgroups",
+ "nuq_orkgoups",
"num_workgroups",
- "num_corkgroups",
- "num_woOkgroups",
- "num_workKK__vvttps",
- "smple5inxxe8",
- "s__mle_qFdex",
- "saqqple_idex",
+ "nm_workgroNNps",
+ "um_workgrovps",
+ "nQQm_orkgroups",
+ "posftrn",
+ "pojition",
+ "poswNN82n",
+ "position",
+ "positon",
+ "porrition",
+ "pGsition",
+ "sample_inFFex",
+ "samleinex",
+ "sample_indrr",
"sample_index",
- "saOpe_33nde66",
- "s6oople_indttQx",
- "sam66le_inex",
- "samxe66masOz",
- "yyample_mask",
- "amplZZHask",
+ "sample_iex",
+ "DaplX_JJndex",
+ "8amleinde",
+ "saplekmak",
+ "samle_mask",
+ "saJple_mak",
"sample_mask",
- "WWaple_maq4k",
- "samplOO_ask",
- "sYohpe_msk",
+ "sample_cask",
+ "sample_maOk",
+ "__attpvve_KKask",
+ "vrtex5inxxe8",
+ "v__rex_qFdex",
+ "veqqtex_idex",
+ "vertex_index",
+ "veOtx_33nde66",
+ "v6ootex_indttQx",
+ "ver66ex_inex",
+ "worzzroup6Oxd",
+ "workgroyyp_id",
+ "wokrHHZpZid",
+ "workgroup_id",
+ "wWWkgqoup44id",
+ "wrkgroOOp_id",
+ "hrkgYooup_d",
};
for (auto _ : state) {
for (auto& str : kStrings) {
diff --git a/src/tint/ast/builtin_value_test.cc b/src/tint/ast/builtin_value_test.cc
index a29810a..55f8e4d 100644
--- a/src/tint/ast/builtin_value_test.cc
+++ b/src/tint/ast/builtin_value_test.cc
@@ -42,57 +42,57 @@
}
static constexpr Case kValidCases[] = {
- {"position", BuiltinValue::kPosition},
- {"vertex_index", BuiltinValue::kVertexIndex},
- {"instance_index", BuiltinValue::kInstanceIndex},
- {"front_facing", BuiltinValue::kFrontFacing},
{"frag_depth", BuiltinValue::kFragDepth},
+ {"front_facing", BuiltinValue::kFrontFacing},
+ {"global_invocation_id", BuiltinValue::kGlobalInvocationId},
+ {"instance_index", BuiltinValue::kInstanceIndex},
{"local_invocation_id", BuiltinValue::kLocalInvocationId},
{"local_invocation_index", BuiltinValue::kLocalInvocationIndex},
- {"global_invocation_id", BuiltinValue::kGlobalInvocationId},
- {"workgroup_id", BuiltinValue::kWorkgroupId},
{"num_workgroups", BuiltinValue::kNumWorkgroups},
+ {"position", BuiltinValue::kPosition},
{"sample_index", BuiltinValue::kSampleIndex},
{"sample_mask", BuiltinValue::kSampleMask},
+ {"vertex_index", BuiltinValue::kVertexIndex},
+ {"workgroup_id", BuiltinValue::kWorkgroupId},
};
static constexpr Case kInvalidCases[] = {
- {"pccsitin", BuiltinValue::kInvalid},
- {"oiti3", BuiltinValue::kInvalid},
- {"positVon", BuiltinValue::kInvalid},
- {"1ertex_index", BuiltinValue::kInvalid},
- {"vertex_Jnqex", BuiltinValue::kInvalid},
- {"velltex_inde77", BuiltinValue::kInvalid},
- {"inpptanceqHHindx", BuiltinValue::kInvalid},
- {"cnsanvendex", BuiltinValue::kInvalid},
- {"istancG_index", BuiltinValue::kInvalid},
- {"front_facvnii", BuiltinValue::kInvalid},
- {"frWWnt_faci8g", BuiltinValue::kInvalid},
- {"fxxonM_facig", BuiltinValue::kInvalid},
- {"fXag_detgg", BuiltinValue::kInvalid},
- {"fag_XuVh", BuiltinValue::kInvalid},
- {"frag_dept3", BuiltinValue::kInvalid},
- {"local_Envocation_id", BuiltinValue::kInvalid},
- {"localiPPvocatioTT_id", BuiltinValue::kInvalid},
- {"localxxnvocationddid", BuiltinValue::kInvalid},
- {"loca44_invocation_index", BuiltinValue::kInvalid},
- {"local_invocSStionVVindex", BuiltinValue::kInvalid},
- {"locRR_invocat22n_index", BuiltinValue::kInvalid},
- {"globalFinvoction_id", BuiltinValue::kInvalid},
- {"gloal_invocation_id", BuiltinValue::kInvalid},
- {"RRlHOOaV_invoction_id", BuiltinValue::kInvalid},
- {"workgyoup_i", BuiltinValue::kInvalid},
- {"wnrrrkg77loup_Gd", BuiltinValue::kInvalid},
- {"00orkgr4up_id", BuiltinValue::kInvalid},
- {"numwroogrops", BuiltinValue::kInvalid},
- {"nzm_wokgroups", BuiltinValue::kInvalid},
- {"uippworkgro11ps", BuiltinValue::kInvalid},
- {"sample_iXXdex", BuiltinValue::kInvalid},
- {"5nnample_99IIdex", BuiltinValue::kInvalid},
- {"samYlaaHHrrndeSS", BuiltinValue::kInvalid},
- {"aHkk_mask", BuiltinValue::kInvalid},
- {"jRRmpl_gsk", BuiltinValue::kInvalid},
- {"smple_mbk", BuiltinValue::kInvalid},
+ {"fragdeccth", BuiltinValue::kInvalid},
+ {"flaget3", BuiltinValue::kInvalid},
+ {"fVag_depth", BuiltinValue::kInvalid},
+ {"1ront_facing", BuiltinValue::kInvalid},
+ {"front_fJcqng", BuiltinValue::kInvalid},
+ {"frllnt_facin77", BuiltinValue::kInvalid},
+ {"global_invoqqtionppHid", BuiltinValue::kInvalid},
+ {"clvbal_inocaionid", BuiltinValue::kInvalid},
+ {"global_Gvocation_id", BuiltinValue::kInvalid},
+ {"invtance_iniiex", BuiltinValue::kInvalid},
+ {"8nstanceWWindex", BuiltinValue::kInvalid},
+ {"insxxanceindex", BuiltinValue::kInvalid},
+ {"lXcal_invoation_igg", BuiltinValue::kInvalid},
+ {"Xocal_nvocatin_Vd", BuiltinValue::kInvalid},
+ {"local_invoca3ion_id", BuiltinValue::kInvalid},
+ {"local_invocation_indeE", BuiltinValue::kInvalid},
+ {"loTTal_invPPcatin_index", BuiltinValue::kInvalid},
+ {"loal_invocadxxion_index", BuiltinValue::kInvalid},
+ {"num_work44roups", BuiltinValue::kInvalid},
+ {"num_wVVrkgSSoups", BuiltinValue::kInvalid},
+ {"Rum_wokgrou2Rs", BuiltinValue::kInvalid},
+ {"oFi9ion", BuiltinValue::kInvalid},
+ {"postion", BuiltinValue::kInvalid},
+ {"ROOoHiiVn", BuiltinValue::kInvalid},
+ {"samply_inde", BuiltinValue::kInvalid},
+ {"snrrmpl77l_indGx", BuiltinValue::kInvalid},
+ {"00ample4index", BuiltinValue::kInvalid},
+ {"smoo_mask", BuiltinValue::kInvalid},
+ {"sampzemask", BuiltinValue::kInvalid},
+ {"ppaplii1_mas", BuiltinValue::kInvalid},
+ {"vertex_iXXdex", BuiltinValue::kInvalid},
+ {"5nnertex_99IIdex", BuiltinValue::kInvalid},
+ {"verYeaaHHrrndeSS", BuiltinValue::kInvalid},
+ {"workkgHo_i", BuiltinValue::kInvalid},
+ {"worRgoupjid", BuiltinValue::kInvalid},
+ {"wrkgrupbid", BuiltinValue::kInvalid},
};
using BuiltinValueParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/ast/extension.cc b/src/tint/ast/extension.cc
index e57b248..a4d823f 100644
--- a/src/tint/ast/extension.cc
+++ b/src/tint/ast/extension.cc
@@ -28,18 +28,18 @@
/// @param str the string to parse
/// @returns the parsed enum, or Extension::kInvalid if the string could not be parsed.
Extension ParseExtension(std::string_view str) {
- if (str == "f16") {
- return Extension::kF16;
+ if (str == "chromium_disable_uniformity_analysis") {
+ return Extension::kChromiumDisableUniformityAnalysis;
}
if (str == "chromium_experimental_dp4a") {
return Extension::kChromiumExperimentalDp4A;
}
- if (str == "chromium_disable_uniformity_analysis") {
- return Extension::kChromiumDisableUniformityAnalysis;
- }
if (str == "chromium_experimental_push_constant") {
return Extension::kChromiumExperimentalPushConstant;
}
+ if (str == "f16") {
+ return Extension::kF16;
+ }
return Extension::kInvalid;
}
@@ -47,14 +47,14 @@
switch (value) {
case Extension::kInvalid:
return out << "invalid";
- case Extension::kF16:
- return out << "f16";
- case Extension::kChromiumExperimentalDp4A:
- return out << "chromium_experimental_dp4a";
case Extension::kChromiumDisableUniformityAnalysis:
return out << "chromium_disable_uniformity_analysis";
+ case Extension::kChromiumExperimentalDp4A:
+ return out << "chromium_experimental_dp4a";
case Extension::kChromiumExperimentalPushConstant:
return out << "chromium_experimental_push_constant";
+ case Extension::kF16:
+ return out << "f16";
}
return out << "<unknown>";
}
diff --git a/src/tint/ast/extension.h b/src/tint/ast/extension.h
index eea9bec..4d1a937 100644
--- a/src/tint/ast/extension.h
+++ b/src/tint/ast/extension.h
@@ -33,10 +33,10 @@
/// @see src/tint/intrinsics.def for extension descriptions
enum class Extension {
kInvalid,
- kF16,
- kChromiumExperimentalDp4A,
kChromiumDisableUniformityAnalysis,
+ kChromiumExperimentalDp4A,
kChromiumExperimentalPushConstant,
+ kF16,
};
/// @param out the std::ostream to write to
@@ -49,6 +49,13 @@
/// @returns the parsed enum, or Extension::kInvalid if the string could not be parsed.
Extension ParseExtension(std::string_view str);
+constexpr const char* kExtensionStrings[] = {
+ "chromium_disable_uniformity_analysis",
+ "chromium_experimental_dp4a",
+ "chromium_experimental_push_constant",
+ "f16",
+};
+
// A unique vector of extensions
using Extensions = utils::UniqueVector<Extension, 4>;
diff --git a/src/tint/ast/extension_bench.cc b/src/tint/ast/extension_bench.cc
index 8fc9d1c..94b5216 100644
--- a/src/tint/ast/extension_bench.cc
+++ b/src/tint/ast/extension_bench.cc
@@ -31,13 +31,13 @@
void ExtensionParser(::benchmark::State& state) {
std::array kStrings{
- "cc6",
- "s",
- "HH6",
- "f16",
- "116",
- "qJ6",
- "f17ll",
+ "chromium_disableuniformiccy_analysis",
+ "chromil3_disable_unifority_analss",
+ "chromium_disable_Vniformity_analysis",
+ "chromium_disable_uniformity_analysis",
+ "chromium_dis1ble_uniformity_analysis",
+ "chromium_qqisable_unifomity_anaJysis",
+ "chrollium_disable_uniformity_analysi77",
"chromippHm_experqqmetal_dp4a",
"chrmium_expecimntal_dp4",
"chrmiumGexpebimental_dp4a",
@@ -45,20 +45,20 @@
"chromium_exverimentiil_dp4a",
"chro8ium_experimenWWal_dp4a",
"chromiMm_eperimxxntal_dp4a",
- "chXggmium_disable_uniformity_aalysis",
- "Xhomiuu_disale_uniformity_analysis",
- "chromium_3isable_uniformity_analysis",
- "chromium_disable_uniformity_analysis",
- "chromiuE_disable_uniformity_analysis",
- "chromium_disable_uniTTormity_aPPalsis",
- "ddhromium_disabexxuniformity_analysis",
- "c44romium_experimental_push_constant",
- "chromium_experimental_pSSsVV_constant",
- "chrom22Rm_experimental_pushRonstant",
+ "chrXmium_experimeggtal_ush_constant",
+ "chromiu_experVmentalpusX_constant",
+ "chro3ium_experimental_push_constant",
"chromium_experimental_push_constant",
- "chromium_exp9rimFntal_ush_constant",
- "chrmium_experimental_push_constant",
- "cOOromium_experiVeHtal_puh_conRRtant",
+ "chromium_experEmental_push_constant",
+ "chPPomiumexperimental_push_conTTtant",
+ "chromixxm_experimentddl_push_constnt",
+ "4416",
+ "fSVV6",
+ "RR2",
+ "f16",
+ "96",
+ "f1",
+ "VOR6",
};
for (auto _ : state) {
for (auto& str : kStrings) {
diff --git a/src/tint/ast/extension_test.cc b/src/tint/ast/extension_test.cc
index 283088d..e43266c 100644
--- a/src/tint/ast/extension_test.cc
+++ b/src/tint/ast/extension_test.cc
@@ -42,25 +42,25 @@
}
static constexpr Case kValidCases[] = {
- {"f16", Extension::kF16},
- {"chromium_experimental_dp4a", Extension::kChromiumExperimentalDp4A},
{"chromium_disable_uniformity_analysis", Extension::kChromiumDisableUniformityAnalysis},
+ {"chromium_experimental_dp4a", Extension::kChromiumExperimentalDp4A},
{"chromium_experimental_push_constant", Extension::kChromiumExperimentalPushConstant},
+ {"f16", Extension::kF16},
};
static constexpr Case kInvalidCases[] = {
- {"cc6", Extension::kInvalid},
- {"s", Extension::kInvalid},
- {"HH6", Extension::kInvalid},
+ {"chromium_disableuniformiccy_analysis", Extension::kInvalid},
+ {"chromil3_disable_unifority_analss", Extension::kInvalid},
+ {"chromium_disable_Vniformity_analysis", Extension::kInvalid},
{"chro1ium_experimental_dp4a", Extension::kInvalid},
{"chrJmium_experiqqetal_dp4a", Extension::kInvalid},
{"chromium_experimenll77l_dp4a", Extension::kInvalid},
- {"chromiumppdisableqquniformity_aalysHHs", Extension::kInvalid},
- {"chromiu_disable_unifovmitc_analyi", Extension::kInvalid},
- {"chromium_diable_uGbformity_analysis", Extension::kInvalid},
- {"chvomium_experimental_push_constiint", Extension::kInvalid},
- {"chromiu8WWexperimental_push_constant", Extension::kInvalid},
- {"chromium_experiMental_push_costanxx", Extension::kInvalid},
+ {"cppromium_experiHHenal_qqush_constant", Extension::kInvalid},
+ {"chromium_xpericental_sh_vonstant", Extension::kInvalid},
+ {"chromium_experimental_Gsh_cbnstant", Extension::kInvalid},
+ {"f1vi", Extension::kInvalid},
+ {"f8WW", Extension::kInvalid},
+ {"fxx", Extension::kInvalid},
};
using ExtensionParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/ast/if_statement.h b/src/tint/ast/if_statement.h
index 81d9756..1c1abc3 100644
--- a/src/tint/ast/if_statement.h
+++ b/src/tint/ast/if_statement.h
@@ -55,7 +55,7 @@
const BlockStatement* const body;
/// The optional else statement, or nullptr
- const Statement* else_statement;
+ const Statement* const else_statement;
};
} // namespace tint::ast
diff --git a/src/tint/ast/interpolate_attribute.cc b/src/tint/ast/interpolate_attribute.cc
index 29e3bfe..7c89ee1 100644
--- a/src/tint/ast/interpolate_attribute.cc
+++ b/src/tint/ast/interpolate_attribute.cc
@@ -12,6 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+// src/tint/ast/interpolate_attribute.cc.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
#include "src/tint/ast/interpolate_attribute.h"
#include <string>
@@ -41,44 +49,64 @@
return ctx->dst->create<InterpolateAttribute>(src, type, sampling);
}
-std::ostream& operator<<(std::ostream& out, InterpolationType type) {
- switch (type) {
- case InterpolationType::kPerspective: {
- out << "perspective";
- break;
- }
- case InterpolationType::kLinear: {
- out << "linear";
- break;
- }
- case InterpolationType::kFlat: {
- out << "flat";
- break;
- }
+/// ParseInterpolationType parses a InterpolationType from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or InterpolationType::kInvalid if the string could not be parsed.
+InterpolationType ParseInterpolationType(std::string_view str) {
+ if (str == "flat") {
+ return InterpolationType::kFlat;
}
- return out;
+ if (str == "linear") {
+ return InterpolationType::kLinear;
+ }
+ if (str == "perspective") {
+ return InterpolationType::kPerspective;
+ }
+ return InterpolationType::kInvalid;
}
-std::ostream& operator<<(std::ostream& out, InterpolationSampling sampling) {
- switch (sampling) {
- case InterpolationSampling::kNone: {
- out << "none";
- break;
- }
- case InterpolationSampling::kCenter: {
- out << "center";
- break;
- }
- case InterpolationSampling::kCentroid: {
- out << "centroid";
- break;
- }
- case InterpolationSampling::kSample: {
- out << "sample";
- break;
- }
+std::ostream& operator<<(std::ostream& out, InterpolationType value) {
+ switch (value) {
+ case InterpolationType::kInvalid:
+ return out << "invalid";
+ case InterpolationType::kFlat:
+ return out << "flat";
+ case InterpolationType::kLinear:
+ return out << "linear";
+ case InterpolationType::kPerspective:
+ return out << "perspective";
}
- return out;
+ return out << "<unknown>";
+}
+
+/// ParseInterpolationSampling parses a InterpolationSampling from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or InterpolationSampling::kInvalid if the string could not be parsed.
+InterpolationSampling ParseInterpolationSampling(std::string_view str) {
+ if (str == "center") {
+ return InterpolationSampling::kCenter;
+ }
+ if (str == "centroid") {
+ return InterpolationSampling::kCentroid;
+ }
+ if (str == "sample") {
+ return InterpolationSampling::kSample;
+ }
+ return InterpolationSampling::kInvalid;
+}
+
+std::ostream& operator<<(std::ostream& out, InterpolationSampling value) {
+ switch (value) {
+ case InterpolationSampling::kInvalid:
+ return out << "invalid";
+ case InterpolationSampling::kCenter:
+ return out << "center";
+ case InterpolationSampling::kCentroid:
+ return out << "centroid";
+ case InterpolationSampling::kSample:
+ return out << "sample";
+ }
+ return out << "<unknown>";
}
} // namespace tint::ast
diff --git a/src/tint/ast/interpolate_attribute.cc.tmpl b/src/tint/ast/interpolate_attribute.cc.tmpl
new file mode 100644
index 0000000..b6f0b35
--- /dev/null
+++ b/src/tint/ast/interpolate_attribute.cc.tmpl
@@ -0,0 +1,50 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate builtin_value.cc
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#include "src/tint/ast/interpolate_attribute.h"
+
+#include <string>
+
+#include "src/tint/program_builder.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::ast::InterpolateAttribute);
+
+namespace tint::ast {
+
+InterpolateAttribute::InterpolateAttribute(ProgramID pid,
+ NodeID nid,
+ const Source& src,
+ InterpolationType ty,
+ InterpolationSampling smpl)
+ : Base(pid, nid, src), type(ty), sampling(smpl) {}
+
+InterpolateAttribute::~InterpolateAttribute() = default;
+
+std::string InterpolateAttribute::Name() const {
+ return "interpolate";
+}
+
+const InterpolateAttribute* InterpolateAttribute::Clone(CloneContext* ctx) const {
+ // Clone arguments outside of create() call to have deterministic ordering
+ auto src = ctx->Clone(source);
+ return ctx->dst->create<InterpolateAttribute>(src, type, sampling);
+}
+
+{{ Eval "ParseEnum" (Sem.Enum "interpolation_type")}}
+
+{{ Eval "EnumOStream" (Sem.Enum "interpolation_type")}}
+
+{{ Eval "ParseEnum" (Sem.Enum "interpolation_sampling")}}
+
+{{ Eval "EnumOStream" (Sem.Enum "interpolation_sampling")}}
+
+} // namespace tint::ast
diff --git a/src/tint/ast/interpolate_attribute.h b/src/tint/ast/interpolate_attribute.h
index 4f9ea9d..c6b4af0 100644
--- a/src/tint/ast/interpolate_attribute.h
+++ b/src/tint/ast/interpolate_attribute.h
@@ -1,4 +1,4 @@
-// Copyright 2021 The Tint Authors.
+// 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.
@@ -12,6 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+////////////////////////////////////////////////////////////////////////////////
+// File generated by tools/src/cmd/gen
+// using the template:
+// src/tint/ast/interpolate_attribute.h.tmpl
+//
+// Do not modify this file directly
+////////////////////////////////////////////////////////////////////////////////
+
#ifndef SRC_TINT_AST_INTERPOLATE_ATTRIBUTE_H_
#define SRC_TINT_AST_INTERPOLATE_ATTRIBUTE_H_
@@ -23,10 +31,52 @@
namespace tint::ast {
/// The interpolation type.
-enum class InterpolationType { kPerspective, kLinear, kFlat };
+enum class InterpolationType {
+ kInvalid,
+ kFlat,
+ kLinear,
+ kPerspective,
+};
+
+/// @param out the std::ostream to write to
+/// @param value the InterpolationType
+/// @returns `out` so calls can be chained
+std::ostream& operator<<(std::ostream& out, InterpolationType value);
+
+/// ParseInterpolationType parses a InterpolationType from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or InterpolationType::kInvalid if the string could not be parsed.
+InterpolationType ParseInterpolationType(std::string_view str);
+
+constexpr const char* kInterpolationTypeStrings[] = {
+ "flat",
+ "linear",
+ "perspective",
+};
/// The interpolation sampling.
-enum class InterpolationSampling { kNone = -1, kCenter, kCentroid, kSample };
+enum class InterpolationSampling {
+ kInvalid,
+ kCenter,
+ kCentroid,
+ kSample,
+};
+
+/// @param out the std::ostream to write to
+/// @param value the InterpolationSampling
+/// @returns `out` so calls can be chained
+std::ostream& operator<<(std::ostream& out, InterpolationSampling value);
+
+/// ParseInterpolationSampling parses a InterpolationSampling from a string.
+/// @param str the string to parse
+/// @returns the parsed enum, or InterpolationSampling::kInvalid if the string could not be parsed.
+InterpolationSampling ParseInterpolationSampling(std::string_view str);
+
+constexpr const char* kInterpolationSamplingStrings[] = {
+ "center",
+ "centroid",
+ "sample",
+};
/// An interpolate attribute
class InterpolateAttribute final : public Castable<InterpolateAttribute, Attribute> {
@@ -60,16 +110,6 @@
const InterpolationSampling sampling;
};
-/// @param out the std::ostream to write to
-/// @param type the interpolation type
-/// @return the std::ostream so calls can be chained
-std::ostream& operator<<(std::ostream& out, InterpolationType type);
-
-/// @param out the std::ostream to write to
-/// @param sampling the interpolation sampling
-/// @return the std::ostream so calls can be chained
-std::ostream& operator<<(std::ostream& out, InterpolationSampling sampling);
-
} // namespace tint::ast
#endif // SRC_TINT_AST_INTERPOLATE_ATTRIBUTE_H_
diff --git a/src/tint/ast/interpolate_attribute.h.tmpl b/src/tint/ast/interpolate_attribute.h.tmpl
new file mode 100644
index 0000000..c225cb6
--- /dev/null
+++ b/src/tint/ast/interpolate_attribute.h.tmpl
@@ -0,0 +1,63 @@
+{{- /*
+--------------------------------------------------------------------------------
+Template file for use with tools/src/cmd/gen to generate interpolate_attribute.h
+
+See:
+* tools/src/cmd/gen for structures used by this template
+* https://golang.org/pkg/text/template/ for documentation on the template syntax
+--------------------------------------------------------------------------------
+*/ -}}
+
+{{- Import "src/tint/templates/enums.tmpl.inc" -}}
+
+#ifndef SRC_TINT_AST_INTERPOLATE_ATTRIBUTE_H_
+#define SRC_TINT_AST_INTERPOLATE_ATTRIBUTE_H_
+
+#include <ostream>
+#include <string>
+
+#include "src/tint/ast/attribute.h"
+
+namespace tint::ast {
+
+/// The interpolation type.
+{{ Eval "DeclareEnum" (Sem.Enum "interpolation_type") }}
+
+/// The interpolation sampling.
+{{ Eval "DeclareEnum" (Sem.Enum "interpolation_sampling") }}
+
+/// An interpolate attribute
+class InterpolateAttribute final : public Castable<InterpolateAttribute, Attribute> {
+ public:
+ /// Create an interpolate attribute.
+ /// @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 type the interpolation type
+ /// @param sampling the interpolation sampling
+ InterpolateAttribute(ProgramID pid,
+ NodeID nid,
+ const Source& src,
+ InterpolationType type,
+ InterpolationSampling sampling);
+ ~InterpolateAttribute() override;
+
+ /// @returns the WGSL name for the attribute
+ std::string Name() const override;
+
+ /// Clones this node and all transitive child nodes using the `CloneContext`
+ /// `ctx`.
+ /// @param ctx the clone context
+ /// @return the newly cloned node
+ const InterpolateAttribute* Clone(CloneContext* ctx) const override;
+
+ /// The interpolation type
+ const InterpolationType type;
+
+ /// The interpolation sampling
+ const InterpolationSampling sampling;
+};
+
+} // namespace tint::ast
+
+#endif // SRC_TINT_AST_INTERPOLATE_ATTRIBUTE_H_
diff --git a/src/tint/ast/pointer.cc b/src/tint/ast/pointer.cc
index 71b7004..96bbdaf 100644
--- a/src/tint/ast/pointer.cc
+++ b/src/tint/ast/pointer.cc
@@ -35,7 +35,7 @@
out << address_space << ", ";
}
out << type->FriendlyName(symbols);
- if (access != ast::Access::kUndefined) {
+ if (access != ast::Access::kInvalid) {
out << ", " << access;
}
out << ">";
diff --git a/src/tint/ast/pointer_test.cc b/src/tint/ast/pointer_test.cc
index aea260a..56a04b0 100644
--- a/src/tint/ast/pointer_test.cc
+++ b/src/tint/ast/pointer_test.cc
@@ -32,7 +32,7 @@
TEST_F(AstPointerTest, FriendlyName) {
auto* i32 = create<I32>();
- auto* p = create<Pointer>(i32, ast::AddressSpace::kWorkgroup, Access::kUndefined);
+ auto* p = create<Pointer>(i32, ast::AddressSpace::kWorkgroup, Access::kInvalid);
EXPECT_EQ(p->FriendlyName(Symbols()), "ptr<workgroup, i32>");
}
diff --git a/src/tint/ast/texel_format.cc b/src/tint/ast/texel_format.cc
index cfac9f1..201cad3 100644
--- a/src/tint/ast/texel_format.cc
+++ b/src/tint/ast/texel_format.cc
@@ -28,8 +28,44 @@
/// @param str the string to parse
/// @returns the parsed enum, or TexelFormat::kInvalid if the string could not be parsed.
TexelFormat ParseTexelFormat(std::string_view str) {
- if (str == "rgba8unorm") {
- return TexelFormat::kRgba8Unorm;
+ if (str == "r32float") {
+ return TexelFormat::kR32Float;
+ }
+ if (str == "r32sint") {
+ return TexelFormat::kR32Sint;
+ }
+ if (str == "r32uint") {
+ return TexelFormat::kR32Uint;
+ }
+ if (str == "rg32float") {
+ return TexelFormat::kRg32Float;
+ }
+ if (str == "rg32sint") {
+ return TexelFormat::kRg32Sint;
+ }
+ if (str == "rg32uint") {
+ return TexelFormat::kRg32Uint;
+ }
+ if (str == "rgba16float") {
+ return TexelFormat::kRgba16Float;
+ }
+ if (str == "rgba16sint") {
+ return TexelFormat::kRgba16Sint;
+ }
+ if (str == "rgba16uint") {
+ return TexelFormat::kRgba16Uint;
+ }
+ if (str == "rgba32float") {
+ return TexelFormat::kRgba32Float;
+ }
+ if (str == "rgba32sint") {
+ return TexelFormat::kRgba32Sint;
+ }
+ if (str == "rgba32uint") {
+ return TexelFormat::kRgba32Uint;
+ }
+ if (str == "rgba8sint") {
+ return TexelFormat::kRgba8Sint;
}
if (str == "rgba8snorm") {
return TexelFormat::kRgba8Snorm;
@@ -37,44 +73,8 @@
if (str == "rgba8uint") {
return TexelFormat::kRgba8Uint;
}
- if (str == "rgba8sint") {
- return TexelFormat::kRgba8Sint;
- }
- if (str == "rgba16uint") {
- return TexelFormat::kRgba16Uint;
- }
- if (str == "rgba16sint") {
- return TexelFormat::kRgba16Sint;
- }
- if (str == "rgba16float") {
- return TexelFormat::kRgba16Float;
- }
- if (str == "r32uint") {
- return TexelFormat::kR32Uint;
- }
- if (str == "r32sint") {
- return TexelFormat::kR32Sint;
- }
- if (str == "r32float") {
- return TexelFormat::kR32Float;
- }
- if (str == "rg32uint") {
- return TexelFormat::kRg32Uint;
- }
- if (str == "rg32sint") {
- return TexelFormat::kRg32Sint;
- }
- if (str == "rg32float") {
- return TexelFormat::kRg32Float;
- }
- if (str == "rgba32uint") {
- return TexelFormat::kRgba32Uint;
- }
- if (str == "rgba32sint") {
- return TexelFormat::kRgba32Sint;
- }
- if (str == "rgba32float") {
- return TexelFormat::kRgba32Float;
+ if (str == "rgba8unorm") {
+ return TexelFormat::kRgba8Unorm;
}
return TexelFormat::kInvalid;
}
@@ -83,38 +83,38 @@
switch (value) {
case TexelFormat::kInvalid:
return out << "invalid";
- case TexelFormat::kRgba8Unorm:
- return out << "rgba8unorm";
+ case TexelFormat::kR32Float:
+ return out << "r32float";
+ case TexelFormat::kR32Sint:
+ return out << "r32sint";
+ case TexelFormat::kR32Uint:
+ return out << "r32uint";
+ case TexelFormat::kRg32Float:
+ return out << "rg32float";
+ case TexelFormat::kRg32Sint:
+ return out << "rg32sint";
+ case TexelFormat::kRg32Uint:
+ return out << "rg32uint";
+ case TexelFormat::kRgba16Float:
+ return out << "rgba16float";
+ case TexelFormat::kRgba16Sint:
+ return out << "rgba16sint";
+ case TexelFormat::kRgba16Uint:
+ return out << "rgba16uint";
+ case TexelFormat::kRgba32Float:
+ return out << "rgba32float";
+ case TexelFormat::kRgba32Sint:
+ return out << "rgba32sint";
+ case TexelFormat::kRgba32Uint:
+ return out << "rgba32uint";
+ case TexelFormat::kRgba8Sint:
+ return out << "rgba8sint";
case TexelFormat::kRgba8Snorm:
return out << "rgba8snorm";
case TexelFormat::kRgba8Uint:
return out << "rgba8uint";
- case TexelFormat::kRgba8Sint:
- return out << "rgba8sint";
- case TexelFormat::kRgba16Uint:
- return out << "rgba16uint";
- case TexelFormat::kRgba16Sint:
- return out << "rgba16sint";
- case TexelFormat::kRgba16Float:
- return out << "rgba16float";
- case TexelFormat::kR32Uint:
- return out << "r32uint";
- case TexelFormat::kR32Sint:
- return out << "r32sint";
- case TexelFormat::kR32Float:
- return out << "r32float";
- case TexelFormat::kRg32Uint:
- return out << "rg32uint";
- case TexelFormat::kRg32Sint:
- return out << "rg32sint";
- case TexelFormat::kRg32Float:
- return out << "rg32float";
- case TexelFormat::kRgba32Uint:
- return out << "rgba32uint";
- case TexelFormat::kRgba32Sint:
- return out << "rgba32sint";
- case TexelFormat::kRgba32Float:
- return out << "rgba32float";
+ case TexelFormat::kRgba8Unorm:
+ return out << "rgba8unorm";
}
return out << "<unknown>";
}
diff --git a/src/tint/ast/texel_format.h b/src/tint/ast/texel_format.h
index 28119b3..9c65fb6 100644
--- a/src/tint/ast/texel_format.h
+++ b/src/tint/ast/texel_format.h
@@ -30,22 +30,22 @@
/// Enumerator of texel formats
enum class TexelFormat {
kInvalid,
- kRgba8Unorm,
+ kR32Float,
+ kR32Sint,
+ kR32Uint,
+ kRg32Float,
+ kRg32Sint,
+ kRg32Uint,
+ kRgba16Float,
+ kRgba16Sint,
+ kRgba16Uint,
+ kRgba32Float,
+ kRgba32Sint,
+ kRgba32Uint,
+ kRgba8Sint,
kRgba8Snorm,
kRgba8Uint,
- kRgba8Sint,
- kRgba16Uint,
- kRgba16Sint,
- kRgba16Float,
- kR32Uint,
- kR32Sint,
- kR32Float,
- kRg32Uint,
- kRg32Sint,
- kRg32Float,
- kRgba32Uint,
- kRgba32Sint,
- kRgba32Float,
+ kRgba8Unorm,
};
/// @param out the std::ostream to write to
@@ -58,6 +58,12 @@
/// @returns the parsed enum, or TexelFormat::kInvalid if the string could not be parsed.
TexelFormat ParseTexelFormat(std::string_view str);
+constexpr const char* kTexelFormatStrings[] = {
+ "r32float", "r32sint", "r32uint", "rg32float", "rg32sint", "rg32uint",
+ "rgba16float", "rgba16sint", "rgba16uint", "rgba32float", "rgba32sint", "rgba32uint",
+ "rgba8sint", "rgba8snorm", "rgba8uint", "rgba8unorm",
+};
+
} // namespace tint::ast
#endif // SRC_TINT_AST_TEXEL_FORMAT_H_
diff --git a/src/tint/ast/texel_format_bench.cc b/src/tint/ast/texel_format_bench.cc
index a17906a..c48e0f6 100644
--- a/src/tint/ast/texel_format_bench.cc
+++ b/src/tint/ast/texel_format_bench.cc
@@ -31,29 +31,29 @@
void TexelFormatParser(::benchmark::State& state) {
std::array kStrings{
- "rgbaunccrm", "rlbanr3", "rVba8unorm", "rgba8unorm", "rgba1unorm",
- "rgbJqqnorm", "rgb7ll8unorm", "rgqqappnoHHm", "rv8scor", "rgbbGsnrm",
- "rgba8snorm", "rgba8vniirm", "rg8a8snoWWm", "Mgbaxxnorm", "rXa8uggnt",
- "rgbXVut", "3gba8uint", "rgba8uint", "rgba8uiEt", "rgTTPauint",
- "ddgbauixxt", "44gba8sint", "VVgbaSSsint", "rba8si2Rt", "rgba8sint",
- "r9bFsint", "rgba8int", "rgVROOsHnt", "ryba1uint", "r77ba1nnullrrt",
- "rgb4006uint", "rgba16uint", "rb1uioot", "rga1uzznt", "r11b1uppiit",
- "XXgba16sint", "IIgb9916nni55t", "rYbaSSrrsiHHat", "rgba16sint", "rbkk6Hit",
- "jgba1sgRR", "rgbab6si", "rgba16fljat", "rgba6float", "rbq6float",
- "rgba16float", "rgba1NNloat", "rgbvv6flot", "rgbaQQ6foat", "r3ffir",
- "r32uijt", "rNNwuin8", "r32uint", "r32int", "rrr2uint",
- "G32uint", "r32sinFF", "32st", "r3rrint", "r32sint",
- "2sint", "D3siJJt", "r38n", "r211lk", "r32floa",
- "r3flJat", "r32float", "r32fcoat", "r32floOt", "r32floKK_vtt",
- "rxx32ui8", "Fg3qq__n", "rg32iqqt", "rg32uint", "rg333uin6",
- "rtto62u9QQt", "rg366uin", "rOx2si6zz", "rg3yysint", "rHHsint",
- "rg32sint", "qWW432snt", "rg3OOsnt", "g32siYt", "g32flo",
- "rg32foaF", "rg32fwat", "rg32float", "G3fKoaff", "KKgq2float",
- "rg32mmlo3t", "rgba32uit", "rqba3uint", "rgbabb2uin", "rgba32uint",
- "rba32iint", "qgba32uiOt", "rgba32uiTTvv", "rgFFa32sint", "rg00Q2sPnt",
- "rgbaP2sint", "rgba32sint", "rgb77s2sint", "rgba32sbbRRC", "rgbXX32sint",
- "rOOOba3CCqoat", "rgbu32fsLt", "rgba3Xfloat", "rgba32float", "rba32float",
- "qqb3float", "rgba32fl22at",
+ "rcc2flot", "3flo3", "r32flVat", "r32float", "132float",
+ "32Jlqqat", "ll3277loat", "ppqq2snHH", "r3cv", "b2siGt",
+ "r32sint", "r32siivt", "8WW2sint", "rxxsint", "rXuingg",
+ "3uiXt", "r32u3nt", "r32uint", "E32uint", "rPTTuint",
+ "r32uidxx", "44g32float", "VVg32SSloat", "r32flo2Rt", "rg32float",
+ "r93Float", "rg32foat", "rgVROOlHat", "rg3ysin", "77grr2sllnnt",
+ "04g32sint", "rg32sint", "g3oont", "rg32zzt", "rgiipps1n",
+ "XXg32uint", "rII39955nnnt", "aagHH2uinYSS", "rg32uint", "rkk3it",
+ "gj3uRRn", "r3bunt", "rgba16fljat", "rgba6float", "rbq6float",
+ "rgba16float", "rgba1NNloat", "rgbvv6flot", "rgbaQQ6foat", "rgb6srnff",
+ "rgba16sijt", "NNgba16ww2t", "rgba16sint", "rgba16snt", "rgba16rrint",
+ "rgba1Gsint", "rgba16uFFnt", "g16uEnt", "rgb16rrint", "rgba16uint",
+ "gba16uit", "rXa1DuiJJt", "rgauint", "rga32klot", "rgb32float",
+ "rgJa32flot", "rgba32float", "rgba32fcoat", "rgba32floOt", "__gttavv2fKKoat",
+ "rg5a32xxnt", "__ba3sqqFt", "rgbqq2sint", "rgba32sint", "33ba32s66nt",
+ "rtt6a3QQooint", "r66ba3sint", "xba32zzinO6", "ryyba32uint", "rbZ32HinZ",
+ "rgba32uint", "rgba3u4WWnq", "rgba32uOOt", "oogba2Yin", "gba8si",
+ "rgba8inF", "rgba8wnt", "rgba8sint", "Gb8Kinff", "KKgqa8sint",
+ "rgbammsi3t", "rgba8snom", "rqba8norm", "rgbabbsnor", "rgba8snorm",
+ "rba8siorm", "qgba8snoOm", "rgba8snoTTvv", "rgbaFFuint", "rgQa00uiP",
+ "rgPa8uint", "rgba8uint", "rgssa77unt", "Cgbbb8uiRRt", "rgba8uinXX",
+ "CqgbaOOunorm", "rgbu8usrL", "rgba8Xnorm", "rgba8unorm", "rgba8unrm",
+ "ba8uqqor", "rgba8unor22",
};
for (auto _ : state) {
for (auto& str : kStrings) {
diff --git a/src/tint/ast/texel_format_test.cc b/src/tint/ast/texel_format_test.cc
index 606d78f..d431918 100644
--- a/src/tint/ast/texel_format_test.cc
+++ b/src/tint/ast/texel_format_test.cc
@@ -42,41 +42,41 @@
}
static constexpr Case kValidCases[] = {
- {"rgba8unorm", TexelFormat::kRgba8Unorm}, {"rgba8snorm", TexelFormat::kRgba8Snorm},
- {"rgba8uint", TexelFormat::kRgba8Uint}, {"rgba8sint", TexelFormat::kRgba8Sint},
- {"rgba16uint", TexelFormat::kRgba16Uint}, {"rgba16sint", TexelFormat::kRgba16Sint},
- {"rgba16float", TexelFormat::kRgba16Float}, {"r32uint", TexelFormat::kR32Uint},
- {"r32sint", TexelFormat::kR32Sint}, {"r32float", TexelFormat::kR32Float},
- {"rg32uint", TexelFormat::kRg32Uint}, {"rg32sint", TexelFormat::kRg32Sint},
- {"rg32float", TexelFormat::kRg32Float}, {"rgba32uint", TexelFormat::kRgba32Uint},
- {"rgba32sint", TexelFormat::kRgba32Sint}, {"rgba32float", TexelFormat::kRgba32Float},
+ {"r32float", TexelFormat::kR32Float}, {"r32sint", TexelFormat::kR32Sint},
+ {"r32uint", TexelFormat::kR32Uint}, {"rg32float", TexelFormat::kRg32Float},
+ {"rg32sint", TexelFormat::kRg32Sint}, {"rg32uint", TexelFormat::kRg32Uint},
+ {"rgba16float", TexelFormat::kRgba16Float}, {"rgba16sint", TexelFormat::kRgba16Sint},
+ {"rgba16uint", TexelFormat::kRgba16Uint}, {"rgba32float", TexelFormat::kRgba32Float},
+ {"rgba32sint", TexelFormat::kRgba32Sint}, {"rgba32uint", TexelFormat::kRgba32Uint},
+ {"rgba8sint", TexelFormat::kRgba8Sint}, {"rgba8snorm", TexelFormat::kRgba8Snorm},
+ {"rgba8uint", TexelFormat::kRgba8Uint}, {"rgba8unorm", TexelFormat::kRgba8Unorm},
};
static constexpr Case kInvalidCases[] = {
- {"rgbaunccrm", TexelFormat::kInvalid}, {"rlbanr3", TexelFormat::kInvalid},
- {"rVba8unorm", TexelFormat::kInvalid}, {"rgba1snorm", TexelFormat::kInvalid},
- {"rgbJqqnorm", TexelFormat::kInvalid}, {"rgb7ll8snorm", TexelFormat::kInvalid},
- {"rgbauippqHH", TexelFormat::kInvalid}, {"rgbaun", TexelFormat::kInvalid},
- {"rba8Gint", TexelFormat::kInvalid}, {"rgvia8sint", TexelFormat::kInvalid},
- {"rgba8WWint", TexelFormat::kInvalid}, {"rgbasxxMt", TexelFormat::kInvalid},
- {"rXba16ungg", TexelFormat::kInvalid}, {"rba1XuVt", TexelFormat::kInvalid},
- {"rgba16uin3", TexelFormat::kInvalid}, {"rgba16sinE", TexelFormat::kInvalid},
- {"TTgba16sPPn", TexelFormat::kInvalid}, {"rgbad6xxint", TexelFormat::kInvalid},
- {"rgba446float", TexelFormat::kInvalid}, {"SSVVba16float", TexelFormat::kInvalid},
- {"rgbRR6float", TexelFormat::kInvalid}, {"rFui9t", TexelFormat::kInvalid},
- {"r32int", TexelFormat::kInvalid}, {"VOORRHnt", TexelFormat::kInvalid},
- {"r3siyt", TexelFormat::kInvalid}, {"lln3rrs77nt", TexelFormat::kInvalid},
- {"r32s4n00", TexelFormat::kInvalid}, {"32ooat", TexelFormat::kInvalid},
- {"r32fzzt", TexelFormat::kInvalid}, {"r3iippl1a", TexelFormat::kInvalid},
- {"XXg32uint", TexelFormat::kInvalid}, {"rII39955nnnt", TexelFormat::kInvalid},
- {"aagHH2uinYSS", TexelFormat::kInvalid}, {"rkk3it", TexelFormat::kInvalid},
- {"gj3sRRn", TexelFormat::kInvalid}, {"r3bsnt", TexelFormat::kInvalid},
- {"rg32flojt", TexelFormat::kInvalid}, {"r32floa", TexelFormat::kInvalid},
- {"rg32lot", TexelFormat::kInvalid}, {"rgb3uit", TexelFormat::kInvalid},
- {"rgjj3uint", TexelFormat::kInvalid}, {"rgb2urnff", TexelFormat::kInvalid},
- {"rgba32sijt", TexelFormat::kInvalid}, {"NNgba32ww2t", TexelFormat::kInvalid},
- {"rgba32snt", TexelFormat::kInvalid}, {"rgba32rrloat", TexelFormat::kInvalid},
- {"rgGa32float", TexelFormat::kInvalid}, {"FFgba32float", TexelFormat::kInvalid},
+ {"rcc2flot", TexelFormat::kInvalid}, {"3flo3", TexelFormat::kInvalid},
+ {"r32flVat", TexelFormat::kInvalid}, {"r32s1nt", TexelFormat::kInvalid},
+ {"rq2Jint", TexelFormat::kInvalid}, {"r32sin7ll", TexelFormat::kInvalid},
+ {"ppqq2unHH", TexelFormat::kInvalid}, {"r3cv", TexelFormat::kInvalid},
+ {"b2uiGt", TexelFormat::kInvalid}, {"rgvi2float", TexelFormat::kInvalid},
+ {"rg328WWoat", TexelFormat::kInvalid}, {"rg32lxxMt", TexelFormat::kInvalid},
+ {"rgXggsnt", TexelFormat::kInvalid}, {"rgXsnu", TexelFormat::kInvalid},
+ {"rg32s3nt", TexelFormat::kInvalid}, {"rg3Euint", TexelFormat::kInvalid},
+ {"PP32TTint", TexelFormat::kInvalid}, {"xxg32ddnt", TexelFormat::kInvalid},
+ {"rgba446float", TexelFormat::kInvalid}, {"SSVVba16float", TexelFormat::kInvalid},
+ {"rgbRR6float", TexelFormat::kInvalid}, {"rga16Fint", TexelFormat::kInvalid},
+ {"rgb16sint", TexelFormat::kInvalid}, {"ORVHa16sint", TexelFormat::kInvalid},
+ {"ryba1uint", TexelFormat::kInvalid}, {"r77ba1nnullrrt", TexelFormat::kInvalid},
+ {"rgb4006uint", TexelFormat::kInvalid}, {"rboofloat", TexelFormat::kInvalid},
+ {"rgbaz2loat", TexelFormat::kInvalid}, {"ppga3ii1floa", TexelFormat::kInvalid},
+ {"XXgba32sint", TexelFormat::kInvalid}, {"IIgb9932nni55t", TexelFormat::kInvalid},
+ {"rYbaSSrrsiHHat", TexelFormat::kInvalid}, {"rbkk2Hit", TexelFormat::kInvalid},
+ {"jgba3ugRR", TexelFormat::kInvalid}, {"rgbab2ui", TexelFormat::kInvalid},
+ {"rgba8sijt", TexelFormat::kInvalid}, {"rba8sint", TexelFormat::kInvalid},
+ {"rba8sqt", TexelFormat::kInvalid}, {"rgba8NNnom", TexelFormat::kInvalid},
+ {"rga8vvorm", TexelFormat::kInvalid}, {"rgba8snorQ", TexelFormat::kInvalid},
+ {"rgbauirf", TexelFormat::kInvalid}, {"rgbajuint", TexelFormat::kInvalid},
+ {"wNNgbauin2", TexelFormat::kInvalid}, {"rgba8unrm", TexelFormat::kInvalid},
+ {"rgba8urrorm", TexelFormat::kInvalid}, {"rgba8Gnorm", TexelFormat::kInvalid},
};
using TexelFormatParseTest = testing::TestWithParam<Case>;
diff --git a/src/tint/inspector/entry_point.cc b/src/tint/inspector/entry_point.cc
index 6d3419f..8cbca14 100644
--- a/src/tint/inspector/entry_point.cc
+++ b/src/tint/inspector/entry_point.cc
@@ -35,32 +35,5 @@
EntryPoint::EntryPoint(EntryPoint&&) = default;
EntryPoint::~EntryPoint() = default;
-InterpolationType ASTToInspectorInterpolationType(ast::InterpolationType ast_type) {
- switch (ast_type) {
- case ast::InterpolationType::kPerspective:
- return InterpolationType::kPerspective;
- case ast::InterpolationType::kLinear:
- return InterpolationType::kLinear;
- case ast::InterpolationType::kFlat:
- return InterpolationType::kFlat;
- }
-
- return InterpolationType::kUnknown;
-}
-
-InterpolationSampling ASTToInspectorInterpolationSampling(ast::InterpolationSampling sampling) {
- switch (sampling) {
- case ast::InterpolationSampling::kNone:
- return InterpolationSampling::kNone;
- case ast::InterpolationSampling::kCenter:
- return InterpolationSampling::kCenter;
- case ast::InterpolationSampling::kCentroid:
- return InterpolationSampling::kCentroid;
- case ast::InterpolationSampling::kSample:
- return InterpolationSampling::kSample;
- }
-
- return InterpolationSampling::kUnknown;
-}
} // namespace tint::inspector
diff --git a/src/tint/inspector/entry_point.h b/src/tint/inspector/entry_point.h
index 5d119d4..4a4706b 100644
--- a/src/tint/inspector/entry_point.h
+++ b/src/tint/inspector/entry_point.h
@@ -84,17 +84,6 @@
InterpolationSampling interpolation_sampling = InterpolationSampling::kUnknown;
};
-/// Convert from internal ast::InterpolationType to public ::InterpolationType.
-/// @param ast_type internal value to convert from
-/// @returns the publicly visible equivalent
-InterpolationType ASTToInspectorInterpolationType(ast::InterpolationType ast_type);
-
-/// Convert from internal ast::InterpolationSampling to public
-/// ::InterpolationSampling
-/// @param sampling internal value to convert from
-/// @returns the publicly visible equivalent
-InterpolationSampling ASTToInspectorInterpolationSampling(ast::InterpolationSampling sampling);
-
/// Reflection data about an override variable referenced by an entry point
struct Override {
/// Name of the override
@@ -175,6 +164,8 @@
bool sample_index_used = false;
/// Does the entry point use the num_workgroups builtin
bool num_workgroups_used = false;
+ /// Does the entry point use the frag_depth builtin
+ bool frag_depth_used = false;
};
} // namespace tint::inspector
diff --git a/src/tint/inspector/inspector.cc b/src/tint/inspector/inspector.cc
index 3520ae3..ddb5b11 100644
--- a/src/tint/inspector/inspector.cc
+++ b/src/tint/inspector/inspector.cc
@@ -117,14 +117,45 @@
return {InterpolationType::kPerspective, InterpolationSampling::kCenter};
}
- auto interpolation_type = interpolation_attribute->type;
- auto sampling = interpolation_attribute->sampling;
- if (interpolation_type != ast::InterpolationType::kFlat &&
- sampling == ast::InterpolationSampling::kNone) {
- sampling = ast::InterpolationSampling::kCenter;
+ auto ast_interpolation_type = interpolation_attribute->type;
+ auto ast_sampling_type = interpolation_attribute->sampling;
+ if (ast_interpolation_type != ast::InterpolationType::kFlat &&
+ ast_sampling_type == ast::InterpolationSampling::kInvalid) {
+ ast_sampling_type = ast::InterpolationSampling::kCenter;
}
- return {ASTToInspectorInterpolationType(interpolation_type),
- ASTToInspectorInterpolationSampling(sampling)};
+
+ auto interpolation_type = InterpolationType::kUnknown;
+ switch (ast_interpolation_type) {
+ case ast::InterpolationType::kPerspective:
+ interpolation_type = InterpolationType::kPerspective;
+ break;
+ case ast::InterpolationType::kLinear:
+ interpolation_type = InterpolationType::kLinear;
+ break;
+ case ast::InterpolationType::kFlat:
+ interpolation_type = InterpolationType::kFlat;
+ break;
+ case ast::InterpolationType::kInvalid:
+ break;
+ }
+
+ auto sampling_type = InterpolationSampling::kUnknown;
+ switch (ast_sampling_type) {
+ case ast::InterpolationSampling::kInvalid:
+ sampling_type = InterpolationSampling::kNone;
+ break;
+ case ast::InterpolationSampling::kCenter:
+ sampling_type = InterpolationSampling::kCenter;
+ break;
+ case ast::InterpolationSampling::kCentroid:
+ sampling_type = InterpolationSampling::kCentroid;
+ break;
+ case ast::InterpolationSampling::kSample:
+ sampling_type = InterpolationSampling::kSample;
+ break;
+ }
+
+ return {interpolation_type, sampling_type};
}
} // namespace
@@ -192,6 +223,8 @@
entry_point.output_sample_mask_used = ContainsBuiltin(
ast::BuiltinValue::kSampleMask, sem->ReturnType(), func->return_type_attributes);
+ entry_point.frag_depth_used = ContainsBuiltin(
+ ast::BuiltinValue::kFragDepth, sem->ReturnType(), func->return_type_attributes);
}
for (auto* var : sem->TransitivelyReferencedGlobals()) {
diff --git a/src/tint/inspector/inspector_test.cc b/src/tint/inspector/inspector_test.cc
index 7e81ba0..7aa017c 100644
--- a/src/tint/inspector/inspector_test.cc
+++ b/src/tint/inspector/inspector_test.cc
@@ -852,6 +852,7 @@
EXPECT_FALSE(result[0].front_facing_used);
EXPECT_FALSE(result[0].sample_index_used);
EXPECT_FALSE(result[0].num_workgroups_used);
+ EXPECT_FALSE(result[0].frag_depth_used);
}
TEST_F(InspectorGetEntryPointTest, InputSampleMaskSimpleReferenced) {
@@ -1134,6 +1135,49 @@
EXPECT_TRUE(result[0].num_workgroups_used);
}
+TEST_F(InspectorGetEntryPointTest, FragDepthSimpleReferenced) {
+ Func("ep_func", {}, ty.f32(),
+ utils::Vector{
+ Return(Expr(0_f)),
+ },
+ utils::Vector{
+ Stage(ast::PipelineStage::kFragment),
+ },
+ utils::Vector{
+ Builtin(ast::BuiltinValue::kFragDepth),
+ });
+
+ Inspector& inspector = Build();
+
+ auto result = inspector.GetEntryPoints();
+
+ ASSERT_EQ(1u, result.size());
+ EXPECT_TRUE(result[0].frag_depth_used);
+}
+
+TEST_F(InspectorGetEntryPointTest, FragDepthStructReferenced) {
+ Structure("out_struct", utils::Vector{
+ Member("inner_frag_depth", ty.f32(),
+ utils::Vector{Builtin(ast::BuiltinValue::kFragDepth)}),
+ });
+
+ Func("ep_func", utils::Empty, ty.type_name("out_struct"),
+ utils::Vector{
+ Decl(Var("out_var", ty.type_name("out_struct"))),
+ Return("out_var"),
+ },
+ utils::Vector{
+ Stage(ast::PipelineStage::kFragment),
+ });
+
+ Inspector& inspector = Build();
+
+ auto result = inspector.GetEntryPoints();
+
+ ASSERT_EQ(1u, result.size());
+ EXPECT_TRUE(result[0].frag_depth_used);
+}
+
TEST_F(InspectorGetEntryPointTest, ImplicitInterpolate) {
Structure("in_struct", utils::Vector{
Member("struct_inner", ty.f32(), utils::Vector{Location(0_a)}),
@@ -1206,7 +1250,7 @@
ast::InterpolationType::kPerspective, ast::InterpolationSampling::kSample,
InterpolationType::kPerspective, InterpolationSampling::kSample},
InspectorGetEntryPointInterpolateTestParams{
- ast::InterpolationType::kPerspective, ast::InterpolationSampling::kNone,
+ ast::InterpolationType::kPerspective, ast::InterpolationSampling::kInvalid,
InterpolationType::kPerspective, InterpolationSampling::kCenter},
InspectorGetEntryPointInterpolateTestParams{
ast::InterpolationType::kLinear, ast::InterpolationSampling::kCenter,
@@ -1218,10 +1262,10 @@
ast::InterpolationType::kLinear, ast::InterpolationSampling::kSample,
InterpolationType::kLinear, InterpolationSampling::kSample},
InspectorGetEntryPointInterpolateTestParams{
- ast::InterpolationType::kLinear, ast::InterpolationSampling::kNone,
+ ast::InterpolationType::kLinear, ast::InterpolationSampling::kInvalid,
InterpolationType::kLinear, InterpolationSampling::kCenter},
InspectorGetEntryPointInterpolateTestParams{
- ast::InterpolationType::kFlat, ast::InterpolationSampling::kNone,
+ ast::InterpolationType::kFlat, ast::InterpolationSampling::kInvalid,
InterpolationType::kFlat, InterpolationSampling::kNone}));
TEST_F(InspectorGetOverrideDefaultValuesTest, Bool) {
diff --git a/src/tint/intrinsics.def b/src/tint/intrinsics.def
index f55b18c..493d224 100644
--- a/src/tint/intrinsics.def
+++ b/src/tint/intrinsics.def
@@ -94,6 +94,20 @@
rgba32float
}
+// https://www.w3.org/TR/WGSL/#interpolation
+enum interpolation_type {
+ perspective
+ linear
+ flat
+}
+
+// https://www.w3.org/TR/WGSL/#interpolation
+enum interpolation_sampling {
+ center
+ centroid
+ sample
+}
+
////////////////////////////////////////////////////////////////////////////////
// WGSL primitive types //
// Types may be decorated with @precedence(N) to prioritize which type //
@@ -161,13 +175,13 @@
// A type matcher that can match one or more types. //
////////////////////////////////////////////////////////////////////////////////
-match abstract_or_scalar: ia | fa | f32 | f16 | i32 | u32 | bool
-match scalar: f32 | f16 | i32 | u32 | bool
-match scalar_no_f32: i32 | f16 | u32 | bool
-match scalar_no_f16: f32 | i32 | u32 | bool
-match scalar_no_i32: f32 | f16 | u32 | bool
-match scalar_no_u32: f32 | f16 | i32 | bool
-match scalar_no_bool: f32 | f16 | i32 | u32
+match scalar: ia | fa | f32 | f16 | i32 | u32 | bool
+match concrete_scalar: f32 | f16 | i32 | u32 | bool
+match scalar_no_f32: ia | fa | i32 | f16 | u32 | bool
+match scalar_no_f16: ia | fa | f32 | i32 | u32 | bool
+match scalar_no_i32: ia | fa | f32 | f16 | u32 | bool
+match scalar_no_u32: ia | fa | f32 | f16 | i32 | bool
+match scalar_no_bool: ia | fa | f32 | f16 | i32 | u32
match fia_fiu32_f16: fa | ia | f32 | i32 | u32 | f16
match fia_fi32_f16: fa | ia | f32 | i32 | f16
match fia_fiu32: fa | ia | f32 | i32 | u32
@@ -510,9 +524,9 @@
fn round<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
fn saturate<T: f32_f16>(T) -> T
fn saturate<T: f32_f16, N: num>(vec<N, T>) -> vec<N, T>
-@const("select_bool") fn select<T: abstract_or_scalar>(T, T, bool) -> T
-@const("select_bool") fn select<T: abstract_or_scalar, N: num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T>
-@const("select_boolvec") fn select<N: num, T: abstract_or_scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
+@const("select_bool") fn select<T: scalar>(T, T, bool) -> T
+@const("select_bool") fn select<T: scalar, N: num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T>
+@const("select_boolvec") fn select<N: num, T: scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T>
fn sign<T: f32_f16>(T) -> T
fn sign<N: num, T: f32_f16>(vec<N, T>) -> vec<N, T>
fn sin<T: f32_f16>(T) -> T
@@ -706,9 +720,9 @@
@const("Zero") ctor f32() -> f32
@const("Zero") ctor f16() -> f16
@const("Zero") ctor bool() -> bool
-@const("Zero") ctor vec2<T: scalar>() -> vec2<T>
-@const("Zero") ctor vec3<T: scalar>() -> vec3<T>
-@const("Zero") ctor vec4<T: scalar>() -> vec4<T>
+@const("Zero") ctor vec2<T: concrete_scalar>() -> vec2<T>
+@const("Zero") ctor vec3<T: concrete_scalar>() -> vec3<T>
+@const("Zero") ctor vec4<T: concrete_scalar>() -> vec4<T>
@const("Zero") ctor mat2x2<T: f32_f16>() -> mat2x2<T>
@const("Zero") ctor mat2x3<T: f32_f16>() -> mat2x3<T>
@const("Zero") ctor mat2x4<T: f32_f16>() -> mat2x4<T>
@@ -725,9 +739,9 @@
@const("Identity") ctor f32(f32) -> f32
@const("Identity") ctor f16(f16) -> f16
@const("Identity") ctor bool(bool) -> bool
-@const("Identity") ctor vec2<T: scalar>(vec2<T>) -> vec2<T>
-@const("Identity") ctor vec3<T: scalar>(vec3<T>) -> vec3<T>
-@const("Identity") ctor vec4<T: scalar>(vec4<T>) -> vec4<T>
+@const("Identity") ctor vec2<T: concrete_scalar>(vec2<T>) -> vec2<T>
+@const("Identity") ctor vec3<T: concrete_scalar>(vec3<T>) -> vec3<T>
+@const("Identity") ctor vec4<T: concrete_scalar>(vec4<T>) -> vec4<T>
@const("Identity") ctor mat2x2<T: f32_f16>(mat2x2<T>) -> mat2x2<T>
@const("Identity") ctor mat2x3<T: f32_f16>(mat2x3<T>) -> mat2x3<T>
@const("Identity") ctor mat2x4<T: f32_f16>(mat2x4<T>) -> mat2x4<T>
@@ -739,24 +753,24 @@
@const("Identity") ctor mat4x4<T: f32_f16>(mat4x4<T>) -> mat4x4<T>
// Vector constructors (splat)
-@const("VecSplat") ctor vec2<T: abstract_or_scalar>(T) -> vec2<T>
-@const("VecSplat") ctor vec3<T: abstract_or_scalar>(T) -> vec3<T>
-@const("VecSplat") ctor vec4<T: abstract_or_scalar>(T) -> vec4<T>
+@const("VecSplat") ctor vec2<T: scalar>(T) -> vec2<T>
+@const("VecSplat") ctor vec3<T: scalar>(T) -> vec3<T>
+@const("VecSplat") ctor vec4<T: scalar>(T) -> vec4<T>
// Vector constructors (scalar)
-@const("VecCtorS") ctor vec2<T: abstract_or_scalar>(x: T, y: T) -> vec2<T>
-@const("VecCtorS") ctor vec3<T: abstract_or_scalar>(x: T, y: T, z: T) -> vec3<T>
-@const("VecCtorS") ctor vec4<T: abstract_or_scalar>(x: T, y: T, z: T, w: T) -> vec4<T>
+@const("VecCtorS") ctor vec2<T: scalar>(x: T, y: T) -> vec2<T>
+@const("VecCtorS") ctor vec3<T: scalar>(x: T, y: T, z: T) -> vec3<T>
+@const("VecCtorS") ctor vec4<T: scalar>(x: T, y: T, z: T, w: T) -> vec4<T>
// Vector constructors (mixed)
-@const("VecCtorM") ctor vec3<T: abstract_or_scalar>(xy: vec2<T>, z: T) -> vec3<T>
-@const("VecCtorM") ctor vec3<T: abstract_or_scalar>(x: T, yz: vec2<T>) -> vec3<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(xyz: vec3<T>, w: T) -> vec4<T>
-@const("VecCtorM") ctor vec4<T: abstract_or_scalar>(x: T, zyw: vec3<T>) -> vec4<T>
+@const("VecCtorM") ctor vec3<T: scalar>(xy: vec2<T>, z: T) -> vec3<T>
+@const("VecCtorM") ctor vec3<T: scalar>(x: T, yz: vec2<T>) -> vec3<T>
+@const("VecCtorM") ctor vec4<T: scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(xyz: vec3<T>, w: T) -> vec4<T>
+@const("VecCtorM") ctor vec4<T: scalar>(x: T, zyw: vec3<T>) -> vec4<T>
// Matrix constructors (scalar)
@const("MatCtorS")
@@ -938,11 +952,11 @@
op && (bool, bool) -> bool
op || (bool, bool) -> bool
-@const op == <T: abstract_or_scalar>(T, T) -> bool
-@const op == <T: abstract_or_scalar, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
+@const op == <T: scalar>(T, T) -> bool
+@const op == <T: scalar, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
-@const op != <T: abstract_or_scalar>(T, T) -> bool
-@const op != <T: abstract_or_scalar, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
+@const op != <T: scalar>(T, T) -> bool
+@const op != <T: scalar, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
@const op < <T: fia_fiu32_f16>(T, T) -> bool
@const op < <T: fia_fiu32_f16, N: num> (vec<N, T>, vec<N, T>) -> vec<N, bool>
diff --git a/src/tint/program_builder.h b/src/tint/program_builder.h
index 116660f..2c99b3b 100644
--- a/src/tint/program_builder.h
+++ b/src/tint/program_builder.h
@@ -173,7 +173,7 @@
const ast::Type* type = nullptr;
ast::AddressSpace address_space = ast::AddressSpace::kNone;
- ast::Access access = ast::Access::kUndefined;
+ ast::Access access = ast::Access::kInvalid;
const ast::Expression* constructor = nullptr;
utils::Vector<const ast::Attribute*, 4> attributes;
@@ -478,11 +478,6 @@
/// returned`Type` will also be destructed.
/// Types are unique (de-aliased), and so calling create() for the same `T`
/// and arguments will return the same pointer.
- /// @warning Use this method to acquire a type only if all of its type
- /// information is provided in the constructor arguments `args`.<br>
- /// If the type requires additional configuration after construction that
- /// affect its fundamental type, build the type with `std::make_unique`, make
- /// any necessary alterations and then call unique_type() instead.
/// @param args the arguments to pass to the type constructor
/// @returns the de-aliased type pointer
template <typename T, typename... ARGS>
@@ -907,7 +902,7 @@
/// @return the pointer to `type` with the given ast::AddressSpace
const ast::Pointer* pointer(const ast::Type* type,
ast::AddressSpace address_space,
- ast::Access access = ast::Access::kUndefined) const {
+ ast::Access access = ast::Access::kInvalid) const {
return builder->create<ast::Pointer>(type, address_space, access);
}
@@ -919,7 +914,7 @@
const ast::Pointer* pointer(const Source& source,
const ast::Type* type,
ast::AddressSpace address_space,
- ast::Access access = ast::Access::kUndefined) const {
+ ast::Access access = ast::Access::kInvalid) const {
return builder->create<ast::Pointer>(source, type, address_space, access);
}
@@ -928,7 +923,7 @@
/// @return the pointer to type `T` with the given ast::AddressSpace.
template <typename T>
const ast::Pointer* pointer(ast::AddressSpace address_space,
- ast::Access access = ast::Access::kUndefined) const {
+ ast::Access access = ast::Access::kInvalid) const {
return pointer(Of<T>(), address_space, access);
}
@@ -939,7 +934,7 @@
template <typename T>
const ast::Pointer* pointer(const Source& source,
ast::AddressSpace address_space,
- ast::Access access = ast::Access::kUndefined) const {
+ ast::Access access = ast::Access::kInvalid) const {
return pointer(source, Of<T>(), address_space, access);
}
@@ -2914,7 +2909,7 @@
const ast::InterpolateAttribute* Interpolate(
const Source& source,
ast::InterpolationType type,
- ast::InterpolationSampling sampling = ast::InterpolationSampling::kNone) {
+ ast::InterpolationSampling sampling = ast::InterpolationSampling::kInvalid) {
return create<ast::InterpolateAttribute>(source, type, sampling);
}
@@ -2924,7 +2919,7 @@
/// @returns the interpolate attribute pointer
const ast::InterpolateAttribute* Interpolate(
ast::InterpolationType type,
- ast::InterpolationSampling sampling = ast::InterpolationSampling::kNone) {
+ ast::InterpolationSampling sampling = ast::InterpolationSampling::kInvalid) {
return create<ast::InterpolateAttribute>(source_, type, sampling);
}
diff --git a/src/tint/reader/spirv/function.cc b/src/tint/reader/spirv/function.cc
index 4b7b3d3..7bbb5df 100644
--- a/src/tint/reader/spirv/function.cc
+++ b/src/tint/reader/spirv/function.cc
@@ -327,6 +327,8 @@
return "exp2";
case GLSLstd450FaceForward:
return "faceForward";
+ case GLSLstd450FindILsb:
+ return "firstTrailingBit";
case GLSLstd450Floor:
return "floor";
case GLSLstd450Fma:
@@ -427,7 +429,6 @@
case GLSLstd450PackDouble2x32:
case GLSLstd450UnpackDouble2x32:
- case GLSLstd450FindILsb:
case GLSLstd450FindSMsb:
case GLSLstd450FindUMsb:
@@ -2557,6 +2558,8 @@
}
auto type_it = identifier_types_.find(id);
if (type_it != identifier_types_.end()) {
+ // We have a local named definition: function parameter, let, or var
+ // declaration.
auto name = namer_.Name(id);
auto* type = type_it->second;
return TypedExpression{
@@ -2585,10 +2588,13 @@
switch (inst->opcode()) {
case SpvOpVariable: {
// This occurs for module-scope variables.
- auto name = namer_.Name(inst->result_id());
- return TypedExpression{
- parser_impl_.ConvertType(inst->type_id(), PtrAs::Ref),
- create<ast::IdentifierExpression>(Source{}, builder_.Symbols().Register(name))};
+ auto name = namer_.Name(id);
+ // Construct the reference type, mapping storage class correctly.
+ const auto* type =
+ RemapPointerProperties(parser_impl_.ConvertType(inst->type_id(), PtrAs::Ref), id);
+ // TODO(crbug.com/tint/1041): Fix access mode
+ return TypedExpression{type, create<ast::IdentifierExpression>(
+ Source{}, builder_.Symbols().Register(name))};
}
case SpvOpUndef:
// Substitute a null value for undef.
@@ -3356,11 +3362,13 @@
for (auto id : sorted_by_index(block_info.hoisted_ids)) {
const auto* def_inst = def_use_mgr_->GetDef(id);
TINT_ASSERT(Reader, def_inst);
- auto* storage_type = RemapAddressSpace(parser_impl_.ConvertType(def_inst->type_id()), id);
+ // Compute the store type. Pointers are not storable, so there is
+ // no need to remap pointer properties.
+ auto* store_type = parser_impl_.ConvertType(def_inst->type_id());
AddStatement(create<ast::VariableDeclStatement>(
- Source{}, parser_impl_.MakeVar(id, ast::AddressSpace::kNone, storage_type, nullptr,
+ Source{}, parser_impl_.MakeVar(id, ast::AddressSpace::kNone, store_type, nullptr,
AttributeList{})));
- auto* type = ty_.Reference(storage_type, ast::AddressSpace::kNone);
+ auto* type = ty_.Reference(store_type, ast::AddressSpace::kNone);
identifier_types_.emplace(id, type);
}
@@ -3449,6 +3457,7 @@
}
expr = AddressOfIfNeeded(expr, &inst);
+ expr.type = RemapPointerProperties(expr.type, inst.result_id());
auto* let = parser_impl_.MakeLet(inst.result_id(), expr.type, expr.expr);
if (!let) {
return false;
@@ -3720,7 +3729,6 @@
if (!expr) {
return false;
}
- expr.type = RemapAddressSpace(expr.type, result_id);
return EmitConstDefOrWriteToHoistedVar(inst, expr);
}
@@ -3777,20 +3785,6 @@
return parser_impl_.RectifyOperandSignedness(inst, std::move(expr));
}
-TypedExpression FunctionEmitter::InferFunctionAddressSpace(TypedExpression expr) {
- TypedExpression result(expr);
- if (const auto* ref = expr.type->UnwrapAlias()->As<Reference>()) {
- if (ref->address_space == ast::AddressSpace::kNone) {
- expr.type = ty_.Reference(ref->type, ast::AddressSpace::kFunction);
- }
- } else if (const auto* ptr = expr.type->UnwrapAlias()->As<Pointer>()) {
- if (ptr->address_space == ast::AddressSpace::kNone) {
- expr.type = ty_.Pointer(ptr->type, ast::AddressSpace::kFunction);
- }
- }
- return expr;
-}
-
TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue(
const spvtools::opt::Instruction& inst) {
if (inst.result_id() == 0) {
@@ -4350,6 +4344,10 @@
const auto num_in_operands = inst.NumInOperands();
bool sink_pointer = false;
+ // The current WGSL expression for the pointer, starting with the base
+ // pointer and updated as each index is incorported. The important part
+ // is the pointee (or "store type"). The address space and access mode will
+ // be patched as needed at the very end, via RemapPointerProperties.
TypedExpression current_expr;
// If the variable was originally gl_PerVertex, then in the AST we
@@ -4418,7 +4416,7 @@
// ever-deeper nested indexing expressions. Start off with an expression
// for the base, and then bury that inside nested indexing expressions.
if (!current_expr) {
- current_expr = InferFunctionAddressSpace(MakeOperand(inst, 0));
+ current_expr = MakeOperand(inst, 0);
if (current_expr.type->Is<Pointer>()) {
current_expr = Dereference(current_expr);
}
@@ -4533,6 +4531,7 @@
GetDefInfo(inst.result_id())->sink_pointer_source_expr = current_expr;
}
+ current_expr.type = RemapPointerProperties(current_expr.type, inst.result_id());
return current_expr;
}
@@ -4799,32 +4798,27 @@
++index;
auto& info = def_info_[result_id];
- // Determine address space for pointer values. Do this in order because
- // we might rely on the address space for a previously-visited definition.
- // Logical pointers can't be transmitted through OpPhi, so remaining
- // pointer definitions are SSA values, and their definitions must be
- // visited before their uses.
const auto* type = type_mgr_->GetType(inst.type_id());
if (type) {
+ // Determine address space and access mode for pointer values. Do this in
+ // order because we might rely on the storage class for a previously-visited
+ // definition.
+ // Logical pointers can't be transmitted through OpPhi, so remaining
+ // pointer definitions are SSA values, and their definitions must be
+ // visited before their uses.
if (type->AsPointer()) {
- if (auto* ast_type = parser_impl_.ConvertType(inst.type_id())) {
- if (auto* ptr = ast_type->As<Pointer>()) {
- info->pointer.address_space = ptr->address_space;
- }
- }
switch (inst.opcode()) {
case SpvOpUndef:
return Fail() << "undef pointer is not valid: " << inst.PrettyPrint();
case SpvOpVariable:
- // Keep the default decision based on the result type.
+ info->pointer = GetPointerInfo(result_id);
break;
case SpvOpAccessChain:
case SpvOpInBoundsAccessChain:
case SpvOpCopyObject:
// Inherit from the first operand. We need this so we can pick up
// a remapped storage buffer.
- info->pointer.address_space =
- GetAddressSpaceForPointerValue(inst.GetSingleWordInOperand(0));
+ info->pointer = GetPointerInfo(inst.GetSingleWordInOperand(0));
break;
default:
return Fail() << "pointer defined in function from unknown opcode: "
@@ -4846,32 +4840,71 @@
return true;
}
-ast::AddressSpace FunctionEmitter::GetAddressSpaceForPointerValue(uint32_t id) {
+DefInfo::Pointer FunctionEmitter::GetPointerInfo(uint32_t id) {
+ // Compute the result from first principles, for a variable.
+ auto get_from_root_identifier =
+ [&](const spvtools::opt::Instruction& inst) -> DefInfo::Pointer {
+ // WGSL root identifiers (or SPIR-V "memory object declarations") are
+ // either variables or function parameters.
+ switch (inst.opcode()) {
+ case SpvOpVariable: {
+ if (const auto* module_var = parser_impl_.GetModuleVariable(id)) {
+ return DefInfo::Pointer{module_var->declared_address_space,
+ module_var->declared_access};
+ }
+ // Local variables are always Function storage class, with default
+ // access mode.
+ return DefInfo::Pointer{ast::AddressSpace::kFunction, ast::Access::kInvalid};
+ }
+ case SpvOpFunctionParameter: {
+ const auto* type = As<Pointer>(parser_impl_.ConvertType(inst.type_id()));
+ // TODO(crbug.com/tint/1041): Add access mode.
+ // Using kUndefined is ok for now, since the only non-default access mode
+ // on a pointer would be for a storage buffer, and baseline SPIR-V doesn't
+ // allow passing pointers to buffers as function parameters.
+ return DefInfo::Pointer{type->address_space, ast::Access::kInvalid};
+ }
+ default:
+ break;
+ }
+ TINT_ASSERT(Reader, false && "expected a memory object declaration");
+ return {};
+ };
+
auto where = def_info_.find(id);
if (where != def_info_.end()) {
- auto candidate = where->second.get()->pointer.address_space;
- if (candidate != ast::AddressSpace::kInvalid) {
- return candidate;
+ const auto& info = where->second;
+ if (info->inst.opcode() == SpvOpVariable) {
+ // Ignore the cache in this case and compute it from scratch.
+ // That's because for a function-scope OpVariable is a
+ // locally-defined value. So its cache entry has been created
+ // with a default PointerInfo object, which has invalid data.
+ //
+ // Instead, you might think that we could forget this weirdness
+ // and instead have more standard cache-like behaviour. But then
+ // for non-function-scope variables we look up information
+ // from a saved ast::Var. But some builtins don't correspond
+ // to a declared ast::Var. This is simpler and more reliable.
+ return get_from_root_identifier(info->inst);
}
+ // Use the cached value.
+ return info->pointer;
}
- const auto type_id = def_use_mgr_->GetDef(id)->type_id();
- if (type_id) {
- auto* ast_type = parser_impl_.ConvertType(type_id);
- if (auto* ptr = As<Pointer>(ast_type)) {
- return ptr->address_space;
- }
- }
- return ast::AddressSpace::kInvalid;
+ const auto* inst = def_use_mgr_->GetDef(id);
+ TINT_ASSERT(Reader, inst);
+ return get_from_root_identifier(*inst);
}
-const Type* FunctionEmitter::RemapAddressSpace(const Type* type, uint32_t result_id) {
+const Type* FunctionEmitter::RemapPointerProperties(const Type* type, uint32_t result_id) {
if (auto* ast_ptr_type = As<Pointer>(type)) {
- // Remap an old-style storage buffer pointer to a new-style storage
- // buffer pointer.
- const auto addr_space = GetAddressSpaceForPointerValue(result_id);
- if (ast_ptr_type->address_space != addr_space) {
- return ty_.Pointer(ast_ptr_type->type, addr_space);
- }
+ const auto pi = GetPointerInfo(result_id);
+ // TODO(crbug.com/tin/t1041): also do access mode
+ return ty_.Pointer(ast_ptr_type->type, pi.address_space);
+ }
+ if (auto* ast_ptr_type = As<Reference>(type)) {
+ const auto pi = GetPointerInfo(result_id);
+ // TODO(crbug.com/tin/t1041): also do access mode
+ return ty_.Reference(ast_ptr_type->type, pi.address_space);
}
return type;
}
diff --git a/src/tint/reader/spirv/function.h b/src/tint/reader/spirv/function.h
index dc6002e..b8240cf 100644
--- a/src/tint/reader/spirv/function.h
+++ b/src/tint/reader/spirv/function.h
@@ -334,7 +334,8 @@
/// This is kInvalid for non-pointers.
ast::AddressSpace address_space = ast::AddressSpace::kInvalid;
- // TODO(crbug.com/tint/1041) track access mode.
+ /// The declared access mode.
+ ast::Access access = ast::Access::kInvalid;
};
/// The expression to use when sinking pointers into their use.
@@ -619,19 +620,23 @@
/// @returns false on failure
bool RegisterLocallyDefinedValues();
- /// Returns the Tint address space for the given SPIR-V ID that is a
- /// pointer value.
+ /// Returns the pointer information needed for the given SPIR-V ID.
+ /// Assumes the given ID yields a value of pointer type. For IDs
+ /// corresponding to WGSL root identifiers (i.e. OpVariable or
+ /// OpFunctionParameter), the info is computed from scratch.
+ /// Otherwise, this looks up pointer info from a base pointer whose
+ /// data is cached in def_info_.
/// @param id a SPIR-V ID for a pointer value
- /// @returns the address space
- ast::AddressSpace GetAddressSpaceForPointerValue(uint32_t id);
+ /// @returns the associated Pointer info
+ DefInfo::Pointer GetPointerInfo(uint32_t id);
- /// Remaps the address space for the type of a locally-defined value,
- /// if necessary. If it's not a pointer type, or if its address space
- /// already matches, then the result is a copy of the `type` argument.
+ /// Remaps the address space and access mode for the type of a
+ /// locally-defined value, if necessary. If it's not a pointer or reference
+ /// type, then the result is a copy of the `type` argument.
/// @param type the AST type
/// @param result_id the SPIR-V ID for the locally defined value
/// @returns an possibly updated type
- const Type* RemapAddressSpace(const Type* type, uint32_t result_id);
+ const Type* RemapPointerProperties(const Type* type, uint32_t result_id);
/// Marks locally defined values when they should get a 'let'
/// definition in WGSL, or a 'var' definition at an outer scope.
@@ -1011,13 +1016,6 @@
/// @returns a new expression node
TypedExpression MakeOperand(const spvtools::opt::Instruction& inst, uint32_t operand_index);
- /// Copies a typed expression to the result, but when the type is a pointer
- /// or reference type, ensures the address space is not defaulted. That is,
- /// it changes a address space of "none" to "function".
- /// @param expr a typed expression
- /// @results a copy of the expression, with possibly updated type
- TypedExpression InferFunctionAddressSpace(TypedExpression expr);
-
/// Returns an expression for a SPIR-V OpFMod instruction.
/// @param inst the SPIR-V instruction
/// @returns an expression
diff --git a/src/tint/reader/spirv/function_glsl_std_450_test.cc b/src/tint/reader/spirv/function_glsl_std_450_test.cc
index c5131e9..7a4fd94 100644
--- a/src/tint/reader/spirv/function_glsl_std_450_test.cc
+++ b/src/tint/reader/spirv/function_glsl_std_450_test.cc
@@ -179,6 +179,8 @@
SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
using SpvParserTest_GlslStd450_Inting_IntingIntingInting =
SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
+using SpvParserTest_GlslStd450_Uinting_Uinting =
+ SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
using SpvParserTest_GlslStd450_Uinting_UintingUinting =
SpvParserTestBase<::testing::TestWithParam<GlslStd450Case>>;
using SpvParserTest_GlslStd450_Uinting_UintingUintingUinting =
@@ -580,7 +582,8 @@
INSTANTIATE_TEST_SUITE_P(Samples,
SpvParserTest_GlslStd450_Inting_Inting,
- ::testing::Values(GlslStd450Case{"SAbs", "abs"}));
+ ::testing::Values(GlslStd450Case{"SAbs", "abs"},
+ GlslStd450Case{"FindILsb", "firstTrailingBit"}));
INSTANTIATE_TEST_SUITE_P(Samples,
SpvParserTest_GlslStd450_Inting_IntingInting,
@@ -591,6 +594,41 @@
SpvParserTest_GlslStd450_Inting_IntingIntingInting,
::testing::Values(GlslStd450Case{"SClamp", "clamp"}));
+TEST_P(SpvParserTest_GlslStd450_Uinting_Uinting, Scalar) {
+ const auto assembly = Preamble() + R"(
+ %1 = OpExtInst %uint %glsl )" +
+ GetParam().opcode +
+ R"( %u1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly;
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(body, HasSubstr("let x_1 : u32 = " + GetParam().wgsl_func + "(u1);")) << body;
+}
+
+TEST_P(SpvParserTest_GlslStd450_Uinting_Uinting, Vector) {
+ const auto assembly = Preamble() + R"(
+ %1 = OpExtInst %v2uint %glsl )" +
+ GetParam().opcode +
+ R"( %v2u1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(body, HasSubstr("let x_1 : vec2<u32> = " + GetParam().wgsl_func + "(v2u1);"))
+ << body;
+}
+
TEST_P(SpvParserTest_GlslStd450_Uinting_UintingUinting, Scalar) {
const auto assembly = Preamble() + R"(
%1 = OpExtInst %uint %glsl )" +
@@ -662,6 +700,10 @@
}
INSTANTIATE_TEST_SUITE_P(Samples,
+ SpvParserTest_GlslStd450_Uinting_Uinting,
+ ::testing::Values(GlslStd450Case{"FindILsb", "firstTrailingBit"}));
+
+INSTANTIATE_TEST_SUITE_P(Samples,
SpvParserTest_GlslStd450_Uinting_UintingUinting,
::testing::Values(GlslStd450Case{"UMax", "max"},
GlslStd450Case{"UMin", "min"}));
@@ -907,6 +949,34 @@
<< body;
}
+TEST_F(SpvParserTest, RectifyOperandsAndResult_FindILsb) {
+ // Check conversion of:
+ // signed results to unsigned result to match first arg.
+ // unsigned results to signed result to match first arg.
+ // This is the first extended instruction we've supported which goes both
+ // ways.
+ const auto assembly = Preamble() + R"(
+ %1 = OpExtInst %uint %glsl FindILsb %i1
+ %2 = OpExtInst %v2uint %glsl FindILsb %v2i1
+ %3 = OpExtInst %int %glsl FindILsb %u1
+ %4 = OpExtInst %v2int %glsl FindILsb %v2u1
+ OpReturn
+ OpFunctionEnd
+ )";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions());
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto body = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(body, HasSubstr(R"(
+let x_1 : u32 = bitcast<u32>(firstTrailingBit(i1));
+let x_2 : vec2<u32> = bitcast<vec2<u32>>(firstTrailingBit(v2i1));
+let x_3 : i32 = bitcast<i32>(firstTrailingBit(u1));
+let x_4 : vec2<i32> = bitcast<vec2<i32>>(firstTrailingBit(v2u1));)"))
+ << body;
+}
+
struct DataPackingCase {
std::string opcode;
std::string wgsl_func;
diff --git a/src/tint/reader/spirv/function_memory_test.cc b/src/tint/reader/spirv/function_memory_test.cc
index dbf9219..a64aac5 100644
--- a/src/tint/reader/spirv/function_memory_test.cc
+++ b/src/tint/reader/spirv/function_memory_test.cc
@@ -938,6 +938,43 @@
)"));
}
+TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughAccessChain_NonCascaded_UsedTwice) {
+ // Use the pointer value twice, which provokes the spirv-reader
+ // to make a let declaration for the pointer. The storage class
+ // must be 'storage', not 'uniform'.
+ const auto assembly = OldStorageBufferPreamble() + R"(
+ %100 = OpFunction %void None %voidfn
+ %entry = OpLabel
+
+ ; the scalar element
+ %1 = OpAccessChain %ptr_uint %myvar %uint_0
+ OpStore %1 %uint_0
+ OpStore %1 %uint_0
+
+ ; element in the runtime array
+ %2 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
+ ; Use the pointer twice
+ %3 = OpLoad %uint %2
+ OpStore %2 %uint_0
+
+ OpReturn
+ OpFunctionEnd
+)";
+ auto p = parser(test::Assemble(assembly));
+ ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
+ auto fe = p->function_emitter(100);
+ EXPECT_TRUE(fe.EmitBody()) << p->error();
+ auto ast_body = fe.ast_body();
+ const auto got = test::ToString(p->program(), ast_body);
+ EXPECT_THAT(got, HasSubstr(R"(let x_1 : ptr<storage, u32> = &(myvar.field0);
+*(x_1) = 0u;
+*(x_1) = 0u;
+let x_2 : ptr<storage, u32> = &(myvar.field1[1u]);
+let x_3 : u32 = *(x_2);
+*(x_2) = 0u;
+)"));
+}
+
TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughAccessChain_NonCascaded_InBoundsAccessChain) {
// Like the previous test, but using OpInBoundsAccessChain.
const auto assembly = OldStorageBufferPreamble() + R"(
@@ -1020,56 +1057,6 @@
p->SkipDumpingPending("crbug.com/tint/1041 track access mode in spirv-reader parser type");
}
-TEST_F(SpvParserMemoryTest, RemapStorageBuffer_ThroughCopyObject_WithHoisting) {
- // TODO(dneto): Hoisting non-storable values (pointers) is not yet supported.
- // It's debatable whether this test should run at all.
- // crbug.com/tint/98
-
- // Like the previous test, but the declaration for the copy-object
- // has its declaration hoisted.
- const auto assembly = OldStorageBufferPreamble() + R"(
- %bool = OpTypeBool
- %cond = OpConstantTrue %bool
-
- %100 = OpFunction %void None %voidfn
-
- %entry = OpLabel
- OpSelectionMerge %99 None
- OpBranchConditional %cond %20 %30
-
- %20 = OpLabel
- %1 = OpAccessChain %ptr_uint %myvar %uint_1 %uint_1
- ; this definintion dominates the use in %99
- %2 = OpCopyObject %ptr_uint %1
- OpBranch %99
-
- %30 = OpLabel
- OpReturn
-
- %99 = OpLabel
- OpStore %2 %uint_0
- OpReturn
-
- OpFunctionEnd
-)";
- auto p = parser(test::Assemble(assembly));
- ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
- auto fe = p->function_emitter(100);
- EXPECT_TRUE(fe.EmitBody()) << p->error();
- auto ast_body = fe.ast_body();
- EXPECT_EQ(test::ToString(p->program(), ast_body),
- R"(var x_2 : ptr<storage, u32>;
-if (true) {
- x_2 = &(myvar.field1[1u]);
-} else {
- return;
-}
-x_2 = 0u;
-return;
-)") << p->error();
- p->SkipDumpingPending("crbug.com/tint/98");
-}
-
std::string RuntimeArrayPreamble() {
return R"(
OpCapability Shader
diff --git a/src/tint/reader/spirv/parser_impl.cc b/src/tint/reader/spirv/parser_impl.cc
index 5aa68eb..63e5cb4 100644
--- a/src/tint/reader/spirv/parser_impl.cc
+++ b/src/tint/reader/spirv/parser_impl.cc
@@ -221,6 +221,7 @@
case GLSLstd450UMin:
case GLSLstd450UMax:
case GLSLstd450UClamp:
+ case GLSLstd450FindILsb:
// TODO(dneto): FindSMsb?
// TODO(dneto): FindUMsb?
return true;
@@ -1566,7 +1567,7 @@
return nullptr;
}
- ast::Access access = ast::Access::kUndefined;
+ ast::Access access = ast::Access::kInvalid;
if (address_space == ast::AddressSpace::kStorage) {
bool read_only = false;
if (auto* tn = storage_type->As<Named>()) {
@@ -1749,7 +1750,7 @@
AttributeList* attributes) {
// Vulkan defaults to perspective-correct interpolation.
ast::InterpolationType type = ast::InterpolationType::kPerspective;
- ast::InterpolationSampling sampling = ast::InterpolationSampling::kNone;
+ ast::InterpolationSampling sampling = ast::InterpolationSampling::kInvalid;
for (const auto& deco : decorations) {
TINT_ASSERT(Reader, deco.size() > 0);
@@ -1804,7 +1805,7 @@
// Apply interpolation.
if (type == ast::InterpolationType::kPerspective &&
- sampling == ast::InterpolationSampling::kNone) {
+ sampling == ast::InterpolationSampling::kInvalid) {
// This is the default. Don't add a decoration.
} else {
attributes->Push(create<ast::InterpolateAttribute>(type, sampling));
diff --git a/src/tint/reader/wgsl/parser_impl.cc b/src/tint/reader/wgsl/parser_impl.cc
index 50dd191..b042ac2 100644
--- a/src/tint/reader/wgsl/parser_impl.cc
+++ b/src/tint/reader/wgsl/parser_impl.cc
@@ -43,6 +43,7 @@
#include "src/tint/sem/external_texture.h"
#include "src/tint/sem/multisampled_texture.h"
#include "src/tint/sem/sampled_texture.h"
+#include "src/tint/utils/string.h"
namespace tint::reader::wgsl {
namespace {
@@ -370,40 +371,36 @@
}
// Match the extension name.
- Expect<std::string> name = {""};
auto& t = peek();
- if (t.IsIdentifier()) {
- synchronized_ = true;
- next();
- name = {t.to_str(), t.source()};
- } else if (t.Is(Token::Type::kF16)) {
- // `f16` is a valid extension name and also a keyword
- synchronized_ = true;
- next();
- name = {"f16", t.source()};
- } else if (t.Is(Token::Type::kParenLeft)) {
+ if (handle_error(t)) {
+ // The token might itself be an error.
+ return Failure::kErrored;
+ }
+
+ if (t.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");
- } else if (handle_error(t)) {
- // The token might itself be an error.
- return Failure::kErrored;
+ }
+
+ auto extension = ast::Extension::kInvalid;
+ if (t.Is(Token::Type::kF16)) {
+ // `f16` is a valid extension name and also a keyword
+ synchronized_ = true;
+ next();
+ extension = ast::Extension::kF16;
} else {
- // Failed to match an extension name.
- synchronized_ = false;
- return add_error(t.source(), "invalid extension name");
+ auto ext = expect_enum("extension", ast::ParseExtension, ast::kExtensionStrings);
+ if (ext.errored) {
+ return Failure::kErrored;
+ }
+ extension = ext.value;
}
if (!expect("enable directive", Token::Type::kSemicolon)) {
return Failure::kErrored;
}
-
- auto extension = ast::ParseExtension(name.value);
- if (extension == ast::Extension::kInvalid) {
- return add_error(name.source, "unsupported extension: '" + name.value + "'");
- }
- builder_.AST().AddEnable(create<ast::Enable>(name.source, extension));
-
+ builder_.AST().AddEnable(create<ast::Enable>(t.source(), extension));
return kSuccess;
});
@@ -763,7 +760,7 @@
return Failure::kErrored;
}
- auto access = expect_access_mode("access control");
+ auto access = expect_access_mode(use);
if (access.errored) {
return Failure::kErrored;
}
@@ -920,12 +917,7 @@
// | 'rgba32sint'
// | 'rgba32float'
Expect<ast::TexelFormat> ParserImpl::expect_texel_format(std::string_view use) {
- auto& t = next();
- auto fmt = ast::ParseTexelFormat(t.to_str());
- if (fmt == ast::TexelFormat::kInvalid) {
- return add_error(t.source(), "invalid format", use);
- }
- return fmt;
+ return expect_enum("texel format", ast::ParseTexelFormat, ast::kTexelFormatStrings, use);
}
Expect<ParserImpl::TypedIdentifier> ParserImpl::expect_ident_with_optional_type_specifier(
@@ -975,22 +967,7 @@
// | 'write'
// | 'read_write'
Expect<ast::Access> ParserImpl::expect_access_mode(std::string_view use) {
- auto ident = expect_ident(use);
- if (ident.errored) {
- return Failure::kErrored;
- }
-
- if (ident.value == "read") {
- return {ast::Access::kRead, ident.source};
- }
- if (ident.value == "write") {
- return {ast::Access::kWrite, ident.source};
- }
- if (ident.value == "read_write") {
- return {ast::Access::kReadWrite, ident.source};
- }
-
- return add_error(ident.source, "invalid value for access control");
+ return expect_enum("access control", ast::ParseAccess, ast::kAccessStrings, use);
}
// variable_qualifier
@@ -1014,7 +991,7 @@
}
return VariableQualifier{sc.value, ac.value};
}
- return Expect<VariableQualifier>{VariableQualifier{sc.value, ast::Access::kUndefined},
+ return Expect<VariableQualifier>{VariableQualifier{sc.value, ast::Access::kInvalid},
source};
});
@@ -1194,6 +1171,66 @@
return type_specifier_without_ident();
}
+template <typename ENUM, size_t N>
+Expect<ENUM> ParserImpl::expect_enum(std::string_view name,
+ ENUM (*parse)(std::string_view str),
+ const char* const (&strings)[N],
+ std::string_view use) {
+ auto& t = peek();
+ if (t.IsIdentifier()) {
+ auto val = parse(t.to_str());
+ if (val != ENUM::kInvalid) {
+ synchronized_ = true;
+ next();
+ return {val, t.source()};
+ }
+ }
+
+ // Was the token itself an error?
+ if (handle_error(t)) {
+ return Failure::kErrored;
+ }
+
+ /// Create a sensible error message
+ std::stringstream err;
+ err << "expected " << name;
+
+ if (!use.empty()) {
+ err << " for " << use;
+ }
+
+ // If the string typed was within kSuggestionDistance of one of the possible enum values,
+ // suggest that. Don't bother with suggestions if the string was extremely long.
+ constexpr size_t kSuggestionDistance = 5;
+ constexpr size_t kSuggestionMaxLength = 64;
+ if (auto got = t.to_str(); !got.empty() && got.size() < kSuggestionMaxLength) {
+ size_t candidate_dist = kSuggestionDistance;
+ const char* candidate = nullptr;
+ for (auto* str : strings) {
+ auto dist = utils::Distance(str, got);
+ if (dist < candidate_dist) {
+ candidate = str;
+ candidate_dist = dist;
+ }
+ }
+ if (candidate) {
+ err << ". Did you mean '" << candidate << "'?";
+ }
+ }
+
+ // List all the possible enumerator values
+ err << "\nPossible values: ";
+ for (auto* str : strings) {
+ if (str != strings[0]) {
+ err << ", ";
+ }
+ err << "'" << str << "'";
+ }
+
+ synchronized_ = false;
+ return add_error(t.source(), err.str());
+}
+
Expect<const ast::Type*> ParserImpl::expect_type(std::string_view use) {
auto type = type_specifier();
if (type.errored) {
@@ -1210,7 +1247,7 @@
const char* use = "ptr declaration";
auto address_space = ast::AddressSpace::kNone;
- auto access = ast::Access::kUndefined;
+ auto access = ast::Access::kInvalid;
auto subtype = expect_lt_gt_block(use, [&]() -> Expect<const ast::Type*> {
auto sc = expect_address_space(use);
@@ -1229,7 +1266,7 @@
}
if (match(Token::Type::kComma)) {
- auto ac = expect_access_mode("access control");
+ auto ac = expect_access_mode(use);
if (ac.errored) {
return Failure::kErrored;
}
@@ -1331,18 +1368,7 @@
//
// Note, we also parse `push_constant` from the experimental extension
Expect<ast::AddressSpace> ParserImpl::expect_address_space(std::string_view use) {
- auto& t = peek();
- auto ident = expect_ident("address space");
- if (ident.errored) {
- return Failure::kErrored;
- }
-
- auto address_space = ast::ParseAddressSpace(ident.value);
- if (address_space == ast::AddressSpace::kInvalid) {
- return add_error(t.source(), "invalid address space", use);
- }
-
- return {address_space, t.source()};
+ return expect_enum("address space", ast::ParseAddressSpace, ast::kAddressSpaceStrings, use);
}
// struct_decl
@@ -1605,25 +1631,12 @@
}
// interpolation_sample_name
-// : 'center'
+// : 'center'
// | 'centroid'
// | 'sample'
Expect<ast::InterpolationSampling> ParserImpl::expect_interpolation_sample_name() {
- auto ident = expect_ident("interpolation sample name");
- if (ident.errored) {
- return Failure::kErrored;
- }
-
- if (ident.value == "center") {
- return {ast::InterpolationSampling::kCenter, ident.source};
- }
- if (ident.value == "centroid") {
- return {ast::InterpolationSampling::kCentroid, ident.source};
- }
- if (ident.value == "sample") {
- return {ast::InterpolationSampling::kSample, ident.source};
- }
- return add_error(ident.source, "invalid interpolation sampling");
+ return expect_enum("interpolation sampling", ast::ParseInterpolationSampling,
+ ast::kInterpolationSamplingStrings);
}
// interpolation_type_name
@@ -1631,22 +1644,8 @@
// | 'linear'
// | 'flat'
Expect<ast::InterpolationType> ParserImpl::expect_interpolation_type_name() {
- auto ident = expect_ident("interpolation type name");
- if (ident.errored) {
- return Failure::kErrored;
- }
-
- if (ident.value == "perspective") {
- return {ast::InterpolationType::kPerspective, ident.source};
- }
- if (ident.value == "linear") {
- return {ast::InterpolationType::kLinear, ident.source};
- }
- if (ident.value == "flat") {
- return {ast::InterpolationType::kFlat, ident.source};
- }
-
- return add_error(ident.source, "invalid interpolation type");
+ return expect_enum("interpolation type", ast::ParseInterpolationType,
+ ast::kInterpolationTypeStrings);
}
// builtin_value_name
@@ -1663,17 +1662,7 @@
// | vertex_index
// | workgroup_id
Expect<ast::BuiltinValue> ParserImpl::expect_builtin() {
- auto ident = expect_ident("builtin");
- if (ident.errored) {
- return Failure::kErrored;
- }
-
- ast::BuiltinValue builtin = ast::ParseBuiltinValue(ident.value);
- if (builtin == ast::BuiltinValue::kInvalid) {
- return add_error(ident.source, "invalid value for builtin attribute");
- }
-
- return {builtin, ident.source};
+ return expect_enum("builtin", ast::ParseBuiltinValue, ast::kBuiltinValueStrings);
}
// compound_statement
@@ -3523,7 +3512,7 @@
return Failure::kErrored;
}
- ast::InterpolationSampling sampling = ast::InterpolationSampling::kNone;
+ ast::InterpolationSampling sampling = ast::InterpolationSampling::kInvalid;
if (match(Token::Type::kComma)) {
if (!peek_is(Token::Type::kParenRight)) {
auto sample = expect_interpolation_sample_name();
diff --git a/src/tint/reader/wgsl/parser_impl.h b/src/tint/reader/wgsl/parser_impl.h
index 5ae4c97..77196a0 100644
--- a/src/tint/reader/wgsl/parser_impl.h
+++ b/src/tint/reader/wgsl/parser_impl.h
@@ -296,7 +296,7 @@
/// Variable address space
ast::AddressSpace address_space = ast::AddressSpace::kNone;
/// Variable access control
- ast::Access access = ast::Access::kUndefined;
+ ast::Access access = ast::Access::kInvalid;
/// Variable type
const ast::Type* type = nullptr;
};
@@ -306,7 +306,7 @@
/// The variable's address space
ast::AddressSpace address_space = ast::AddressSpace::kNone;
/// The variable's access control
- ast::Access access = ast::Access::kUndefined;
+ ast::Access access = ast::Access::kInvalid;
};
/// MatrixDimensions contains the column and row information for a matrix
@@ -868,6 +868,18 @@
Expect<const ast::Type*> expect_type_specifier_matrix(const Source& s,
const MatrixDimensions& dims);
+ /// Parses the given enum, providing sensible error messages if the next token does not match
+ /// any of the enum values.
+ /// @param name the name of the enumerator
+ /// @param parse the optimized function used to parse the enum
+ /// @param strings the list of possible strings in the enum
+ /// @param use an optional description of what was being parsed if an error was raised.
+ template <typename ENUM, size_t N>
+ Expect<ENUM> expect_enum(std::string_view name,
+ ENUM (*parse)(std::string_view str),
+ const char* const (&strings)[N],
+ std::string_view use = "");
+
Expect<const ast::Type*> expect_type(std::string_view use);
Maybe<const ast::Statement*> non_block_statement();
diff --git a/src/tint/reader/wgsl/parser_impl_address_space_test.cc b/src/tint/reader/wgsl/parser_impl_address_space_test.cc
index d9d6d5e..251c1d0 100644
--- a/src/tint/reader/wgsl/parser_impl_address_space_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_address_space_test.cc
@@ -54,7 +54,8 @@
auto sc = p->expect_address_space("test");
EXPECT_EQ(sc.errored, true);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:1: invalid address space for test");
+ EXPECT_EQ(p->error(), R"(1:1: expected address space for test
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
} // namespace
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 ed57be8..aad8eb6 100644
--- a/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_enable_directive_test.cc
@@ -61,7 +61,21 @@
p->enable_directive();
// Error when unknown extension found
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:8: unsupported extension: 'NotAValidExtensionName'");
+ EXPECT_EQ(p->error(), R"(1:8: expected extension
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
+ auto program = p->program();
+ auto& ast = program.AST();
+ EXPECT_EQ(ast.Enables().Length(), 0u);
+ EXPECT_EQ(ast.GlobalDeclarations().Length(), 0u);
+}
+
+TEST_F(EnableDirectiveTest, InvalidIdentifierSuggest) {
+ auto p = parser("enable f15;");
+ p->enable_directive();
+ // Error when unknown extension found
+ EXPECT_TRUE(p->has_error());
+ EXPECT_EQ(p->error(), R"(1:8: expected extension. Did you mean 'f16'?
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
auto program = p->program();
auto& ast = program.AST();
EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -108,7 +122,8 @@
auto p = parser("enable <f16;");
p->translation_unit();
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:8: invalid extension name");
+ EXPECT_EQ(p->error(), R"(1:8: expected extension
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
auto program = p->program();
auto& ast = program.AST();
EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -118,7 +133,8 @@
auto p = parser("enable =;");
p->translation_unit();
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:8: invalid extension name");
+ EXPECT_EQ(p->error(), R"(1:8: expected extension
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
auto program = p->program();
auto& ast = program.AST();
EXPECT_EQ(ast.Enables().Length(), 0u);
@@ -128,7 +144,8 @@
auto p = parser("enable vec4;");
p->translation_unit();
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:8: invalid extension name");
+ EXPECT_EQ(p->error(), R"(1:8: expected extension
+Possible values: 'chromium_disable_uniformity_analysis', 'chromium_experimental_dp4a', 'chromium_experimental_push_constant', 'f16')");
auto program = p->program();
auto& ast = program.AST();
EXPECT_EQ(ast.Enables().Length(), 0u);
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 7ea5aaa..af9734c 100644
--- a/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_error_msg_test.cc
@@ -765,7 +765,8 @@
}
TEST_F(ParserImplErrorTest, GlobalDeclStaticAssertMissingLParen) {
- EXPECT("static_assert true);", R"(test.wgsl:1:19 error: expected ';' for static assertion declaration
+ EXPECT("static_assert true);",
+ R"(test.wgsl:1:19 error: expected ';' for static assertion declaration
static_assert true);
^
)");
@@ -804,7 +805,8 @@
TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingSubtype) {
EXPECT("var x : texture_storage_2d<>;",
- R"(test.wgsl:1:28 error: invalid format for storage texture type
+ R"(test.wgsl:1:28 error: expected texel format for storage texture type
+Possible values: 'r32float', 'r32sint', 'r32uint', 'rg32float', 'rg32sint', 'rg32uint', 'rgba16float', 'rgba16sint', 'rgba16uint', 'rgba32float', 'rgba32sint', 'rgba32uint', 'rgba8sint', 'rgba8snorm', 'rgba8uint', 'rgba8unorm'
var x : texture_storage_2d<>;
^
)");
@@ -812,7 +814,8 @@
TEST_F(ParserImplErrorTest, GlobalDeclStorageTextureMissingInvalidSubtype) {
EXPECT("var x : texture_storage_2d<1>;",
- R"(test.wgsl:1:28 error: invalid format for storage texture type
+ R"(test.wgsl:1:28 error: expected texel format for storage texture type
+Possible values: 'r32float', 'r32sint', 'r32uint', 'rg32float', 'rg32sint', 'rg32uint', 'rgba16float', 'rgba16sint', 'rgba16uint', 'rgba32float', 'rgba32sint', 'rgba32uint', 'rgba8sint', 'rgba8snorm', 'rgba8uint', 'rgba8unorm'
var x : texture_storage_2d<1>;
^
)");
@@ -989,17 +992,19 @@
TEST_F(ParserImplErrorTest, GlobalDeclVarAttrBuiltinInvalidIdentifer) {
EXPECT("@builtin(1) var i : i32;",
- R"(test.wgsl:1:10 error: expected identifier for builtin
+ 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(x) var i : i32;",
- R"(test.wgsl:1:10 error: invalid value for builtin attribute
-@builtin(x) var i : i32;
- ^
+ 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;
+ ^^^^^^^^^^
)");
}
@@ -1107,7 +1112,8 @@
TEST_F(ParserImplErrorTest, GlobalDeclVarPtrMissingAddressSpace) {
EXPECT("var i : ptr<meow, u32>;",
- R"(test.wgsl:1:13 error: invalid address space for ptr declaration
+ R"(test.wgsl:1:13 error: expected address space for ptr declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup'
var i : ptr<meow, u32>;
^^^^
)");
@@ -1139,7 +1145,8 @@
TEST_F(ParserImplErrorTest, GlobalDeclVarStorageDeclInvalidClass) {
EXPECT("var<fish> i : i32",
- R"(test.wgsl:1:5 error: invalid address space for variable declaration
+ R"(test.wgsl:1:5 error: expected address space for variable declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup'
var<fish> i : i32
^^^^
)");
diff --git a/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
index d6e475a..388ad67 100644
--- a/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_global_variable_decl_test.cc
@@ -165,7 +165,8 @@
EXPECT_TRUE(e.errored);
EXPECT_FALSE(e.matched);
EXPECT_EQ(e.value, nullptr);
- EXPECT_EQ(p->error(), "1:5: invalid address space for variable declaration");
+ EXPECT_EQ(p->error(), R"(1:5: expected address space for variable declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
} // namespace
diff --git a/src/tint/reader/wgsl/parser_impl_texel_format_test.cc b/src/tint/reader/wgsl/parser_impl_texel_format_test.cc
index e7afb9e..1697359 100644
--- a/src/tint/reader/wgsl/parser_impl_texel_format_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_texel_format_test.cc
@@ -17,15 +17,16 @@
namespace tint::reader::wgsl {
namespace {
-TEST_F(ParserImplTest, ImageStorageType_Invalid) {
+TEST_F(ParserImplTest, TexelFormat_Invalid) {
auto p = parser("1234");
auto t = p->expect_texel_format("test");
EXPECT_TRUE(t.errored);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:1: invalid format for test");
+ EXPECT_EQ(p->error(), R"(1:1: expected texel format for test
+Possible values: 'r32float', 'r32sint', 'r32uint', 'rg32float', 'rg32sint', 'rg32uint', 'rgba16float', 'rgba16sint', 'rgba16uint', 'rgba32float', 'rgba32sint', 'rgba32uint', 'rgba8sint', 'rgba8snorm', 'rgba8uint', 'rgba8unorm')");
}
-TEST_F(ParserImplTest, ImageStorageType_R32Uint) {
+TEST_F(ParserImplTest, TexelFormat_R32Uint) {
auto p = parser("r32uint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -33,7 +34,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_R32Sint) {
+TEST_F(ParserImplTest, TexelFormat_R32Sint) {
auto p = parser("r32sint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -41,7 +42,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_R32Float) {
+TEST_F(ParserImplTest, TexelFormat_R32Float) {
auto p = parser("r32float");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -49,7 +50,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba8Unorm) {
+TEST_F(ParserImplTest, TexelFormat_Rgba8Unorm) {
auto p = parser("rgba8unorm");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -57,7 +58,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba8Snorm) {
+TEST_F(ParserImplTest, TexelFormat_Rgba8Snorm) {
auto p = parser("rgba8snorm");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -65,7 +66,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba8Uint) {
+TEST_F(ParserImplTest, TexelFormat_Rgba8Uint) {
auto p = parser("rgba8uint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -73,7 +74,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba8Sint) {
+TEST_F(ParserImplTest, TexelFormat_Rgba8Sint) {
auto p = parser("rgba8sint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -81,7 +82,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rg32Uint) {
+TEST_F(ParserImplTest, TexelFormat_Rg32Uint) {
auto p = parser("rg32uint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -89,7 +90,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rg32Sint) {
+TEST_F(ParserImplTest, TexelFormat_Rg32Sint) {
auto p = parser("rg32sint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -97,7 +98,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rg32Float) {
+TEST_F(ParserImplTest, TexelFormat_Rg32Float) {
auto p = parser("rg32float");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -105,7 +106,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba16Uint) {
+TEST_F(ParserImplTest, TexelFormat_Rgba16Uint) {
auto p = parser("rgba16uint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -113,7 +114,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba16Sint) {
+TEST_F(ParserImplTest, TexelFormat_Rgba16Sint) {
auto p = parser("rgba16sint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -121,7 +122,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba16Float) {
+TEST_F(ParserImplTest, TexelFormat_Rgba16Float) {
auto p = parser("rgba16float");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -129,7 +130,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba32Uint) {
+TEST_F(ParserImplTest, TexelFormat_Rgba32Uint) {
auto p = parser("rgba32uint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -137,7 +138,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba32Sint) {
+TEST_F(ParserImplTest, TexelFormat_Rgba32Sint) {
auto p = parser("rgba32sint");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
@@ -145,7 +146,7 @@
EXPECT_FALSE(p->has_error());
}
-TEST_F(ParserImplTest, ImageStorageType_Rgba32Float) {
+TEST_F(ParserImplTest, TexelFormat_Rgba32Float) {
auto p = parser("rgba32float");
auto t = p->expect_texel_format("test");
EXPECT_FALSE(t.errored);
diff --git a/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc b/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
index 1143c52..ce31276 100644
--- a/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_texture_sampler_test.cc
@@ -218,7 +218,19 @@
EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
- EXPECT_EQ(p->error(), "1:20: invalid format for storage texture type");
+ EXPECT_EQ(p->error(), R"(1:20: expected texel format for storage texture type
+Possible values: 'r32float', 'r32sint', 'r32uint', 'rg32float', 'rg32sint', 'rg32uint', 'rgba16float', 'rgba16sint', 'rgba16uint', 'rgba32float', 'rgba32sint', 'rgba32uint', 'rgba8sint', 'rgba8snorm', 'rgba8uint', 'rgba8unorm')");
+}
+
+TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidTypeSuggest) {
+ auto p = parser("texture_storage_1d<rg32_float, read>");
+ auto t = p->texture_and_sampler_types();
+ EXPECT_EQ(t.value, nullptr);
+ EXPECT_FALSE(t.matched);
+ EXPECT_TRUE(t.errored);
+ EXPECT_EQ(p->error(),
+ R"(1:20: expected texel format for storage texture type. Did you mean 'rg32float'?
+Possible values: 'r32float', 'r32sint', 'r32uint', 'rg32float', 'rg32sint', 'rg32uint', 'rgba16float', 'rgba16sint', 'rgba16uint', 'rgba32float', 'rgba32sint', 'rgba32uint', 'rgba8sint', 'rgba8snorm', 'rgba8uint', 'rgba8unorm')");
}
TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_InvalidAccess) {
@@ -227,7 +239,8 @@
EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
- EXPECT_EQ(p->error(), "1:30: invalid value for access control");
+ EXPECT_EQ(p->error(), R"(1:30: expected access control for storage texture type. Did you mean 'read'?
+Possible values: 'read', 'read_write', 'write')");
}
TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingType) {
@@ -236,7 +249,8 @@
EXPECT_EQ(t.value, nullptr);
EXPECT_FALSE(t.matched);
EXPECT_TRUE(t.errored);
- EXPECT_EQ(p->error(), "1:20: invalid format for storage texture type");
+ EXPECT_EQ(p->error(), R"(1:20: expected texel format for storage texture type
+Possible values: 'r32float', 'r32sint', 'r32uint', 'rg32float', 'rg32sint', 'rg32uint', 'rgba16float', 'rgba16sint', 'rgba16uint', 'rgba32float', 'rgba32sint', 'rgba32uint', 'rgba8sint', 'rgba8snorm', 'rgba8uint', 'rgba8unorm')");
}
TEST_F(ParserImplTest, TextureSamplerTypes_StorageTexture_MissingLessThan) {
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 0e018e0..ad8946f 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,8 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:5: expected identifier for address space");
+ ASSERT_EQ(p->error(), R"(1:5: expected address space for ptr declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
TEST_F(ParserImplTest, TypeDecl_Ptr_MissingType) {
@@ -292,7 +293,8 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:20: expected identifier for access control");
+ ASSERT_EQ(p->error(), R"(1:20: expected access control for ptr declaration
+Possible values: 'read', 'read_write', 'write')");
}
TEST_F(ParserImplTest, TypeDecl_Ptr_MissingParams) {
@@ -302,7 +304,8 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:5: expected identifier for address space");
+ ASSERT_EQ(p->error(), R"(1:5: expected address space for ptr declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
TEST_F(ParserImplTest, TypeDecl_Ptr_BadAddressSpace) {
@@ -312,7 +315,9 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:5: invalid address space for ptr declaration");
+ ASSERT_EQ(p->error(),
+ R"(1:5: expected address space for ptr declaration. Did you mean 'uniform'?
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
TEST_F(ParserImplTest, TypeDecl_Ptr_BadAccess) {
@@ -322,7 +327,8 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:20: invalid value for access control");
+ ASSERT_EQ(p->error(), R"(1:20: expected access control for ptr declaration
+Possible values: 'read', 'read_write', 'write')");
}
TEST_F(ParserImplTest, TypeDecl_Atomic) {
diff --git a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
index 186b364..e37a347 100644
--- a/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_type_decl_without_ident_test.cc
@@ -263,7 +263,8 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:5: expected identifier for address space");
+ ASSERT_EQ(p->error(), R"(1:5: expected address space for ptr declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingType) {
@@ -283,7 +284,8 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:20: expected identifier for access control");
+ ASSERT_EQ(p->error(), R"(1:20: expected access control for ptr declaration
+Possible values: 'read', 'read_write', 'write')");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_MissingParams) {
@@ -293,7 +295,8 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:5: expected identifier for address space");
+ ASSERT_EQ(p->error(), R"(1:5: expected address space for ptr declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_BadAddressSpace) {
@@ -303,7 +306,9 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:5: invalid address space for ptr declaration");
+ ASSERT_EQ(p->error(),
+ R"(1:5: expected address space for ptr declaration. Did you mean 'uniform'?
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Ptr_BadAccess) {
@@ -313,7 +318,8 @@
EXPECT_FALSE(t.matched);
ASSERT_EQ(t.value, nullptr);
ASSERT_TRUE(p->has_error());
- ASSERT_EQ(p->error(), "1:20: invalid value for access control");
+ ASSERT_EQ(p->error(), R"(1:20: expected access control for ptr declaration
+Possible values: 'read', 'read_write', 'write')");
}
TEST_F(ParserImplTest, TypeDeclWithoutIdent_Atomic) {
diff --git a/src/tint/reader/wgsl/parser_impl_variable_attribute_list_test.cc b/src/tint/reader/wgsl/parser_impl_variable_attribute_list_test.cc
index a814ea6..79574a8 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_attribute_list_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_attribute_list_test.cc
@@ -58,8 +58,19 @@
EXPECT_TRUE(attrs.errored);
EXPECT_FALSE(attrs.matched);
EXPECT_TRUE(attrs.value.IsEmpty());
- EXPECT_EQ(p->error(), "1:10: invalid value for builtin attribute");
+ EXPECT_EQ(p->error(), R"(1:10: expected builtin
+Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
+TEST_F(ParserImplTest, AttributeList_InvalidValueSuggest) {
+ auto p = parser("@builtin(instanceindex)");
+ auto attrs = p->attribute_list();
+ EXPECT_TRUE(p->has_error());
+ EXPECT_TRUE(attrs.errored);
+ EXPECT_FALSE(attrs.matched);
+ EXPECT_TRUE(attrs.value.IsEmpty());
+ EXPECT_EQ(p->error(), R"(1:10: expected builtin. Did you mean 'instance_index'?
+Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
+}
} // namespace
} // namespace tint::reader::wgsl
diff --git a/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc b/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
index 7c52838..70af3b2 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_attribute_test.cc
@@ -177,7 +177,8 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:9: expected identifier for builtin");
+ EXPECT_EQ(p->error(), R"(1:9: expected builtin
+Possible values: 'frag_depth', 'front_facing', 'global_invocation_id', 'instance_index', 'local_invocation_id', 'local_invocation_index', 'num_workgroups', 'position', 'sample_index', 'sample_mask', 'vertex_index', 'workgroup_id')");
}
TEST_F(ParserImplTest, Attribute_Builtin_InvalidValue) {
@@ -187,7 +188,19 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:9: invalid value for builtin attribute");
+ 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) {
@@ -197,7 +210,8 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:9: expected identifier for builtin");
+ 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) {
@@ -213,7 +227,7 @@
auto* interp = var_attr->As<ast::InterpolateAttribute>();
EXPECT_EQ(interp->type, ast::InterpolationType::kFlat);
- EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kNone);
+ EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kInvalid);
}
TEST_F(ParserImplTest, Attribute_Interpolate_Single_TrailingComma) {
@@ -229,7 +243,7 @@
auto* interp = var_attr->As<ast::InterpolateAttribute>();
EXPECT_EQ(interp->type, ast::InterpolationType::kFlat);
- EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kNone);
+ EXPECT_EQ(interp->sampling, ast::InterpolationSampling::kInvalid);
}
TEST_F(ParserImplTest, Attribute_Interpolate_Single_DoubleTrailingComma) {
@@ -239,7 +253,8 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:18: expected identifier for interpolation sample name");
+ EXPECT_EQ(p->error(), R"(1:18: expected interpolation sampling
+Possible values: 'center', 'centroid', 'sample')");
}
TEST_F(ParserImplTest, Attribute_Interpolate_Perspective_Center) {
@@ -333,7 +348,8 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:13: expected identifier for interpolation type name");
+ EXPECT_EQ(p->error(), R"(1:13: expected interpolation type
+Possible values: 'flat', 'linear', 'perspective')");
}
TEST_F(ParserImplTest, Attribute_Interpolate_InvalidFirstValue) {
@@ -343,7 +359,8 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:13: invalid interpolation type");
+ EXPECT_EQ(p->error(), R"(1:13: expected interpolation type
+Possible values: 'flat', 'linear', 'perspective')");
}
TEST_F(ParserImplTest, Attribute_Interpolate_InvalidSecondValue) {
@@ -353,7 +370,8 @@
EXPECT_TRUE(attr.errored);
EXPECT_EQ(attr.value, nullptr);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:26: invalid interpolation sampling");
+ EXPECT_EQ(p->error(), R"(1:26: expected interpolation sampling. Did you mean 'sample'?
+Possible values: 'center', 'centroid', 'sample')");
}
TEST_F(ParserImplTest, Attribute_Binding) {
diff --git a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
index 8427c6e..5cf038b 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_decl_test.cc
@@ -105,7 +105,9 @@
EXPECT_FALSE(v.matched);
EXPECT_TRUE(v.errored);
EXPECT_TRUE(p->has_error());
- EXPECT_EQ(p->error(), "1:5: invalid address space for variable declaration");
+ EXPECT_EQ(p->error(),
+ R"(1:5: expected address space for variable declaration. Did you mean 'uniform'?
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
} // namespace
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 9f8663d..32c1a54 100644
--- a/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_variable_qualifier_test.cc
@@ -47,11 +47,11 @@
ParserImplTest,
VariableQualifierTest,
testing::Values(
- VariableStorageData{"uniform", ast::AddressSpace::kUniform, ast::Access::kUndefined},
- VariableStorageData{"workgroup", ast::AddressSpace::kWorkgroup, ast::Access::kUndefined},
- VariableStorageData{"storage", ast::AddressSpace::kStorage, ast::Access::kUndefined},
- VariableStorageData{"private", ast::AddressSpace::kPrivate, ast::Access::kUndefined},
- VariableStorageData{"function", ast::AddressSpace::kFunction, ast::Access::kUndefined},
+ VariableStorageData{"uniform", ast::AddressSpace::kUniform, ast::Access::kInvalid},
+ VariableStorageData{"workgroup", ast::AddressSpace::kWorkgroup, ast::Access::kInvalid},
+ VariableStorageData{"storage", ast::AddressSpace::kStorage, ast::Access::kInvalid},
+ VariableStorageData{"private", ast::AddressSpace::kPrivate, ast::Access::kInvalid},
+ VariableStorageData{"function", ast::AddressSpace::kFunction, ast::Access::kInvalid},
VariableStorageData{"storage, read", ast::AddressSpace::kStorage, ast::Access::kRead},
VariableStorageData{"storage, write", ast::AddressSpace::kStorage, ast::Access::kWrite},
VariableStorageData{"storage, read_write", ast::AddressSpace::kStorage,
@@ -63,7 +63,8 @@
EXPECT_TRUE(p->has_error());
EXPECT_TRUE(sc.errored);
EXPECT_FALSE(sc.matched);
- EXPECT_EQ(p->error(), "1:2: invalid address space for variable declaration");
+ EXPECT_EQ(p->error(), R"(1:2: expected address space for variable declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
TEST_F(ParserImplTest, VariableQualifier_Empty) {
@@ -72,7 +73,8 @@
EXPECT_TRUE(p->has_error());
EXPECT_TRUE(sc.errored);
EXPECT_FALSE(sc.matched);
- EXPECT_EQ(p->error(), "1:2: expected identifier for address space");
+ EXPECT_EQ(p->error(), R"(1:2: expected address space for variable declaration
+Possible values: 'function', 'private', 'push_constant', 'storage', 'uniform', 'workgroup')");
}
TEST_F(ParserImplTest, VariableQualifier_MissingLessThan) {
diff --git a/src/tint/reader/wgsl/parser_impl_while_stmt_test.cc b/src/tint/reader/wgsl/parser_impl_while_stmt_test.cc
index 247f340..3b83ecc 100644
--- a/src/tint/reader/wgsl/parser_impl_while_stmt_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_while_stmt_test.cc
@@ -23,6 +23,17 @@
// Test an empty while loop.
TEST_F(WhileStmtTest, Empty) {
+ auto p = parser("while true { }");
+ auto wl = p->while_statement();
+ EXPECT_FALSE(p->has_error()) << p->error();
+ EXPECT_FALSE(wl.errored);
+ ASSERT_TRUE(wl.matched);
+ EXPECT_TRUE(Is<ast::Expression>(wl->condition));
+ EXPECT_TRUE(wl->body->Empty());
+}
+
+// Test an empty while loop with parentheses.
+TEST_F(WhileStmtTest, EmptyWithParentheses) {
auto p = parser("while (true) { }");
auto wl = p->while_statement();
EXPECT_FALSE(p->has_error()) << p->error();
@@ -46,17 +57,6 @@
// Test a while loop with complex condition.
TEST_F(WhileStmtTest, ComplexCondition) {
- auto p = parser("while ((a + 1 - 2) == 3) { }");
- auto wl = p->while_statement();
- EXPECT_FALSE(p->has_error()) << p->error();
- EXPECT_FALSE(wl.errored);
- ASSERT_TRUE(wl.matched);
- EXPECT_TRUE(Is<ast::Expression>(wl->condition));
- EXPECT_TRUE(wl->body->Empty());
-}
-
-// Test a while loop with no brackets.
-TEST_F(WhileStmtTest, NoBrackets) {
auto p = parser("while (a + 1 - 2) == 3 { }");
auto wl = p->while_statement();
EXPECT_FALSE(p->has_error()) << p->error();
@@ -66,9 +66,20 @@
EXPECT_TRUE(wl->body->Empty());
}
+// Test a while loop with complex condition, with parentheses.
+TEST_F(WhileStmtTest, ComplexConditionWithParentheses) {
+ auto p = parser("while ((a + 1 - 2) == 3) { }");
+ auto wl = p->while_statement();
+ EXPECT_FALSE(p->has_error()) << p->error();
+ EXPECT_FALSE(wl.errored);
+ ASSERT_TRUE(wl.matched);
+ EXPECT_TRUE(Is<ast::Expression>(wl->condition));
+ EXPECT_TRUE(wl->body->Empty());
+}
+
class WhileStmtErrorTest : public ParserImplTest {
public:
- void TestForWithError(std::string for_str, std::string error_str) {
+ void TestWhileWithError(std::string for_str, std::string error_str) {
auto p_for = parser(for_str);
auto e_for = p_for->while_statement();
@@ -85,7 +96,7 @@
std::string while_str = "while bool) { }";
std::string error_str = "1:11: expected '(' for type constructor";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
// Test a while loop with missing condition is invalid.
@@ -93,7 +104,7 @@
std::string while_str = "while () {}";
std::string error_str = "1:8: unable to parse expression";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
// Test a while loop with missing right parenthesis is invalid.
@@ -101,7 +112,7 @@
std::string while_str = "while (true {}";
std::string error_str = "1:13: expected ')'";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
// Test a while loop with missing left brace is invalid.
@@ -109,7 +120,7 @@
std::string while_str = "while (true) }";
std::string error_str = "1:14: expected '{'";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
// Test a for loop with missing right brace is invalid.
@@ -117,7 +128,7 @@
std::string while_str = "while (true) {";
std::string error_str = "1:15: expected '}'";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
// Test a while loop with an invalid break condition.
@@ -125,7 +136,7 @@
std::string while_str = "while ((0 == 1) { }";
std::string error_str = "1:17: expected ')'";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
// Test a while loop with a break condition not matching
@@ -134,7 +145,7 @@
std::string while_str = "while (var i: i32 = 0) { }";
std::string error_str = "1:8: unable to parse expression";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
// Test a while loop with an invalid body.
@@ -142,7 +153,7 @@
std::string while_str = "while (true) { let x: i32; }";
std::string error_str = "1:26: expected '=' for 'let' declaration";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
// Test a for loop with a body not matching statements
@@ -150,7 +161,7 @@
std::string while_str = "while (true) { fn main() {} }";
std::string error_str = "1:16: expected '}'";
- TestForWithError(while_str, error_str);
+ TestWhileWithError(while_str, error_str);
}
} // namespace
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 796487b..018dbfd 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -721,7 +721,7 @@
}
TEST_F(StructMemberAttributeTest, Align_Attribute_Var) {
- GlobalVar(Source{{1, 2}}, "val", ty.f32(), ast::AddressSpace::kPrivate, ast::Access::kUndefined,
+ GlobalVar(Source{{1, 2}}, "val", ty.f32(), ast::AddressSpace::kPrivate, ast::Access::kInvalid,
Expr(1.23_f));
Structure(Source{{6, 4}}, "mystruct",
@@ -1386,16 +1386,16 @@
ResolverAttributeValidationTest,
InterpolateParameterTest,
testing::Values(
- Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kNone, true},
+ Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kInvalid, true},
Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kCenter, true},
Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kCentroid, true},
Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kSample, true},
- Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kNone, true},
+ Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kInvalid, true},
Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kCenter, true},
Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kCentroid, true},
Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kSample, true},
// flat interpolation must not have a sampling type
- Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kNone, true},
+ Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kInvalid, true},
Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kCenter, false},
Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kCentroid, false},
Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kSample, false}));
@@ -1443,7 +1443,7 @@
utils::Vector{
Builtin(ast::BuiltinValue::kPosition),
Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone),
+ ast::InterpolationSampling::kInvalid),
}),
},
ty.void_(), utils::Empty,
@@ -1467,7 +1467,7 @@
utils::Vector{
Builtin(ast::BuiltinValue::kPosition),
Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone),
+ ast::InterpolationSampling::kInvalid),
});
EXPECT_FALSE(r()->Resolve());
@@ -1480,7 +1480,7 @@
utils::Vector{
Member("a", ty.f32(),
utils::Vector{Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone)}),
+ ast::InterpolationSampling::kInvalid)}),
});
EXPECT_FALSE(r()->Resolve());
diff --git a/src/tint/resolver/const_eval.cc b/src/tint/resolver/const_eval.cc
index 9ddf24f..d9fde25 100644
--- a/src/tint/resolver/const_eval.cc
+++ b/src/tint/resolver/const_eval.cc
@@ -231,27 +231,29 @@
return this;
}
return ZeroTypeDispatch(target_ty, [&](auto zero_to) -> ImplResult {
- // `T` is the source type, `value` is the source value.
+ // `value` is the source value.
+ // `FROM` is the source type.
// `TO` is the target type.
using TO = std::decay_t<decltype(zero_to)>;
+ using FROM = T;
if constexpr (std::is_same_v<TO, bool>) {
// [x -> bool]
return builder.create<Element<TO>>(target_ty, !IsPositiveZero(value));
- } else if constexpr (std::is_same_v<T, bool>) {
+ } else if constexpr (std::is_same_v<FROM, bool>) {
// [bool -> x]
return builder.create<Element<TO>>(target_ty, TO(value ? 1 : 0));
} else if (auto conv = CheckedConvert<TO>(value)) {
// Conversion success
return builder.create<Element<TO>>(target_ty, conv.Get());
// --- Below this point are the failure cases ---
- } else if constexpr (IsAbstract<T>) {
+ } else if constexpr (IsAbstract<FROM>) {
// [abstract-numeric -> x] - materialization failure
std::stringstream ss;
ss << "value " << value << " cannot be represented as ";
ss << "'" << builder.FriendlyName(target_ty) << "'";
builder.Diagnostics().add_error(tint::diag::System::Resolver, ss.str(), source);
return utils::Failure;
- } else if constexpr (IsFloatingPoint<UnwrapNumber<TO>>) {
+ } else if constexpr (IsFloatingPoint<TO>) {
// [x -> floating-point] - number not exactly representable
// https://www.w3.org/TR/WGSL/#floating-point-conversion
switch (conv.Failure()) {
@@ -260,8 +262,8 @@
case ConversionFailure::kExceedsPositiveLimit:
return builder.create<Element<TO>>(target_ty, TO::Inf());
}
- } else {
- // [x -> integer] - number not exactly representable
+ } else if constexpr (IsFloatingPoint<FROM>) {
+ // [floating-point -> integer] - number not exactly representable
// https://www.w3.org/TR/WGSL/#floating-point-conversion
switch (conv.Failure()) {
case ConversionFailure::kExceedsNegativeLimit:
@@ -269,6 +271,10 @@
case ConversionFailure::kExceedsPositiveLimit:
return builder.create<Element<TO>>(target_ty, TO::Highest());
}
+ } else if constexpr (IsIntegral<FROM>) {
+ // [integer -> integer] - number not exactly representable
+ // Static cast
+ return builder.create<Element<TO>>(target_ty, static_cast<TO>(value));
}
return nullptr; // Expression is not constant.
});
@@ -842,11 +848,7 @@
return nullptr; // Single argument is not constant.
}
- if (auto conv = Convert(ty, args[0], source)) {
- return conv.Get();
- }
-
- return nullptr;
+ return Convert(ty, args[0], source);
}
ConstEval::Result ConstEval::Zero(const sem::Type* ty,
diff --git a/src/tint/resolver/const_eval_test.cc b/src/tint/resolver/const_eval_test.cc
index 8bded4c..cf3cda0 100644
--- a/src/tint/resolver/const_eval_test.cc
+++ b/src/tint/resolver/const_eval_test.cc
@@ -44,6 +44,30 @@
template <typename T>
const auto k3PiOver4 = T(UnwrapNumber<T>(2.356194490192344928846));
+/// Walks the sem::Constant @p c, accumulating all the inner-most scalar values into @p args
+void CollectScalarArgs(const sem::Constant* c, builder::ScalarArgs& args) {
+ Switch(
+ c->Type(), //
+ [&](const sem::Bool*) { args.values.Push(c->As<bool>()); },
+ [&](const sem::I32*) { args.values.Push(c->As<i32>()); },
+ [&](const sem::U32*) { args.values.Push(c->As<u32>()); },
+ [&](const sem::F32*) { args.values.Push(c->As<f32>()); },
+ [&](const sem::F16*) { args.values.Push(c->As<f16>()); },
+ [&](Default) {
+ size_t i = 0;
+ while (auto* child = c->Index(i++)) {
+ CollectScalarArgs(child, args);
+ }
+ });
+}
+
+/// Walks the sem::Constant @p c, returning all the inner-most scalar values.
+builder::ScalarArgs ScalarArgsFrom(const sem::Constant* c) {
+ builder::ScalarArgs out;
+ CollectScalarArgs(c, out);
+ return out;
+}
+
template <typename T>
constexpr auto Negate(const Number<T>& v) {
if constexpr (std::is_integral_v<T>) {
@@ -1211,283 +1235,6 @@
EXPECT_EQ(sem->ConstantValue()->Index(2)->As<bool>(), false);
}
-TEST_F(ResolverConstEvalTest, Vec3_Convert_f32_to_i32) {
- auto* expr = vec3<i32>(vec3<f32>(1.1_f, 2.2_f, 3.3_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f32) {
- auto* expr = vec3<f32>(vec3<u32>(10_u, 20_u, 30_u));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_f16_to_i32) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<i32>(vec3<f16>(1.1_h, 2.2_h, 3.3_h));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2_i);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3_i);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<u32>(10_u, 20_u, 30_u));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_i32) {
- auto* expr = vec3<i32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), i32::Highest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), i32::Lowest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), i32::Highest());
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_u32) {
- auto* expr = vec3<u32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), u32::Highest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), u32::Lowest());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), u32::Highest());
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- constexpr auto kInfinity = std::numeric_limits<double>::infinity();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), kInfinity);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -kInfinity);
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), kInfinity);
-}
-
-TEST_F(ResolverConstEvalTest, Vec3_Convert_Small_f32_to_f16) {
- Enable(ast::Extension::kF16);
-
- auto* expr = vec3<f16>(vec3<f32>(1e-20_f, -2e-30_f, 3e-40_f));
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- auto* vec = sem->Type()->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- EXPECT_TRUE(vec->type()->Is<sem::F16>());
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
- EXPECT_FALSE(sem->ConstantValue()->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->AllZero());
-
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0.0);
- EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As<AFloat>().value));
-
- EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
- EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -0.0);
- EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As<AFloat>().value));
-
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
- EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
- EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0.0);
- EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As<AFloat>().value));
-}
-
TEST_F(ResolverConstEvalTest, Mat2x3_ZeroInit_f32) {
auto* expr = mat2x3<f32>();
WrapInFunction(expr);
@@ -2475,6 +2222,519 @@
}
////////////////////////////////////////////////////////////////////////////////////////////////////
+// Conversion
+////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace conv {
+
+using Scalar = std::variant< //
+ builder::Value<AInt>,
+ builder::Value<AFloat>,
+ builder::Value<u32>,
+ builder::Value<i32>,
+ builder::Value<f32>,
+ builder::Value<f16>,
+ builder::Value<bool>>;
+
+static std::ostream& operator<<(std::ostream& o, const Scalar& scalar) {
+ std::visit(
+ [&](auto&& v) {
+ using ValueType = std::decay_t<decltype(v)>;
+ o << ValueType::DataType::Name() << "(";
+ for (auto& a : v.args.values) {
+ o << std::get<typename ValueType::ElementType>(a);
+ if (&a != &v.args.values.Back()) {
+ o << ", ";
+ }
+ }
+ o << ")";
+ },
+ scalar);
+ return o;
+}
+
+enum class Kind {
+ kScalar,
+ kVector,
+};
+
+static std::ostream& operator<<(std::ostream& o, const Kind& k) {
+ switch (k) {
+ case Kind::kScalar:
+ return o << "scalar";
+ case Kind::kVector:
+ return o << "vector";
+ }
+ return o << "<unknown>";
+}
+
+struct Case {
+ Scalar input;
+ Scalar expected;
+ builder::CreatePtrs type;
+ bool unrepresentable = false;
+};
+
+static std::ostream& operator<<(std::ostream& o, const Case& c) {
+ if (c.unrepresentable) {
+ o << "[unrepresentable] input: " << c.input;
+ } else {
+ o << "input: " << c.input << ", expected: " << c.expected;
+ }
+ return o << ", type: " << c.type;
+}
+
+template <typename TO, typename FROM>
+Case Success(FROM input, TO expected) {
+ return {builder::Val(input), builder::Val(expected), builder::CreatePtrsFor<TO>()};
+}
+
+template <typename TO, typename FROM>
+Case Unrepresentable(FROM input) {
+ return {builder::Val(input), builder::Val(0_i), builder::CreatePtrsFor<TO>(),
+ /* unrepresentable */ true};
+}
+
+using ResolverConstEvalConvTest = ResolverTestWithParam<std::tuple<Kind, Case>>;
+
+TEST_P(ResolverConstEvalConvTest, Test) {
+ const auto& kind = std::get<0>(GetParam());
+ const auto& input = std::get<1>(GetParam()).input;
+ const auto& expected = std::get<1>(GetParam()).expected;
+ const auto& type = std::get<1>(GetParam()).type;
+ const auto unrepresentable = std::get<1>(GetParam()).unrepresentable;
+
+ auto* input_val = std::visit([&](auto val) { return val.Expr(*this); }, input);
+ auto* expr = Construct(type.ast(*this), input_val);
+ if (kind == Kind::kVector) {
+ expr = Construct(ty.vec(nullptr, 3), expr);
+ }
+ WrapInFunction(expr);
+
+ auto* target_sem_ty = type.sem(*this);
+ if (kind == Kind::kVector) {
+ target_sem_ty = create<sem::Vector>(target_sem_ty, 3u);
+ }
+
+ if (unrepresentable) {
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), testing::HasSubstr("cannot be represented as"));
+ } else {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TYPE(sem->Type(), target_sem_ty);
+ ASSERT_NE(sem->ConstantValue(), nullptr);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), target_sem_ty);
+
+ auto expected_values = std::visit([&](auto&& val) { return val.args; }, expected);
+ if (kind == Kind::kVector) {
+ expected_values.values.Push(expected_values.values[0]);
+ expected_values.values.Push(expected_values.values[0]);
+ }
+ auto got_values = ScalarArgsFrom(sem->ConstantValue());
+ EXPECT_EQ(expected_values, got_values);
+ }
+}
+INSTANTIATE_TEST_SUITE_P(ScalarAndVector,
+ ResolverConstEvalConvTest,
+ testing::Combine(testing::Values(Kind::kScalar, Kind::kVector),
+ testing::ValuesIn({
+ // TODO(crbug.com/tint/1502): Add f16 tests
+ // i32 -> u32
+ Success(0_i, 0_u),
+ Success(1_i, 1_u),
+ Success(-1_i, 0xffffffff_u),
+ Success(2_i, 2_u),
+ Success(-2_i, 0xfffffffe_u),
+ // i32 -> f32
+ Success(0_i, 0_f),
+ Success(1_i, 1_f),
+ Success(-1_i, -1_f),
+ Success(2_i, 2_f),
+ Success(-2_i, -2_f),
+ // i32 -> bool
+ Success(0_i, false),
+ Success(1_i, true),
+ Success(-1_i, true),
+ Success(2_i, true),
+ Success(-2_i, true),
+ // u32 -> i32
+ Success(0_u, 0_i),
+ Success(1_u, 1_i),
+ Success(0xffffffff_u, -1_i),
+ Success(2_u, 2_i),
+ Success(0xfffffffe_u, -2_i),
+ // u32 -> f32
+ Success(0_u, 0_f),
+ Success(1_u, 1_f),
+ Success(2_u, 2_f),
+ Success(0xffffffff_u, 0xffffffff_f),
+ // u32 -> bool
+ Success(0_u, false),
+ Success(1_u, true),
+ Success(2_u, true),
+ Success(0xffffffff_u, true),
+ // f32 -> i32
+ Success(0_f, 0_i),
+ Success(1_f, 1_i),
+ Success(2_f, 2_i),
+ Success(1e20_f, i32::Highest()),
+ Success(-1e20_f, i32::Lowest()),
+ // f32 -> u32
+ Success(0_f, 0_i),
+ Success(1_f, 1_i),
+ Success(-1_f, u32::Lowest()),
+ Success(2_f, 2_i),
+ Success(1e20_f, u32::Highest()),
+ Success(-1e20_f, u32::Lowest()),
+ // f32 -> bool
+ Success(0_f, false),
+ Success(1_f, true),
+ Success(-1_f, true),
+ Success(2_f, true),
+ Success(1e20_f, true),
+ Success(-1e20_f, true),
+ // abstract-int -> i32
+ Success(0_a, 0_i),
+ Success(1_a, 1_i),
+ Success(-1_a, -1_i),
+ Success(0x7fffffff_a, i32::Highest()),
+ Success(-0x80000000_a, i32::Lowest()),
+ Unrepresentable<i32>(0x80000000_a),
+ // abstract-int -> u32
+ Success(0_a, 0_u),
+ Success(1_a, 1_u),
+ Success(0xffffffff_a, 0xffffffff_u),
+ Unrepresentable<u32>(0x100000000_a),
+ Unrepresentable<u32>(-1_a),
+ // abstract-int -> f32
+ Success(0_a, 0_f),
+ Success(1_a, 1_f),
+ Success(0xffffffff_a, 0xffffffff_f),
+ Success(0x100000000_a, 0x100000000_f),
+ Success(-0x100000000_a, -0x100000000_f),
+ Success(0x7fffffffffffffff_a, 0x7fffffffffffffff_f),
+ Success(-0x7fffffffffffffff_a, -0x7fffffffffffffff_f),
+ // abstract-int -> bool
+ Success(0_a, false),
+ Success(1_a, true),
+ Success(0xffffffff_a, true),
+ Success(0x100000000_a, true),
+ Success(-0x100000000_a, true),
+ Success(0x7fffffffffffffff_a, true),
+ Success(-0x7fffffffffffffff_a, true),
+ // abstract-float -> i32
+ Success(0.0_a, 0_i),
+ Success(1.0_a, 1_i),
+ Success(-1.0_a, -1_i),
+ Success(AFloat(0x7fffffff), i32::Highest()),
+ Success(-AFloat(0x80000000), i32::Lowest()),
+ Unrepresentable<i32>(0x80000000_a),
+ // abstract-float -> u32
+ Success(0.0_a, 0_u),
+ Success(1.0_a, 1_u),
+ Success(AFloat(0xffffffff), 0xffffffff_u),
+ Unrepresentable<u32>(AFloat(0x100000000)),
+ Unrepresentable<u32>(AFloat(-1)),
+ // abstract-float -> f32
+ Success(0.0_a, 0_f),
+ Success(1.0_a, 1_f),
+ Success(AFloat(0xffffffff), 0xffffffff_f),
+ Success(AFloat(0x100000000), 0x100000000_f),
+ Success(-AFloat(0x100000000), -0x100000000_f),
+ Unrepresentable<f32>(1e40_a),
+ Unrepresentable<f32>(-1e40_a),
+ // abstract-float -> bool
+ Success(0.0_a, false),
+ Success(1.0_a, true),
+ Success(AFloat(0xffffffff), true),
+ Success(AFloat(0x100000000), true),
+ Success(-AFloat(0x100000000), true),
+ Success(1e40_a, true),
+ Success(-1e40_a, true),
+ })));
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_f32_to_i32) {
+ auto* expr = vec3<i32>(vec3<f32>(1.1_f, 2.2_f, 3.3_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f32) {
+ auto* expr = vec3<f32>(vec3<u32>(10_u, 20_u, 30_u));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_f16_to_i32) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<i32>(vec3<f16>(1.1_h, 2.2_h, 3.3_h));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), 1_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), 2_i);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), 3_i);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_u32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<u32>(10_u, 20_u, 30_u));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 10.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), 20.f);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 30.f);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_i32) {
+ auto* expr = vec3<i32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), i32::Highest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), i32::Lowest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), i32::Highest());
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_u32) {
+ auto* expr = vec3<u32>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AInt>(), u32::Highest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AInt>(), u32::Lowest());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AInt>(), u32::Highest());
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Large_f32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<f32>(1e10_f, -1e20_f, 1e30_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ constexpr auto kInfinity = std::numeric_limits<double>::infinity();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), kInfinity);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -kInfinity);
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), kInfinity);
+}
+
+TEST_F(ResolverConstEvalTest, Vec3_Convert_Small_f32_to_f16) {
+ Enable(ast::Extension::kF16);
+
+ auto* expr = vec3<f16>(vec3<f32>(1e-20_f, -2e-30_f, 3e-40_f));
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ auto* vec = sem->Type()->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ EXPECT_TRUE(vec->type()->Is<sem::F16>());
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TYPE(sem->ConstantValue()->Type(), sem->Type());
+ EXPECT_FALSE(sem->ConstantValue()->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->AllZero());
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(0)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(0)->As<AFloat>(), 0.0);
+ EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(0)->As<AFloat>().value));
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(1)->AllEqual());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AnyZero());
+ EXPECT_FALSE(sem->ConstantValue()->Index(1)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(1)->As<AFloat>(), -0.0);
+ EXPECT_TRUE(std::signbit(sem->ConstantValue()->Index(1)->As<AFloat>().value));
+
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllEqual());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AnyZero());
+ EXPECT_TRUE(sem->ConstantValue()->Index(2)->AllZero());
+ EXPECT_EQ(sem->ConstantValue()->Index(2)->As<AFloat>(), 0.0);
+ EXPECT_FALSE(std::signbit(sem->ConstantValue()->Index(2)->As<AFloat>().value));
+}
+
+} // namespace conv
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
// Indexing
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -3020,12 +3280,14 @@
using T = typename std::decay_t<decltype(expected)>::ElementType;
auto* input_expr = std::visit([&](auto&& value) { return value.Expr(*this); }, c.input);
- auto* expected_expr = create<ast::UnaryOpExpression>(op, input_expr);
- GlobalConst("C", expected_expr);
+ auto* expr = create<ast::UnaryOpExpression>(op, input_expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ GlobalConst("C", expr);
+ auto* expected_expr = expected.Expr(*this);
+ GlobalConst("E", expected_expr);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expected_expr);
+ auto* sem = Sem().Get(expr);
const sem::Constant* value = sem->ConstantValue();
ASSERT_NE(value, nullptr);
EXPECT_TYPE(value->Type(), sem->Type());
diff --git a/src/tint/resolver/intrinsic_table.cc b/src/tint/resolver/intrinsic_table.cc
index 0eaaa37..a82f9e8 100644
--- a/src/tint/resolver/intrinsic_table.cc
+++ b/src/tint/resolver/intrinsic_table.cc
@@ -1186,7 +1186,7 @@
for (auto& p : match.parameters) {
params.Push(builder.create<sem::Parameter>(
nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::AddressSpace::kNone,
- ast::Access::kUndefined, p.usage));
+ ast::Access::kInvalid, p.usage));
}
sem::PipelineStageSet supported_stages;
if (match.overload->flags.Contains(OverloadFlag::kSupportsVertexPipeline)) {
@@ -1384,7 +1384,7 @@
for (auto& p : match.parameters) {
params.Push(builder.create<sem::Parameter>(
nullptr, static_cast<uint32_t>(params.Length()), p.type, ast::AddressSpace::kNone,
- ast::Access::kUndefined, p.usage));
+ ast::Access::kInvalid, p.usage));
}
auto eval_stage = match.overload->const_eval_fn ? sem::EvaluationStage::kConstant
: sem::EvaluationStage::kRuntime;
@@ -1397,9 +1397,9 @@
// Conversion.
auto* target = utils::GetOrCreate(converters, match, [&]() {
- auto param = builder.create<sem::Parameter>(
- nullptr, 0u, match.parameters[0].type, ast::AddressSpace::kNone,
- ast::Access::kUndefined, match.parameters[0].usage);
+ auto param = builder.create<sem::Parameter>(nullptr, 0u, match.parameters[0].type,
+ ast::AddressSpace::kNone, ast::Access::kInvalid,
+ match.parameters[0].usage);
auto eval_stage = match.overload->const_eval_fn ? sem::EvaluationStage::kConstant
: sem::EvaluationStage::kRuntime;
return builder.create<sem::TypeConversion>(match.return_type, param, eval_stage);
@@ -1643,7 +1643,13 @@
auto earliest_eval_stage = sem::EvaluationStage::kConstant;
- ss << intrinsic_name << "(";
+ ss << intrinsic_name;
+ if (overload->flags.Contains(OverloadFlag::kIsConverter) && overload->template_types) {
+ ss << "<";
+ ss << overload->template_types[0].name;
+ ss << ">";
+ }
+ ss << "(";
for (size_t p = 0; p < overload->num_parameters; p++) {
auto& parameter = overload->parameters[p];
if (p > 0) {
diff --git a/src/tint/resolver/intrinsic_table.inl b/src/tint/resolver/intrinsic_table.inl
index 67cb23f..426b78b 100644
--- a/src/tint/resolver/intrinsic_table.inl
+++ b/src/tint/resolver/intrinsic_table.inl
@@ -1550,8 +1550,8 @@
return "__atomic_compare_exchange_result<" + T + ">";
}
-/// TypeMatcher for 'match abstract_or_scalar'
-class AbstractOrScalar : public TypeMatcher {
+/// TypeMatcher for 'match scalar'
+class Scalar : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
@@ -1566,7 +1566,7 @@
std::string String(MatchState* state) const override;
};
-const sem::Type* AbstractOrScalar::Match(MatchState& state, const sem::Type* ty) const {
+const sem::Type* Scalar::Match(MatchState& state, const sem::Type* ty) const {
if (match_ia(state, ty)) {
return build_ia(state);
}
@@ -1591,7 +1591,7 @@
return nullptr;
}
-std::string AbstractOrScalar::String(MatchState*) const {
+std::string Scalar::String(MatchState*) const {
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
@@ -1599,8 +1599,8 @@
return ss.str();
}
-/// TypeMatcher for 'match scalar'
-class Scalar : public TypeMatcher {
+/// TypeMatcher for 'match concrete_scalar'
+class ConcreteScalar : public TypeMatcher {
public:
/// Checks whether the given type matches the matcher rules, and returns the
/// expected, canonicalized type on success.
@@ -1615,7 +1615,7 @@
std::string String(MatchState* state) const override;
};
-const sem::Type* Scalar::Match(MatchState& state, const sem::Type* ty) const {
+const sem::Type* ConcreteScalar::Match(MatchState& state, const sem::Type* ty) const {
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1634,7 +1634,7 @@
return nullptr;
}
-std::string Scalar::String(MatchState*) const {
+std::string ConcreteScalar::String(MatchState*) const {
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
@@ -1659,6 +1659,12 @@
};
const sem::Type* ScalarNoF32::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1678,7 +1684,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << I32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << I32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
return ss.str();
}
@@ -1699,6 +1705,12 @@
};
const sem::Type* ScalarNoF16::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1718,7 +1730,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << F32().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << F32().String(nullptr) << ", " << I32().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
return ss.str();
}
@@ -1739,6 +1751,12 @@
};
const sem::Type* ScalarNoI32::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_u32(state, ty)) {
return build_u32(state);
}
@@ -1758,7 +1776,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << U32().String(nullptr) << " or " << Bool().String(nullptr);
return ss.str();
}
@@ -1779,6 +1797,12 @@
};
const sem::Type* ScalarNoU32::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1798,7 +1822,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << Bool().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << Bool().String(nullptr);
return ss.str();
}
@@ -1819,6 +1843,12 @@
};
const sem::Type* ScalarNoBool::Match(MatchState& state, const sem::Type* ty) const {
+ if (match_ia(state, ty)) {
+ return build_ia(state);
+ }
+ if (match_fa(state, ty)) {
+ return build_fa(state);
+ }
if (match_i32(state, ty)) {
return build_i32(state);
}
@@ -1838,7 +1868,7 @@
std::stringstream ss;
// Note: We pass nullptr to the TypeMatcher::String() functions, as 'matcher's do not support
// template arguments, nor can they match sub-types. As such, they have no use for the MatchState.
- ss << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << U32().String(nullptr);
+ ss << Ia().String(nullptr) << ", " << Fa().String(nullptr) << ", " << F32().String(nullptr) << ", " << F16().String(nullptr) << ", " << I32().String(nullptr) << " or " << U32().String(nullptr);
return ss.str();
}
@@ -2580,8 +2610,8 @@
FrexpResult FrexpResult_;
FrexpResultVec FrexpResultVec_;
AtomicCompareExchangeResult AtomicCompareExchangeResult_;
- AbstractOrScalar AbstractOrScalar_;
Scalar Scalar_;
+ ConcreteScalar ConcreteScalar_;
ScalarNoF32 ScalarNoF32_;
ScalarNoF16 ScalarNoF16_;
ScalarNoI32 ScalarNoI32_;
@@ -2666,8 +2696,8 @@
/* [47] */ &FrexpResult_,
/* [48] */ &FrexpResultVec_,
/* [49] */ &AtomicCompareExchangeResult_,
- /* [50] */ &AbstractOrScalar_,
- /* [51] */ &Scalar_,
+ /* [50] */ &Scalar_,
+ /* [51] */ &ConcreteScalar_,
/* [52] */ &ScalarNoF32_,
/* [53] */ &ScalarNoF16_,
/* [54] */ &ScalarNoI32_,
@@ -14348,9 +14378,9 @@
},
{
/* [67] */
- /* fn select<T : abstract_or_scalar>(T, T, bool) -> T */
- /* fn select<T : abstract_or_scalar, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
- /* fn select<N : num, T : abstract_or_scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
+ /* fn select<T : scalar>(T, T, bool) -> T */
+ /* fn select<T : scalar, N : num>(vec<N, T>, vec<N, T>, bool) -> vec<N, T> */
+ /* fn select<N : num, T : scalar>(vec<N, T>, vec<N, T>, vec<N, bool>) -> vec<N, T> */
/* num overloads */ 3,
/* overloads */ &kOverloads[276],
},
@@ -14876,15 +14906,15 @@
},
{
/* [10] */
- /* op ==<T : abstract_or_scalar>(T, T) -> bool */
- /* op ==<T : abstract_or_scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
+ /* op ==<T : scalar>(T, T) -> bool */
+ /* op ==<T : scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
/* num overloads */ 2,
/* overloads */ &kOverloads[408],
},
{
/* [11] */
- /* op !=<T : abstract_or_scalar>(T, T) -> bool */
- /* op !=<T : abstract_or_scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
+ /* op !=<T : scalar>(T, T) -> bool */
+ /* op !=<T : scalar, N : num>(vec<N, T>, vec<N, T>) -> vec<N, bool> */
/* num overloads */ 2,
/* overloads */ &kOverloads[394],
},
@@ -14995,10 +15025,10 @@
},
{
/* [5] */
- /* ctor vec2<T : scalar>() -> vec2<T> */
- /* ctor vec2<T : scalar>(vec2<T>) -> vec2<T> */
- /* ctor vec2<T : abstract_or_scalar>(T) -> vec2<T> */
- /* ctor vec2<T : abstract_or_scalar>(x: T, y: T) -> vec2<T> */
+ /* ctor vec2<T : concrete_scalar>() -> vec2<T> */
+ /* ctor vec2<T : concrete_scalar>(vec2<T>) -> vec2<T> */
+ /* ctor vec2<T : scalar>(T) -> vec2<T> */
+ /* ctor vec2<T : scalar>(x: T, y: T) -> vec2<T> */
/* conv vec2<T : f32, U : scalar_no_f32>(vec2<U>) -> vec2<f32> */
/* conv vec2<T : f16, U : scalar_no_f16>(vec2<U>) -> vec2<f16> */
/* conv vec2<T : i32, U : scalar_no_i32>(vec2<U>) -> vec2<i32> */
@@ -15009,12 +15039,12 @@
},
{
/* [6] */
- /* ctor vec3<T : scalar>() -> vec3<T> */
- /* ctor vec3<T : scalar>(vec3<T>) -> vec3<T> */
- /* ctor vec3<T : abstract_or_scalar>(T) -> vec3<T> */
- /* ctor vec3<T : abstract_or_scalar>(x: T, y: T, z: T) -> vec3<T> */
- /* ctor vec3<T : abstract_or_scalar>(xy: vec2<T>, z: T) -> vec3<T> */
- /* ctor vec3<T : abstract_or_scalar>(x: T, yz: vec2<T>) -> vec3<T> */
+ /* ctor vec3<T : concrete_scalar>() -> vec3<T> */
+ /* ctor vec3<T : concrete_scalar>(vec3<T>) -> vec3<T> */
+ /* ctor vec3<T : scalar>(T) -> vec3<T> */
+ /* ctor vec3<T : scalar>(x: T, y: T, z: T) -> vec3<T> */
+ /* ctor vec3<T : scalar>(xy: vec2<T>, z: T) -> vec3<T> */
+ /* ctor vec3<T : scalar>(x: T, yz: vec2<T>) -> vec3<T> */
/* conv vec3<T : f32, U : scalar_no_f32>(vec3<U>) -> vec3<f32> */
/* conv vec3<T : f16, U : scalar_no_f16>(vec3<U>) -> vec3<f16> */
/* conv vec3<T : i32, U : scalar_no_i32>(vec3<U>) -> vec3<i32> */
@@ -15025,16 +15055,16 @@
},
{
/* [7] */
- /* ctor vec4<T : scalar>() -> vec4<T> */
- /* ctor vec4<T : scalar>(vec4<T>) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(x: T, y: T, z: T, w: T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(xyz: vec3<T>, w: T) -> vec4<T> */
- /* ctor vec4<T : abstract_or_scalar>(x: T, zyw: vec3<T>) -> vec4<T> */
+ /* ctor vec4<T : concrete_scalar>() -> vec4<T> */
+ /* ctor vec4<T : concrete_scalar>(vec4<T>) -> vec4<T> */
+ /* ctor vec4<T : scalar>(T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(x: T, y: T, z: T, w: T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(xy: vec2<T>, z: T, w: T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(x: T, yz: vec2<T>, w: T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(x: T, y: T, zw: vec2<T>) -> vec4<T> */
+ /* ctor vec4<T : scalar>(xy: vec2<T>, zw: vec2<T>) -> vec4<T> */
+ /* ctor vec4<T : scalar>(xyz: vec3<T>, w: T) -> vec4<T> */
+ /* ctor vec4<T : scalar>(x: T, zyw: vec3<T>) -> vec4<T> */
/* conv vec4<T : f32, U : scalar_no_f32>(vec4<U>) -> vec4<f32> */
/* conv vec4<T : f16, U : scalar_no_f16>(vec4<U>) -> vec4<f16> */
/* conv vec4<T : i32, U : scalar_no_i32>(vec4<U>) -> vec4<i32> */
diff --git a/src/tint/resolver/intrinsic_table.inl.tmpl b/src/tint/resolver/intrinsic_table.inl.tmpl
index fb95eae..7b69df0 100644
--- a/src/tint/resolver/intrinsic_table.inl.tmpl
+++ b/src/tint/resolver/intrinsic_table.inl.tmpl
@@ -1,6 +1,6 @@
{{- /*
--------------------------------------------------------------------------------
-Template file for use with tools/src/cmd/gen to generate builtin_table.inl
+Template file for use with tools/src/cmd/gen to generate intrinsic_table.inl
Used by BuiltinTable.cc for builtin overload resolution.
To update the generated file, run:
diff --git a/src/tint/resolver/intrinsic_table_test.cc b/src/tint/resolver/intrinsic_table_test.cc
index 34cec07..57a6731 100644
--- a/src/tint/resolver/intrinsic_table_test.cc
+++ b/src/tint/resolver/intrinsic_table_test.cc
@@ -793,11 +793,11 @@
vec3() -> vec3<T> where: T is f32, f16, i32, u32 or bool
5 candidate conversions:
- vec3(vec3<U>) -> vec3<f32> where: T is f32, U is i32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<f16> where: T is f16, U is f32, i32, u32 or bool
- vec3(vec3<U>) -> vec3<i32> where: T is i32, U is f32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<u32> where: T is u32, U is f32, f16, i32 or bool
- vec3(vec3<U>) -> vec3<bool> where: T is bool, U is f32, f16, i32 or u32
+ vec3<T>(vec3<U>) -> vec3<f32> where: T is f32, U is abstract-int, abstract-float, i32, f16, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<f16> where: T is f16, U is abstract-int, abstract-float, f32, i32, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<i32> where: T is i32, U is abstract-int, abstract-float, f32, f16, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<u32> where: T is u32, U is abstract-int, abstract-float, f32, f16, i32 or bool
+ vec3<T>(vec3<U>) -> vec3<bool> where: T is bool, U is abstract-int, abstract-float, f32, f16, i32 or u32
)");
}
@@ -819,11 +819,11 @@
vec3() -> vec3<T> where: T is f32, f16, i32, u32 or bool
5 candidate conversions:
- vec3(vec3<U>) -> vec3<f32> where: T is f32, U is i32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<f16> where: T is f16, U is f32, i32, u32 or bool
- vec3(vec3<U>) -> vec3<i32> where: T is i32, U is f32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<u32> where: T is u32, U is f32, f16, i32 or bool
- vec3(vec3<U>) -> vec3<bool> where: T is bool, U is f32, f16, i32 or u32
+ vec3<T>(vec3<U>) -> vec3<f32> where: T is f32, U is abstract-int, abstract-float, i32, f16, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<f16> where: T is f16, U is abstract-int, abstract-float, f32, i32, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<i32> where: T is i32, U is abstract-int, abstract-float, f32, f16, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<u32> where: T is u32, U is abstract-int, abstract-float, f32, f16, i32 or bool
+ vec3<T>(vec3<U>) -> vec3<bool> where: T is bool, U is abstract-int, abstract-float, f32, f16, i32 or u32
)");
}
@@ -875,11 +875,11 @@
vec3(x: T, y: T, z: T) -> vec3<T> where: T is abstract-int, abstract-float, f32, f16, i32, u32 or bool
5 candidate conversions:
- vec3(vec3<U>) -> vec3<f32> where: T is f32, U is i32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<f16> where: T is f16, U is f32, i32, u32 or bool
- vec3(vec3<U>) -> vec3<i32> where: T is i32, U is f32, f16, u32 or bool
- vec3(vec3<U>) -> vec3<u32> where: T is u32, U is f32, f16, i32 or bool
- vec3(vec3<U>) -> vec3<bool> where: T is bool, U is f32, f16, i32 or u32
+ vec3<T>(vec3<U>) -> vec3<f32> where: T is f32, U is abstract-int, abstract-float, i32, f16, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<f16> where: T is f16, U is abstract-int, abstract-float, f32, i32, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<i32> where: T is i32, U is abstract-int, abstract-float, f32, f16, u32 or bool
+ vec3<T>(vec3<U>) -> vec3<u32> where: T is u32, U is abstract-int, abstract-float, f32, f16, i32 or bool
+ vec3<T>(vec3<U>) -> vec3<bool> where: T is bool, U is abstract-int, abstract-float, f32, f16, i32 or u32
)");
}
@@ -904,7 +904,7 @@
ASSERT_NE(result.target, nullptr);
EXPECT_EQ(result.target->ReturnType(), i32);
EXPECT_EQ(result.target->Parameters().Length(), 1u);
- EXPECT_EQ(result.target->Parameters()[0]->Type(), i32);
+ EXPECT_EQ(result.target->Parameters()[0]->Type(), ai);
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 4d31d70..0fe6691 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -251,7 +251,7 @@
[&](const ast::Pointer* t) -> sem::Pointer* {
if (auto* el = Type(t->type)) {
auto access = t->access;
- if (access == ast::kUndefined) {
+ if (access == ast::Access::kInvalid) {
access = DefaultAccessForAddressSpace(t->address_space);
}
return builder_->create<sem::Pointer>(el, t->address_space, access);
@@ -386,13 +386,12 @@
sem::Variable* sem = nullptr;
if (is_global) {
sem = builder_->create<sem::GlobalVariable>(
- v, ty, sem::EvaluationStage::kRuntime, ast::AddressSpace::kNone,
- ast::Access::kUndefined, /* constant_value */ nullptr, sem::BindingPoint{},
- std::nullopt);
+ v, ty, sem::EvaluationStage::kRuntime, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ /* constant_value */ nullptr, sem::BindingPoint{}, std::nullopt);
} else {
sem = builder_->create<sem::LocalVariable>(v, ty, sem::EvaluationStage::kRuntime,
- ast::AddressSpace::kNone,
- ast::Access::kUndefined, current_statement_,
+ ast::AddressSpace::kNone, ast::Access::kInvalid,
+ current_statement_,
/* constant_value */ nullptr);
}
@@ -442,7 +441,7 @@
}
auto* sem = builder_->create<sem::GlobalVariable>(
- v, ty, sem::EvaluationStage::kOverride, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ v, ty, sem::EvaluationStage::kOverride, ast::AddressSpace::kNone, ast::Access::kInvalid,
/* constant_value */ nullptr, sem::BindingPoint{}, std::nullopt);
sem->SetConstructor(rhs);
@@ -527,10 +526,10 @@
auto* sem = is_global ? static_cast<sem::Variable*>(builder_->create<sem::GlobalVariable>(
c, ty, sem::EvaluationStage::kConstant, ast::AddressSpace::kNone,
- ast::Access::kUndefined, value, sem::BindingPoint{}, std::nullopt))
+ ast::Access::kInvalid, value, sem::BindingPoint{}, std::nullopt))
: static_cast<sem::Variable*>(builder_->create<sem::LocalVariable>(
c, ty, sem::EvaluationStage::kConstant, ast::AddressSpace::kNone,
- ast::Access::kUndefined, current_statement_, value));
+ ast::Access::kInvalid, current_statement_, value));
sem->SetConstructor(rhs);
builder_->Sem().Add(c, sem);
@@ -589,7 +588,7 @@
}
auto access = var->declared_access;
- if (access == ast::Access::kUndefined) {
+ if (access == ast::Access::kInvalid) {
access = DefaultAccessForAddressSpace(address_space);
}
@@ -753,9 +752,9 @@
location = c->As<uint32_t>();
}
- auto* sem = builder_->create<sem::Parameter>(
- param, index, ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
- sem::ParameterUsage::kNone, binding_point, location);
+ auto* sem = builder_->create<sem::Parameter>(param, index, ty, ast::AddressSpace::kNone,
+ ast::Access::kInvalid, sem::ParameterUsage::kNone,
+ binding_point, location);
builder_->Sem().Add(param, sem);
return sem;
}
@@ -1789,7 +1788,7 @@
static_cast<uint32_t>(i), // index
arr->ElemType(), // type
ast::AddressSpace::kNone, // address_space
- ast::Access::kUndefined);
+ ast::Access::kInvalid);
});
return builder_->create<sem::TypeConstructor>(arr, std::move(params),
args_stage);
@@ -1818,7 +1817,7 @@
static_cast<uint32_t>(i), // index
str->Members()[i]->Type(), // type
ast::AddressSpace::kNone, // address_space
- ast::Access::kUndefined); // access
+ ast::Access::kInvalid); // access
}
return builder_->create<sem::TypeConstructor>(str, std::move(params),
args_stage);
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index 501cd0f..923f3ff 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -17,6 +17,7 @@
#include <functional>
#include <memory>
+#include <ostream>
#include <string>
#include <tuple>
#include <utility>
@@ -174,13 +175,15 @@
struct ptr {};
/// Type used to accept scalars as arguments. Can be either a single value that gets splatted for
-/// composite types, or all values requried by the composite type.
+/// composite types, or all values required by the composite type.
struct ScalarArgs {
/// Constructor
+ ScalarArgs() = default;
+
+ /// Constructor
/// @param single_value single value to initialize with
template <typename T>
- ScalarArgs(T single_value) // NOLINT: implicit on purpose
- : values(utils::Vector<Storage, 1>{single_value}) {}
+ explicit ScalarArgs(T single_value) : values(utils::Vector<Storage, 1>{single_value}) {}
/// Constructor
/// @param all_values all values to initialize the composite type with
@@ -192,6 +195,10 @@
}
}
+ /// @param other the other ScalarArgs to compare against
+ /// @returns true if all values are equal to the values in @p other
+ bool operator==(const ScalarArgs& other) const { return values == other.values; }
+
/// Valid scalar types for args
using Storage = std::variant<i32, u32, f32, f16, AInt, AFloat, bool>;
@@ -199,10 +206,28 @@
utils::Vector<Storage, 16> values;
};
+/// @param o the std::ostream to write to
+/// @param args the ScalarArgs
+/// @return the std::ostream so calls can be chained
+inline std::ostream& operator<<(std::ostream& o, const ScalarArgs& args) {
+ o << "[";
+ bool first = true;
+ for (auto& val : args.values) {
+ if (!first) {
+ o << ", ";
+ }
+ first = false;
+ std::visit([&](auto&& v) { o << v; }, val);
+ }
+ o << "]";
+ return o;
+}
+
using ast_type_func_ptr = const ast::Type* (*)(ProgramBuilder& b);
using ast_expr_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, ScalarArgs args);
using ast_expr_from_double_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, double v);
using sem_type_func_ptr = const sem::Type* (*)(ProgramBuilder& b);
+using type_name_func_ptr = std::string (*)();
template <typename T>
struct DataType {};
@@ -241,7 +266,7 @@
/// @param v arg of type double that will be cast to bool.
/// @return a new AST expression of the bool type
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "bool"; }
@@ -272,7 +297,7 @@
/// @param v arg of type double that will be cast to i32.
/// @return a new AST i32 literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "i32"; }
@@ -303,7 +328,7 @@
/// @param v arg of type double that will be cast to u32.
/// @return a new AST u32 literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "u32"; }
@@ -334,7 +359,7 @@
/// @param v arg of type double that will be cast to f32.
/// @return a new AST f32 literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<f32>(v));
+ return Expr(b, ScalarArgs{static_cast<f32>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "f32"; }
@@ -365,7 +390,7 @@
/// @param v arg of type double that will be cast to f16.
/// @return a new AST f16 literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "f16"; }
@@ -395,7 +420,7 @@
/// @param v arg of type double that will be cast to AFloat.
/// @return a new AST abstract-float literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "abstract-float"; }
@@ -425,7 +450,7 @@
/// @param v arg of type double that will be cast to AInt.
/// @return a new AST abstract-int literal value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() { return "abstract-int"; }
@@ -463,7 +488,7 @@
const bool one_value = args.values.Length() == 1;
utils::Vector<const ast::Expression*, N> r;
for (size_t i = 0; i < N; ++i) {
- r.Push(DataType<T>::Expr(b, one_value ? args.values[0] : args.values[i]));
+ r.Push(DataType<T>::Expr(b, ScalarArgs{one_value ? args.values[0] : args.values[i]}));
}
return r;
}
@@ -471,7 +496,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST vector value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() {
@@ -514,7 +539,7 @@
utils::Vector<const ast::Expression*, N> r;
for (uint32_t i = 0; i < N; ++i) {
if (one_value) {
- r.Push(DataType<vec<M, T>>::Expr(b, args.values[0]));
+ r.Push(DataType<vec<M, T>>::Expr(b, ScalarArgs{args.values[0]}));
} else {
utils::Vector<T, M> v;
for (size_t j = 0; j < M; ++j) {
@@ -529,7 +554,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST matrix value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() {
@@ -585,7 +610,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST expression of the alias type
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
@@ -626,7 +651,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST expression of the pointer type
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
@@ -680,7 +705,7 @@
const bool one_value = args.values.Length() == 1;
utils::Vector<const ast::Expression*, N> r;
for (uint32_t i = 0; i < N; i++) {
- r.Push(DataType<T>::Expr(b, one_value ? args.values[0] : args.values[i]));
+ r.Push(DataType<T>::Expr(b, ScalarArgs{one_value ? args.values[0] : args.values[i]}));
}
return r;
}
@@ -688,7 +713,7 @@
/// @param v arg of type double that will be cast to ElementType
/// @return a new AST array value expression
static inline const ast::Expression* ExprFromDouble(ProgramBuilder& b, double v) {
- return Expr(b, static_cast<ElementType>(v));
+ return Expr(b, ScalarArgs{static_cast<ElementType>(v)});
}
/// @returns the WGSL name for the type
static inline std::string Name() {
@@ -706,13 +731,23 @@
ast_expr_from_double_func_ptr expr_from_double;
/// sem type create function
sem_type_func_ptr sem;
+ /// type name function
+ type_name_func_ptr name;
};
+/// @param o the std::ostream to write to
+/// @param ptrs the CreatePtrs
+/// @return the std::ostream so calls can be chained
+inline std::ostream& operator<<(std::ostream& o, const CreatePtrs& ptrs) {
+ return o << (ptrs.name ? ptrs.name() : "<unknown>");
+}
+
/// Returns a CreatePtrs struct instance with all creation pointer types for
/// type `T`
template <typename T>
constexpr CreatePtrs CreatePtrsFor() {
- return {DataType<T>::AST, DataType<T>::Expr, DataType<T>::ExprFromDouble, DataType<T>::Sem};
+ return {DataType<T>::AST, DataType<T>::Expr, DataType<T>::ExprFromDouble, DataType<T>::Sem,
+ DataType<T>::Name};
}
/// Value<T> is an instance of a value of type DataType<T>. Useful for storing values to create
@@ -729,15 +764,15 @@
/// Creates a Value<T> with `args`
/// @param args the args that will be passed to the expression
/// @returns a Value<T>
- static Value Create(ScalarArgs args) { return Value{DataType::Expr, std::move(args)}; }
+ static Value Create(ScalarArgs args) { return Value{CreatePtrsFor<T>(), std::move(args)}; }
/// Creates an `ast::Expression` for the type T passing in previously stored args
/// @param b the ProgramBuilder
/// @returns an expression node
- const ast::Expression* Expr(ProgramBuilder& b) const { return (*expr)(b, args); }
+ const ast::Expression* Expr(ProgramBuilder& b) const { return (*create.expr)(b, args); }
- /// ast expression type create function
- ast_expr_func_ptr expr;
+ /// functions to create values / types of the value
+ CreatePtrs create;
/// args to create expression with
ScalarArgs args;
};
@@ -764,7 +799,7 @@
/// Creates a `Value<T>` from a scalar `v`
template <typename T>
auto Val(T v) {
- return Value<T>::Create(v);
+ return Value<T>::Create(ScalarArgs{v});
}
/// Creates a `Value<vec<N, T>>` from N scalar `args`
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 7cc5cef..1271f06 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -1165,7 +1165,7 @@
// var a : texture_storage_1d<ru32int>;
auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
- ast::TexelFormat::kR32Uint, ast::Access::kUndefined);
+ ast::TexelFormat::kR32Uint, ast::Access::kInvalid);
GlobalVar("a", st, Group(0_a), Binding(0_a));
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 687d17d..35ba7e9 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -263,7 +263,7 @@
switch (t->access) {
case ast::Access::kWrite:
break;
- case ast::Access::kUndefined:
+ case ast::Access::kInvalid:
AddError("storage texture missing access control", t->source);
return false;
default:
@@ -344,8 +344,8 @@
break; // Allowed an initializer
default:
// https://gpuweb.github.io/gpuweb/wgsl/#var-and-let
- // Optionally has an initializer expression, if the variable is in the
- // private or function address spacees.
+ // Optionally has an initializer expression, if the variable is in the private or
+ // function address spacees.
AddError("var of address space '" + utils::ToString(address_space) +
"' cannot have an initializer. var initializers are only "
"supported for the address spacees "
@@ -440,9 +440,8 @@
return false;
}
- // For uniform buffers, validate that the number of bytes between the
- // previous member of type struct and the current is a multiple of 16
- // bytes.
+ // For uniform buffers, validate that the number of bytes between the previous member of
+ // type struct and the current is a multiple of 16 bytes.
auto* const prev_member = (i == 0) ? nullptr : str->Members()[i - 1];
if (prev_member && is_uniform_struct(prev_member->Type())) {
const uint32_t prev_to_curr_offset = m->Offset() - prev_member->Offset();
@@ -469,24 +468,22 @@
}
}
- // For uniform buffer array members, validate that array elements are
- // aligned to 16 bytes
+ // For uniform buffer array members, validate that array elements are aligned to 16 bytes
if (auto* arr = store_ty->As<sem::Array>()) {
// Recurse into the element type.
- // TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested
- // element type here, but we can't easily get that from the semantic node.
- // We should consider recursing through the AST type nodes instead.
+ // TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested element type here, but
+ // we can't easily get that from the semantic node. We should consider recursing through the
+ // AST type nodes instead.
if (!AddressSpaceLayout(arr->ElemType(), address_space, source, layouts)) {
return false;
}
if (address_space == ast::AddressSpace::kUniform) {
- // We already validated that this array member is itself aligned to 16
- // bytes above, so we only need to validate that stride is a multiple
- // of 16 bytes.
+ // We already validated that this array member is itself aligned to 16 bytes above, so
+ // we only need to validate that stride is a multiple of 16 bytes.
if (arr->Stride() % 16 != 0) {
- // Since WGSL has no stride attribute, try to provide a useful hint
- // for how the shader author can resolve the issue.
+ // Since WGSL has no stride attribute, try to provide a useful hint for how the
+ // shader author can resolve the issue.
std::string hint;
if (arr->ElemType()->is_scalar()) {
hint =
@@ -622,9 +619,9 @@
}
// https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
- // The access mode always has a default, and except for variables in the
- // storage address space, must not be written.
- if (var->declared_access != ast::Access::kUndefined) {
+ // The access mode always has a default, and except for variables in the storage address
+ // space, must not be written.
+ if (var->declared_access != ast::Access::kInvalid) {
if (global->AddressSpace() == ast::AddressSpace::kStorage) {
// The access mode for the storage address space can only be 'read' or
// 'read_write'.
@@ -684,8 +681,7 @@
case ast::AddressSpace::kStorage:
case ast::AddressSpace::kHandle: {
// https://gpuweb.github.io/gpuweb/wgsl/#resource-interface
- // Each resource variable must be declared with both group and binding
- // attributes.
+ // Each resource variable must be declared with both group and binding attributes.
if (!decl->HasBindingPoint()) {
AddError("resource variables require @group and @binding attributes", decl->source);
return false;
@@ -709,8 +705,8 @@
}
// https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
-// Atomic types may only be instantiated by variables in the workgroup storage
-// class or by storage buffer variables with a read_write access mode.
+// Atomic types may only be instantiated by variables in the workgroup storage class or by storage
+// buffer variables with a read_write access mode.
bool Validator::AtomicVariable(
const sem::Variable* var,
std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const {
@@ -763,9 +759,8 @@
if (storage_ty->is_handle() && var->declared_address_space != ast::AddressSpace::kNone) {
// https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
- // If the store type is a texture type or a sampler type, then the
- // variable declaration must not have a address space attribute. The
- // address space will always be handle.
+ // If the store type is a texture type or a sampler type, then the variable declaration must
+ // not have a address space attribute. The address space will always be handle.
AddError(
"variables of type '" + sem_.TypeNameOf(storage_ty) + "' must not have a address space",
var->source);
@@ -1026,7 +1021,7 @@
}
if (attr->type == ast::InterpolationType::kFlat &&
- attr->sampling != ast::InterpolationSampling::kNone) {
+ attr->sampling != ast::InterpolationSampling::kInvalid) {
AddError("flat interpolation attribute must not have a sampling parameter", attr->source);
return false;
}
@@ -1127,10 +1122,10 @@
auto* decl = func->Declaration();
// Use a lambda to validate the entry point attributes for a type.
- // Persistent state is used to track which builtins and locations have
- // already been seen, in order to catch conflicts.
- // TODO(jrprice): This state could be stored in sem::Function instead, and
- // then passed to sem::Function since it would be useful there too.
+ // Persistent state is used to track which builtins and locations have already been seen, in
+ // order to catch conflicts.
+ // TODO(jrprice): This state could be stored in sem::Function instead, and then passed to
+ // sem::Function since it would be useful there too.
std::unordered_set<ast::BuiltinValue> builtins;
std::unordered_set<uint32_t> locations;
enum class ParamOrRetType {
@@ -1145,8 +1140,7 @@
bool is_struct_member,
std::optional<uint32_t> location) {
// Temporally forbid using f16 types in entry point IO.
- // TODO(tint:1473, tint:1502): Remove this error after f16 is supported in entry point
- // IO.
+ // TODO(tint:1473, tint:1502): Remove this error after f16 is supported in entry point IO.
if (Is<sem::F16>(sem::Type::DeepestElementOf(ty))) {
AddError("entry point IO of f16 types is not implemented yet", source);
return false;
@@ -1324,9 +1318,8 @@
}
}
- // Clear IO sets after parameter validation. Builtin and location attributes
- // in return types should be validated independently from those used in
- // parameters.
+ // Clear IO sets after parameter validation. Builtin and location attributes in return types
+ // should be validated independently from those used in parameters.
builtins.clear();
locations.clear();
@@ -1352,20 +1345,16 @@
}
}
if (!found) {
- AddError(
- "a vertex shader must include the 'position' builtin in its return "
- "type",
- decl->source);
+ AddError("a vertex shader must include the 'position' builtin in its return type",
+ decl->source);
return false;
}
}
if (decl->PipelineStage() == ast::PipelineStage::kCompute) {
if (!ast::HasAttribute<ast::WorkgroupAttribute>(decl->attributes)) {
- AddError(
- "a compute shader must include 'workgroup_size' in its "
- "attributes",
- decl->source);
+ AddError("a compute shader must include 'workgroup_size' in its attributes",
+ decl->source);
return false;
}
}
@@ -1385,17 +1374,15 @@
IsValidationEnabled(res.first->second->attributes,
ast::DisabledValidation::kBindingPointCollision)) {
// https://gpuweb.github.io/gpuweb/wgsl/#resource-interface
- // Bindings must not alias within a shader stage: two different
- // variables in the resource interface of a given shader must not have
- // the same group and binding values, when considered as a pair of
- // values.
+ // Bindings must not alias within a shader stage: two different variables in the
+ // resource interface of a given shader must not have the same group and binding values,
+ // when considered as a pair of values.
auto func_name = symbols_.NameFor(decl->symbol);
- AddError("entry point '" + func_name +
- "' references multiple variables that use the "
- "same resource binding @group(" +
- std::to_string(bp.group) + "), @binding(" + std::to_string(bp.binding) +
- ")",
- var_decl->source);
+ AddError(
+ "entry point '" + func_name +
+ "' references multiple variables that use the same resource binding @group(" +
+ std::to_string(bp.group) + "), @binding(" + std::to_string(bp.binding) + ")",
+ var_decl->source);
AddNote("first resource binding usage declared here", res.first->second->source);
return false;
}
@@ -1453,9 +1440,9 @@
if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
auto fail = [&](const char* note_msg, const Source& note_src) {
constexpr const char* kErrorMsg =
- "break statement in a continuing block must be the single statement "
- "of an if statement's true or false block, and that if statement "
- "must be the last statement of the continuing block";
+ "break statement in a continuing block must be the single statement of an if "
+ "statement's true or false block, and that if statement must be the last statement "
+ "of the continuing block";
AddError(kErrorMsg, stmt->Declaration()->source);
AddNote(note_msg, note_src);
return false;
@@ -1491,15 +1478,14 @@
if (if_stmt->Parent()->Declaration() != continuing) {
return fail(
- "if statement containing break statement is not directly in "
- "continuing block",
+ "if statement containing break statement is not directly in continuing block",
if_stmt->Declaration()->source);
}
if (auto* cont_block = continuing->As<ast::BlockStatement>()) {
if (if_stmt->Declaration() != cont_block->Last()) {
return fail(
- "if statement containing break statement is not the last "
- "statement of the continuing block",
+ "if statement containing break statement is not the last statement of the "
+ "continuing block",
if_stmt->Declaration()->source);
}
}
@@ -1570,10 +1556,8 @@
if (c->Declaration() != s->Declaration()->body.Back()) {
return true;
}
- AddError(
- "a fallthrough statement must not be used in the last switch "
- "case",
- stmt->Declaration()->source);
+ AddError("a fallthrough statement must not be used in the last switch case",
+ stmt->Declaration()->source);
return false;
}
}
@@ -1826,8 +1810,8 @@
IsValidationEnabled(param->Declaration()->attributes,
ast::DisabledValidation::kIgnoreInvalidPointerArgument)) {
AddError(
- "expected an address-of expression of a variable identifier "
- "expression or a function parameter",
+ "expected an address-of expression of a variable identifier expression or a "
+ "function parameter",
arg_expr->source);
return false;
}
@@ -1886,8 +1870,7 @@
auto* value_ty = sem_.TypeOf(value);
if (member->Type() != value_ty->UnwrapRef()) {
AddError(
- "type in struct constructor does not match struct member type: "
- "expected '" +
+ "type in struct constructor does not match struct member type: expected '" +
sem_.TypeNameOf(member->Type()) + "', found '" + sem_.TypeNameOf(value_ty) +
"'",
value->source);
@@ -2140,9 +2123,8 @@
// at least the size of the element type, and be a multiple of the
// element type's alignment value.
AddError(
- "arrays decorated with the stride attribute must have a stride "
- "that is at least the size of the element type, and be a multiple "
- "of the element type's alignment value",
+ "arrays decorated with the stride attribute must have a stride that is at least the "
+ "size of the element type, and be a multiple of the element type's alignment value",
attr->source);
return false;
}
@@ -2189,8 +2171,7 @@
}
} else if (!IsFixedFootprint(member->Type())) {
AddError(
- "a struct that contains a runtime array cannot be nested inside "
- "another struct",
+ "a struct that contains a runtime array cannot be nested inside another struct",
member->Declaration()->source);
return false;
}
@@ -2283,8 +2264,8 @@
AddError("cannot apply 'location' attribute to declaration of type '" + invalid_type + "'",
source);
AddNote(
- "'location' attribute must only be applied to declarations of "
- "numeric scalar or numeric vector type",
+ "'location' attribute must only be applied to declarations of numeric scalar or "
+ "numeric vector type",
loc_attr->source);
return false;
}
@@ -2304,11 +2285,9 @@
const sem::Type* ret_type,
sem::Statement* current_statement) const {
if (func_type->UnwrapRef() != ret_type) {
- AddError(
- "return statement type must match its function "
- "return type, returned '" +
- sem_.TypeNameOf(ret_type) + "', expected '" + sem_.TypeNameOf(func_type) + "'",
- ret->source);
+ AddError("return statement type must match its function return type, returned '" +
+ sem_.TypeNameOf(ret_type) + "', expected '" + sem_.TypeNameOf(func_type) + "'",
+ ret->source);
return false;
}
@@ -2327,10 +2306,8 @@
bool Validator::SwitchStatement(const ast::SwitchStatement* s) {
auto* cond_ty = sem_.TypeOf(s->condition)->UnwrapRef();
if (!cond_ty->is_integer_scalar()) {
- AddError(
- "switch statement selector expression must be of a "
- "scalar integer type",
- s->condition->source);
+ AddError("switch statement selector expression must be of a scalar integer type",
+ s->condition->source);
return false;
}
@@ -2351,8 +2328,7 @@
for (auto* selector : case_stmt->selectors) {
if (cond_ty != sem_.TypeOf(selector)) {
AddError(
- "the case selector values must have the same "
- "type as the selector expression.",
+ "the case selector values must have the same type as the selector expression.",
case_stmt->source);
return false;
}
@@ -2397,8 +2373,8 @@
if (!ty->IsConstructible() &&
!ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler, sem::AbstractNumeric>()) {
AddError("cannot assign '" + sem_.TypeNameOf(rhs_ty) +
- "' to '_'. '_' can only be assigned a constructible, pointer, "
- "texture or sampler type",
+ "' to '_'. '_' can only be assigned a constructible, pointer, texture or "
+ "sampler type",
rhs->source);
return false;
}
diff --git a/src/tint/sem/pointer.cc b/src/tint/sem/pointer.cc
index 3918233..cb73724 100644
--- a/src/tint/sem/pointer.cc
+++ b/src/tint/sem/pointer.cc
@@ -25,7 +25,7 @@
Pointer::Pointer(const Type* subtype, ast::AddressSpace address_space, ast::Access access)
: subtype_(subtype), address_space_(address_space), access_(access) {
TINT_ASSERT(Semantic, !subtype->Is<Reference>());
- TINT_ASSERT(Semantic, access != ast::Access::kUndefined);
+ TINT_ASSERT(Semantic, access != ast::Access::kInvalid);
}
size_t Pointer::Hash() const {
diff --git a/src/tint/sem/reference.cc b/src/tint/sem/reference.cc
index a3a9f24..297b779 100644
--- a/src/tint/sem/reference.cc
+++ b/src/tint/sem/reference.cc
@@ -24,7 +24,7 @@
Reference::Reference(const Type* subtype, ast::AddressSpace address_space, ast::Access access)
: subtype_(subtype), address_space_(address_space), access_(access) {
TINT_ASSERT(Semantic, !subtype->Is<Reference>());
- TINT_ASSERT(Semantic, access != ast::Access::kUndefined);
+ TINT_ASSERT(Semantic, access != ast::Access::kInvalid);
}
size_t Reference::Hash() const {
diff --git a/src/tint/templates/enums.tmpl.inc b/src/tint/templates/enums.tmpl.inc
index d05d763..4726d80 100644
--- a/src/tint/templates/enums.tmpl.inc
+++ b/src/tint/templates/enums.tmpl.inc
@@ -28,6 +28,14 @@
/// @returns the parsed enum, or {{$enum}}::kInvalid if the string could not be parsed.
{{$enum}} Parse{{$enum}}(std::string_view str);
+constexpr const char* k{{$enum}}Strings[] = {
+{{- range $entry := $.Entries }}
+{{- if not $entry.IsInternal}}
+ "{{$entry.Name}}",
+{{- end }}
+{{- end }}
+};
+
{{- end -}}
diff --git a/src/tint/transform/binding_remapper.cc b/src/tint/transform/binding_remapper.cc
index a9a3dfb..66d8e99 100644
--- a/src/tint/transform/binding_remapper.cc
+++ b/src/tint/transform/binding_remapper.cc
@@ -121,7 +121,7 @@
auto ac_it = remappings->access_controls.find(from);
if (ac_it != remappings->access_controls.end()) {
ast::Access ac = ac_it->second;
- if (ac > ast::Access::kLastValid) {
+ if (ac == ast::Access::kInvalid) {
ctx.dst->Diagnostics().add_error(
diag::System::Transform,
"invalid access mode (" + std::to_string(static_cast<uint32_t>(ac)) + ")");
diff --git a/src/tint/transform/canonicalize_entry_point_io.cc b/src/tint/transform/canonicalize_entry_point_io.cc
index 0ff1084..38a1b28 100644
--- a/src/tint/transform/canonicalize_entry_point_io.cc
+++ b/src/tint/transform/canonicalize_entry_point_io.cc
@@ -45,6 +45,42 @@
std::optional<uint32_t> location;
};
+/// FXC is sensitive to field order in structures, this is used by StructMemberComparator to ensure
+/// that FXC is happy with the order of emitted fields.
+uint32_t BuiltinOrder(ast::BuiltinValue builtin) {
+ switch (builtin) {
+ case ast::BuiltinValue::kPosition:
+ return 1;
+ case ast::BuiltinValue::kVertexIndex:
+ return 2;
+ case ast::BuiltinValue::kInstanceIndex:
+ return 3;
+ case ast::BuiltinValue::kFrontFacing:
+ return 4;
+ case ast::BuiltinValue::kFragDepth:
+ return 5;
+ case ast::BuiltinValue::kLocalInvocationId:
+ return 6;
+ case ast::BuiltinValue::kLocalInvocationIndex:
+ return 7;
+ case ast::BuiltinValue::kGlobalInvocationId:
+ return 8;
+ case ast::BuiltinValue::kWorkgroupId:
+ return 9;
+ case ast::BuiltinValue::kNumWorkgroups:
+ return 10;
+ case ast::BuiltinValue::kSampleIndex:
+ return 11;
+ case ast::BuiltinValue::kSampleMask:
+ return 12;
+ case ast::BuiltinValue::kPointSize:
+ return 13;
+ default:
+ break;
+ }
+ 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.
@@ -68,8 +104,8 @@
// `b` has location attribute and `a` does not: `b` goes first.
return false;
}
- // Both are builtins: order doesn't matter, just use enum value.
- return a_blt->builtin < b_blt->builtin;
+ // Both are builtins: order matters for FXC.
+ return BuiltinOrder(a_blt->builtin) < BuiltinOrder(b_blt->builtin);
}
}
@@ -187,7 +223,7 @@
(ast::HasAttribute<ast::LocationAttribute>(attributes) ||
cfg.shader_style == ShaderStyle::kSpirv)) {
attributes.Push(ctx.dst->Interpolate(ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone));
+ ast::InterpolationSampling::kInvalid));
}
// Disable validation for use of the `input` address space.
@@ -256,7 +292,7 @@
ast::HasAttribute<ast::LocationAttribute>(attributes) &&
!ast::HasAttribute<ast::InterpolateAttribute>(attributes)) {
attributes.Push(ctx.dst->Interpolate(ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone));
+ ast::InterpolationSampling::kInvalid));
}
// In GLSL, if it's a builtin, override the name with the
diff --git a/src/tint/transform/clamp_frag_depth.cc b/src/tint/transform/clamp_frag_depth.cc
new file mode 100644
index 0000000..1be231a
--- /dev/null
+++ b/src/tint/transform/clamp_frag_depth.cc
@@ -0,0 +1,202 @@
+// 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/clamp_frag_depth.h"
+
+ #include <utility>
+
+#include "src/tint/ast/attribute.h"
+#include "src/tint/ast/builtin_attribute.h"
+#include "src/tint/ast/builtin_value.h"
+#include "src/tint/ast/function.h"
+#include "src/tint/ast/module.h"
+#include "src/tint/ast/struct.h"
+#include "src/tint/ast/type.h"
+#include "src/tint/program_builder.h"
+#include "src/tint/sem/function.h"
+#include "src/tint/sem/statement.h"
+#include "src/tint/sem/struct.h"
+#include "src/tint/utils/scoped_assignment.h"
+#include "src/tint/utils/vector.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::transform::ClampFragDepth);
+
+namespace tint::transform {
+
+namespace {
+
+bool ContainsFragDepth(utils::VectorRef<const ast::Attribute*> attributes) {
+ for (auto* attribute : attributes) {
+ if (auto* builtin_attribute = attribute->As<ast::BuiltinAttribute>()) {
+ if (builtin_attribute->builtin == ast::BuiltinValue::kFragDepth) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ReturnsFragDepthAsValue(const ast::Function* fn) {
+ return ContainsFragDepth(fn->return_type_attributes);
+}
+
+bool ReturnsFragDepthInStruct(const sem::Info& sem, const ast::Function* fn) {
+ if (auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()) {
+ for (auto* member : struct_ty->Members()) {
+ if (ContainsFragDepth(member->Declaration()->attributes)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+} // anonymous namespace
+
+ClampFragDepth::ClampFragDepth() = default;
+ClampFragDepth::~ClampFragDepth() = default;
+
+bool ClampFragDepth::ShouldRun(const Program* program, const DataMap&) const {
+ 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;
+}
+
+void ClampFragDepth::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
+ // Abort on any use of push constants in the module.
+ for (auto* global : ctx.src->AST().GlobalVariables()) {
+ if (auto* var = global->As<ast::Var>()) {
+ if (var->declared_address_space == ast::AddressSpace::kPushConstant) {
+ TINT_ICE(Transform, ctx.dst->Diagnostics())
+ << "ClampFragDepth doesn't know how to handle module that already use push "
+ "constants.";
+ return;
+ }
+ }
+ }
+
+ auto& b = *ctx.dst;
+ auto& sem = ctx.src->Sem();
+ auto& sym = ctx.src->Symbols();
+
+ // At least one entry-point needs clamping. Add the following to the module:
+ //
+ // enable chromium_experimental_push_constant;
+ //
+ // struct FragDepthClampArgs {
+ // min : f32,
+ // max : f32,
+ // }
+ // var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+ //
+ // fn clamp_frag_depth(v : f32) -> f32 {
+ // return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+ // }
+ b.Enable(ast::Extension::kChromiumExperimentalPushConstant);
+
+ b.Structure(b.Symbols().New("FragDepthClampArgs"),
+ utils::Vector{b.Member("min", b.ty.f32()), b.Member("max", b.ty.f32())});
+
+ auto args_sym = b.Symbols().New("frag_depth_clamp_args");
+ b.GlobalVar(args_sym, b.ty.type_name("FragDepthClampArgs"), ast::AddressSpace::kPushConstant);
+
+ auto base_fn_sym = b.Symbols().New("clamp_frag_depth");
+ b.Func(base_fn_sym, utils::Vector{b.Param("v", b.ty.f32())}, b.ty.f32(),
+ utils::Vector{b.Return(b.Call("clamp", "v", b.MemberAccessor(args_sym, "min"),
+ b.MemberAccessor(args_sym, "max")))});
+
+ // If true, the currently cloned function returns frag depth directly as a scalar
+ bool returns_frag_depth_as_value = false;
+
+ // If valid, the currently cloned function returns frag depth in a struct
+ // The symbol is the name of the helper function to apply the depth clamping.
+ Symbol returns_frag_depth_as_struct_helper;
+
+ // Map of io struct to helper function to return the structure with the depth clamping applied.
+ utils::Hashmap<const ast::Struct*, Symbol, 4u> io_structs_clamp_helpers;
+
+ // Register a callback that will be called for each visted AST function.
+ // This call wraps the cloning of the function's statements, and will assign to
+ // `returns_frag_depth_as_value` or `returns_frag_depth_as_struct_helper` if the function's
+ // return value requires depth clamping.
+ ctx.ReplaceAll([&](const ast::Function* fn) {
+ if (fn->PipelineStage() != ast::PipelineStage::kFragment) {
+ return ctx.CloneWithoutTransform(fn);
+ }
+
+ if (ReturnsFragDepthAsValue(fn)) {
+ TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_value, true);
+ return ctx.CloneWithoutTransform(fn);
+ }
+
+ if (ReturnsFragDepthInStruct(sem, fn)) {
+ // At most once per I/O struct, add the conversion function:
+ //
+ // fn clamp_frag_depth_S(s : S) -> S {
+ // return S(s.first, s.second, clamp_frag_depth(s.frag_depth), s.last);
+ // }
+ auto* struct_ty = sem.Get(fn)->ReturnType()->As<sem::Struct>()->Declaration();
+ auto helper = io_structs_clamp_helpers.GetOrCreate(struct_ty, [&] {
+ auto* return_ty = fn->return_type;
+ auto fn_sym = b.Symbols().New("clamp_frag_depth_" +
+ sym.NameFor(return_ty->As<ast::TypeName>()->name));
+
+ utils::Vector<const ast::Expression*, 8u> constructor_args;
+ for (auto* member : struct_ty->members) {
+ const ast::Expression* arg = b.MemberAccessor("s", ctx.Clone(member->symbol));
+ if (ContainsFragDepth(member->attributes)) {
+ arg = b.Call(base_fn_sym, arg);
+ }
+ constructor_args.Push(arg);
+ }
+ utils::Vector params{b.Param("s", ctx.Clone(return_ty))};
+ utils::Vector body{
+ b.Return(b.Construct(ctx.Clone(return_ty), std::move(constructor_args))),
+ };
+ b.Func(fn_sym, params, ctx.Clone(return_ty), body);
+ return fn_sym;
+ });
+
+ TINT_SCOPED_ASSIGNMENT(returns_frag_depth_as_struct_helper, helper);
+ return ctx.CloneWithoutTransform(fn);
+ }
+
+ return ctx.CloneWithoutTransform(fn);
+ });
+
+ // Replace the return statements `return expr` with `return clamp_frag_depth(expr)`.
+ ctx.ReplaceAll([&](const ast::ReturnStatement* stmt) -> const ast::ReturnStatement* {
+ if (returns_frag_depth_as_value) {
+ return b.Return(stmt->source, b.Call(base_fn_sym, ctx.Clone(stmt->value)));
+ }
+ if (returns_frag_depth_as_struct_helper.IsValid()) {
+ return b.Return(stmt->source,
+ b.Call(returns_frag_depth_as_struct_helper, ctx.Clone(stmt->value)));
+ }
+ return nullptr;
+ });
+
+ ctx.Clone();
+}
+
+} // namespace tint::transform
diff --git a/src/tint/transform/clamp_frag_depth.h b/src/tint/transform/clamp_frag_depth.h
new file mode 100644
index 0000000..3b15f11
--- /dev/null
+++ b/src/tint/transform/clamp_frag_depth.h
@@ -0,0 +1,81 @@
+// 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_CLAMP_FRAG_DEPTH_H_
+#define SRC_TINT_TRANSFORM_CLAMP_FRAG_DEPTH_H_
+
+#include "src/tint/transform/transform.h"
+
+// Forward declarations
+namespace tint {
+class CloneContext;
+} // namespace tint
+
+namespace tint::transform {
+
+/// Add clamping of the `@builtin(frag_depth)` output of fragment shaders using two push constants
+/// provided by the outside environment. For example the following code:
+///
+/// ```
+/// @fragment fn main() -> @builtin(frag_depth) f32 {
+/// return 0.0;
+/// }
+/// ```
+///
+/// Is transformed to:
+///
+/// ```
+/// enable chromium_experimental_push_constant;
+///
+/// struct FragDepthClampArgs {
+/// min : f32,
+/// max : f32,
+/// }
+///
+/// var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+///
+/// fn clamp_frag_depth(v : f32) -> f32 {
+/// return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+/// }
+///
+/// @fragment
+/// fn main() -> @builtin(frag_depth) f32 {
+/// return clamp_frag_depth(0.0);
+/// }
+/// ```
+class ClampFragDepth final : public Castable<ClampFragDepth, Transform> {
+ public:
+ /// Constructor
+ ClampFragDepth();
+ /// Destructor
+ ~ClampFragDepth() 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_CLAMP_FRAG_DEPTH_H_
diff --git a/src/tint/transform/clamp_frag_depth_test.cc b/src/tint/transform/clamp_frag_depth_test.cc
new file mode 100644
index 0000000..b94d5af
--- /dev/null
+++ b/src/tint/transform/clamp_frag_depth_test.cc
@@ -0,0 +1,381 @@
+// Copyright 2021 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/clamp_frag_depth.h"
+
+#include "src/tint/transform/test_helper.h"
+
+namespace tint::transform {
+namespace {
+
+using ClampFragDepthTest = TransformTest;
+
+TEST_F(ClampFragDepthTest, ShouldRunEmptyModule) {
+ auto* src = R"()";
+
+ EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
+}
+
+TEST_F(ClampFragDepthTest, ShouldRunNoFragmentShader) {
+ auto* src = R"(
+ fn f() -> f32 {
+ return 0.0;
+ }
+
+ @compute @workgroup_size(1) fn cs() {
+ }
+
+ @vertex fn vs() -> @builtin(position) vec4<f32> {
+ return vec4<f32>();
+ }
+ )";
+
+ EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
+}
+
+TEST_F(ClampFragDepthTest, ShouldRunFragmentShaderNoReturnType) {
+ auto* src = R"(
+ @fragment fn main() {
+ }
+ )";
+
+ EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
+}
+
+TEST_F(ClampFragDepthTest, ShouldRunFragmentShaderNoFragDepth) {
+ auto* src = R"(
+ @fragment fn main() -> @location(0) f32 {
+ return 0.0;
+ }
+
+ struct S {
+ @location(0) a : f32,
+ @builtin(sample_mask) b : u32,
+ }
+ @fragment fn main2() -> S {
+ return S();
+ }
+ )";
+
+ EXPECT_FALSE(ShouldRun<ClampFragDepth>(src));
+}
+
+TEST_F(ClampFragDepthTest, ShouldRunFragDepthAsDirectReturn) {
+ auto* src = R"(
+ @fragment fn main() -> @builtin(frag_depth) f32 {
+ return 0.0;
+ }
+ )";
+
+ EXPECT_TRUE(ShouldRun<ClampFragDepth>(src));
+}
+
+TEST_F(ClampFragDepthTest, ShouldRunFragDepthInStruct) {
+ auto* src = R"(
+ struct S {
+ @location(0) a : f32,
+ @builtin(frag_depth) b : f32,
+ @location(1) c : f32,
+ }
+ @fragment fn main() -> S {
+ return S();
+ }
+ )";
+
+ EXPECT_TRUE(ShouldRun<ClampFragDepth>(src));
+}
+
+TEST_F(ClampFragDepthTest, SingleReturnOfFragDepth) {
+ auto* src = R"(
+ @fragment fn main() -> @builtin(frag_depth) f32 {
+ return 0.0;
+ }
+ )";
+
+ auto* expect = R"(
+enable chromium_experimental_push_constant;
+
+struct FragDepthClampArgs {
+ min : f32,
+ max : f32,
+}
+
+var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+
+fn clamp_frag_depth(v : f32) -> f32 {
+ return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+}
+
+@fragment
+fn main() -> @builtin(frag_depth) f32 {
+ return clamp_frag_depth(0.0);
+}
+)";
+
+ auto got = Run<ClampFragDepth>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(ClampFragDepthTest, MultipleReturnOfFragDepth) {
+ auto* src = R"(
+ @fragment fn main() -> @builtin(frag_depth) f32 {
+ if (false) {
+ return 1.0;
+ }
+ return 0.0;
+ }
+ )";
+
+ auto* expect = R"(
+enable chromium_experimental_push_constant;
+
+struct FragDepthClampArgs {
+ min : f32,
+ max : f32,
+}
+
+var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+
+fn clamp_frag_depth(v : f32) -> f32 {
+ return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+}
+
+@fragment
+fn main() -> @builtin(frag_depth) f32 {
+ if (false) {
+ return clamp_frag_depth(1.0);
+ }
+ return clamp_frag_depth(0.0);
+}
+)";
+
+ auto got = Run<ClampFragDepth>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(ClampFragDepthTest, OtherFunctionWithoutFragDepth) {
+ auto* src = R"(
+ @fragment fn main() -> @builtin(frag_depth) f32 {
+ return 0.0;
+ }
+ @fragment fn friend() -> @location(0) f32 {
+ return 0.0;
+ }
+ )";
+
+ auto* expect = R"(
+enable chromium_experimental_push_constant;
+
+struct FragDepthClampArgs {
+ min : f32,
+ max : f32,
+}
+
+var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+
+fn clamp_frag_depth(v : f32) -> f32 {
+ return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+}
+
+@fragment
+fn main() -> @builtin(frag_depth) f32 {
+ return clamp_frag_depth(0.0);
+}
+
+@fragment
+fn friend() -> @location(0) f32 {
+ return 0.0;
+}
+)";
+
+ auto got = Run<ClampFragDepth>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(ClampFragDepthTest, SimpleReturnOfStruct) {
+ auto* src = R"(
+ struct S {
+ @builtin(frag_depth) frag_depth : f32,
+ }
+
+ @fragment fn main() -> S {
+ return S(0.0);
+ }
+ )";
+
+ auto* expect = R"(
+enable chromium_experimental_push_constant;
+
+struct FragDepthClampArgs {
+ min : f32,
+ max : f32,
+}
+
+var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+
+fn clamp_frag_depth(v : f32) -> f32 {
+ return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+}
+
+struct S {
+ @builtin(frag_depth)
+ frag_depth : f32,
+}
+
+fn clamp_frag_depth_S(s : S) -> S {
+ return S(clamp_frag_depth(s.frag_depth));
+}
+
+@fragment
+fn main() -> S {
+ return clamp_frag_depth_S(S(0.0));
+}
+)";
+
+ auto got = Run<ClampFragDepth>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(ClampFragDepthTest, MixOfFunctionReturningStruct) {
+ auto* src = R"(
+ struct S {
+ @builtin(frag_depth) frag_depth : f32,
+ }
+ struct S2 {
+ @builtin(frag_depth) frag_depth : f32,
+ }
+
+ @fragment fn returnS() -> S {
+ return S(0.0);
+ }
+ @fragment fn againReturnS() -> S {
+ return S(0.0);
+ }
+ @fragment fn returnS2() -> S2 {
+ return S2(0.0);
+ }
+ )";
+
+ // clamp_frag_depth_S is emitted only once.
+ // S2 gets its own clamping function.
+ auto* expect = R"(
+enable chromium_experimental_push_constant;
+
+struct FragDepthClampArgs {
+ min : f32,
+ max : f32,
+}
+
+var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+
+fn clamp_frag_depth(v : f32) -> f32 {
+ return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+}
+
+struct S {
+ @builtin(frag_depth)
+ frag_depth : f32,
+}
+
+struct S2 {
+ @builtin(frag_depth)
+ frag_depth : f32,
+}
+
+fn clamp_frag_depth_S(s : S) -> S {
+ return S(clamp_frag_depth(s.frag_depth));
+}
+
+@fragment
+fn returnS() -> S {
+ return clamp_frag_depth_S(S(0.0));
+}
+
+@fragment
+fn againReturnS() -> S {
+ return clamp_frag_depth_S(S(0.0));
+}
+
+fn clamp_frag_depth_S2(s : S2) -> S2 {
+ return S2(clamp_frag_depth(s.frag_depth));
+}
+
+@fragment
+fn returnS2() -> S2 {
+ return clamp_frag_depth_S2(S2(0.0));
+}
+)";
+
+ auto got = Run<ClampFragDepth>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(ClampFragDepthTest, ComplexIOStruct) {
+ auto* src = R"(
+ struct S {
+ @location(0) blou : vec4<f32>,
+ @location(1) bi : vec4<f32>,
+ @builtin(frag_depth) frag_depth : f32,
+ @location(2) boul : i32,
+ @builtin(sample_mask) ga : u32,
+ }
+
+ @fragment fn main() -> S {
+ return S(vec4<f32>(), vec4<f32>(), 0.0, 1, 0u);
+ }
+ )";
+
+ auto* expect = R"(
+enable chromium_experimental_push_constant;
+
+struct FragDepthClampArgs {
+ min : f32,
+ max : f32,
+}
+
+var<push_constant> frag_depth_clamp_args : FragDepthClampArgs;
+
+fn clamp_frag_depth(v : f32) -> f32 {
+ return clamp(v, frag_depth_clamp_args.min, frag_depth_clamp_args.max);
+}
+
+struct S {
+ @location(0)
+ blou : vec4<f32>,
+ @location(1)
+ bi : vec4<f32>,
+ @builtin(frag_depth)
+ frag_depth : f32,
+ @location(2)
+ boul : i32,
+ @builtin(sample_mask)
+ ga : u32,
+}
+
+fn clamp_frag_depth_S(s : S) -> S {
+ return S(s.blou, s.bi, clamp_frag_depth(s.frag_depth), s.boul, s.ga);
+}
+
+@fragment
+fn main() -> S {
+ return clamp_frag_depth_S(S(vec4<f32>(), vec4<f32>(), 0.0, 1, 0u));
+}
+)";
+
+ auto got = Run<ClampFragDepth>(src);
+ EXPECT_EQ(expect, str(got));
+}
+
+} // namespace
+} // namespace tint::transform
diff --git a/src/tint/transform/std140_exhaustive_test.cc b/src/tint/transform/std140_exhaustive_test.cc
new file mode 100644
index 0000000..01d2dae
--- /dev/null
+++ b/src/tint/transform/std140_exhaustive_test.cc
@@ -0,0 +1,4872 @@
+// 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/std140.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "src/tint/transform/test_helper.h"
+#include "src/tint/utils/string.h"
+
+namespace tint::transform {
+namespace {
+
+enum class MatrixType { f32, f16 };
+
+struct MatrixCase {
+ uint32_t columns;
+ uint32_t rows;
+ MatrixType type;
+
+ size_t ElementSize() const { return type == MatrixType::f16 ? 2 : 4; }
+
+ size_t ColumnVectorAlign() const { return (rows == 3 ? 4 : rows) * ElementSize(); }
+
+ bool NotStd140Compatible() const { return ColumnVectorAlign() != 16; }
+
+ // Return if this matrix type can be used as element type of an uniform buffer, i.e. the
+ // array stride is multiple of 16.
+ bool CanBeUsedAsUniformArrayElememts() const {
+ const size_t array_stride = columns * ColumnVectorAlign();
+ return (array_stride % 16 == 0);
+ }
+
+ std::string Shape() const { return std::to_string(columns) + "x" + std::to_string(rows); }
+
+ std::string ElementType() const { return type == MatrixType::f16 ? "f16" : "f32"; }
+
+ std::string Mat() const { return "mat" + Shape() + "<" + ElementType() + ">"; }
+
+ std::string ColumnVector() const {
+ return "vec" + std::to_string(rows) + "<" + (type == MatrixType::f32 ? "f32" : "f16") + ">";
+ }
+
+ std::string ColumnVectorSwizzle() const {
+ switch (rows) {
+ case 2:
+ return "yx";
+ case 3:
+ return "yzx";
+ case 4:
+ return "wzxy";
+ }
+ return "";
+ }
+
+ // For each column, replaces "${col_id_for_tmpl}" by column index in `tmpl` to get a string, and
+ // join all these strings with `seperator`. If `tmpl_for_last_column` is not empty, use it
+ // instead of `tmpl` for the last column.
+ std::string JoinTemplatedStringForEachMatrixColumn(
+ std::string tmpl,
+ std::string seperator,
+ std::string tmpl_for_last_column = "") const {
+ std::string result;
+ if (tmpl_for_last_column.size() == 0) {
+ tmpl_for_last_column = tmpl;
+ }
+ for (size_t c = 0; c < columns - 1; c++) {
+ if (c > 0) {
+ result += seperator;
+ }
+ std::string string_for_current_column =
+ utils::ReplaceAll(tmpl, "${col_id_for_tmpl}", std::to_string(c));
+ result += string_for_current_column;
+ }
+ result += seperator;
+ std::string string_for_last_column = utils::ReplaceAll(
+ tmpl_for_last_column, "${col_id_for_tmpl}", std::to_string(columns - 1));
+ result += string_for_last_column;
+ return result;
+ }
+
+ std::string ExpendedColumnVectors(uint32_t leading_space, std::string name) const {
+ std::string space(leading_space, ' ');
+ return JoinTemplatedStringForEachMatrixColumn(
+ space + name + "${col_id_for_tmpl} : " + ColumnVector() + ",", "\n");
+ }
+
+ std::string ExpendedColumnVectorsInline(std::string name, std::string seperator) const {
+ return JoinTemplatedStringForEachMatrixColumn(name + "${col_id_for_tmpl}", seperator);
+ }
+
+ std::string ExpendedColumnVectorsWithLastSize(uint32_t leading_space,
+ std::string name,
+ uint32_t last_size) const {
+ std::string space(leading_space, ' ');
+ return JoinTemplatedStringForEachMatrixColumn(
+ space + name + "${col_id_for_tmpl} : " + ColumnVector() + ",", "\n",
+ space + "@size(" + std::to_string(last_size) + ")\n" + space + name +
+ "${col_id_for_tmpl} : " + ColumnVector() + ",");
+ }
+
+ // Replace user-given fields and predefined fields in a given string `str`.
+ // First, for each pair of string in `replacement_pairs`, replace all occurrences of the first
+ // string of pair with second string. Then, replace several predefined fields with the matrix
+ // information. E.g. for a matrix mat4x3<f32>, would replace "${mat}" with "mat4x3<f32>",
+ // replace "${shape}" with "4x3", "${elem_type}" with "f32", "${col_vector_type}" with
+ // "vec3<f32>", and "${swizzle}" with "yzx".
+ std::string ReplaceFieldsInString(
+ std::string str,
+ std::initializer_list<std::pair<std::string, std::string>> replacement_pairs = {}) const {
+ for (auto& replace : replacement_pairs) {
+ str = utils::ReplaceAll(str, replace.first, replace.second);
+ }
+ str = utils::ReplaceAll(str, "${mat}", Mat());
+ str = utils::ReplaceAll(str, "${shape}", Shape());
+ str = utils::ReplaceAll(str, "${elem_type}", ElementType());
+ str = utils::ReplaceAll(str, "${col_vector_type}", ColumnVector());
+ str = utils::ReplaceAll(str, "${swizzle}", ColumnVectorSwizzle());
+ return str;
+ }
+};
+
+inline std::ostream& operator<<(std::ostream& os, const MatrixCase& c) {
+ return os << c.Mat();
+}
+
+using Std140Test_Matrix = TransformTestWithParam<MatrixCase>;
+
+TEST_P(Std140Test_Matrix, SingleStructMatUniform) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
+
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, CustomAlign) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ before : i32,
+ @align(128)
+ m : ${mat},
+ after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ before : i32,
+ @align(128)
+ m : ${mat},
+ after : i32,
+}
+
+struct S_std140 {
+ before : i32,
+ @align(128i)
+${col_vectors}
+ after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, CustomSizeMat) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ before : i32,
+ @size(128)
+ m : ${mat},
+ after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ uint32_t last_size =
+ 128 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+
+ expect = R"(
+enable f16;
+
+struct S {
+ before : i32,
+ @size(128)
+ m : ${mat},
+ after : i32,
+}
+
+struct S_std140 {
+ before : i32,
+${col_vectors}
+ after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, CustomAlignAndSize) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ before : i32,
+ @align(128) @size(128)
+ m : ${mat},
+ after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ uint32_t last_size =
+ 128 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+
+ expect = R"(
+enable f16;
+
+struct S {
+ before : i32,
+ @align(128) @size(128)
+ m : ${mat},
+ after : i32,
+}
+
+struct S_std140 {
+ before : i32,
+ @align(128i)
+${col_vectors}
+ after : i32,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, MatrixUsageInForLoop) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ for(var i = u32(s.m[0][0]); (i < u32(s.m[i][1])); i += u32(s.m[1][i])) {
+ }
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_1(p0 : u32) -> ${elem_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${elem_type}();
+ }
+ }
+}
+
+fn f() {
+ for(var i = u32(s.m_0[0u]); (i < u32(load_s_m_p0_1(u32(i)))); i += u32(s.m_1[i])) {
+ }
+}
+)";
+
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return s.m_0[1u];
+ // }
+ // case 1u: {
+ // return s.m_1[1u];
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return s.m_${col_id_for_tmpl}[1u];
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadMatrix) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> m : ${mat};
+
+fn f() {
+ let l = m;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> m : mat${shape}_${elem_type};
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let l = conv_mat${shape}_${elem_type}(m);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadColumn_ConstIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : ${mat};
+
+fn f() {
+ let l = a[${cloumn_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
+
+fn f() {
+ let l = a.col${cloumn_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${cloumn_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${cloumn_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col;
+ }
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadColumn_VariableIndex) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : ${mat};
+
+fn f() {
+ let I = 1;
+ let l = a[I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
+
+fn load_a_p0(p0 : u32) -> ${col_vector_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_p0(u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a.col0;
+ // }
+ // case 1u: {
+ // return a.col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a.col${col_id_for_tmpl};
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadColumnSwizzle_ConstIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : ${mat};
+
+fn f() {
+ let l = a[${cloumn_index}].${swizzle};
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
+
+fn f() {
+ let l = a.col${cloumn_index}.${swizzle};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${cloumn_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${cloumn_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col;
+ }
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadColumnSwizzle_VariableIndex) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : ${mat};
+
+fn f() {
+ let I = 1;
+ let l = a[I].${swizzle};
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
+
+fn load_a_p0_${swizzle}(p0 : u32) -> ${col_vector_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_p0_${swizzle}(u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a.col0.${swizzle};
+ // }
+ // case 1u: {
+ // return a.col1.${swizzle};
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a.col${col_id_for_tmpl}.${swizzle};
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : ${mat};
+
+fn f() {
+ let l = a[${col_index}][${row_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
+
+fn f() {
+ let l = a.col${col_index}[${row_index}u];
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ for (uint32_t row = 0; row < matrix.rows; row++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
+ src = utils::ReplaceAll(src, "${row_index}", std::to_string(row));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
+ expect = utils::ReplaceAll(expect, "${row_index}", std::to_string(row));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col << " row " << row;
+ }
+ }
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : ${mat};
+
+fn f() {
+ let I = 0;
+ let l = a[I][${row_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
+
+fn load_a_p0_${row_index}(p0 : u32) -> ${elem_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${elem_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let l = load_a_p0_${row_index}(u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a.col0[${row_index}u];
+ // }
+ // case 1u: {
+ // return a.col1[${row_index}u];
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a.col${col_id_for_tmpl}[${row_index}u];
+ })",
+ "\n");
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t row = 0; row < matrix.rows; row++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${row_index}", std::to_string(row));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${row_index}", std::to_string(row));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing row " << row;
+ }
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : ${mat};
+
+fn f() {
+ let I = 0;
+ let l = a[${col_index}][I];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
+
+fn f() {
+ let I = 0;
+ let l = a.col${col_index}[I];
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col;
+ }
+}
+
+TEST_P(Std140Test_Matrix, MatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : ${mat};
+
+fn f() {
+ let I = 0;
+ let l = a[I][I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : mat${shape}_${elem_type};
+
+fn load_a_p0_p1(p0 : u32, p1 : u32) -> ${elem_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${elem_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let l = load_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a.col0[p1];
+ // }
+ // case 1u: {
+ // return a.col1[p1];
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a.col${col_id_for_tmpl}[p1];
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_NameCollision) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ m_1 : i32,
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ m_1 : i32,
+ m : ${mat},
+}
+
+struct S_std140 {
+ m_1 : i32,
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m__")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_LoadStruct) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let l = s;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_S(val : S_std140) -> S {
+ return S(${mat}(${col_vectors_inline}));
+}
+
+fn f() {
+ let l = conv_S(s);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_LoadMatrix) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let l = s.m;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m() -> ${mat} {
+ let s = &(s);
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let l = load_s_m();
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_LoadColumn_ConstIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let l = s.m[${col_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+ let l = s.m_${col_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col;
+ }
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_LoadColumn_VariableIndex) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let I = 0;
+ let l = s.m[I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0(p0 : u32) -> ${col_vector_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let l = load_s_m_p0(u32(I));
+}
+)";
+
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return s.m_0;
+ // }
+ // case 1u: {
+ // return s.m_1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return s.m_${col_id_for_tmpl};
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vector_type}", matrix.ColumnVector()},
+ {"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let l = s.m[${col_index}][${row_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+ let l = s.m_${col_index}[${row_index}u];
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ for (uint32_t row = 0; row < matrix.rows; row++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
+ src = utils::ReplaceAll(src, "${row_index}", std::to_string(row));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
+ expect = utils::ReplaceAll(expect, "${row_index}", std::to_string(row));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col << " row " << row;
+ }
+ }
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let I = 0;
+ let l = s.m[I][${row_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_${row_index}(p0 : u32) -> ${elem_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${elem_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let l = load_s_m_p0_${row_index}(u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return s.m_0[${row_index}u];
+ // }
+ // case 1u: {
+ // return s.m_1[${row_index}u];
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return s.m_${col_id_for_tmpl}[${row_index}u];
+ })",
+ "\n");
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
+ {"${col_table}", col_table}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t row = 0; row < matrix.rows; row++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${row_index}", std::to_string(row));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${row_index}", std::to_string(row));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing row " << row;
+ }
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let I = 0;
+ let l = s.m[${col_index}][I];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+ let I = 0;
+ let l = s.m_${col_index}[I];
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${col_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${col_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col;
+ }
+}
+
+TEST_P(Std140Test_Matrix, StructMatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let I = 0;
+ let l = s.m[I][I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_p1(p0 : u32, p1 : u32) -> ${elem_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${elem_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let l = load_s_m_p0_p1(u32(I), u32(I));
+}
+)";
+
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return s.m_0[p1];
+ // }
+ // case 1u: {
+ // return s.m_1[p1];
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return s.m_${col_id_for_tmpl}[p1];
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "m_")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadArray) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let l = a;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+ return S(${mat}(${col_vectors_inline}));
+}
+
+fn conv_arr3_S(val : array<S_std140, 3u>) -> array<S, 3u> {
+ var arr : array<S, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_S(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let l = conv_arr3_S(a);
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadStruct_ConstIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let l = a[${array_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+ return S(${mat}(${col_vectors_inline}));
+}
+
+fn f() {
+ let l = conv_S(a[${array_index}u]);
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
+ }
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadStruct_VariableIndex) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn conv_S(val : S_std140) -> S {
+ return S(${mat}(${col_vectors_inline}));
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_S(a[I]);
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadMatrix_ConstArrayIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let l = a[${array_index}].m;
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_${array_index}_m() -> ${mat} {
+ let s = &(a[${array_index}u]);
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let l = load_a_${array_index}_m();
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
+ }
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadMatrix_VariableArrayIndex) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[I].m;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_p0_m(p0 : u32) -> ${mat} {
+ let s = &(a[p0]);
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_p0_m(u32(I));
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let l = a[${array_index}].m[${cloumn_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn f() {
+ let l = a[${array_index}u].m_${cloumn_index};
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ src = utils::ReplaceAll(src, "${cloumn_index}", std::to_string(col));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+ expect = utils::ReplaceAll(expect, "${cloumn_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got))
+ << "accessing array element " << array_index << " col " << col;
+ }
+ }
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[I].m[${cloumn_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn f() {
+ let I = 1;
+ let l = a[I].m_${cloumn_index};
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${cloumn_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${cloumn_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col;
+ }
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[${array_index}].m[I];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_${array_index}_m_p0(p0 : u32) -> ${col_vector_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_${array_index}_m_p0(u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a[${array_index}u].m_0;
+ // }
+ // case 1u: {
+ // return a[${array_index}u].m_1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[${array_index}u].m_${col_id_for_tmpl};
+ })",
+ "\n");
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_table}", col_table}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
+ }
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> a : array<S, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[I].m[I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+
+fn load_a_p0_m_p1(p0 : u32, p1 : u32) -> ${col_vector_type} {
+ switch(p1) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_p0_m_p1(u32(I), u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a[p0].m_0;
+ // }
+ // case 1u: {
+ // return a[p0].m_1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[p0].m_${col_id_for_tmpl};
+ })",
+ "\n");
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructArrayStructMatUniform_Loads) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct Inner {
+ @size(64)
+ m : ${mat},
+}
+
+struct Outer {
+ a : array<Inner, 4>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let K = 0;
+ let l_a : array<Outer, 4> = a;
+ let l_a_1 : Outer = a[1];
+ let l_a_I : Outer = a[I];
+ let l_a_2_a : array<Inner, 4> = a[2].a;
+ let l_a_I_a : array<Inner, 4> = a[I].a;
+ let l_a_3_a_1 : Inner = a[3].a[1];
+ let l_a_3_a_I : Inner = a[3].a[I];
+ let l_a_I_a_1 : Inner = a[I].a[1];
+ let l_a_I_a_J : Inner = a[I].a[J];
+ let l_a_0_a_2_m : ${mat} = a[0].a[2].m;
+ let l_a_0_a_I_m : ${mat} = a[0].a[I].m;
+ let l_a_I_a_2_m : ${mat} = a[I].a[2].m;
+ let l_a_I_a_J_m : ${mat} = a[I].a[J].m;
+ let l_a_1_a_3_m_0 : ${col_vector_type} = a[1].a[3].m[0];
+ let l_a_I_a_J_m_K : ${col_vector_type} = a[I].a[J].m[K];
+ let l_a_2_a_0_m_1_0 : ${elem_type} = a[2].a[0].m[1][0];
+ let l_a_I_a_J_m_K_I : ${elem_type} = a[I].a[J].m[K][I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct Inner {
+ @size(64)
+ m : ${mat},
+}
+
+struct Inner_std140 {
+${col_vectors}
+}
+
+struct Outer {
+ a : array<Inner, 4>,
+}
+
+struct Outer_std140 {
+ a : array<Inner_std140, 4u>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
+
+fn conv_Inner(val : Inner_std140) -> Inner {
+ return Inner(${mat}(${col_vectors_inline_conv_Inner}));
+}
+
+fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
+ var arr : array<Inner, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_Inner(val[i]);
+ }
+ return arr;
+}
+
+fn conv_Outer(val : Outer_std140) -> Outer {
+ return Outer(conv_arr4_Inner(val.a));
+}
+
+fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
+ var arr : array<Outer, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_Outer(val[i]);
+ }
+ return arr;
+}
+
+fn load_a_0_a_2_m() -> ${mat} {
+ let s = &(a[0u].a[2u]);
+ return ${mat}(${col_vectors_inline_load_matrix});
+}
+
+fn load_a_0_a_p0_m(p0 : u32) -> ${mat} {
+ let s = &(a[0u].a[p0]);
+ return ${mat}(${col_vectors_inline_load_matrix});
+}
+
+fn load_a_p0_a_2_m(p0 : u32) -> ${mat} {
+ let s = &(a[p0].a[2u]);
+ return ${mat}(${col_vectors_inline_load_matrix});
+}
+
+fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> ${mat} {
+ let s = &(a[p0].a[p1]);
+ return ${mat}(${col_vectors_inline_load_matrix});
+}
+
+fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> ${col_vector_type} {
+ switch(p2) {
+${col_table_load_column}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> ${elem_type} {
+ switch(p2) {
+${col_table_load_element}
+ default: {
+ return ${elem_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let K = 0;
+ let l_a : array<Outer, 4> = conv_arr4_Outer(a);
+ let l_a_1 : Outer = conv_Outer(a[1u]);
+ let l_a_I : Outer = conv_Outer(a[I]);
+ let l_a_2_a : array<Inner, 4> = conv_arr4_Inner(a[2u].a);
+ let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
+ let l_a_3_a_1 : Inner = conv_Inner(a[3u].a[1u]);
+ let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
+ let l_a_I_a_1 : Inner = conv_Inner(a[I].a[1u]);
+ let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
+ let l_a_0_a_2_m : ${mat} = load_a_0_a_2_m();
+ let l_a_0_a_I_m : ${mat} = load_a_0_a_p0_m(u32(I));
+ let l_a_I_a_2_m : ${mat} = load_a_p0_a_2_m(u32(I));
+ let l_a_I_a_J_m : ${mat} = load_a_p0_a_p1_m(u32(I), u32(J));
+ let l_a_1_a_3_m_0 : ${col_vector_type} = a[1u].a[3u].m_0;
+ let l_a_I_a_J_m_K : ${col_vector_type} = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+ let l_a_2_a_0_m_1_0 : ${elem_type} = a[2u].a[0u].m_1[0u];
+ let l_a_I_a_J_m_K_I : ${elem_type} = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
+}
+)";
+ std::string col_tableLoadColumn = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[p0].a[p1].m_${col_id_for_tmpl};
+ })",
+ "\n");
+ std::string col_tableLoadElement = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[p0].a[p1].m_${col_id_for_tmpl}[p3];
+ })",
+ "\n");
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline_conv_Inner}",
+ matrix.ExpendedColumnVectorsInline("val.m_", ", ")},
+ {"${col_vectors_inline_load_matrix}",
+ matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")},
+ {"${col_table_load_column}", col_tableLoadColumn},
+ {"${col_table_load_element}", col_tableLoadElement}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructArrayStructMatUniform_LoadsViaPtrs) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct Inner {
+ @size(64)
+ m : ${mat},
+}
+
+struct Outer {
+ a : array<Inner, 4>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let K = 0;
+ let p_a = &(a);
+ let p_a_3 = &((*(p_a))[3]);
+ let p_a_I = &((*(p_a))[I]);
+ let p_a_3_a = &((*(p_a_3)).a);
+ let p_a_I_a = &((*(p_a_I)).a);
+ let p_a_3_a_2 = &((*(p_a_3_a))[2]);
+ let p_a_3_a_I = &((*(p_a_3_a))[I]);
+ let p_a_I_a_2 = &((*(p_a_I_a))[2]);
+ let p_a_I_a_J = &((*(p_a_I_a))[J]);
+ let p_a_3_a_2_m = &((*(p_a_3_a_2)).m);
+ let p_a_3_a_I_m = &((*(p_a_3_a_I)).m);
+ let p_a_I_a_2_m = &((*(p_a_I_a_2)).m);
+ let p_a_I_a_J_m = &((*(p_a_I_a_J)).m);
+ let p_a_3_a_2_m_1 = &((*(p_a_3_a_2_m))[1]);
+ let p_a_I_a_J_m_K = &((*(p_a_I_a_J_m))[K]);
+ let l_a : array<Outer, 4> = *(p_a);
+ let l_a_3 : Outer = *(p_a_3);
+ let l_a_I : Outer = *(p_a_I);
+ let l_a_3_a : array<Inner, 4> = *(p_a_3_a);
+ let l_a_I_a : array<Inner, 4> = *(p_a_I_a);
+ let l_a_3_a_2 : Inner = *(p_a_3_a_2);
+ let l_a_3_a_I : Inner = *(p_a_3_a_I);
+ let l_a_I_a_2 : Inner = *(p_a_I_a_2);
+ let l_a_I_a_J : Inner = *(p_a_I_a_J);
+ let l_a_3_a_2_m : ${mat} = *(p_a_3_a_2_m);
+ let l_a_3_a_I_m : ${mat} = *(p_a_3_a_I_m);
+ let l_a_I_a_2_m : ${mat} = *(p_a_I_a_2_m);
+ let l_a_I_a_J_m : ${mat} = *(p_a_I_a_J_m);
+ let l_a_3_a_2_m_1 : ${col_vector_type} = *(p_a_3_a_2_m_1);
+ let l_a_I_a_J_m_K : ${col_vector_type} = *(p_a_I_a_J_m_K);
+ let l_a_2_a_0_m_1_0 : ${elem_type} = (*(p_a_3_a_2_m_1))[0];
+ let l_a_I_a_J_m_K_I : ${elem_type} = (*(p_a_I_a_J_m_K))[I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct Inner {
+ @size(64)
+ m : ${mat},
+}
+
+struct Inner_std140 {
+${col_vectors}
+}
+
+struct Outer {
+ a : array<Inner, 4>,
+}
+
+struct Outer_std140 {
+ a : array<Inner_std140, 4u>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
+
+fn conv_Inner(val : Inner_std140) -> Inner {
+ return Inner(${mat}(${col_vectors_inline_conv_Inner}));
+}
+
+fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
+ var arr : array<Inner, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_Inner(val[i]);
+ }
+ return arr;
+}
+
+fn conv_Outer(val : Outer_std140) -> Outer {
+ return Outer(conv_arr4_Inner(val.a));
+}
+
+fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
+ var arr : array<Outer, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_Outer(val[i]);
+ }
+ return arr;
+}
+
+fn load_a_3_a_2_m() -> ${mat} {
+ let s = &(a[3u].a[2u]);
+ return ${mat}(${col_vectors_inline_load_matrix});
+}
+
+fn load_a_3_a_p0_m(p0 : u32) -> ${mat} {
+ let s = &(a[3u].a[p0]);
+ return ${mat}(${col_vectors_inline_load_matrix});
+}
+
+fn load_a_p0_a_2_m(p0 : u32) -> ${mat} {
+ let s = &(a[p0].a[2u]);
+ return ${mat}(${col_vectors_inline_load_matrix});
+}
+
+fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> ${mat} {
+ let s = &(a[p0].a[p1]);
+ return ${mat}(${col_vectors_inline_load_matrix});
+}
+
+fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> ${col_vector_type} {
+ switch(p2) {
+${col_table_load_column}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> ${elem_type} {
+ switch(p2) {
+${col_table_load_element}
+ default: {
+ return ${elem_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let K = 0;
+ let p_a = conv_arr4_Outer(a);
+ let p_a_3 = conv_Outer(a[3u]);
+ let p_a_I = conv_Outer(a[I]);
+ let p_a_3_a = conv_arr4_Inner(a[3u].a);
+ let p_a_I_a = conv_arr4_Inner(a[I].a);
+ let p_a_3_a_2 = conv_Inner(a[3u].a[2u]);
+ let p_a_3_a_I = conv_Inner(a[3u].a[I]);
+ let p_a_I_a_2 = conv_Inner(a[I].a[2u]);
+ let p_a_I_a_J = conv_Inner(a[I].a[J]);
+ let p_a_3_a_2_m = load_a_3_a_2_m();
+ let p_a_3_a_I_m = load_a_3_a_p0_m(u32(I));
+ let p_a_I_a_2_m = load_a_p0_a_2_m(u32(I));
+ let p_a_I_a_J_m = load_a_p0_a_p1_m(u32(I), u32(J));
+ let p_a_3_a_2_m_1 = a[3u].a[2u].m_1;
+ let p_a_I_a_J_m_K = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+ let l_a : array<Outer, 4> = conv_arr4_Outer(a);
+ let l_a_3 : Outer = conv_Outer(a[3u]);
+ let l_a_I : Outer = conv_Outer(a[I]);
+ let l_a_3_a : array<Inner, 4> = conv_arr4_Inner(a[3u].a);
+ let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
+ let l_a_3_a_2 : Inner = conv_Inner(a[3u].a[2u]);
+ let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
+ let l_a_I_a_2 : Inner = conv_Inner(a[I].a[2u]);
+ let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
+ let l_a_3_a_2_m : ${mat} = load_a_3_a_2_m();
+ let l_a_3_a_I_m : ${mat} = load_a_3_a_p0_m(u32(I));
+ let l_a_I_a_2_m : ${mat} = load_a_p0_a_2_m(u32(I));
+ let l_a_I_a_J_m : ${mat} = load_a_p0_a_p1_m(u32(I), u32(J));
+ let l_a_3_a_2_m_1 : ${col_vector_type} = a[3u].a[2u].m_1;
+ let l_a_I_a_J_m_K : ${col_vector_type} = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+ let l_a_2_a_0_m_1_0 : ${elem_type} = a[3u].a[2u].m_1[0u];
+ let l_a_I_a_J_m_K_I : ${elem_type} = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
+}
+)";
+ std::string col_tableLoadColumn = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[p0].a[p1].m_${col_id_for_tmpl};
+ })",
+ "\n");
+ std::string col_tableLoadElement = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[p0].a[p1].m_${col_id_for_tmpl}[p3];
+ })",
+ "\n");
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline_conv_Inner}",
+ matrix.ExpendedColumnVectorsInline("val.m_", ", ")},
+ {"${col_vectors_inline_load_matrix}",
+ matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")},
+ {"${col_table_load_column}", col_tableLoadColumn},
+ {"${col_table_load_element}", col_tableLoadElement}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyArray_UniformToStorage) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+ s = u;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn conv_S(val : S_std140) -> S {
+ return S(${mat}(${col_vectors_inline}));
+}
+
+fn conv_arr4_S(val : array<S_std140, 4u>) -> array<S, 4u> {
+ var arr : array<S, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_S(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ s = conv_arr4_S(u);
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyStruct_UniformToWorkgroup) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+ w[0] = u[1];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+ v : vec4<i32>,
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+var<workgroup> w : array<S, 4>;
+
+fn conv_S(val : S_std140) -> S {
+ return S(val.v, ${mat}(${col_vectors_inline}));
+}
+
+fn f() {
+ w[0] = conv_S(u[1u]);
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyMatrix_UniformToPrivate) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+ p[2].m = u[1].m;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+ v : vec4<i32>,
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+var<private> p : array<S, 4>;
+
+fn load_u_1_m() -> ${mat} {
+ let s = &(u[1u]);
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ p[2].m = load_u_1_m();
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyColumn_UniformToStorage) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+ s[3].m[1] = u[2].m[0];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+ s[3].m[1] = u[2u].m_0;
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyColumnSwizzle_UniformToWorkgroup) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+ w[3].m[1] = u[2].m[0].${swizzle}.${swizzle};
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+ w[3].m[1] = u[2u].m_0.${swizzle}.${swizzle};
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_Matrix, ArrayStructMatUniform_CopyScalar_UniformToPrivate) {
+ auto matrix = GetParam();
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : ${mat},
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+ p[3].m[1].x = u[2].m[0].y;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : ${mat},
+}
+
+struct S_std140 {
+ v : vec4<i32>,
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+ p[3].m[1].x = u[2u].m_0[1u];
+}
+)";
+ uint32_t last_size =
+ 64 - static_cast<uint32_t>(matrix.ColumnVectorAlign() * (matrix.columns - 1));
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectorsWithLastSize(2, "m_", last_size)},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("(*(s)).m_", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+ Std140Test_Matrix,
+ ::testing::ValuesIn(std::vector<MatrixCase>{
+ {2, 2, MatrixType::f32},
+ {2, 3, MatrixType::f32},
+ {2, 4, MatrixType::f32},
+ {3, 2, MatrixType::f32},
+ {3, 3, MatrixType::f32},
+ {3, 4, MatrixType::f32},
+ {4, 2, MatrixType::f32},
+ {4, 3, MatrixType::f32},
+ {4, 4, MatrixType::f32},
+ }));
+
+using Std140Test_MatrixArray = TransformTestWithParam<MatrixCase>;
+
+TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadArray) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
+
+fn f() {
+ let l = a;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn conv_arr3_mat${shape}_${elem_type}(val : array<mat${shape}_${elem_type}, 3u>) -> array<${mat}, 3u> {
+ var arr : array<${mat}, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat${shape}_${elem_type}(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let l = conv_arr3_mat${shape}_${elem_type}(a);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadMatrix_ConstArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
+
+fn f() {
+ let l = a[${array_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let l = conv_mat${shape}_${elem_type}(a[${array_index}u]);
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
+ }
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadMatrix_VariableArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_mat${shape}_${elem_type}(a[I]);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
+
+fn f() {
+ let l = a[${array_index}][${cloumn_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
+
+fn f() {
+ let l = a[${array_index}u].col${cloumn_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ src = utils::ReplaceAll(src, "${cloumn_index}", std::to_string(col));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+ expect = utils::ReplaceAll(expect, "${cloumn_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got))
+ << "accessing array element " << array_index << " col " << col;
+ }
+ }
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][${cloumn_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
+
+fn f() {
+ let I = 1;
+ let l = a[I].col${cloumn_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${cloumn_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${cloumn_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col;
+ }
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[${array_index}][I];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
+
+fn load_a_${array_index}_p0(p0 : u32) -> ${col_vector_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_${array_index}_p0(u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a[${array_index}u].col0;
+ // }
+ // case 1u: {
+ // return a[${array_index}u].col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[${array_index}u].col${col_id_for_tmpl};
+ })",
+ "\n");
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
+ }
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<${mat}, 3>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<mat${shape}_${elem_type}, 3u>;
+
+fn load_a_p0_p1(p0 : u32, p1 : u32) -> ${col_vector_type} {
+ switch(p1) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_p0_p1(u32(I), u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a[p0].col0;
+ // }
+ // case 1u: {
+ // return a[p0].col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[p0].col${col_id_for_tmpl};
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray, StructArrayMatUniform_LoadStruct) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let l = s;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+struct S_std140 {
+ a : array<mat${shape}_${elem_type}, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn conv_arr3_mat${shape}_${elem_type}(val : array<mat${shape}_${elem_type}, 3u>) -> array<${mat}, 3u> {
+ var arr : array<${mat}, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat${shape}_${elem_type}(val[i]);
+ }
+ return arr;
+}
+
+fn conv_S(val : S_std140) -> S {
+ return S(conv_arr3_mat${shape}_${elem_type}(val.a));
+}
+
+fn f() {
+ let l = conv_S(s);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray, StructArrayMatUniform_LoadArray) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let l = s.a;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+struct S_std140 {
+ a : array<mat${shape}_${elem_type}, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn conv_arr3_mat${shape}_${elem_type}(val : array<mat${shape}_${elem_type}, 3u>) -> array<${mat}, 3u> {
+ var arr : array<${mat}, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat${shape}_${elem_type}(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let l = conv_arr3_mat${shape}_${elem_type}(s.a);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray, StructArrayMatUniform_LoadMatrix_ConstArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let l = s.a[${array_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+struct S_std140 {
+ a : array<mat${shape}_${elem_type}, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let l = conv_mat${shape}_${elem_type}(s.a[${array_index}u]);
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
+ }
+}
+
+TEST_P(Std140Test_MatrixArray, StructArrayMatUniform_LoadMatrix_VariableArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let I = 1;
+ let l = s.a[I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+struct S_std140 {
+ a : array<mat${shape}_${elem_type}, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_mat${shape}_${elem_type}(s.a[I]);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray, StructArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let l = s.a[${array_index}][${cloumn_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+struct S_std140 {
+ a : array<mat${shape}_${elem_type}, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+ let l = s.a[${array_index}u].col${cloumn_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ src = utils::ReplaceAll(src, "${cloumn_index}", std::to_string(col));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+ expect = utils::ReplaceAll(expect, "${cloumn_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got))
+ << "accessing array element " << array_index << " col " << col;
+ }
+ }
+}
+
+TEST_P(Std140Test_MatrixArray,
+ StructArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let I = 1;
+ let l = s.a[I][${cloumn_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+struct S_std140 {
+ a : array<mat${shape}_${elem_type}, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn f() {
+ let I = 1;
+ let l = s.a[I].col${cloumn_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${cloumn_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${cloumn_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing col " << col;
+ }
+}
+
+TEST_P(Std140Test_MatrixArray,
+ StructArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let I = 1;
+ let l = s.a[${array_index}][I];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+struct S_std140 {
+ a : array<mat${shape}_${elem_type}, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_a_${array_index}_p0(p0 : u32) -> ${col_vector_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_s_a_${array_index}_p0(u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return s.a[${array_index}u].col0;
+ // }
+ // case 1u: {
+ // return s.a[${array_index}u].col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return s.a[${array_index}u].col${col_id_for_tmpl};
+ })",
+ "\n");
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t array_index = 0; array_index < 3; array_index++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${array_index}", std::to_string(array_index));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${array_index}", std::to_string(array_index));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element " << array_index;
+ }
+}
+
+TEST_P(Std140Test_MatrixArray,
+ StructArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ let I = 1;
+ let l = s.a[I][I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+struct S {
+ a : array<${mat}, 3>,
+}
+
+struct S_std140 {
+ a : array<mat${shape}_${elem_type}, 3u>,
+}
+
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_a_p0_p1(p0 : u32, p1 : u32) -> ${col_vector_type} {
+ switch(p1) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_s_a_p0_p1(u32(I), u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return s.a[p0].col0;
+ // }
+ // case 1u: {
+ // return s.a[p0].col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return s.a[p0].col${col_id_for_tmpl};
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayArrayMatUniform_LoadArrays) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let l = a;
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn conv_arr3_mat${shape}_${elem_type}(val : array<mat${shape}_${elem_type}, 3u>) -> array<${mat}, 3u> {
+ var arr : array<${mat}, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat${shape}_${elem_type}(val[i]);
+ }
+ return arr;
+}
+
+fn conv_arr4_arr3_mat${shape}_${elem_type}(val : array<array<mat${shape}_${elem_type}, 3u>, 4u>) -> array<array<${mat}, 3u>, 4u> {
+ var arr : array<array<${mat}, 3u>, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_arr3_mat${shape}_${elem_type}(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let l = conv_arr4_arr3_mat${shape}_${elem_type}(a);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayArrayMatUniform_LoadArray_ConstOuterArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let l = a[${outer_array_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn conv_arr3_mat${shape}_${elem_type}(val : array<mat${shape}_${elem_type}, 3u>) -> array<${mat}, 3u> {
+ var arr : array<${mat}, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat${shape}_${elem_type}(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let l = conv_arr3_mat${shape}_${elem_type}(a[${outer_array_index}u]);
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t outer = 0; outer < 4; outer++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${outer_array_index}", std::to_string(outer));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${outer_array_index}", std::to_string(outer));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element " << outer;
+ }
+}
+
+TEST_P(Std140Test_MatrixArray, ArrayArrayMatUniform_LoadArray_VariableOuterArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn conv_arr3_mat${shape}_${elem_type}(val : array<mat${shape}_${elem_type}, 3u>) -> array<${mat}, 3u> {
+ var arr : array<${mat}, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat${shape}_${elem_type}(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_arr3_mat${shape}_${elem_type}(a[I]);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_ConstInnerArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let l = a[${outer_array_index}][${inner_array_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let l = conv_mat${shape}_${elem_type}(a[${outer_array_index}u][${inner_array_index}u]);
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t outer = 0; outer < 4; outer++) {
+ for (uint32_t inner = 0; inner < 3; inner++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${outer_array_index}", std::to_string(outer));
+ src = utils::ReplaceAll(src, "${inner_array_index}", std::to_string(inner));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${outer_array_index}", std::to_string(outer));
+ expect = utils::ReplaceAll(expect, "${inner_array_index}", std::to_string(inner));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got))
+ << "accessing array element [" << outer << "][" << inner << "]";
+ }
+ }
+}
+
+TEST_P(Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_VariableInnerArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[${outer_array_index}][I];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_mat${shape}_${elem_type}(a[${outer_array_index}u][I]);
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t outer = 0; outer < 4; outer++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${outer_array_index}", std::to_string(outer));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${outer_array_index}", std::to_string(outer));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element [" << outer << "][I]";
+ }
+}
+
+TEST_P(Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_ConstInnerArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][${inner_array_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_mat${shape}_${elem_type}(a[I][${inner_array_index}u]);
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t inner = 0; inner < 3; inner++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${inner_array_index}", std::to_string(inner));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${inner_array_index}", std::to_string(inner));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element [I][" << inner << "]";
+ }
+}
+
+TEST_P(Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_VariableInnerArrayIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][I];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn conv_mat${shape}_${elem_type}(val : mat${shape}_${elem_type}) -> ${mat} {
+ return ${mat}(${col_vectors_inline});
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_mat${shape}_${elem_type}(a[I][I]);
+}
+)";
+ expect = matrix.ReplaceFieldsInString(
+ expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_P(Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let l = a[${outer_array_index}][${inner_array_index}][${column_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn f() {
+ let l = a[${outer_array_index}u][${inner_array_index}u].col${column_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect,
+ {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_vectors_inline}", matrix.ExpendedColumnVectorsInline("val.col", ", ")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t outer = 0; outer < 4; outer++) {
+ for (uint32_t inner = 0; inner < 3; inner++) {
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${outer_array_index}", std::to_string(outer));
+ src = utils::ReplaceAll(src, "${inner_array_index}", std::to_string(inner));
+ src = utils::ReplaceAll(src, "${column_index}", std::to_string(col));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${outer_array_index}", std::to_string(outer));
+ expect = utils::ReplaceAll(expect, "${inner_array_index}", std::to_string(inner));
+ expect = utils::ReplaceAll(expect, "${column_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got))
+ << "accessing array element [" << outer << "][" << inner << "] col " << col;
+ }
+ }
+ }
+}
+
+TEST_P(
+ Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[${outer_array_index}][${inner_array_index}][I];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn load_a_${outer_array_index}_${inner_array_index}_p0(p0 : u32) -> ${col_vector_type} {
+ switch(p0) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_${outer_array_index}_${inner_array_index}_p0(u32(I));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a[${outer_array_index}u][${inner_array_index}u].col0;
+ // }
+ // case 1u: {
+ // return a[${outer_array_index}u][${inner_array_index}u].col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[${outer_array_index}u][${inner_array_index}u].col${col_id_for_tmpl};
+ })",
+ "\n");
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t outer = 0; outer < 4; outer++) {
+ for (uint32_t inner = 0; inner < 3; inner++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${outer_array_index}", std::to_string(outer));
+ src = utils::ReplaceAll(src, "${inner_array_index}", std::to_string(inner));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${outer_array_index}", std::to_string(outer));
+ expect = utils::ReplaceAll(expect, "${inner_array_index}", std::to_string(inner));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got))
+ << "accessing array element [" << outer << "][" << inner << "]";
+ }
+ }
+}
+
+TEST_P(
+ Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[${outer_array_index}][I][${column_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn f() {
+ let I = 1;
+ let l = a[${outer_array_index}u][I].col${column_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t outer = 0; outer < 4; outer++) {
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${outer_array_index}", std::to_string(outer));
+ src = utils::ReplaceAll(src, "${column_index}", std::to_string(col));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${outer_array_index}", std::to_string(outer));
+ expect = utils::ReplaceAll(expect, "${column_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got))
+ << "accessing array element [" << outer << "][I] col " << col;
+ }
+ }
+}
+
+TEST_P(
+ Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = a[${outer_array_index}][I][J];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn load_a_${outer_array_index}_p0_p1(p0 : u32, p1 : u32) -> ${col_vector_type} {
+ switch(p1) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = load_a_${outer_array_index}_p0_p1(u32(I), u32(J));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a[${outer_array_index}u][p0].col0;
+ // }
+ // case 1u: {
+ // return a[${outer_array_index}u][p0].col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[${outer_array_index}u][p0].col${col_id_for_tmpl};
+ })",
+ "\n");
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t outer = 0; outer < 4; outer++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${outer_array_index}", std::to_string(outer));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${outer_array_index}", std::to_string(outer));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element [" << outer << "][I]";
+ }
+}
+
+TEST_P(
+ Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][${inner_array_index}][${column_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][${inner_array_index}u].col${column_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t inner = 0; inner < 3; inner++) {
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${inner_array_index}", std::to_string(inner));
+ src = utils::ReplaceAll(src, "${column_index}", std::to_string(col));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${inner_array_index}", std::to_string(inner));
+ expect = utils::ReplaceAll(expect, "${column_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got))
+ << "accessing array element [I][" << inner << "] col " << col;
+ }
+ }
+}
+
+TEST_P(
+ Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = a[I][${inner_array_index}][J];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn load_a_p0_${inner_array_index}_p1(p0 : u32, p1 : u32) -> ${col_vector_type} {
+ switch(p1) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = load_a_p0_${inner_array_index}_p1(u32(I), u32(J));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a[p0][${inner_array_index}u].col0;
+ // }
+ // case 1u: {
+ // return a[p0][${inner_array_index}u].col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[p0][${inner_array_index}u].col${col_id_for_tmpl};
+ })",
+ "\n");
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t inner = 0; inner < 3; inner++) {
+ std::string src =
+ utils::ReplaceAll(tmpl_src, "${inner_array_index}", std::to_string(inner));
+ std::string expect =
+ utils::ReplaceAll(tmpl_expect, "${inner_array_index}", std::to_string(inner));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element [I][" << inner << "]";
+ }
+}
+
+TEST_P(
+ Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string tmpl_src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = a[I][J][${column_index}];
+}
+)";
+ tmpl_src = matrix.ReplaceFieldsInString(tmpl_src);
+
+ std::string tmpl_expect;
+ if (matrix.NotStd140Compatible()) {
+ tmpl_expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = a[I][J].col${column_index};
+}
+)";
+ tmpl_expect = matrix.ReplaceFieldsInString(
+ tmpl_expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")}});
+ } else {
+ tmpl_expect = tmpl_src;
+ }
+
+ for (uint32_t col = 0; col < matrix.columns; col++) {
+ std::string src = utils::ReplaceAll(tmpl_src, "${column_index}", std::to_string(col));
+ std::string expect = utils::ReplaceAll(tmpl_expect, "${column_index}", std::to_string(col));
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got)) << "accessing array element [I][J] col " << col;
+ }
+}
+
+TEST_P(
+ Std140Test_MatrixArray,
+ ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex) {
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
+ return;
+ }
+
+ std::string src = R"(
+enable f16;
+
+@group(0) @binding(0) var<uniform> a : array<array<${mat}, 3>, 4>;
+
+fn f() {
+ let I = 0;
+ let J = 1;
+ let K = 2;
+ let l = a[I][J][K];
+}
+)";
+ src = matrix.ReplaceFieldsInString(src);
+
+ std::string expect;
+ if (matrix.NotStd140Compatible()) {
+ expect = R"(
+enable f16;
+
+struct mat${shape}_${elem_type} {
+${col_vectors}
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat${shape}_${elem_type}, 3u>, 4u>;
+
+fn load_a_p0_p1_p2(p0 : u32, p1 : u32, p2 : u32) -> ${col_vector_type} {
+ switch(p2) {
+${col_table}
+ default: {
+ return ${col_vector_type}();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let J = 1;
+ let K = 2;
+ let l = load_a_p0_p1_p2(u32(I), u32(J), u32(K));
+}
+)";
+ // col_table is the switch cases for all column index.
+ // Example for a matrix having 2 columns:
+ // case 0u: {
+ // return a[p0][p1].col0;
+ // }
+ // case 1u: {
+ // return a[p0][p1].col1;
+ // }
+ std::string col_table = matrix.JoinTemplatedStringForEachMatrixColumn( //
+ R"( case ${col_id_for_tmpl}u: {
+ return a[p0][p1].col${col_id_for_tmpl};
+ })",
+ "\n");
+ expect = matrix.ReplaceFieldsInString(
+ expect, {{"${col_vectors}", matrix.ExpendedColumnVectors(2, "col")},
+ {"${col_table}", col_table}});
+ } else {
+ expect = src;
+ }
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+ Std140Test_MatrixArray,
+ ::testing::ValuesIn(std::vector<MatrixCase>{
+ {2, 2, MatrixType::f32},
+ {2, 3, MatrixType::f32},
+ {2, 4, MatrixType::f32},
+ {3, 2, MatrixType::f32},
+ {3, 3, MatrixType::f32},
+ {3, 4, MatrixType::f32},
+ {4, 2, MatrixType::f32},
+ {4, 3, MatrixType::f32},
+ {4, 4, MatrixType::f32},
+ }));
+
+} // namespace
+} // namespace tint::transform
diff --git a/src/tint/transform/std140_test.cc b/src/tint/transform/std140_test.cc
index 6e0b170..1ec2e09 100644
--- a/src/tint/transform/std140_test.cc
+++ b/src/tint/transform/std140_test.cc
@@ -32,7 +32,7 @@
EXPECT_FALSE(ShouldRun<Std140>(src));
}
-TEST_F(Std140Test, ShouldRunStructMat2x2Unused) {
+TEST_F(Std140Test, ShouldRunStructMat2x2F32Unused) {
auto* src = R"(
struct Unused {
m : mat2x2<f32>,
@@ -42,30 +42,68 @@
EXPECT_FALSE(ShouldRun<Std140>(src));
}
-struct ShouldRunCase {
+TEST_F(Std140Test, ShouldRunStructMat2x2F16Unused) {
+ auto* src = R"(
+enable f16;
+
+struct Unused {
+ m : mat2x2<f16>,
+}
+)";
+
+ EXPECT_FALSE(ShouldRun<Std140>(src));
+}
+
+enum class MatrixType { f32, f16 };
+
+struct MatrixCase {
uint32_t columns;
uint32_t rows;
- bool should_run;
+ MatrixType type;
- std::string Mat() const { return "mat" + std::to_string(columns) + "x" + std::to_string(rows); }
+ size_t ElementSize() const { return type == MatrixType::f16 ? 2 : 4; }
+
+ size_t ColumnVectorAlign() const { return (rows == 3 ? 4 : rows) * ElementSize(); }
+
+ bool NotStd140Compatible() const { return ColumnVectorAlign() != 16; }
+
+ // Return if this matrix type can be used as element type of an uniform buffer, i.e. the
+ // array stride is multiple of 16.
+ bool CanBeUsedAsUniformArrayElememts() const {
+ const size_t arrayStride = columns * ColumnVectorAlign();
+ return (arrayStride % 16 == 0);
+ }
+
+ std::string Shape() const { return std::to_string(columns) + "x" + std::to_string(rows); }
+
+ std::string ElementType() const { return type == MatrixType::f16 ? "f16" : "f32"; }
+
+ std::string Mat() const { return "mat" + Shape() + "<" + ElementType() + ">"; }
+
+ // Replace predefined field `${mat}` with the matrix shape. E.g. for a matrix mat4x3<f32>, would
+ // replace "${mat}" with "mat4x3<f32>".
+ std::string ReplaceMatInString(std::string str) const {
+ str = utils::ReplaceAll(str, "${mat}", Mat());
+ return str;
+ }
};
-inline std::ostream& operator<<(std::ostream& os, const ShouldRunCase& c) {
+inline std::ostream& operator<<(std::ostream& os, const MatrixCase& c) {
return os << c.Mat();
}
-using Std140TestShouldRun = TransformTestWithParam<ShouldRunCase>;
+using Std140TestShouldRun = TransformTestWithParam<MatrixCase>;
TEST_P(Std140TestShouldRun, StructStorage) {
std::string src = R"(
struct S {
- m : ${mat}<f32>,
+ m : ${mat},
}
@group(0) @binding(0) var<storage> s : S;
)";
- src = utils::ReplaceAll(src, "${mat}", GetParam().Mat());
+ src = GetParam().ReplaceMatInString(src);
EXPECT_FALSE(ShouldRun<Std140>(src));
}
@@ -73,57 +111,56 @@
TEST_P(Std140TestShouldRun, StructUniform) {
std::string src = R"(
struct S {
- m : ${mat}<f32>,
+ m : ${mat},
}
@group(0) @binding(0) var<uniform> s : S;
)";
- src = utils::ReplaceAll(src, "${mat}", GetParam().Mat());
+ src = GetParam().ReplaceMatInString(src);
- EXPECT_EQ(ShouldRun<Std140>(src), GetParam().should_run);
+ EXPECT_EQ(ShouldRun<Std140>(src), GetParam().NotStd140Compatible());
}
TEST_P(Std140TestShouldRun, ArrayStorage) {
std::string src = R"(
-@group(0) @binding(0) var<storage> s : array<${mat}<f32>, 2>;
+@group(0) @binding(0) var<storage> s : array<${mat}, 2>;
)";
- src = utils::ReplaceAll(src, "${mat}", GetParam().Mat());
+ src = GetParam().ReplaceMatInString(src);
EXPECT_FALSE(ShouldRun<Std140>(src));
}
TEST_P(Std140TestShouldRun, ArrayUniform) {
- if (GetParam().columns == 3u && GetParam().rows == 2u) {
- // This permutation is invalid. Skip the test:
- // error: uniform storage requires that array elements be aligned to 16 bytes, but array
- // element alignment is currently 24. Consider wrapping the element type in a struct and
- // using the @size attribute.
+ auto matrix = GetParam();
+
+ if (!matrix.CanBeUsedAsUniformArrayElememts()) {
+ // This permutation is invalid, skip the test.
return;
}
std::string src = R"(
-@group(0) @binding(0) var<uniform> s : array<${mat}<f32>, 2>;
+@group(0) @binding(0) var<uniform> s : array<${mat}, 2>;
)";
- src = utils::ReplaceAll(src, "${mat}", GetParam().Mat());
+ src = GetParam().ReplaceMatInString(src);
- EXPECT_EQ(ShouldRun<Std140>(src), GetParam().should_run);
+ EXPECT_EQ(ShouldRun<Std140>(src), matrix.NotStd140Compatible());
}
INSTANTIATE_TEST_SUITE_P(Std140TestShouldRun,
Std140TestShouldRun,
- ::testing::ValuesIn(std::vector<ShouldRunCase>{
- {2, 2, true},
- {2, 3, false},
- {2, 4, false},
- {3, 2, true},
- {3, 3, false},
- {3, 4, false},
- {4, 2, true},
- {4, 3, false},
- {4, 4, false},
+ ::testing::ValuesIn(std::vector<MatrixCase>{
+ {2, 2, MatrixType::f32},
+ {2, 3, MatrixType::f32},
+ {2, 4, MatrixType::f32},
+ {3, 2, MatrixType::f32},
+ {3, 3, MatrixType::f32},
+ {3, 4, MatrixType::f32},
+ {4, 2, MatrixType::f32},
+ {4, 3, MatrixType::f32},
+ {4, 4, MatrixType::f32},
}));
TEST_F(Std140Test, EmptyModule) {
@@ -136,26 +173,137 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, SingleStructMat4x4Uniform) {
+using Std140Test_F32 = Std140Test;
+
+TEST_F(Std140Test_F32, StructMatricesUniform) {
auto* src = R"(
-struct S {
+struct S2x2F32 {
+ m : mat2x2<f32>,
+}
+struct S3x2F32 {
+ m : mat3x2<f32>,
+}
+struct S4x2F32 {
+ m : mat4x2<f32>,
+}
+struct S2x3F32 {
+ m : mat2x3<f32>,
+}
+struct S3x3F32 {
+ m : mat3x3<f32>,
+}
+struct S4x3F32 {
+ m : mat4x3<f32>,
+}
+struct S2x4F32 {
+ m : mat2x4<f32>,
+}
+struct S3x4F32 {
+ m : mat3x4<f32>,
+}
+struct S4x4F32 {
m : mat4x4<f32>,
}
-@group(0) @binding(0) var<uniform> s : S;
+@group(2) @binding(2) var<uniform> s2x2f32 : S2x2F32;
+@group(3) @binding(2) var<uniform> s3x2f32 : S3x2F32;
+@group(4) @binding(2) var<uniform> s4x2f32 : S4x2F32;
+@group(2) @binding(3) var<uniform> s2x3f32 : S2x3F32;
+@group(3) @binding(3) var<uniform> s3x3f32 : S3x3F32;
+@group(4) @binding(3) var<uniform> s4x3f32 : S4x3F32;
+@group(2) @binding(4) var<uniform> s2x4f32 : S2x4F32;
+@group(3) @binding(4) var<uniform> s3x4f32 : S3x4F32;
+@group(4) @binding(4) var<uniform> s4x4f32 : S4x4F32;
)";
- auto* expect = src; // Nothing violates std140 layout
+ auto* expect = R"(
+struct S2x2F32 {
+ m : mat2x2<f32>,
+}
+
+struct S2x2F32_std140 {
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+}
+
+struct S3x2F32 {
+ m : mat3x2<f32>,
+}
+
+struct S3x2F32_std140 {
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
+}
+
+struct S4x2F32 {
+ m : mat4x2<f32>,
+}
+
+struct S4x2F32_std140 {
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
+ m_3 : vec2<f32>,
+}
+
+struct S2x3F32 {
+ m : mat2x3<f32>,
+}
+
+struct S3x3F32 {
+ m : mat3x3<f32>,
+}
+
+struct S4x3F32 {
+ m : mat4x3<f32>,
+}
+
+struct S2x4F32 {
+ m : mat2x4<f32>,
+}
+
+struct S3x4F32 {
+ m : mat3x4<f32>,
+}
+
+struct S4x4F32 {
+ m : mat4x4<f32>,
+}
+
+@group(2) @binding(2) var<uniform> s2x2f32 : S2x2F32_std140;
+
+@group(3) @binding(2) var<uniform> s3x2f32 : S3x2F32_std140;
+
+@group(4) @binding(2) var<uniform> s4x2f32 : S4x2F32_std140;
+
+@group(2) @binding(3) var<uniform> s2x3f32 : S2x3F32;
+
+@group(3) @binding(3) var<uniform> s3x3f32 : S3x3F32;
+
+@group(4) @binding(3) var<uniform> s4x3f32 : S4x3F32;
+
+@group(2) @binding(4) var<uniform> s2x4f32 : S2x4F32;
+
+@group(3) @binding(4) var<uniform> s3x4f32 : S3x4F32;
+
+@group(4) @binding(4) var<uniform> s4x4f32 : S4x4F32;
+)";
auto got = Run<Std140>(src);
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, SingleStructMat2x2Uniform) {
+// In the following tests we only test `mat2x2<f32>` for matrix used as array element type and
+// `mat3x2<f32>` otherwise, and set all constant column index to 1, row index 0, inner array index
+// 2, and outer array index 3. For exhaustive tests, i.e. tests on all matrix shape and different
+// valid constant index, please refer to std140_exhaustive_test.cc
+
+TEST_F(Std140Test_F32, SingleStructMatUniform_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -163,12 +311,13 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
@@ -179,11 +328,12 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, CustomAlignMat3x2) {
+TEST_F(Std140Test_F32, CustomAlign_Mat3x2F32) {
auto* src = R"(
struct S {
before : i32,
- @align(128) m : mat3x2<f32>,
+ @align(128)
+ m : mat3x2<f32>,
after : i32,
}
@@ -215,11 +365,12 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, CustomSizeMat3x2) {
+TEST_F(Std140Test_F32, CustomSizeMat_Mat3x2F32) {
auto* src = R"(
struct S {
before : i32,
- @size(128) m : mat3x2<f32>,
+ @size(128)
+ m : mat3x2<f32>,
after : i32,
}
@@ -251,11 +402,12 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, CustomAlignAndSizeMat3x2) {
+TEST_F(Std140Test_F32, CustomAlignAndSize_Mat3x2F32) {
auto* src = R"(
struct S {
before : i32,
- @align(128) @size(128) m : mat3x2<f32>,
+ @align(128) @size(128)
+ m : mat3x2<f32>,
after : i32,
}
@@ -288,119 +440,54 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMatricesUniform) {
+TEST_F(Std140Test_F32, MatrixUsageInForLoop_Mat3x2F32) {
auto* src = R"(
-struct S2x2 {
- m : mat2x2<f32>,
-}
-struct S3x2 {
+struct S {
m : mat3x2<f32>,
}
-struct S4x2 {
- m : mat4x2<f32>,
-}
-struct S2x3 {
- m : mat2x3<f32>,
-}
-struct S3x3 {
- m : mat3x3<f32>,
-}
-struct S4x3 {
- m : mat4x3<f32>,
-}
-struct S2x4 {
- m : mat2x4<f32>,
-}
-struct S3x4 {
- m : mat3x4<f32>,
-}
-struct S4x4 {
- m : mat4x4<f32>,
-}
-@group(2) @binding(2) var<uniform> s2x2 : S2x2;
-@group(3) @binding(2) var<uniform> s3x2 : S3x2;
-@group(4) @binding(2) var<uniform> s4x2 : S4x2;
-@group(2) @binding(3) var<uniform> s2x3 : S2x3;
-@group(3) @binding(3) var<uniform> s3x3 : S3x3;
-@group(4) @binding(3) var<uniform> s4x3 : S4x3;
-@group(2) @binding(4) var<uniform> s2x4 : S2x4;
-@group(3) @binding(4) var<uniform> s3x4 : S3x4;
-@group(4) @binding(4) var<uniform> s4x4 : S4x4;
+@group(0) @binding(0) var<uniform> s : S;
+
+fn f() {
+ for(var i = u32(s.m[0][0]); (i < u32(s.m[i][1])); i += u32(s.m[1][i])) {
+ }
+}
)";
auto* expect = R"(
-struct S2x2 {
- m : mat2x2<f32>,
-}
-
-struct S2x2_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
-}
-
-struct S3x2 {
+struct S {
m : mat3x2<f32>,
}
-struct S3x2_std140 {
+struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
m_2 : vec2<f32>,
}
-struct S4x2 {
- m : mat4x2<f32>,
+@group(0) @binding(0) var<uniform> s : S_std140;
+
+fn load_s_m_p0_1(p0 : u32) -> f32 {
+ switch(p0) {
+ case 0u: {
+ return s.m_0[1u];
+ }
+ case 1u: {
+ return s.m_1[1u];
+ }
+ case 2u: {
+ return s.m_2[1u];
+ }
+ default: {
+ return f32();
+ }
+ }
}
-struct S4x2_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- m_2 : vec2<f32>,
- m_3 : vec2<f32>,
+fn f() {
+ for(var i = u32(s.m_0[0u]); (i < u32(load_s_m_p0_1(u32(i)))); i += u32(s.m_1[i])) {
+ }
}
-
-struct S2x3 {
- m : mat2x3<f32>,
-}
-
-struct S3x3 {
- m : mat3x3<f32>,
-}
-
-struct S4x3 {
- m : mat4x3<f32>,
-}
-
-struct S2x4 {
- m : mat2x4<f32>,
-}
-
-struct S3x4 {
- m : mat3x4<f32>,
-}
-
-struct S4x4 {
- m : mat4x4<f32>,
-}
-
-@group(2) @binding(2) var<uniform> s2x2 : S2x2_std140;
-
-@group(3) @binding(2) var<uniform> s3x2 : S3x2_std140;
-
-@group(4) @binding(2) var<uniform> s4x2 : S4x2_std140;
-
-@group(2) @binding(3) var<uniform> s2x3 : S2x3;
-
-@group(3) @binding(3) var<uniform> s3x3 : S3x3;
-
-@group(4) @binding(3) var<uniform> s4x3 : S4x3;
-
-@group(2) @binding(4) var<uniform> s2x4 : S2x4;
-
-@group(3) @binding(4) var<uniform> s3x4 : S3x4;
-
-@group(4) @binding(4) var<uniform> s4x4 : S4x4;
)";
auto got = Run<Std140>(src);
@@ -408,11 +495,345 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_NameCollision) {
+TEST_F(Std140Test_F32, MatUniform_LoadMatrix_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> m : mat3x2<f32>;
+
+fn f() {
+ let l = m;
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> m : mat3x2_f32;
+
+fn conv_mat3x2_f32(val : mat3x2_f32) -> mat3x2<f32> {
+ return mat3x2<f32>(val.col0, val.col1, val.col2);
+}
+
+fn f() {
+ let l = conv_mat3x2_f32(m);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadColumn_ConstIndex_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+ let l = a[1];
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn f() {
+ let l = a.col1;
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadColumn_VariableIndex_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+ let I = 1;
+ let l = a[I];
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn load_a_p0(p0 : u32) -> vec2<f32> {
+ switch(p0) {
+ case 0u: {
+ return a.col0;
+ }
+ case 1u: {
+ return a.col1;
+ }
+ case 2u: {
+ return a.col2;
+ }
+ default: {
+ return vec2<f32>();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_p0(u32(I));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadColumnSwizzle_ConstIndex_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+ let l = a[1].yx;
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn f() {
+ let l = a.col1.yx;
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadColumnSwizzle_VariableIndex_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+ let I = 1;
+ let l = a[I].yx;
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn load_a_p0_yx(p0 : u32) -> vec2<f32> {
+ switch(p0) {
+ case 0u: {
+ return a.col0.yx;
+ }
+ case 1u: {
+ return a.col1.yx;
+ }
+ case 2u: {
+ return a.col2.yx;
+ }
+ default: {
+ return vec2<f32>();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_p0_yx(u32(I));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+ let l = a[1][0];
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn f() {
+ let l = a.col1[0u];
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+ let I = 0;
+ let l = a[I][0];
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn load_a_p0_0(p0 : u32) -> f32 {
+ switch(p0) {
+ case 0u: {
+ return a.col0[0u];
+ }
+ case 1u: {
+ return a.col1[0u];
+ }
+ case 2u: {
+ return a.col2[0u];
+ }
+ default: {
+ return f32();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let l = load_a_p0_0(u32(I));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+ let I = 0;
+ let l = a[1][I];
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn f() {
+ let I = 0;
+ let l = a.col1[I];
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, MatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex_Mat3x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : mat3x2<f32>;
+
+fn f() {
+ let I = 0;
+ let l = a[I][I];
+}
+)";
+
+ auto* expect = R"(
+struct mat3x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+ col2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : mat3x2_f32;
+
+fn load_a_p0_p1(p0 : u32, p1 : u32) -> f32 {
+ switch(p0) {
+ case 0u: {
+ return a.col0[p1];
+ }
+ case 1u: {
+ return a.col1[p1];
+ }
+ case 2u: {
+ return a.col2[p1];
+ }
+ default: {
+ return f32();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let l = load_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, StructMatUniform_NameCollision_Mat3x2F32) {
auto* src = R"(
struct S {
m_1 : i32,
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -421,13 +842,14 @@
auto* expect = R"(
struct S {
m_1 : i32,
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_1 : i32,
m__0 : vec2<f32>,
m__1 : vec2<f32>,
+ m__2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
@@ -438,10 +860,10 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_LoadStruct) {
+TEST_F(Std140Test_F32, StructMatUniform_LoadStruct_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -453,18 +875,19 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
fn conv_S(val : S_std140) -> S {
- return S(mat2x2<f32>(val.m_0, val.m_1));
+ return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
}
fn f() {
@@ -477,10 +900,10 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_LoadMatrix) {
+TEST_F(Std140Test_F32, StructMatUniform_LoadMatrix_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -492,19 +915,20 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
-fn load_s_m() -> mat2x2<f32> {
+fn load_s_m() -> mat3x2<f32> {
let s = &(s);
- return mat2x2<f32>((*(s)).m_0, (*(s)).m_1);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
}
fn f() {
@@ -517,45 +941,10 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_LoadColumn0) {
+TEST_F(Std140Test_F32, StructMatUniform_LoadColumn_ConstIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
- let l = s.m[0];
-}
-)";
-
- auto* expect = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
- let l = s.m_0;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, StructMat2x2Uniform_LoadColumn1) {
- auto* src = R"(
-struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -567,12 +956,13 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
@@ -587,10 +977,10 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_LoadColumnI) {
+TEST_F(Std140Test_F32, StructMatUniform_LoadColumn_VariableIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -603,12 +993,13 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
@@ -621,6 +1012,9 @@
case 1u: {
return s.m_1;
}
+ case 2u: {
+ return s.m_2;
+ }
default: {
return vec2<f32>();
}
@@ -638,45 +1032,10 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar00) {
+TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_ConstColumnIndex_ConstRowIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
- let l = s.m[0][0];
-}
-)";
-
- auto* expect = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
- let l = s.m_0[0u];
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar10) {
- auto* src = R"(
-struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -688,12 +1047,13 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
@@ -708,10 +1068,10 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalarI0) {
+TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_VariableColumnIndex_ConstRowIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -724,12 +1084,13 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
@@ -742,6 +1103,9 @@
case 1u: {
return s.m_1[0u];
}
+ case 2u: {
+ return s.m_2[0u];
+ }
default: {
return f32();
}
@@ -759,168 +1123,10 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar01) {
+TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_ConstColumnIndex_VariableRowIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
- let l = s.m[0][1];
-}
-)";
-
- auto* expect = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
- let l = s.m_0[1u];
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar11) {
- auto* src = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
- let l = s.m[1][1];
-}
-)";
-
- auto* expect = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
- let l = s.m_1[1u];
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalarI1) {
- auto* src = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
- let I = 0;
- let l = s.m[I][1];
-}
-)";
-
- auto* expect = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn load_s_m_p0_1(p0 : u32) -> f32 {
- switch(p0) {
- case 0u: {
- return s.m_0[1u];
- }
- case 1u: {
- return s.m_1[1u];
- }
- default: {
- return f32();
- }
- }
-}
-
-fn f() {
- let I = 0;
- let l = load_s_m_p0_1(u32(I));
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar0I) {
- auto* src = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
- let I = 0;
- let l = s.m[0][I];
-}
-)";
-
- auto* expect = R"(
-struct S {
- m : mat2x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
- let I = 0;
- let l = s.m_0[I];
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalar1I) {
- auto* src = R"(
-struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -933,12 +1139,13 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
@@ -954,10 +1161,10 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructMat2x2Uniform_LoadScalarII) {
+TEST_F(Std140Test_F32, StructMatUniform_LoadScalar_VariableColumnIndex_VariableRowIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> s : S;
@@ -970,12 +1177,13 @@
auto* expect = R"(
struct S {
- m : mat2x2<f32>,
+ m : mat3x2<f32>,
}
struct S_std140 {
m_0 : vec2<f32>,
m_1 : vec2<f32>,
+ m_2 : vec2<f32>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
@@ -988,6 +1196,9 @@
case 1u: {
return s.m_1[p1];
}
+ case 2u: {
+ return s.m_2[p1];
+ }
default: {
return f32();
}
@@ -1005,10 +1216,11 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadArray) {
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadArray_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
+ @size(64)
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
@@ -1055,16 +1267,17 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadStruct0) {
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadStruct_ConstIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
+ @size(64)
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
- let l = a[0];
+ let l = a[2];
}
)";
@@ -1088,7 +1301,7 @@
}
fn f() {
- let l = conv_S(a[0u]);
+ let l = conv_S(a[2u]);
}
)";
@@ -1097,54 +1310,13 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadStruct1) {
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadStruct_VariableIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
- let l = a[1];
-}
-)";
-
- auto* expect = R"(
-struct S {
@size(64)
m : mat3x2<f32>,
}
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn conv_S(val : S_std140) -> S {
- return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn f() {
- let l = conv_S(a[1u]);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadStructI) {
- auto* src = R"(
-struct S {
- @size(64) m : mat3x2<f32>,
-}
-
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
@@ -1183,16 +1355,17 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix0) {
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadMatrix_ConstArrayIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
+ @size(64)
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
- let l = a[0].m;
+ let l = a[2].m;
}
)";
@@ -1211,13 +1384,13 @@
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-fn load_a_0_m() -> mat3x2<f32> {
- let s = &(a[0u]);
+fn load_a_2_m() -> mat3x2<f32> {
+ let s = &(a[2u]);
return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
}
fn f() {
- let l = load_a_0_m();
+ let l = load_a_2_m();
}
)";
@@ -1226,55 +1399,13 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix1) {
+TEST_F(Std140Test_F32, ArrayStructMatUniform_LoadMatrix_VariableArrayIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
- let l = a[1].m;
-}
-)";
-
- auto* expect = R"(
-struct S {
@size(64)
m : mat3x2<f32>,
}
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn load_a_1_m() -> mat3x2<f32> {
- let s = &(a[1u]);
- return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn f() {
- let l = load_a_1_m();
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrixI) {
- auto* src = R"(
-struct S {
- @size(64) m : mat3x2<f32>,
-}
-
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
@@ -1314,16 +1445,18 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix0Column0) {
+TEST_F(Std140Test_F32,
+ ArrayStructMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
+ @size(64)
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
- let l = a[0].m[0];
+ let l = a[2].m[1];
}
)";
@@ -1343,7 +1476,7 @@
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
fn f() {
- let l = a[0u].m_0;
+ let l = a[2u].m_1;
}
)";
@@ -1352,55 +1485,19 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix1Column0) {
+TEST_F(Std140Test_F32,
+ ArrayStructMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
- let l = a[1].m[0];
-}
-)";
-
- auto* expect = R"(
-struct S {
@size(64)
m : mat3x2<f32>,
}
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn f() {
- let l = a[1u].m_0;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrixIColumn0) {
- auto* src = R"(
-struct S {
- @size(64) m : mat3x2<f32>,
-}
-
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
let I = 1;
- let l = a[I].m[0];
+ let l = a[I].m[1];
}
)";
@@ -1421,7 +1518,7 @@
fn f() {
let I = 1;
- let l = a[I].m_0;
+ let l = a[I].m_1;
}
)";
@@ -1430,16 +1527,19 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix0Column1) {
+TEST_F(Std140Test_F32,
+ ArrayStructMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
+ @size(64)
+ m : mat3x2<f32>,
}
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
- let l = a[0].m[1];
+ let I = 1;
+ let l = a[2].m[I];
}
)";
@@ -1458,8 +1558,26 @@
@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
+fn load_a_2_m_p0(p0 : u32) -> vec2<f32> {
+ switch(p0) {
+ case 0u: {
+ return a[2u].m_0;
+ }
+ case 1u: {
+ return a[2u].m_1;
+ }
+ case 2u: {
+ return a[2u].m_2;
+ }
+ default: {
+ return vec2<f32>();
+ }
+ }
+}
+
fn f() {
- let l = a[0u].m_1;
+ let I = 1;
+ let l = load_a_2_m_p0(u32(I));
}
)";
@@ -1468,50 +1586,14 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrix1Column1) {
+TEST_F(Std140Test_F32,
+ ArrayStructMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat3x2F32) {
auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S, 3>;
-
-fn f() {
- let l = a[1].m[1];
-}
-)";
-
- auto* expect = R"(
-struct S {
@size(64)
m : mat3x2<f32>,
}
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<S_std140, 3u>;
-
-fn f() {
- let l = a[1u].m_1;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_LoadMatrixIColumnI) {
- auto* src = R"(
-struct S {
- @size(64) m : mat3x2<f32>,
-}
-
@group(0) @binding(0) var<uniform> a : array<S, 3>;
fn f() {
@@ -1563,121 +1645,120 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, Mat4x2Uniform_LoadMatrix) {
+TEST_F(Std140Test_F32, ArrayStructArrayStructMatUniform_Loads_Mat3x2F32) {
auto* src = R"(
-@group(0) @binding(0) var<uniform> m : mat4x2<f32>;
-
-fn f() {
- let l = m;
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
+struct Inner {
+ @size(64)
+ m : mat3x2<f32>,
}
-@group(0) @binding(0) var<uniform> m : mat4x2_f32;
-
-fn conv_mat4x2_f32(val : mat4x2_f32) -> mat4x2<f32> {
- return mat4x2<f32>(val.col0, val.col1, val.col2, val.col3);
+struct Outer {
+ a : array<Inner, 4>,
}
-fn f() {
- let l = conv_mat4x2_f32(m);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, Mat2x2Uniform_LoadColumn0) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat2x2<f32>;
-
-fn f() {
- let l = a[0];
-}
-)";
-
- auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat2x2_f32;
-
-fn f() {
- let l = a.col0;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, Mat4x2Uniform_LoadColumn1) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat4x2<f32>;
-
-fn f() {
- let l = a[1];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat4x2_f32;
-
-fn f() {
- let l = a.col1;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, Mat2x2Uniform_LoadColumnI) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat2x2<f32>;
+@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
fn f() {
let I = 1;
-
- let l = a[I];
+ let J = 2;
+ let K = 0;
+ let l_a : array<Outer, 4> = a;
+ let l_a_1 : Outer = a[1];
+ let l_a_I : Outer = a[I];
+ let l_a_2_a : array<Inner, 4> = a[2].a;
+ let l_a_I_a : array<Inner, 4> = a[I].a;
+ let l_a_3_a_1 : Inner = a[3].a[1];
+ let l_a_3_a_I : Inner = a[3].a[I];
+ let l_a_I_a_1 : Inner = a[I].a[1];
+ let l_a_I_a_J : Inner = a[I].a[J];
+ let l_a_0_a_2_m : mat3x2<f32> = a[0].a[2].m;
+ let l_a_0_a_I_m : mat3x2<f32> = a[0].a[I].m;
+ let l_a_I_a_2_m : mat3x2<f32> = a[I].a[2].m;
+ let l_a_I_a_J_m : mat3x2<f32> = a[I].a[J].m;
+ let l_a_1_a_3_m_0 : vec2<f32> = a[1].a[3].m[0];
+ let l_a_I_a_J_m_K : vec2<f32> = a[I].a[J].m[K];
+ let l_a_2_a_0_m_1_0 : f32 = a[2].a[0].m[1][0];
+ let l_a_I_a_J_m_K_I : f32 = a[I].a[J].m[K][I];
}
)";
auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
+struct Inner {
+ @size(64)
+ m : mat3x2<f32>,
}
-@group(0) @binding(0) var<uniform> a : mat2x2_f32;
+struct Inner_std140 {
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ @size(48)
+ m_2 : vec2<f32>,
+}
-fn load_a_p0(p0 : u32) -> vec2<f32> {
- switch(p0) {
+struct Outer {
+ a : array<Inner, 4>,
+}
+
+struct Outer_std140 {
+ a : array<Inner_std140, 4u>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
+
+fn conv_Inner(val : Inner_std140) -> Inner {
+ return Inner(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
+ var arr : array<Inner, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_Inner(val[i]);
+ }
+ return arr;
+}
+
+fn conv_Outer(val : Outer_std140) -> Outer {
+ return Outer(conv_arr4_Inner(val.a));
+}
+
+fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
+ var arr : array<Outer, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_Outer(val[i]);
+ }
+ return arr;
+}
+
+fn load_a_0_a_2_m() -> mat3x2<f32> {
+ let s = &(a[0u].a[2u]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_0_a_p0_m(p0 : u32) -> mat3x2<f32> {
+ let s = &(a[0u].a[p0]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_2_m(p0 : u32) -> mat3x2<f32> {
+ let s = &(a[p0].a[2u]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> mat3x2<f32> {
+ let s = &(a[p0].a[p1]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
+ switch(p2) {
case 0u: {
- return a.col0;
+ return a[p0].a[p1].m_0;
}
case 1u: {
- return a.col1;
+ return a[p0].a[p1].m_1;
+ }
+ case 2u: {
+ return a[p0].a[p1].m_2;
}
default: {
return vec2<f32>();
@@ -1685,157 +1766,16 @@
}
}
-fn f() {
- let I = 1;
- let l = load_a_p0(u32(I));
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, Mat2x2Uniform_LoadColumn1Swizzle) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat2x2<f32>;
-
-fn f() {
- let l = a[1].yx;
-}
-)";
-
- auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat2x2_f32;
-
-fn f() {
- let l = a.col1.yx;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, Mat4x2Uniform_LoadColumnISwizzle) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat4x2<f32>;
-
-fn f() {
- let I = 1;
-
- let l = a[I].yx;
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat4x2_f32;
-
-fn load_a_p0_yx(p0 : u32) -> vec2<f32> {
- switch(p0) {
+fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> f32 {
+ switch(p2) {
case 0u: {
- return a.col0.yx;
+ return a[p0].a[p1].m_0[p3];
}
case 1u: {
- return a.col1.yx;
+ return a[p0].a[p1].m_1[p3];
}
case 2u: {
- return a.col2.yx;
- }
- case 3u: {
- return a.col3.yx;
- }
- default: {
- return vec2<f32>();
- }
- }
-}
-
-fn f() {
- let I = 1;
- let l = load_a_p0_yx(u32(I));
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, Mat2x2Uniform_LoadColumn1Element1) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat2x2<f32>;
-
-fn f() {
- let l = a[1][1];
-}
-)";
-
- auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat2x2_f32;
-
-fn f() {
- let l = a.col1[1u];
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, Mat4x2Uniform_LoadColumnIElementI) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : mat4x2<f32>;
-
-fn f() {
- let I = 1;
-
- let l = a[I][I];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : mat4x2_f32;
-
-fn load_a_p0_p1(p0 : u32, p1 : u32) -> f32 {
- switch(p0) {
- case 0u: {
- return a.col0[p1];
- }
- case 1u: {
- return a.col1[p1];
- }
- case 2u: {
- return a.col2[p1];
- }
- case 3u: {
- return a.col3[p1];
+ return a[p0].a[p1].m_2[p3];
}
default: {
return f32();
@@ -1845,7 +1785,25 @@
fn f() {
let I = 1;
- let l = load_a_p0_p1(u32(I), u32(I));
+ let J = 2;
+ let K = 0;
+ let l_a : array<Outer, 4> = conv_arr4_Outer(a);
+ let l_a_1 : Outer = conv_Outer(a[1u]);
+ let l_a_I : Outer = conv_Outer(a[I]);
+ let l_a_2_a : array<Inner, 4> = conv_arr4_Inner(a[2u].a);
+ let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
+ let l_a_3_a_1 : Inner = conv_Inner(a[3u].a[1u]);
+ let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
+ let l_a_I_a_1 : Inner = conv_Inner(a[I].a[1u]);
+ let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
+ let l_a_0_a_2_m : mat3x2<f32> = load_a_0_a_2_m();
+ let l_a_0_a_I_m : mat3x2<f32> = load_a_0_a_p0_m(u32(I));
+ let l_a_I_a_2_m : mat3x2<f32> = load_a_p0_a_2_m(u32(I));
+ let l_a_I_a_J_m : mat3x2<f32> = load_a_p0_a_p1_m(u32(I), u32(J));
+ let l_a_1_a_3_m_0 : vec2<f32> = a[1u].a[3u].m_0;
+ let l_a_I_a_J_m_K : vec2<f32> = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+ let l_a_2_a_0_m_1_0 : f32 = a[2u].a[0u].m_1[0u];
+ let l_a_I_a_J_m_K_I : f32 = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
}
)";
@@ -1854,7 +1812,492 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayMat2x2Uniform_LoadArray) {
+TEST_F(Std140Test_F32, ArrayStructArrayStructMatUniform_LoadsViaPtrs_Mat3x2F32) {
+ auto* src = R"(
+struct Inner {
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+struct Outer {
+ a : array<Inner, 4>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let K = 0;
+ let p_a = &(a);
+ let p_a_3 = &((*(p_a))[3]);
+ let p_a_I = &((*(p_a))[I]);
+ let p_a_3_a = &((*(p_a_3)).a);
+ let p_a_I_a = &((*(p_a_I)).a);
+ let p_a_3_a_2 = &((*(p_a_3_a))[2]);
+ let p_a_3_a_I = &((*(p_a_3_a))[I]);
+ let p_a_I_a_2 = &((*(p_a_I_a))[2]);
+ let p_a_I_a_J = &((*(p_a_I_a))[J]);
+ let p_a_3_a_2_m = &((*(p_a_3_a_2)).m);
+ let p_a_3_a_I_m = &((*(p_a_3_a_I)).m);
+ let p_a_I_a_2_m = &((*(p_a_I_a_2)).m);
+ let p_a_I_a_J_m = &((*(p_a_I_a_J)).m);
+ let p_a_3_a_2_m_1 = &((*(p_a_3_a_2_m))[1]);
+ let p_a_I_a_J_m_K = &((*(p_a_I_a_J_m))[K]);
+ let l_a : array<Outer, 4> = *(p_a);
+ let l_a_3 : Outer = *(p_a_3);
+ let l_a_I : Outer = *(p_a_I);
+ let l_a_3_a : array<Inner, 4> = *(p_a_3_a);
+ let l_a_I_a : array<Inner, 4> = *(p_a_I_a);
+ let l_a_3_a_2 : Inner = *(p_a_3_a_2);
+ let l_a_3_a_I : Inner = *(p_a_3_a_I);
+ let l_a_I_a_2 : Inner = *(p_a_I_a_2);
+ let l_a_I_a_J : Inner = *(p_a_I_a_J);
+ let l_a_3_a_2_m : mat3x2<f32> = *(p_a_3_a_2_m);
+ let l_a_3_a_I_m : mat3x2<f32> = *(p_a_3_a_I_m);
+ let l_a_I_a_2_m : mat3x2<f32> = *(p_a_I_a_2_m);
+ let l_a_I_a_J_m : mat3x2<f32> = *(p_a_I_a_J_m);
+ let l_a_3_a_2_m_1 : vec2<f32> = *(p_a_3_a_2_m_1);
+ let l_a_I_a_J_m_K : vec2<f32> = *(p_a_I_a_J_m_K);
+ let l_a_2_a_0_m_1_0 : f32 = (*(p_a_3_a_2_m_1))[0];
+ let l_a_I_a_J_m_K_I : f32 = (*(p_a_I_a_J_m_K))[I];
+}
+)";
+
+ auto* expect = R"(
+struct Inner {
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+struct Inner_std140 {
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ @size(48)
+ m_2 : vec2<f32>,
+}
+
+struct Outer {
+ a : array<Inner, 4>,
+}
+
+struct Outer_std140 {
+ a : array<Inner_std140, 4u>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
+
+fn conv_Inner(val : Inner_std140) -> Inner {
+ return Inner(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
+ var arr : array<Inner, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_Inner(val[i]);
+ }
+ return arr;
+}
+
+fn conv_Outer(val : Outer_std140) -> Outer {
+ return Outer(conv_arr4_Inner(val.a));
+}
+
+fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
+ var arr : array<Outer, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_Outer(val[i]);
+ }
+ return arr;
+}
+
+fn load_a_3_a_2_m() -> mat3x2<f32> {
+ let s = &(a[3u].a[2u]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_3_a_p0_m(p0 : u32) -> mat3x2<f32> {
+ let s = &(a[3u].a[p0]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_2_m(p0 : u32) -> mat3x2<f32> {
+ let s = &(a[p0].a[2u]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_p1_m(p0 : u32, p1 : u32) -> mat3x2<f32> {
+ let s = &(a[p0].a[p1]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn load_a_p0_a_p1_m_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
+ switch(p2) {
+ case 0u: {
+ return a[p0].a[p1].m_0;
+ }
+ case 1u: {
+ return a[p0].a[p1].m_1;
+ }
+ case 2u: {
+ return a[p0].a[p1].m_2;
+ }
+ default: {
+ return vec2<f32>();
+ }
+ }
+}
+
+fn load_a_p0_a_p1_m_p2_p3(p0 : u32, p1 : u32, p2 : u32, p3 : u32) -> f32 {
+ switch(p2) {
+ case 0u: {
+ return a[p0].a[p1].m_0[p3];
+ }
+ case 1u: {
+ return a[p0].a[p1].m_1[p3];
+ }
+ case 2u: {
+ return a[p0].a[p1].m_2[p3];
+ }
+ default: {
+ return f32();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let K = 0;
+ let p_a = conv_arr4_Outer(a);
+ let p_a_3 = conv_Outer(a[3u]);
+ let p_a_I = conv_Outer(a[I]);
+ let p_a_3_a = conv_arr4_Inner(a[3u].a);
+ let p_a_I_a = conv_arr4_Inner(a[I].a);
+ let p_a_3_a_2 = conv_Inner(a[3u].a[2u]);
+ let p_a_3_a_I = conv_Inner(a[3u].a[I]);
+ let p_a_I_a_2 = conv_Inner(a[I].a[2u]);
+ let p_a_I_a_J = conv_Inner(a[I].a[J]);
+ let p_a_3_a_2_m = load_a_3_a_2_m();
+ let p_a_3_a_I_m = load_a_3_a_p0_m(u32(I));
+ let p_a_I_a_2_m = load_a_p0_a_2_m(u32(I));
+ let p_a_I_a_J_m = load_a_p0_a_p1_m(u32(I), u32(J));
+ let p_a_3_a_2_m_1 = a[3u].a[2u].m_1;
+ let p_a_I_a_J_m_K = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+ let l_a : array<Outer, 4> = conv_arr4_Outer(a);
+ let l_a_3 : Outer = conv_Outer(a[3u]);
+ let l_a_I : Outer = conv_Outer(a[I]);
+ let l_a_3_a : array<Inner, 4> = conv_arr4_Inner(a[3u].a);
+ let l_a_I_a : array<Inner, 4> = conv_arr4_Inner(a[I].a);
+ let l_a_3_a_2 : Inner = conv_Inner(a[3u].a[2u]);
+ let l_a_3_a_I : Inner = conv_Inner(a[3u].a[I]);
+ let l_a_I_a_2 : Inner = conv_Inner(a[I].a[2u]);
+ let l_a_I_a_J : Inner = conv_Inner(a[I].a[J]);
+ let l_a_3_a_2_m : mat3x2<f32> = load_a_3_a_2_m();
+ let l_a_3_a_I_m : mat3x2<f32> = load_a_3_a_p0_m(u32(I));
+ let l_a_I_a_2_m : mat3x2<f32> = load_a_p0_a_2_m(u32(I));
+ let l_a_I_a_J_m : mat3x2<f32> = load_a_p0_a_p1_m(u32(I), u32(J));
+ let l_a_3_a_2_m_1 : vec2<f32> = a[3u].a[2u].m_1;
+ let l_a_I_a_J_m_K : vec2<f32> = load_a_p0_a_p1_m_p2(u32(I), u32(J), u32(K));
+ let l_a_2_a_0_m_1_0 : f32 = a[3u].a[2u].m_1[0u];
+ let l_a_I_a_J_m_K_I : f32 = load_a_p0_a_p1_m_p2_p3(u32(I), u32(J), u32(K), u32(I));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyArray_UniformToStorage_Mat3x2F32) {
+ auto* src = R"(
+struct S {
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+ s = u;
+}
+)";
+
+ auto* expect = R"(
+struct S {
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+struct S_std140 {
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ @size(48)
+ m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn conv_S(val : S_std140) -> S {
+ return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn conv_arr4_S(val : array<S_std140, 4u>) -> array<S, 4u> {
+ var arr : array<S, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_S(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ s = conv_arr4_S(u);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyStruct_UniformToWorkgroup_Mat3x2F32) {
+ auto* src = R"(
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+ w[0] = u[1];
+}
+)";
+
+ auto* expect = R"(
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+struct S_std140 {
+ v : vec4<i32>,
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ @size(48)
+ m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+var<workgroup> w : array<S, 4>;
+
+fn conv_S(val : S_std140) -> S {
+ return S(val.v, mat3x2<f32>(val.m_0, val.m_1, val.m_2));
+}
+
+fn f() {
+ w[0] = conv_S(u[1u]);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyMatrix_UniformToPrivate_Mat3x2F32) {
+ auto* src = R"(
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+ p[2].m = u[1].m;
+}
+)";
+
+ auto* expect = R"(
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+struct S_std140 {
+ v : vec4<i32>,
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ @size(48)
+ m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+var<private> p : array<S, 4>;
+
+fn load_u_1_m() -> mat3x2<f32> {
+ let s = &(u[1u]);
+ return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
+}
+
+fn f() {
+ p[2].m = load_u_1_m();
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyColumn_UniformToStorage_Mat3x2F32) {
+ auto* src = R"(
+struct S {
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+ s[3].m[1] = u[2].m[0];
+}
+)";
+
+ auto* expect = R"(
+struct S {
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+struct S_std140 {
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ @size(48)
+ m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
+
+fn f() {
+ s[3].m[1] = u[2u].m_0;
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyColumnSwizzle_UniformToWorkgroup_Mat3x2F32) {
+ auto* src = R"(
+struct S {
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 4>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+ w[3].m[1] = u[2].m[0].yx.yx;
+}
+)";
+
+ auto* expect = R"(
+struct S {
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+struct S_std140 {
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ @size(48)
+ m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+
+var<workgroup> w : array<S, 4>;
+
+fn f() {
+ w[3].m[1] = u[2u].m_0.yx.yx;
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayStructMatUniform_CopyScalar_UniformToPrivate_Mat3x2F32) {
+ auto* src = R"(
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S, 3>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+ p[3].m[1].x = u[2].m[0].y;
+}
+)";
+
+ auto* expect = R"(
+struct S {
+ v : vec4<i32>,
+ @size(64)
+ m : mat3x2<f32>,
+}
+
+struct S_std140 {
+ v : vec4<i32>,
+ m_0 : vec2<f32>,
+ m_1 : vec2<f32>,
+ @size(48)
+ m_2 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> u : array<S_std140, 3u>;
+
+var<private> p : array<S, 4>;
+
+fn f() {
+ p[3].m[1].x = u[2u].m_0[1u];
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadArray_Mat2x2F32) {
auto* src = R"(
@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
@@ -1893,45 +2336,12 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayMat4x2Uniform_LoadMatrix0) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat4x2<f32>, 3>;
-
-fn f() {
- let l = a[0];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat4x2_f32, 3u>;
-
-fn conv_mat4x2_f32(val : mat4x2_f32) -> mat4x2<f32> {
- return mat4x2<f32>(val.col0, val.col1, val.col2, val.col3);
-}
-
-fn f() {
- let l = conv_mat4x2_f32(a[0u]);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayMat2x2Uniform_LoadMatrix1) {
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadMatrix_ConstArrayIndex_Mat2x2F32) {
auto* src = R"(
@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
fn f() {
- let l = a[1];
+ let l = a[2];
}
)";
@@ -1948,7 +2358,7 @@
}
fn f() {
- let l = conv_mat2x2_f32(a[1u]);
+ let l = conv_mat2x2_f32(a[2u]);
}
)";
@@ -1957,9 +2367,9 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayMat4x2Uniform_LoadMatrixI) {
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadMatrix_VariableArrayIndex_Mat2x2F32) {
auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat4x2<f32>, 3>;
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
fn f() {
let I = 1;
@@ -1968,22 +2378,20 @@
)";
auto* expect = R"(
-struct mat4x2_f32 {
+struct mat2x2_f32 {
col0 : vec2<f32>,
col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
}
-@group(0) @binding(0) var<uniform> a : array<mat4x2_f32, 3u>;
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-fn conv_mat4x2_f32(val : mat4x2_f32) -> mat4x2<f32> {
- return mat4x2<f32>(val.col0, val.col1, val.col2, val.col3);
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
}
fn f() {
let I = 1;
- let l = conv_mat4x2_f32(a[I]);
+ let l = conv_mat2x2_f32(a[I]);
}
)";
@@ -1992,12 +2400,12 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayMat2x2Uniform_LoadMatrix1Column0) {
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x2F32) {
auto* src = R"(
@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
fn f() {
- let l = a[1][0];
+ let l = a[2][1];
}
)";
@@ -2010,7 +2418,7 @@
@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
fn f() {
- let l = a[1u].col0;
+ let l = a[2u].col1;
}
)";
@@ -2019,36 +2427,7 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayMat4x2Uniform_LoadMatrix0Column1) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat4x2<f32>, 3>;
-
-fn f() {
- let l = a[0][1];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<mat4x2_f32, 3u>;
-
-fn f() {
- let l = a[0u].col1;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayMat2x2Uniform_LoadMatrixIColumn1) {
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x2F32) {
auto* src = R"(
@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
@@ -2077,39 +2456,31 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayMat4x2Uniform_LoadMatrix1ColumnI) {
+TEST_F(Std140Test_F32, ArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x2F32) {
auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<mat4x2<f32>, 3>;
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
fn f() {
let I = 1;
- let l = a[1][I];
+ let l = a[2][I];
}
)";
auto* expect = R"(
-struct mat4x2_f32 {
+struct mat2x2_f32 {
col0 : vec2<f32>,
col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
}
-@group(0) @binding(0) var<uniform> a : array<mat4x2_f32, 3u>;
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-fn load_a_1_p0(p0 : u32) -> vec2<f32> {
+fn load_a_2_p0(p0 : u32) -> vec2<f32> {
switch(p0) {
case 0u: {
- return a[1u].col0;
+ return a[2u].col0;
}
case 1u: {
- return a[1u].col1;
- }
- case 2u: {
- return a[1u].col2;
- }
- case 3u: {
- return a[1u].col3;
+ return a[2u].col1;
}
default: {
return vec2<f32>();
@@ -2119,7 +2490,7 @@
fn f() {
let I = 1;
- let l = load_a_1_p0(u32(I));
+ let l = load_a_2_p0(u32(I));
}
)";
@@ -2128,12 +2499,14 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayArrayMat2x2Uniform_LoadArrays) {
+TEST_F(Std140Test_F32,
+ ArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x2F32) {
auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+@group(0) @binding(0) var<uniform> a : array<mat2x2<f32>, 3>;
fn f() {
- let l = a;
+ let I = 1;
+ let l = a[I][I];
}
)";
@@ -2143,307 +2516,15 @@
col1 : vec2<f32>,
}
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+@group(0) @binding(0) var<uniform> a : array<mat2x2_f32, 3u>;
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
- return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
- var arr : array<mat2x2<f32>, 3u>;
- for(var i : u32; (i < 3u); i = (i + 1)) {
- arr[i] = conv_mat2x2_f32(val[i]);
- }
- return arr;
-}
-
-fn conv_arr4_arr3_mat2x2_f32(val : array<array<mat2x2_f32, 3u>, 4u>) -> array<array<mat2x2<f32>, 3u>, 4u> {
- var arr : array<array<mat2x2<f32>, 3u>, 4u>;
- for(var i : u32; (i < 4u); i = (i + 1)) {
- arr[i] = conv_arr3_mat2x2_f32(val[i]);
- }
- return arr;
-}
-
-fn f() {
- let l = conv_arr4_arr3_mat2x2_f32(a);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayArrayMat4x2Uniform_LoadArray0) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat4x2<f32>, 3>, 4>;
-
-fn f() {
- let l = a[0];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat4x2_f32, 3u>, 4u>;
-
-fn conv_mat4x2_f32(val : mat4x2_f32) -> mat4x2<f32> {
- return mat4x2<f32>(val.col0, val.col1, val.col2, val.col3);
-}
-
-fn conv_arr3_mat4x2_f32(val : array<mat4x2_f32, 3u>) -> array<mat4x2<f32>, 3u> {
- var arr : array<mat4x2<f32>, 3u>;
- for(var i : u32; (i < 3u); i = (i + 1)) {
- arr[i] = conv_mat4x2_f32(val[i]);
- }
- return arr;
-}
-
-fn f() {
- let l = conv_arr3_mat4x2_f32(a[0u]);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayArrayMat2x2Uniform_LoadArray1) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>,4>;
-
-fn f() {
- let l = a[1];
-}
-)";
-
- auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
- return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
- var arr : array<mat2x2<f32>, 3u>;
- for(var i : u32; (i < 3u); i = (i + 1)) {
- arr[i] = conv_mat2x2_f32(val[i]);
- }
- return arr;
-}
-
-fn f() {
- let l = conv_arr3_mat2x2_f32(a[1u]);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayArrayMat2x2Uniform_LoadArrayI) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>,4>;
-
-fn f() {
- let I = 1;
- let l = a[I];
-}
-)";
-
- auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
- return mat2x2<f32>(val.col0, val.col1);
-}
-
-fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
- var arr : array<mat2x2<f32>, 3u>;
- for(var i : u32; (i < 3u); i = (i + 1)) {
- arr[i] = conv_mat2x2_f32(val[i]);
- }
- return arr;
-}
-
-fn f() {
- let I = 1;
- let l = conv_arr3_mat2x2_f32(a[I]);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-TEST_F(Std140Test, ArrayArrayMat2x2Uniform_LoadMatrix12Column0) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
- let l = a[1][2][0];
-}
-)";
-
- auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn f() {
- let l = a[1u][2u].col0;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayArrayMat4x2Uniform_LoadMatrix2IColumn1) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat4x2<f32>, 3>, 4>;
-
-fn f() {
- let I = 1;
- let l = a[2][I][1];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat4x2_f32, 3u>, 4u>;
-
-fn f() {
- let I = 1;
- let l = a[2u][I].col1;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayArrayMat2x2Uniform_LoadMatrixI2Column1) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
- let I = 1;
- let l = a[I][2][1];
-}
-)";
-
- auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn f() {
- let I = 1;
- let l = a[I][2u].col1;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayArrayMat2x2Uniform_LoadMatrixIIColumn1) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
-
-fn f() {
- let I = 1;
- let l = a[I][I][1];
-}
-)";
-
- auto* expect = R"(
-struct mat2x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
-
-fn f() {
- let I = 1;
- let l = a[I][I].col1;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayArrayMat4x2Uniform_LoadMatrix12ColumnI) {
- auto* src = R"(
-@group(0) @binding(0) var<uniform> a : array<array<mat4x2<f32>, 3>, 4>;
-
-fn f() {
- let I = 1;
- let l = a[1][2][I];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<array<mat4x2_f32, 3u>, 4u>;
-
-fn load_a_1_2_p0(p0 : u32) -> vec2<f32> {
- switch(p0) {
+fn load_a_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+ switch(p1) {
case 0u: {
- return a[1u][2u].col0;
+ return a[p0].col0;
}
case 1u: {
- return a[1u][2u].col1;
- }
- case 2u: {
- return a[1u][2u].col2;
- }
- case 3u: {
- return a[1u][2u].col3;
+ return a[p0].col1;
}
default: {
return vec2<f32>();
@@ -2453,7 +2534,7 @@
fn f() {
let I = 1;
- let l = load_a_1_2_p0(u32(I));
+ let l = load_a_p0_p1(u32(I), u32(I));
}
)";
@@ -2462,11 +2543,11 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructArrayMat2x2Uniform_LoadStruct) {
+TEST_F(Std140Test_F32, StructArrayMatUniform_LoadStruct_Mat2x2F32) {
auto* src = R"(
struct S {
a : array<mat2x2<f32>, 3>,
-};
+}
@group(0) @binding(0) var<uniform> s : S;
@@ -2517,11 +2598,11 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructArrayMat2x2Uniform_LoadArray) {
+TEST_F(Std140Test_F32, StructArrayMatUniform_LoadArray_Mat2x2F32) {
auto* src = R"(
struct S {
a : array<mat2x2<f32>, 3>,
-};
+}
@group(0) @binding(0) var<uniform> s : S;
@@ -2568,61 +2649,16 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructArrayMat4x2Uniform_LoadMatrix0) {
- auto* src = R"(
-struct S {
- a : array<mat4x2<f32>, 3>,
-};
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
- let l = s.a[0];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-struct S {
- a : array<mat4x2<f32>, 3>,
-}
-
-struct S_std140 {
- a : array<mat4x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn conv_mat4x2_f32(val : mat4x2_f32) -> mat4x2<f32> {
- return mat4x2<f32>(val.col0, val.col1, val.col2, val.col3);
-}
-
-fn f() {
- let l = conv_mat4x2_f32(s.a[0u]);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, StructArrayMat2x2Uniform_LoadMatrix1) {
+TEST_F(Std140Test_F32, StructArrayMatUniform_LoadMatrix_ConstArrayIndex_Mat2x2F32) {
auto* src = R"(
struct S {
a : array<mat2x2<f32>, 3>,
-};
+}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
- let l = s.a[1];
+ let l = s.a[2];
}
)";
@@ -2647,7 +2683,7 @@
}
fn f() {
- let l = conv_mat2x2_f32(s.a[1u]);
+ let l = conv_mat2x2_f32(s.a[2u]);
}
)";
@@ -2656,11 +2692,11 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructArrayMat4x2Uniform_LoadMatrixI) {
+TEST_F(Std140Test_F32, StructArrayMatUniform_LoadMatrix_VariableArrayIndex_Mat2x2F32) {
auto* src = R"(
struct S {
- a : array<mat4x2<f32>, 3>,
-};
+ a : array<mat2x2<f32>, 3>,
+}
@group(0) @binding(0) var<uniform> s : S;
@@ -2671,30 +2707,28 @@
)";
auto* expect = R"(
-struct mat4x2_f32 {
+struct mat2x2_f32 {
col0 : vec2<f32>,
col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
}
struct S {
- a : array<mat4x2<f32>, 3>,
+ a : array<mat2x2<f32>, 3>,
}
struct S_std140 {
- a : array<mat4x2_f32, 3u>,
+ a : array<mat2x2_f32, 3u>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
-fn conv_mat4x2_f32(val : mat4x2_f32) -> mat4x2<f32> {
- return mat4x2<f32>(val.col0, val.col1, val.col2, val.col3);
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
}
fn f() {
let I = 1;
- let l = conv_mat4x2_f32(s.a[I]);
+ let l = conv_mat2x2_f32(s.a[I]);
}
)";
@@ -2703,16 +2737,17 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructArrayMat2x2Uniform_LoadMatrix1Column0) {
+TEST_F(Std140Test_F32,
+ StructArrayMatUniform_LoadColumn_ConstArrayIndex_ConstColumnIndex_Mat2x2F32) {
auto* src = R"(
struct S {
a : array<mat2x2<f32>, 3>,
-};
+}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
- let l = s.a[1][0];
+ let l = s.a[2][1];
}
)";
@@ -2733,7 +2768,7 @@
@group(0) @binding(0) var<uniform> s : S_std140;
fn f() {
- let l = s.a[1u].col0;
+ let l = s.a[2u].col1;
}
)";
@@ -2742,52 +2777,12 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructArrayMat4x2Uniform_LoadMatrix0Column1) {
- auto* src = R"(
-struct S {
- a : array<mat4x2<f32>, 3>,
-};
-
-@group(0) @binding(0) var<uniform> s : S;
-
-fn f() {
- let l = s.a[0][1];
-}
-)";
-
- auto* expect = R"(
-struct mat4x2_f32 {
- col0 : vec2<f32>,
- col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
-}
-
-struct S {
- a : array<mat4x2<f32>, 3>,
-}
-
-struct S_std140 {
- a : array<mat4x2_f32, 3u>,
-}
-
-@group(0) @binding(0) var<uniform> s : S_std140;
-
-fn f() {
- let l = s.a[0u].col1;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, StructArrayMat2x2Uniform_LoadMatrixIColumn1) {
+TEST_F(Std140Test_F32,
+ StructArrayMatUniform_LoadColumn_VariableArrayIndex_ConstColumnIndex_Mat2x2F32) {
auto* src = R"(
struct S {
a : array<mat2x2<f32>, 3>,
-};
+}
@group(0) @binding(0) var<uniform> s : S;
@@ -2824,51 +2819,44 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, StructArrayMat4x2Uniform_LoadMatrix1ColumnI) {
+TEST_F(Std140Test_F32,
+ StructArrayMatUniform_LoadColumn_ConstArrayIndex_VariableColumnIndex_Mat2x2F32) {
auto* src = R"(
struct S {
- a : array<mat4x2<f32>, 3>,
-};
+ a : array<mat2x2<f32>, 3>,
+}
@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 1;
- let l = s.a[1][I];
+ let l = s.a[2][I];
}
)";
auto* expect = R"(
-struct mat4x2_f32 {
+struct mat2x2_f32 {
col0 : vec2<f32>,
col1 : vec2<f32>,
- col2 : vec2<f32>,
- col3 : vec2<f32>,
}
struct S {
- a : array<mat4x2<f32>, 3>,
+ a : array<mat2x2<f32>, 3>,
}
struct S_std140 {
- a : array<mat4x2_f32, 3u>,
+ a : array<mat2x2_f32, 3u>,
}
@group(0) @binding(0) var<uniform> s : S_std140;
-fn load_s_a_1_p0(p0 : u32) -> vec2<f32> {
+fn load_s_a_2_p0(p0 : u32) -> vec2<f32> {
switch(p0) {
case 0u: {
- return s.a[1u].col0;
+ return s.a[2u].col0;
}
case 1u: {
- return s.a[1u].col1;
- }
- case 2u: {
- return s.a[1u].col2;
- }
- case 3u: {
- return s.a[1u].col3;
+ return s.a[2u].col1;
}
default: {
return vec2<f32>();
@@ -2878,7 +2866,7 @@
fn f() {
let I = 1;
- let l = load_s_a_1_p0(u32(I));
+ let l = load_s_a_2_p0(u32(I));
}
)";
@@ -2887,536 +2875,627 @@
EXPECT_EQ(expect, str(got));
}
-TEST_F(Std140Test, ArrayStructArrayStructMat4x2Uniform_Loads) {
+TEST_F(Std140Test_F32,
+ StructArrayMatUniform_LoadColumn_VariableArrayIndex_VariableColumnIndex_Mat2x2F32) {
auto* src = R"(
-struct Inner {
- m : mat4x2<f32>,
+struct S {
+ a : array<mat2x2<f32>, 3>,
}
-struct Outer {
- a : array<Inner, 4>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
+@group(0) @binding(0) var<uniform> s : S;
fn f() {
let I = 1;
-
- let l_a : array<Outer, 4> = a;
- let l_a_1 : Outer = a[1];
- let l_a_2_a : array<Inner, 4> = a[2].a;
- let l_a_3_a_1 : Inner = a[3].a[1];
- let l_a_0_a_2_m : mat4x2<f32> = a[0].a[2].m;
- let l_a_1_a_3_m_0 : vec2<f32> = a[1].a[3].m[0];
- let l_a_2_a_0_m_1_0 : f32 = a[2].a[0].m[1][0];
+ let l = s.a[I][I];
}
)";
auto* expect = R"(
-struct Inner {
- m : mat4x2<f32>,
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
}
-struct Inner_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- m_2 : vec2<f32>,
- m_3 : vec2<f32>,
-}
-
-struct Outer {
- a : array<Inner, 4>,
-}
-
-struct Outer_std140 {
- a : array<Inner_std140, 4u>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
-
-fn conv_Inner(val : Inner_std140) -> Inner {
- return Inner(mat4x2<f32>(val.m_0, val.m_1, val.m_2, val.m_3));
-}
-
-fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
- var arr : array<Inner, 4u>;
- for(var i : u32; (i < 4u); i = (i + 1)) {
- arr[i] = conv_Inner(val[i]);
- }
- return arr;
-}
-
-fn conv_Outer(val : Outer_std140) -> Outer {
- return Outer(conv_arr4_Inner(val.a));
-}
-
-fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
- var arr : array<Outer, 4u>;
- for(var i : u32; (i < 4u); i = (i + 1)) {
- arr[i] = conv_Outer(val[i]);
- }
- return arr;
-}
-
-fn load_a_0_a_2_m() -> mat4x2<f32> {
- let s = &(a[0u].a[2u]);
- return mat4x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2, (*(s)).m_3);
-}
-
-fn f() {
- let I = 1;
- let l_a : array<Outer, 4> = conv_arr4_Outer(a);
- let l_a_1 : Outer = conv_Outer(a[1u]);
- let l_a_2_a : array<Inner, 4> = conv_arr4_Inner(a[2u].a);
- let l_a_3_a_1 : Inner = conv_Inner(a[3u].a[1u]);
- let l_a_0_a_2_m : mat4x2<f32> = load_a_0_a_2_m();
- let l_a_1_a_3_m_0 : vec2<f32> = a[1u].a[3u].m_0;
- let l_a_2_a_0_m_1_0 : f32 = a[2u].a[0u].m_1[0u];
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructArrayStructMat4x2Uniform_LoadsViaPtrs) {
- // Note: Std140Test requires the PromoteSideEffectsToDecl transform to have been run first, so
- // side-effects in the let-chain will not be a problem.
- auto* src = R"(
-struct Inner {
- m : mat4x2<f32>,
-}
-
-struct Outer {
- a : array<Inner, 4>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<Outer, 4>;
-
-fn f() {
- let I = 1;
-
- let p_a = &a;
- let p_a_3 = &((*p_a)[3]);
- let p_a_3_a = &((*p_a_3).a);
- let p_a_3_a_2 = &((*p_a_3_a)[2]);
- let p_a_3_a_2_m = &((*p_a_3_a_2).m);
- let p_a_3_a_2_m_1 = &((*p_a_3_a_2_m)[1]);
-
-
- let l_a : array<Outer, 4> = *p_a;
- let l_a_3 : Outer = *p_a_3;
- let l_a_3_a : array<Inner, 4> = *p_a_3_a;
- let l_a_3_a_2 : Inner = *p_a_3_a_2;
- let l_a_3_a_2_m : mat4x2<f32> = *p_a_3_a_2_m;
- let l_a_3_a_2_m_1 : vec2<f32> = *p_a_3_a_2_m_1;
- let l_a_2_a_0_m_1_0 : f32 = (*p_a_3_a_2_m_1)[0];
-}
-)";
-
- auto* expect = R"(
-struct Inner {
- m : mat4x2<f32>,
-}
-
-struct Inner_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- m_2 : vec2<f32>,
- m_3 : vec2<f32>,
-}
-
-struct Outer {
- a : array<Inner, 4>,
-}
-
-struct Outer_std140 {
- a : array<Inner_std140, 4u>,
-}
-
-@group(0) @binding(0) var<uniform> a : array<Outer_std140, 4u>;
-
-fn conv_Inner(val : Inner_std140) -> Inner {
- return Inner(mat4x2<f32>(val.m_0, val.m_1, val.m_2, val.m_3));
-}
-
-fn conv_arr4_Inner(val : array<Inner_std140, 4u>) -> array<Inner, 4u> {
- var arr : array<Inner, 4u>;
- for(var i : u32; (i < 4u); i = (i + 1)) {
- arr[i] = conv_Inner(val[i]);
- }
- return arr;
-}
-
-fn conv_Outer(val : Outer_std140) -> Outer {
- return Outer(conv_arr4_Inner(val.a));
-}
-
-fn conv_arr4_Outer(val : array<Outer_std140, 4u>) -> array<Outer, 4u> {
- var arr : array<Outer, 4u>;
- for(var i : u32; (i < 4u); i = (i + 1)) {
- arr[i] = conv_Outer(val[i]);
- }
- return arr;
-}
-
-fn load_a_3_a_2_m() -> mat4x2<f32> {
- let s = &(a[3u].a[2u]);
- return mat4x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2, (*(s)).m_3);
-}
-
-fn f() {
- let I = 1;
- let p_a = conv_arr4_Outer(a);
- let p_a_3 = conv_Outer(a[3u]);
- let p_a_3_a = conv_arr4_Inner(a[3u].a);
- let p_a_3_a_2 = conv_Inner(a[3u].a[2u]);
- let p_a_3_a_2_m = load_a_3_a_2_m();
- let p_a_3_a_2_m_1 = a[3u].a[2u].m_1;
- let l_a : array<Outer, 4> = conv_arr4_Outer(a);
- let l_a_3 : Outer = conv_Outer(a[3u]);
- let l_a_3_a : array<Inner, 4> = conv_arr4_Inner(a[3u].a);
- let l_a_3_a_2 : Inner = conv_Inner(a[3u].a[2u]);
- let l_a_3_a_2_m : mat4x2<f32> = load_a_3_a_2_m();
- let l_a_3_a_2_m_1 : vec2<f32> = a[3u].a[2u].m_1;
- let l_a_2_a_0_m_1_0 : f32 = a[3u].a[2u].m_1[0u];
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyArray_UniformToStorage) {
- auto* src = R"(
struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
-
-fn f() {
- s = u;
-}
-)";
-
- auto* expect =
- R"(
-struct S {
- @size(64)
- m : mat3x2<f32>,
+ a : array<mat2x2<f32>, 3>,
}
struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
+ a : array<mat2x2_f32, 3u>,
}
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
+@group(0) @binding(0) var<uniform> s : S_std140;
-@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
-
-fn conv_S(val : S_std140) -> S {
- return S(mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn conv_arr4_S(val : array<S_std140, 4u>) -> array<S, 4u> {
- var arr : array<S, 4u>;
- for(var i : u32; (i < 4u); i = (i + 1)) {
- arr[i] = conv_S(val[i]);
- }
- return arr;
-}
-
-fn f() {
- s = conv_arr4_S(u);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyStruct_UniformToWorkgroup) {
- auto* src = R"(
-struct S {
- v : vec4<i32>,
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-var<workgroup> w : array<S, 4>;
-
-fn f() {
- w[0] = u[1];
-}
-)";
-
- auto* expect =
- R"(
-struct S {
- v : vec4<i32>,
- @size(64)
- m : mat3x2<f32>,
-}
-
-struct S_std140 {
- v : vec4<i32>,
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
-
-var<workgroup> w : array<S, 4>;
-
-fn conv_S(val : S_std140) -> S {
- return S(val.v, mat3x2<f32>(val.m_0, val.m_1, val.m_2));
-}
-
-fn f() {
- w[0] = conv_S(u[1u]);
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyMatrix_UniformToPrivate) {
- auto* src = R"(
-struct S {
- v : vec4<i32>,
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-var<private> p : array<S, 4>;
-
-fn f() {
- p[2].m = u[1].m;
-}
-)";
-
- auto* expect = R"(
-struct S {
- v : vec4<i32>,
- @size(64)
- m : mat3x2<f32>,
-}
-
-struct S_std140 {
- v : vec4<i32>,
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
-
-var<private> p : array<S, 4>;
-
-fn load_u_1_m() -> mat3x2<f32> {
- let s = &(u[1u]);
- return mat3x2<f32>((*(s)).m_0, (*(s)).m_1, (*(s)).m_2);
-}
-
-fn f() {
- p[2].m = load_u_1_m();
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyColumn_UniformToStorage) {
- auto* src = R"(
-struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
-
-fn f() {
- s[3].m[1] = u[2].m[0];
-}
-)";
-
- auto* expect =
- R"(
-struct S {
- @size(64)
- m : mat3x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
-
-@group(0) @binding(1) var<storage, read_write> s : array<S, 4>;
-
-fn f() {
- s[3].m[1] = u[2u].m_0;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopySwizzle_UniformToWorkgroup) {
- auto* src = R"(
-struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-var<workgroup> w : array<S, 4>;
-
-fn f() {
- w[3].m[1] = u[2].m[0].yx.xy;
-}
-)";
-
- auto* expect =
- R"(
-struct S {
- @size(64)
- m : mat3x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
-
-var<workgroup> w : array<S, 4>;
-
-fn f() {
- w[3].m[1] = u[2u].m_0.yx.xy;
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, ArrayStructMat3x2Uniform_CopyScalar_UniformToPrivate) {
- auto* src = R"(
-struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S, 4>;
-var<private> w : array<S, 4>;
-
-fn f() {
- w[3].m[1].x = u[2].m[0].y;
-}
-)";
-
- auto* expect =
- R"(
-struct S {
- @size(64)
- m : mat3x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : array<S_std140, 4u>;
-
-var<private> w : array<S, 4>;
-
-fn f() {
- w[3].m[1].x = u[2u].m_0[1u];
-}
-)";
-
- auto got = Run<Std140>(src);
-
- EXPECT_EQ(expect, str(got));
-}
-
-TEST_F(Std140Test, MatrixUsageInForLoop) {
- auto* src = R"(
-struct S {
- @size(64) m : mat3x2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : S;
-
-fn f() {
- for (var i = u32(u.m[0][0]); i < u32(u.m[i][1]); i += u32(u.m[1][i])) {
- }
-}
-)";
-
- auto* expect =
- R"(
-struct S {
- @size(64)
- m : mat3x2<f32>,
-}
-
-struct S_std140 {
- m_0 : vec2<f32>,
- m_1 : vec2<f32>,
- @size(48)
- m_2 : vec2<f32>,
-}
-
-@group(0) @binding(0) var<uniform> u : S_std140;
-
-fn load_u_m_p0_1(p0 : u32) -> f32 {
- switch(p0) {
+fn load_s_a_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+ switch(p1) {
case 0u: {
- return u.m_0[1u];
+ return s.a[p0].col0;
}
case 1u: {
- return u.m_1[1u];
- }
- case 2u: {
- return u.m_2[1u];
+ return s.a[p0].col1;
}
default: {
- return f32();
+ return vec2<f32>();
}
}
}
fn f() {
- for(var i = u32(u.m_0[0u]); (i < u32(load_u_m_p0_1(u32(i)))); i += u32(u.m_1[i])) {
+ let I = 1;
+ let l = load_s_a_p0_p1(u32(I), u32(I));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArrays_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let l = a;
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+ var arr : array<mat2x2<f32>, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat2x2_f32(val[i]);
}
+ return arr;
+}
+
+fn conv_arr4_arr3_mat2x2_f32(val : array<array<mat2x2_f32, 3u>, 4u>) -> array<array<mat2x2<f32>, 3u>, 4u> {
+ var arr : array<array<mat2x2<f32>, 3u>, 4u>;
+ for(var i : u32; (i < 4u); i = (i + 1)) {
+ arr[i] = conv_arr3_mat2x2_f32(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let l = conv_arr4_arr3_mat2x2_f32(a);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArray_ConstOuterArrayIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let l = a[3];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+ var arr : array<mat2x2<f32>, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat2x2_f32(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let l = conv_arr3_mat2x2_f32(a[3u]);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32, ArrayArrayMatUniform_LoadArray_VariableOuterArrayIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[I];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn conv_arr3_mat2x2_f32(val : array<mat2x2_f32, 3u>) -> array<mat2x2<f32>, 3u> {
+ var arr : array<mat2x2<f32>, 3u>;
+ for(var i : u32; (i < 3u); i = (i + 1)) {
+ arr[i] = conv_mat2x2_f32(val[i]);
+ }
+ return arr;
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_arr3_mat2x2_f32(a[I]);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+ ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_ConstInnerArrayIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let l = a[3][2];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+ let l = conv_mat2x2_f32(a[3u][2u]);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+ ArrayArrayMatUniform_LoadMatrix_ConstOuterArrayIndex_VariableInnerArrayIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[3][I];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_mat2x2_f32(a[3u][I]);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+ ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_ConstInnerArrayIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][2];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_mat2x2_f32(a[I][2u]);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(Std140Test_F32,
+ ArrayArrayMatUniform_LoadMatrix_VariableOuterArrayIndex_VariableInnerArrayIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][I];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn conv_mat2x2_f32(val : mat2x2_f32) -> mat2x2<f32> {
+ return mat2x2<f32>(val.col0, val.col1);
+}
+
+fn f() {
+ let I = 1;
+ let l = conv_mat2x2_f32(a[I][I]);
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+ Std140Test_F32,
+ ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let l = a[3][2][1];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn f() {
+ let l = a[3u][2u].col1;
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+ Std140Test_F32,
+ ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[3][2][I];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn load_a_3_2_p0(p0 : u32) -> vec2<f32> {
+ switch(p0) {
+ case 0u: {
+ return a[3u][2u].col0;
+ }
+ case 1u: {
+ return a[3u][2u].col1;
+ }
+ default: {
+ return vec2<f32>();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let l = load_a_3_2_p0(u32(I));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+ Std140Test_F32,
+ ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[3][I][1];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn f() {
+ let I = 1;
+ let l = a[3u][I].col1;
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+ Std140Test_F32,
+ ArrayArrayMatUniform_LoadColumn_ConstOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = a[3][I][J];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn load_a_3_p0_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+ switch(p1) {
+ case 0u: {
+ return a[3u][p0].col0;
+ }
+ case 1u: {
+ return a[3u][p0].col1;
+ }
+ default: {
+ return vec2<f32>();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = load_a_3_p0_p1(u32(I), u32(J));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+ Std140Test_F32,
+ ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][2][1];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn f() {
+ let I = 1;
+ let l = a[I][2u].col1;
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+ Std140Test_F32,
+ ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_ConstInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = a[I][2][J];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn load_a_p0_2_p1(p0 : u32, p1 : u32) -> vec2<f32> {
+ switch(p1) {
+ case 0u: {
+ return a[p0][2u].col0;
+ }
+ case 1u: {
+ return a[p0][2u].col1;
+ }
+ default: {
+ return vec2<f32>();
+ }
+ }
+}
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = load_a_p0_2_p1(u32(I), u32(J));
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+ Std140Test_F32,
+ ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_ConstColumnIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = a[I][J][1];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn f() {
+ let I = 1;
+ let J = 2;
+ let l = a[I][J].col1;
+}
+)";
+
+ auto got = Run<Std140>(src);
+
+ EXPECT_EQ(expect, str(got));
+}
+
+TEST_F(
+ Std140Test_F32,
+ ArrayArrayMatUniform_LoadColumn_VariableOuterArrayIndex_VariableInnerArrayIndex_VariableColumnIndex_Mat2x2F32) {
+ auto* src = R"(
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2<f32>, 3>, 4>;
+
+fn f() {
+ let I = 0;
+ let J = 1;
+ let K = 2;
+ let l = a[I][J][K];
+}
+)";
+
+ auto* expect = R"(
+struct mat2x2_f32 {
+ col0 : vec2<f32>,
+ col1 : vec2<f32>,
+}
+
+@group(0) @binding(0) var<uniform> a : array<array<mat2x2_f32, 3u>, 4u>;
+
+fn load_a_p0_p1_p2(p0 : u32, p1 : u32, p2 : u32) -> vec2<f32> {
+ switch(p2) {
+ case 0u: {
+ return a[p0][p1].col0;
+ }
+ case 1u: {
+ return a[p0][p1].col1;
+ }
+ default: {
+ return vec2<f32>();
+ }
+ }
+}
+
+fn f() {
+ let I = 0;
+ let J = 1;
+ let K = 2;
+ let l = load_a_p0_p1_p2(u32(I), u32(J), u32(K));
}
)";
diff --git a/src/tint/utils/string.cc b/src/tint/utils/string.cc
new file mode 100644
index 0000000..a0d3b85
--- /dev/null
+++ b/src/tint/utils/string.cc
@@ -0,0 +1,51 @@
+// 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 <algorithm>
+
+#include "src/tint/utils/string.h"
+#include "src/tint/utils/vector.h"
+
+namespace tint::utils {
+
+size_t Distance(const std::string& str_a, const std::string& str_b) {
+ const auto len_a = str_a.size();
+ const auto len_b = str_b.size();
+
+ Vector<size_t, 64> mat;
+ mat.Reserve((len_a + 1) * (len_b + 1));
+
+ auto at = [&](size_t a, size_t b) -> size_t& { return mat[a + b * (len_a + 1)]; };
+
+ at(0, 0) = 0;
+ for (size_t a = 1; a <= len_a; a++) {
+ at(a, 0) = a;
+ }
+ for (size_t b = 1; b <= len_b; b++) {
+ at(0, b) = b;
+ }
+ for (size_t b = 1; b <= len_b; b++) {
+ for (size_t a = 1; a <= len_a; a++) {
+ bool eq = str_a[a - 1] == str_b[b - 1];
+ at(a, b) = std::min({
+ at(a - 1, b) + 1,
+ at(a, b - 1) + 1,
+ at(a - 1, b - 1) + (eq ? 0 : 1),
+ });
+ }
+ }
+ return at(len_a, len_b);
+}
+
+} // namespace tint::utils
diff --git a/src/tint/utils/string.h b/src/tint/utils/string.h
index de1f4d6..9f65395 100644
--- a/src/tint/utils/string.h
+++ b/src/tint/utils/string.h
@@ -44,6 +44,11 @@
return s.str();
}
+/// @param a the first string
+/// @param b the second string
+/// @returns the Levenshtein distance between @p a and @p b
+size_t Distance(const std::string& a, const std::string& b);
+
} // namespace tint::utils
#endif // SRC_TINT_UTILS_STRING_H_
diff --git a/src/tint/utils/string_test.cc b/src/tint/utils/string_test.cc
index d1cab51..6c8008c 100644
--- a/src/tint/utils/string_test.cc
+++ b/src/tint/utils/string_test.cc
@@ -20,21 +20,33 @@
namespace {
TEST(StringTest, ReplaceAll) {
- ASSERT_EQ("xybbcc", ReplaceAll("aabbcc", "aa", "xy"));
- ASSERT_EQ("aaxycc", ReplaceAll("aabbcc", "bb", "xy"));
- ASSERT_EQ("aabbxy", ReplaceAll("aabbcc", "cc", "xy"));
- ASSERT_EQ("xyxybbcc", ReplaceAll("aabbcc", "a", "xy"));
- ASSERT_EQ("aaxyxycc", ReplaceAll("aabbcc", "b", "xy"));
- ASSERT_EQ("aabbxyxy", ReplaceAll("aabbcc", "c", "xy"));
+ EXPECT_EQ("xybbcc", ReplaceAll("aabbcc", "aa", "xy"));
+ EXPECT_EQ("aaxycc", ReplaceAll("aabbcc", "bb", "xy"));
+ EXPECT_EQ("aabbxy", ReplaceAll("aabbcc", "cc", "xy"));
+ EXPECT_EQ("xyxybbcc", ReplaceAll("aabbcc", "a", "xy"));
+ EXPECT_EQ("aaxyxycc", ReplaceAll("aabbcc", "b", "xy"));
+ EXPECT_EQ("aabbxyxy", ReplaceAll("aabbcc", "c", "xy"));
// Replacement string includes the searched-for string.
// This proves that the algorithm needs to advance 'pos'
// past the replacement.
- ASSERT_EQ("aabxybbxybcc", ReplaceAll("aabbcc", "b", "bxyb"));
+ EXPECT_EQ("aabxybbxybcc", ReplaceAll("aabbcc", "b", "bxyb"));
}
TEST(StringTest, ToString) {
- ASSERT_EQ("123", ToString(123));
- ASSERT_EQ("hello", ToString("hello"));
+ EXPECT_EQ("123", ToString(123));
+ EXPECT_EQ("hello", ToString("hello"));
+}
+
+TEST(StringTest, Distance) {
+ EXPECT_EQ(Distance("hello world", "hello world"), 0u);
+ EXPECT_EQ(Distance("hello world", "helloworld"), 1u);
+ EXPECT_EQ(Distance("helloworld", "hello world"), 1u);
+ EXPECT_EQ(Distance("hello world", "hello world"), 1u);
+ EXPECT_EQ(Distance("hello world", "hello world"), 1u);
+ EXPECT_EQ(Distance("Hello World", "hello world"), 2u);
+ EXPECT_EQ(Distance("hello world", "Hello World"), 2u);
+ EXPECT_EQ(Distance("Hello world", ""), 11u);
+ EXPECT_EQ(Distance("", "Hello world"), 11u);
}
} // namespace
diff --git a/src/tint/utils/vector.h b/src/tint/utils/vector.h
index f0cbf58..705817a 100644
--- a/src/tint/utils/vector.h
+++ b/src/tint/utils/vector.h
@@ -463,13 +463,15 @@
/// Equality operator
/// @param other the other vector
/// @returns true if this vector is the same length as `other`, and all elements are equal.
- bool operator==(const Vector& other) const {
+ template <typename T2, size_t N2>
+ bool operator==(const Vector<T2, N2>& other) const {
const size_t len = Length();
- if (len == other.Length()) {
- for (size_t i = 0; i < len; i++) {
- if ((*this)[i] != other[i]) {
- return false;
- }
+ if (len != other.Length()) {
+ return false;
+ }
+ for (size_t i = 0; i < len; i++) {
+ if ((*this)[i] != other[i]) {
+ return false;
}
}
return true;
@@ -479,7 +481,10 @@
/// @param other the other vector
/// @returns true if this vector is not the same length as `other`, or all elements are not
/// equal.
- bool operator!=(const Vector& other) const { return !(*this == other); }
+ template <typename T2, size_t N2>
+ bool operator!=(const Vector<T2, N2>& other) const {
+ return !(*this == other);
+ }
private:
/// Friend class (differing specializations of this class)
diff --git a/src/tint/utils/vector_test.cc b/src/tint/utils/vector_test.cc
index b4e9fb5..a3cc1f7 100644
--- a/src/tint/utils/vector_test.cc
+++ b/src/tint/utils/vector_test.cc
@@ -2060,6 +2060,15 @@
EXPECT_EQ(vec_ref.end(), &vec[0] + 3);
}
+TEST(TintVectorTest, Equality) {
+ EXPECT_EQ((Vector<int, 2>{1, 2}), (Vector<int, 2>{1, 2}));
+ EXPECT_EQ((Vector<int, 1>{1, 2}), (Vector<int, 3>{1, 2}));
+ EXPECT_NE((Vector{1, 2}), (Vector{1}));
+ EXPECT_NE((Vector{1}), (Vector{1, 2}));
+ EXPECT_NE((Vector{1, 2}), (Vector{2, 1}));
+ EXPECT_NE((Vector{2, 1}), (Vector{1, 2}));
+}
+
} // namespace
} // namespace tint::utils
diff --git a/src/tint/writer/append_vector.cc b/src/tint/writer/append_vector.cc
index edf9132..98e9efc 100644
--- a/src/tint/writer/append_vector.cc
+++ b/src/tint/writer/append_vector.cc
@@ -137,7 +137,7 @@
auto* scalar_cast_target = b->create<sem::TypeConversion>(
packed_el_sem_ty,
b->create<sem::Parameter>(nullptr, 0u, scalar_sem->Type()->UnwrapRef(),
- ast::AddressSpace::kNone, ast::Access::kUndefined),
+ ast::AddressSpace::kNone, ast::Access::kInvalid),
sem::EvaluationStage::kRuntime);
auto* scalar_cast_sem = b->create<sem::Call>(
scalar_cast_ast, scalar_cast_target, sem::EvaluationStage::kRuntime,
@@ -158,7 +158,7 @@
[&](const tint::sem::Expression* arg, size_t i) -> const sem::Parameter* {
return b->create<sem::Parameter>(
nullptr, static_cast<uint32_t>(i), arg->Type()->UnwrapRef(),
- ast::AddressSpace::kNone, ast::Access::kUndefined);
+ ast::AddressSpace::kNone, ast::Access::kInvalid);
}),
sem::EvaluationStage::kRuntime);
auto* constructor_sem =
diff --git a/src/tint/writer/glsl/generator_impl.cc b/src/tint/writer/glsl/generator_impl.cc
index 429249d..da67b3d 100644
--- a/src/tint/writer/glsl/generator_impl.cc
+++ b/src/tint/writer/glsl/generator_impl.cc
@@ -509,20 +509,20 @@
{
auto decl = line(&b);
if (!EmitTypeAndName(decl, ret_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, fn_name)) {
+ ast::Access::kInvalid, fn_name)) {
return "";
}
{
ScopedParen sp(decl);
const auto* ty = TypeOf(expr->lhs)->UnwrapRef();
if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, "lhs")) {
+ ast::Access::kInvalid, "lhs")) {
return "";
}
decl << ", ";
ty = TypeOf(expr->rhs)->UnwrapRef();
if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, "rhs")) {
+ ast::Access::kInvalid, "rhs")) {
return "";
}
}
@@ -935,7 +935,7 @@
{
auto pre = line();
if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kUndefined, result)) {
+ ast::Access::kInvalid, result)) {
return false;
}
pre << ";";
@@ -1213,7 +1213,7 @@
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kUndefined, "")) {
+ ast::Access::kInvalid, "")) {
return false;
}
l << " result;";
@@ -1239,7 +1239,7 @@
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kUndefined, "")) {
+ ast::Access::kInvalid, "")) {
return false;
}
l << " result;";
@@ -2049,6 +2049,7 @@
switch (interpolate->type) {
case ast::InterpolationType::kPerspective:
case ast::InterpolationType::kLinear:
+ case ast::InterpolationType::kInvalid:
break;
case ast::InterpolationType::kFlat:
out << "flat ";
@@ -2060,7 +2061,7 @@
break;
case ast::InterpolationSampling::kSample:
case ast::InterpolationSampling::kCenter:
- case ast::InterpolationSampling::kNone:
+ case ast::InterpolationSampling::kInvalid:
break;
}
}
@@ -2197,7 +2198,7 @@
return true;
},
[&](const sem::Vector* v) {
- if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
@@ -2218,7 +2219,7 @@
return true;
},
[&](const sem::Matrix* m) {
- if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
@@ -2235,7 +2236,7 @@
return true;
},
[&](const sem::Array* a) {
- if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
@@ -2259,7 +2260,7 @@
return true;
},
[&](const sem::Struct* s) {
- if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
@@ -2350,7 +2351,7 @@
}
}
} else if (auto* str = type->As<sem::Struct>()) {
- if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
bool first = true;
@@ -2364,7 +2365,7 @@
EmitZeroValue(out, member->Type());
}
} else if (auto* arr = type->As<sem::Array>()) {
- if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
ScopedParen sp(out);
@@ -2971,7 +2972,7 @@
auto out = line();
// TODO(senorblanco): handle const
- if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid,
builder_.Symbols().NameFor(let->symbol))) {
return false;
}
@@ -2993,7 +2994,7 @@
auto out = line();
out << "const ";
- if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid,
builder_.Symbols().NameFor(var->symbol))) {
return false;
}
@@ -3021,7 +3022,7 @@
{
auto decl = line(&b);
if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kUndefined, fn_name)) {
+ ast::Access::kInvalid, fn_name)) {
return "";
}
{
@@ -3036,8 +3037,8 @@
decl << "inout ";
ty = ptr->StoreType();
}
- if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, param_name)) {
+ if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ param_name)) {
return "";
}
parameter_names.emplace_back(std::move(param_name));
diff --git a/src/tint/writer/hlsl/generator_impl.cc b/src/tint/writer/hlsl/generator_impl.cc
index 4ada084..94e80e8 100644
--- a/src/tint/writer/hlsl/generator_impl.cc
+++ b/src/tint/writer/hlsl/generator_impl.cc
@@ -325,8 +325,7 @@
std::string fn;
{
std::ostringstream ss;
- if (!EmitType(ss, vec, tint::ast::AddressSpace::kInvalid, ast::Access::kUndefined,
- "")) {
+ if (!EmitType(ss, vec, tint::ast::AddressSpace::kInvalid, ast::Access::kInvalid, "")) {
return "";
}
fn = UniqueIdentifier("set_" + ss.str());
@@ -334,13 +333,13 @@
{
auto out = line(&helpers_);
out << "void " << fn << "(inout ";
- if (!EmitTypeAndName(out, vec, ast::AddressSpace::kInvalid, ast::Access::kUndefined,
+ if (!EmitTypeAndName(out, vec, ast::AddressSpace::kInvalid, ast::Access::kInvalid,
"vec")) {
return "";
}
out << ", int idx, ";
if (!EmitTypeAndName(out, vec->type(), ast::AddressSpace::kInvalid,
- ast::Access::kUndefined, "val")) {
+ ast::Access::kInvalid, "val")) {
return "";
}
out << ") {";
@@ -399,8 +398,7 @@
std::string fn;
{
std::ostringstream ss;
- if (!EmitType(ss, mat, tint::ast::AddressSpace::kInvalid, ast::Access::kUndefined,
- "")) {
+ if (!EmitType(ss, mat, tint::ast::AddressSpace::kInvalid, ast::Access::kInvalid, "")) {
return "";
}
fn = UniqueIdentifier("set_vector_" + ss.str());
@@ -408,13 +406,13 @@
{
auto out = line(&helpers_);
out << "void " << fn << "(inout ";
- if (!EmitTypeAndName(out, mat, ast::AddressSpace::kInvalid, ast::Access::kUndefined,
+ if (!EmitTypeAndName(out, mat, ast::AddressSpace::kInvalid, ast::Access::kInvalid,
"mat")) {
return "";
}
out << ", int col, ";
if (!EmitTypeAndName(out, mat->ColumnType(), ast::AddressSpace::kInvalid,
- ast::Access::kUndefined, "val")) {
+ ast::Access::kInvalid, "val")) {
return "";
}
out << ") {";
@@ -468,8 +466,7 @@
std::string fn;
{
std::ostringstream ss;
- if (!EmitType(ss, mat, tint::ast::AddressSpace::kInvalid, ast::Access::kUndefined,
- "")) {
+ if (!EmitType(ss, mat, tint::ast::AddressSpace::kInvalid, ast::Access::kInvalid, "")) {
return "";
}
fn = UniqueIdentifier("set_scalar_" + ss.str());
@@ -477,13 +474,13 @@
{
auto out = line(&helpers_);
out << "void " << fn << "(inout ";
- if (!EmitTypeAndName(out, mat, ast::AddressSpace::kInvalid, ast::Access::kUndefined,
+ if (!EmitTypeAndName(out, mat, ast::AddressSpace::kInvalid, ast::Access::kInvalid,
"mat")) {
return "";
}
out << ", int col, int row, ";
if (!EmitTypeAndName(out, mat->type(), ast::AddressSpace::kInvalid,
- ast::Access::kUndefined, "val")) {
+ ast::Access::kInvalid, "val")) {
return "";
}
out << ") {";
@@ -658,7 +655,7 @@
if (auto* vec = ty->As<sem::Vector>()) {
auto* elem_ty = vec->type();
- if (!EmitType(out, ty, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, ty, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
@@ -723,7 +720,7 @@
std::string ty_name;
{
std::ostringstream ss;
- if (!EmitType(ss, ty, tint::ast::AddressSpace::kInvalid, ast::Access::kUndefined, "")) {
+ if (!EmitType(ss, ty, tint::ast::AddressSpace::kInvalid, ast::Access::kInvalid, "")) {
return "";
}
ty_name = ss.str();
@@ -1431,12 +1428,12 @@
auto rmw = [&](const char* hlsl) -> bool {
{
auto fn = line(&buf);
- if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
name)) {
return false;
}
fn << "(RWByteAddressBuffer buffer, uint offset, ";
- if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
"value")) {
return false;
}
@@ -1452,7 +1449,7 @@
{
auto l = line(&buf);
- if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
"original_value")) {
return false;
}
@@ -1501,8 +1498,8 @@
// InterlockedOr using 0 as the OR value
{
auto fn = line(&buf);
- if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, name)) {
+ if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ name)) {
return false;
}
fn << "(RWByteAddressBuffer buffer, uint offset) {";
@@ -1517,8 +1514,8 @@
{
auto l = line(&buf);
- if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, "value")) {
+ if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ "value")) {
return false;
}
l << " = 0;";
@@ -1535,8 +1532,8 @@
{
auto fn = line(&buf);
fn << "void " << name << "(RWByteAddressBuffer buffer, uint offset, ";
- if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, "value")) {
+ if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ "value")) {
return false;
}
fn << ") {";
@@ -1551,7 +1548,7 @@
{
auto l = line(&buf);
- if (!EmitTypeAndName(l, value_ty, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ if (!EmitTypeAndName(l, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
"ignored")) {
return false;
}
@@ -1566,18 +1563,18 @@
auto* value_ty = params[2]->Type()->UnwrapRef();
{
auto fn = line(&buf);
- if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, name)) {
+ if (!EmitTypeAndName(fn, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ name)) {
return false;
}
fn << "(RWByteAddressBuffer buffer, uint offset, ";
- if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, "compare")) {
+ if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ "compare")) {
return false;
}
fn << ", ";
- if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, "value")) {
+ if (!EmitTypeAndName(fn, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ "value")) {
return false;
}
fn << ") {";
@@ -1592,8 +1589,8 @@
{ // T result = {0};
auto l = line(&buf);
- if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, "result")) {
+ if (!EmitTypeAndName(l, result_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ "result")) {
return false;
}
l << "=";
@@ -1628,7 +1625,7 @@
if (!builtin->ReturnType()->Is<sem::Void>()) {
auto pre = line();
if (!EmitTypeAndName(pre, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kUndefined, result)) {
+ ast::Access::kInvalid, result)) {
return false;
}
pre << " = ";
@@ -1691,8 +1688,8 @@
{ // T result = 0;
auto pre = line();
auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
- if (!EmitTypeAndName(pre, value_ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, result)) {
+ if (!EmitTypeAndName(pre, value_ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ result)) {
return false;
}
pre << " = ";
@@ -1732,7 +1729,7 @@
{ // T compare_value = <compare_value>;
auto pre = line();
if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
- ast::AddressSpace::kNone, ast::Access::kUndefined, compare)) {
+ ast::AddressSpace::kNone, ast::Access::kInvalid, compare)) {
return false;
}
pre << " = ";
@@ -1842,7 +1839,7 @@
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kUndefined, "")) {
+ ast::Access::kInvalid, "")) {
return false;
}
l << " result;";
@@ -1884,7 +1881,7 @@
{
auto l = line(b);
if (!EmitType(l, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kUndefined, "")) {
+ ast::Access::kInvalid, "")) {
return false;
}
l << " result = {sig, int" << width << "(exp)};";
@@ -2721,7 +2718,7 @@
auto const* type = v->Type();
auto address_space = ast::AddressSpace::kNone;
- auto access = ast::Access::kUndefined;
+ auto access = ast::Access::kInvalid;
if (auto* ptr = type->As<sem::Pointer>()) {
type = ptr->StoreType();
@@ -3010,6 +3007,8 @@
case ast::InterpolationType::kFlat:
modifiers += "nointerpolation ";
break;
+ case ast::InterpolationType::kInvalid:
+ break;
}
switch (sampling) {
case ast::InterpolationSampling::kCentroid:
@@ -3019,7 +3018,7 @@
modifiers += "sample ";
break;
case ast::InterpolationSampling::kCenter:
- case ast::InterpolationSampling::kNone:
+ case ast::InterpolationSampling::kInvalid:
break;
}
return modifiers;
@@ -3141,7 +3140,7 @@
return true;
}
- if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, v, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
@@ -3158,7 +3157,7 @@
return true;
},
[&](const sem::Matrix* m) {
- if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, m, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
@@ -3177,7 +3176,7 @@
[&](const sem::Array* a) {
if (constant->AllZero()) {
out << "(";
- if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, a, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
out << ")0";
@@ -3207,7 +3206,7 @@
[&](const sem::Struct* s) {
if (constant->AllZero()) {
out << "(";
- if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kUndefined, "")) {
+ if (!EmitType(out, s, ast::AddressSpace::kNone, ast::Access::kInvalid, "")) {
return false;
}
out << ")0";
@@ -3328,12 +3327,12 @@
[&](const sem::Struct*) {
out << "(";
TINT_DEFER(out << ")" << value);
- return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
+ return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid, "");
},
[&](const sem::Array*) {
out << "(";
TINT_DEFER(out << ")" << value);
- return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined, "");
+ return EmitType(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid, "");
},
[&](Default) {
diagnostics_.add_error(
@@ -4081,7 +4080,7 @@
auto out = line();
out << "const ";
- if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kUndefined,
+ if (!EmitTypeAndName(out, type, ast::AddressSpace::kNone, ast::Access::kInvalid,
builder_.Symbols().NameFor(let->symbol))) {
return false;
}
@@ -4109,7 +4108,7 @@
{
auto decl = line(&b);
if (!EmitTypeAndName(decl, builtin->ReturnType(), ast::AddressSpace::kNone,
- ast::Access::kUndefined, fn_name)) {
+ ast::Access::kInvalid, fn_name)) {
return "";
}
{
@@ -4124,8 +4123,8 @@
decl << "inout ";
ty = ptr->StoreType();
}
- if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone,
- ast::Access::kUndefined, param_name)) {
+ if (!EmitTypeAndName(decl, ty, ast::AddressSpace::kNone, ast::Access::kInvalid,
+ param_name)) {
return "";
}
parameter_names.emplace_back(std::move(param_name));
diff --git a/src/tint/writer/msl/generator_impl.cc b/src/tint/writer/msl/generator_impl.cc
index 9517ec8..5db1d8f 100644
--- a/src/tint/writer/msl/generator_impl.cc
+++ b/src/tint/writer/msl/generator_impl.cc
@@ -1947,7 +1947,7 @@
case ast::InterpolationSampling::kSample:
attr = "sample_";
break;
- case ast::InterpolationSampling::kNone:
+ case ast::InterpolationSampling::kInvalid:
break;
}
switch (type) {
@@ -1960,6 +1960,8 @@
case ast::InterpolationType::kFlat:
attr += "flat";
break;
+ case ast::InterpolationType::kInvalid:
+ break;
}
return attr;
}
diff --git a/src/tint/writer/msl/generator_impl_cast_test.cc b/src/tint/writer/msl/generator_impl_cast_test.cc
index 4b9e3f2..74b3be6 100644
--- a/src/tint/writer/msl/generator_impl_cast_test.cc
+++ b/src/tint/writer/msl/generator_impl_cast_test.cc
@@ -51,7 +51,7 @@
std::stringstream out;
ASSERT_TRUE(gen.EmitExpression(out, cast)) << gen.error();
- EXPECT_EQ(out.str(), "0u");
+ EXPECT_EQ(out.str(), "2147483648u");
}
} // namespace
diff --git a/src/tint/writer/spirv/builder.cc b/src/tint/writer/spirv/builder.cc
index 5bd6407..57a363d 100644
--- a/src/tint/writer/spirv/builder.cc
+++ b/src/tint/writer/spirv/builder.cc
@@ -766,7 +766,7 @@
push_annot(spv::Op::OpDecorate,
{Operand(var_id), U32Operand(SpvDecorationNonWritable)});
break;
- case ast::Access::kUndefined:
+ case ast::Access::kInvalid:
case ast::Access::kReadWrite:
break;
}
@@ -3684,11 +3684,11 @@
// references are not legal in WGSL, so only considering the top-level type is
// fine.
if (auto* ptr = type->As<sem::Pointer>()) {
- type =
- builder_.create<sem::Pointer>(ptr->StoreType(), ptr->AddressSpace(), ast::kReadWrite);
+ type = builder_.create<sem::Pointer>(ptr->StoreType(), ptr->AddressSpace(),
+ ast::Access::kReadWrite);
} else if (auto* ref = type->As<sem::Reference>()) {
- type =
- builder_.create<sem::Pointer>(ref->StoreType(), ref->AddressSpace(), ast::kReadWrite);
+ type = builder_.create<sem::Pointer>(ref->StoreType(), ref->AddressSpace(),
+ ast::Access::kReadWrite);
}
return utils::GetOrCreate(type_to_id_, type, [&]() -> uint32_t {
@@ -4088,6 +4088,7 @@
push_annot(spv::Op::OpDecorate, {Operand(id), U32Operand(SpvDecorationFlat)});
break;
case ast::InterpolationType::kPerspective:
+ case ast::InterpolationType::kInvalid:
break;
}
switch (sampling) {
@@ -4099,7 +4100,7 @@
push_annot(spv::Op::OpDecorate, {Operand(id), U32Operand(SpvDecorationSample)});
break;
case ast::InterpolationSampling::kCenter:
- case ast::InterpolationSampling::kNone:
+ case ast::InterpolationSampling::kInvalid:
break;
}
}
diff --git a/src/tint/writer/wgsl/generator_impl.cc b/src/tint/writer/wgsl/generator_impl.cc
index 05f1a5a..cf43f23 100644
--- a/src/tint/writer/wgsl/generator_impl.cc
+++ b/src/tint/writer/wgsl/generator_impl.cc
@@ -433,7 +433,7 @@
if (!EmitType(out, ptr->type)) {
return false;
}
- if (ptr->access != ast::Access::kUndefined) {
+ if (ptr->access != ast::Access::kInvalid) {
out << ", ";
if (!EmitAccess(out, ptr->access)) {
return false;
@@ -656,9 +656,9 @@
out << "var";
auto address_space = var->declared_address_space;
auto ac = var->declared_access;
- if (address_space != ast::AddressSpace::kNone || ac != ast::Access::kUndefined) {
+ if (address_space != ast::AddressSpace::kNone || ac != ast::Access::kInvalid) {
out << "<" << address_space;
- if (ac != ast::Access::kUndefined) {
+ if (ac != ast::Access::kInvalid) {
out << ", ";
if (!EmitAccess(out, ac)) {
return false;
@@ -769,7 +769,7 @@
},
[&](const ast::InterpolateAttribute* interpolate) {
out << "interpolate(" << interpolate->type;
- if (interpolate->sampling != ast::InterpolationSampling::kNone) {
+ if (interpolate->sampling != ast::InterpolationSampling::kInvalid) {
out << ", " << interpolate->sampling;
}
out << ")";