diff --git a/src/BUILD.gn b/src/BUILD.gn index 0ff76ce00d..eba4825f9e 100644 --- a/src/BUILD.gn +++ b/src/BUILD.gn @@ -548,6 +548,8 @@ libtint_source_set("libtint_core_all_src") { "symbol_table.cc", "symbol_table.h", "traits.h", + "transform/array_length_from_uniform.cc", + "transform/array_length_from_uniform.h", "transform/binding_remapper.cc", "transform/binding_remapper.h", "transform/bound_array_accessors.cc", diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc5d550a70..6bc55b05e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -273,6 +273,8 @@ set(TINT_LIB_SRCS symbol.cc symbol.h traits.h + transform/array_length_from_uniform.cc + transform/array_length_from_uniform.h transform/binding_remapper.cc transform/binding_remapper.h transform/bound_array_accessors.cc @@ -858,6 +860,7 @@ if(${TINT_BUILD_TESTS}) if(${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_WGSL_WRITER}) list(APPEND TINT_TEST_SRCS + transform/array_length_from_uniform.cc transform/binding_remapper_test.cc transform/bound_array_accessors_test.cc transform/calculate_array_length_test.cc diff --git a/src/transform/array_length_from_uniform.cc b/src/transform/array_length_from_uniform.cc new file mode 100644 index 0000000000..e965cae76c --- /dev/null +++ b/src/transform/array_length_from_uniform.cc @@ -0,0 +1,165 @@ +// 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/transform/array_length_from_uniform.h" + +#include +#include + +#include "src/ast/struct_block_decoration.h" +#include "src/program_builder.h" +#include "src/sem/call.h" +#include "src/sem/variable.h" + +TINT_INSTANTIATE_TYPEINFO(tint::transform::ArrayLengthFromUniform::Config); +TINT_INSTANTIATE_TYPEINFO(tint::transform::ArrayLengthFromUniform::Result); + +namespace tint { +namespace transform { + +ArrayLengthFromUniform::ArrayLengthFromUniform() = default; +ArrayLengthFromUniform::~ArrayLengthFromUniform() = default; + +Output ArrayLengthFromUniform::Run(const Program* in, const DataMap& data) { + ProgramBuilder out; + CloneContext ctx(&out, in); + + auto* cfg = data.Get(); + if (cfg == nullptr) { + out.Diagnostics().add_error( + "missing transform data for ArrayLengthFromUniform"); + return Output(Program(std::move(out))); + } + + auto& sem = ctx.src->Sem(); + + const char* kBufferSizeMemberName = "buffer_size"; + + // Determine the size of the buffer size array. + uint32_t max_buffer_size_index = 0; + for (auto& idx : cfg->bindpoint_to_size_index) { + if (idx.second > max_buffer_size_index) { + max_buffer_size_index = idx.second; + } + } + + // Get (or create, on first call) the uniform buffer that will receive the + // size of each storage buffer in the module. + ast::Variable* buffer_size_ubo = nullptr; + auto get_ubo = [&]() { + if (!buffer_size_ubo) { + auto* buffer_size_struct = ctx.dst->Structure( + ctx.dst->Sym(), + {ctx.dst->Member( + kBufferSizeMemberName, + ctx.dst->ty.array(ctx.dst->ty.u32(), max_buffer_size_index + 1))}, + ast::DecorationList{ctx.dst->create()}); + buffer_size_ubo = ctx.dst->Global( + ctx.dst->Sym(), ctx.dst->ty.Of(buffer_size_struct), + ast::StorageClass::kUniform, + ast::DecorationList{ + ctx.dst->create(cfg->ubo_binding.group), + ctx.dst->create( + cfg->ubo_binding.binding)}); + } + return buffer_size_ubo; + }; + + // Find all calls to the arrayLength() intrinsic. + for (auto* node : ctx.src->ASTNodes().Objects()) { + auto* call_expr = node->As(); + if (!call_expr) { + continue; + } + + auto* call = sem.Get(call_expr); + auto* intrinsic = call->Target()->As(); + if (!intrinsic || intrinsic->Type() != sem::IntrinsicType::kArrayLength) { + continue; + } + + // Get the storage buffer that contains the runtime array. + // We assume that the argument to `arrayLength` has the form + // `&resource.array`, which requires that `InlinePointerLets` and `Simplify` + // have been run before this transform. + auto* param = call_expr->params()[0]->As(); + if (!param || param->op() != ast::UnaryOp::kAddressOf) { + TINT_ICE(ctx.dst->Diagnostics()) + << "expected form of arrayLength argument to be &resource.array"; + break; + } + auto* accessor = param->expr()->As(); + if (!accessor) { + TINT_ICE(ctx.dst->Diagnostics()) + << "expected form of arrayLength argument to be &resource.array"; + break; + } + auto* storage_buffer_expr = accessor->structure(); + auto* storage_buffer_sem = + sem.Get(storage_buffer_expr)->As(); + if (!storage_buffer_sem) { + TINT_ICE(ctx.dst->Diagnostics()) + << "expected form of arrayLength argument to be &resource.array"; + break; + } + + // Get the index to use for the buffer size array. + auto binding = storage_buffer_sem->Variable()->BindingPoint(); + auto idx_itr = cfg->bindpoint_to_size_index.find(binding); + if (idx_itr == cfg->bindpoint_to_size_index.end()) { + ctx.dst->Diagnostics().add_error( + "missing size index mapping for binding point (" + + std::to_string(binding.group) + "," + + std::to_string(binding.binding) + ")"); + continue; + } + + // Load the total storage buffer size from the UBO. + auto* total_storage_buffer_size = ctx.dst->IndexAccessor( + ctx.dst->MemberAccessor(get_ubo()->symbol(), kBufferSizeMemberName), + idx_itr->second); + + // Calculate actual array length + // total_storage_buffer_size - array_offset + // array_length = ---------------------------------------- + // array_stride + auto* storage_buffer_type = + storage_buffer_sem->Type()->UnwrapRef()->As(); + auto* array_member_sem = storage_buffer_type->Members().back(); + uint32_t array_offset = array_member_sem->Offset(); + uint32_t array_stride = array_member_sem->Size(); + auto* array_length = ctx.dst->Div( + ctx.dst->Sub(total_storage_buffer_size, array_offset), array_stride); + + ctx.Replace(call_expr, array_length); + } + + ctx.Clone(); + + return Output{Program(std::move(out)), + std::make_unique(buffer_size_ubo ? true : false)}; +} + +ArrayLengthFromUniform::Config::Config(sem::BindingPoint ubo_bp) + : ubo_binding(ubo_bp) {} +ArrayLengthFromUniform::Config::Config(const Config&) = default; +ArrayLengthFromUniform::Config::~Config() = default; + +ArrayLengthFromUniform::Result::Result(bool needs_sizes) + : needs_buffer_sizes(needs_sizes) {} +ArrayLengthFromUniform::Result::Result(const Result&) = default; +ArrayLengthFromUniform::Result::~Result() = default; + +} // namespace transform +} // namespace tint diff --git a/src/transform/array_length_from_uniform.h b/src/transform/array_length_from_uniform.h new file mode 100644 index 0000000000..7ccb588c86 --- /dev/null +++ b/src/transform/array_length_from_uniform.h @@ -0,0 +1,104 @@ +// 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. + +#ifndef SRC_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_ +#define SRC_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_ + +#include + +#include "src/sem/binding_point.h" +#include "src/transform/transform.h" + +namespace tint { + +// Forward declarations +class CloneContext; + +namespace transform { + +/// ArrayLengthFromUniform is a transform that implements calls to arrayLength() +/// by calculating the length from the total size of the storage buffer, which +/// is received via a uniform buffer. +/// +/// The generated uniform buffer will have the form: +/// ``` +/// [[block]] +/// struct buffer_size_struct { +/// buffer_size : array; +/// }; +/// +/// [[group(0), binding(30)]] +/// var buffer_size_ubo : buffer_size_struct; +/// ``` +/// The binding group and number used for this uniform buffer is provided via +/// the `Config` transform input. The `Config` struct also defines the mapping +/// from a storage buffer's `BindingPoint` to the array index that will be used +/// to get the size of that buffer. +/// +/// This transform assumes that the `InlinePointerLets` and `Simplify` +/// transforms have been run before it so that arguments to the arrayLength +/// builtin always have the form `&resource.array`. +class ArrayLengthFromUniform : public Transform { + public: + /// Constructor + ArrayLengthFromUniform(); + /// Destructor + ~ArrayLengthFromUniform() override; + + /// Configuration options for the ArrayLengthFromUniform transform. + struct Config : public Castable { + /// Constructor + /// @param ubo_bp the binding point to use for the generated uniform buffer. + explicit Config(sem::BindingPoint ubo_bp); + + /// Copy constructor + Config(const Config&); + + /// Destructor + ~Config() override; + + /// The binding point to use for the generated uniform buffer. + sem::BindingPoint ubo_binding; + + /// The mapping from binding point to the index for the buffer size lookup. + std::unordered_map bindpoint_to_size_index; + }; + + /// Information produced about what the transform did. + struct Result : public Castable { + /// Constructor + /// @param needs_sizes True if the transform generated the buffer sizes UBO. + explicit Result(bool needs_sizes); + + /// Copy constructor + Result(const Result&); + + /// Destructor + ~Result() override; + + /// True if the transform generated the buffer sizes UBO. + bool const needs_buffer_sizes; + }; + + /// Runs the transform on `program`, returning the transformation result. + /// @param program the source program to transform + /// @param data optional extra transform-specific data + /// @returns the transformation result + Output Run(const Program* program, const DataMap& data = {}) override; +}; + +} // namespace transform +} // namespace tint + +#endif // SRC_TRANSFORM_ARRAY_LENGTH_FROM_UNIFORM_H_ diff --git a/src/transform/array_length_from_uniform_test.cc b/src/transform/array_length_from_uniform_test.cc new file mode 100644 index 0000000000..006e094fc9 --- /dev/null +++ b/src/transform/array_length_from_uniform_test.cc @@ -0,0 +1,283 @@ +// 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/transform/array_length_from_uniform.h" + +#include + +#include "src/transform/test_helper.h" + +namespace tint { +namespace transform { +namespace { + +using ArrayLengthFromUniformTest = TransformTest; + +TEST_F(ArrayLengthFromUniformTest, Error_MissingTransformData) { + auto* src = ""; + + auto* expect = "error: missing transform data for ArrayLengthFromUniform"; + + auto got = Run(src); + + EXPECT_EQ(expect, str(got)); +} + +TEST_F(ArrayLengthFromUniformTest, Basic) { + auto* src = R"( +[[block]] +struct SB { + x : i32; + arr : array; +}; + +[[group(0), binding(0)]] var sb : SB; + +[[stage(compute)]] +fn main() { + var len : u32 = arrayLength(&sb.arr); +} +)"; + + auto* expect = R"( +[[block]] +struct tint_symbol { + buffer_size : array; +}; + +[[group(0), binding(30)]] var tint_symbol_1 : tint_symbol; + +[[block]] +struct SB { + x : i32; + arr : array; +}; + +[[group(0), binding(0)]] var sb : SB; + +[[stage(compute)]] +fn main() { + var len : u32 = ((tint_symbol_1.buffer_size[0u] - 4u) / 4u); +} +)"; + + ArrayLengthFromUniform::Config cfg({0, 30u}); + cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0); + + DataMap data; + data.Add(std::move(cfg)); + + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); + EXPECT_TRUE( + got.data.Get()->needs_buffer_sizes); +} + +TEST_F(ArrayLengthFromUniformTest, WithStride) { + auto* src = R"( +[[block]] +struct SB { + x : i32; + y : f32; + arr : [[stride(64)]] array; +}; + +[[group(0), binding(0)]] var sb : SB; + +[[stage(compute)]] +fn main() { + var len : u32 = arrayLength(&sb.arr); +} +)"; + + auto* expect = R"( +[[block]] +struct tint_symbol { + buffer_size : array; +}; + +[[group(0), binding(30)]] var tint_symbol_1 : tint_symbol; + +[[block]] +struct SB { + x : i32; + y : f32; + arr : [[stride(64)]] array; +}; + +[[group(0), binding(0)]] var sb : SB; + +[[stage(compute)]] +fn main() { + var len : u32 = ((tint_symbol_1.buffer_size[0u] - 8u) / 64u); +} +)"; + + ArrayLengthFromUniform::Config cfg({0, 30u}); + cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0); + + DataMap data; + data.Add(std::move(cfg)); + + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); + EXPECT_TRUE( + got.data.Get()->needs_buffer_sizes); +} + +TEST_F(ArrayLengthFromUniformTest, MultipleStorageBuffers) { + auto* src = R"( +[[block]] +struct SB1 { + x : i32; + arr1 : array; +}; + +[[block]] +struct SB2 { + x : i32; + arr2 : array>; +}; + +[[group(0), binding(2)]] var sb1 : SB1; + +[[group(1), binding(2)]] var sb2 : SB2; + +[[stage(compute)]] +fn main() { + var len1 : u32 = arrayLength(&(sb1.arr1)); + var len2 : u32 = arrayLength(&(sb2.arr2)); + var x : u32 = (len1 + len2); +} +)"; + + auto* expect = R"( +[[block]] +struct tint_symbol { + buffer_size : array; +}; + +[[group(0), binding(30)]] var tint_symbol_1 : tint_symbol; + +[[block]] +struct SB1 { + x : i32; + arr1 : array; +}; + +[[block]] +struct SB2 { + x : i32; + arr2 : array>; +}; + +[[group(0), binding(2)]] var sb1 : SB1; + +[[group(1), binding(2)]] var sb2 : SB2; + +[[stage(compute)]] +fn main() { + var len1 : u32 = ((tint_symbol_1.buffer_size[0u] - 4u) / 4u); + var len2 : u32 = ((tint_symbol_1.buffer_size[1u] - 16u) / 16u); + var x : u32 = (len1 + len2); +} +)"; + + ArrayLengthFromUniform::Config cfg({0, 30u}); + cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 2u}, 0); + cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{1u, 2u}, 1); + + DataMap data; + data.Add(std::move(cfg)); + + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); + EXPECT_TRUE( + got.data.Get()->needs_buffer_sizes); +} + +TEST_F(ArrayLengthFromUniformTest, NoArrayLengthCalls) { + auto* src = R"( +[[block]] +struct SB { + x : i32; + arr : array; +}; + +[[group(0), binding(0)]] var sb : SB; + +[[stage(compute)]] +fn main() { + ignore(&(sb.arr)); +} +)"; + + ArrayLengthFromUniform::Config cfg({0, 30u}); + cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 0}, 0); + + DataMap data; + data.Add(std::move(cfg)); + + auto got = Run(src, data); + + EXPECT_EQ(src, str(got)); + EXPECT_FALSE( + got.data.Get()->needs_buffer_sizes); +} + +TEST_F(ArrayLengthFromUniformTest, MissingBindingPointToIndexMapping) { + auto* src = R"( +[[block]] +struct SB1 { + x : i32; + arr1 : array; +}; + +[[block]] +struct SB2 { + x : i32; + arr2 : array>; +}; + +[[group(0), binding(2)]] var sb1 : SB1; + +[[group(1), binding(2)]] var sb2 : SB2; + +[[stage(compute)]] +fn main() { + var len1 : u32 = arrayLength(&(sb1.arr1)); + var len2 : u32 = arrayLength(&(sb2.arr2)); + var x : u32 = (len1 + len2); +} +)"; + + auto* expect = "error: missing size index mapping for binding point (1,2)"; + + ArrayLengthFromUniform::Config cfg({0, 30u}); + cfg.bindpoint_to_size_index.emplace(sem::BindingPoint{0, 2}, 0); + + DataMap data; + data.Add(std::move(cfg)); + + auto got = Run(src, data); + + EXPECT_EQ(expect, str(got)); +} + +} // namespace +} // namespace transform +} // namespace tint diff --git a/src/transform/msl.cc b/src/transform/msl.cc index f32c01aabb..6d45462fa1 100644 --- a/src/transform/msl.cc +++ b/src/transform/msl.cc @@ -14,6 +14,7 @@ #include "src/transform/msl.h" +#include #include #include #include @@ -24,6 +25,7 @@ #include "src/sem/function.h" #include "src/sem/statement.h" #include "src/sem/variable.h" +#include "src/transform/array_length_from_uniform.h" #include "src/transform/canonicalize_entry_point_io.h" #include "src/transform/external_texture_transform.h" #include "src/transform/inline_pointer_lets.h" @@ -34,15 +36,38 @@ #include "src/transform/wrap_arrays_in_structs.h" #include "src/transform/zero_init_workgroup_memory.h" +TINT_INSTANTIATE_TYPEINFO(tint::transform::Msl::Config); +TINT_INSTANTIATE_TYPEINFO(tint::transform::Msl::Result); + namespace tint { namespace transform { Msl::Msl() = default; Msl::~Msl() = default; -Output Msl::Run(const Program* in, const DataMap&) { +Output Msl::Run(const Program* in, const DataMap& inputs) { Manager manager; - DataMap data; + DataMap internal_inputs; + + auto* cfg = inputs.Get(); + + // Build the config for the array length transform. + uint32_t buffer_size_ubo_index = kDefaultBufferSizeUniformIndex; + if (cfg) { + buffer_size_ubo_index = cfg->buffer_size_ubo_index; + } + auto array_length_from_uniform_cfg = ArrayLengthFromUniform::Config( + sem::BindingPoint{0, buffer_size_ubo_index}); + + // Use the SSBO binding numbers as the indices for the buffer size lookups. + for (auto* var : in->AST().GlobalVariables()) { + auto* sem_var = in->Sem().Get(var); + if (sem_var->StorageClass() == ast::StorageClass::kStorage) { + array_length_from_uniform_cfg.bindpoint_to_size_index.emplace( + sem_var->BindingPoint(), sem_var->BindingPoint().binding); + } + } + // ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as // ZeroInitWorkgroupMemory may inject new builtin parameters. manager.Add(); @@ -53,9 +78,14 @@ Output Msl::Run(const Program* in, const DataMap&) { manager.Add(); manager.Add(); manager.Add(); - data.Add( + // ArrayLengthFromUniform must come after InlinePointerLets and Simplify, as + // it assumes that the form of the array length argument is &var.array. + manager.Add(); + internal_inputs.Add( + std::move(array_length_from_uniform_cfg)); + internal_inputs.Add( CanonicalizeEntryPointIO::BuiltinStyle::kParameter); - auto out = manager.Run(in, data); + auto out = manager.Run(in, internal_inputs); if (!out.program.IsValid()) { return out; } @@ -66,7 +96,10 @@ Output Msl::Run(const Program* in, const DataMap&) { // storage class(es) as transform options. HandleModuleScopeVariables(ctx); ctx.Clone(); - return Output{Program(std::move(builder))}; + + auto result = std::make_unique( + out.data.Get()->needs_buffer_sizes); + return Output{Program(std::move(builder)), std::move(result)}; } void Msl::HandleModuleScopeVariables(CloneContext& ctx) const { @@ -241,5 +274,15 @@ void Msl::HandleModuleScopeVariables(CloneContext& ctx) const { } } +Msl::Config::Config(uint32_t buffer_size_ubo_idx) + : buffer_size_ubo_index(buffer_size_ubo_idx) {} +Msl::Config::Config(const Config&) = default; +Msl::Config::~Config() = default; + +Msl::Result::Result(bool needs_buffer_sizes) + : needs_storage_buffer_sizes(needs_buffer_sizes) {} +Msl::Result::Result(const Result&) = default; +Msl::Result::~Result() = default; + } // namespace transform } // namespace tint diff --git a/src/transform/msl.h b/src/transform/msl.h index 01ccb1a64d..2870f9306c 100644 --- a/src/transform/msl.h +++ b/src/transform/msl.h @@ -25,6 +25,41 @@ namespace transform { /// behavior. class Msl : public Transform { public: + /// The default buffer slot to use for the storage buffer size buffer. + const uint32_t kDefaultBufferSizeUniformIndex = 30; + + /// Configuration options for the Msl sanitizer transform. + struct Config : public Castable { + /// Constructor + /// @param buffer_size_ubo_idx the index to use for the buffer size UBO + explicit Config(uint32_t buffer_size_ubo_idx); + + /// Copy constructor + Config(const Config&); + + /// Destructor + ~Config() override; + + /// The index to use when generating a UBO to receive storage buffer sizes. + uint32_t buffer_size_ubo_index; + }; + + /// Information produced by the sanitizer that users may need to act on. + struct Result : public Castable { + /// Constructor + /// @param needs_buffer_sizes True if the shader needs a UBO of buffer sizes + explicit Result(bool needs_buffer_sizes); + + /// Copy constructor + Result(const Result&); + + /// Destructor + ~Result() override; + + /// True if the shader needs a UBO of buffer sizes. + bool const needs_storage_buffer_sizes; + }; + /// Constructor Msl(); ~Msl() override; diff --git a/test/BUILD.gn b/test/BUILD.gn index 04d781da93..2871a80ab8 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -275,6 +275,7 @@ tint_unittests_source_set("tint_unittests_core_src") { "../src/symbol_table_test.cc", "../src/symbol_test.cc", "../src/traits_test.cc", + "../src/transform/array_length_from_uniform_test.cc", "../src/transform/binding_remapper_test.cc", "../src/transform/bound_array_accessors_test.cc", "../src/transform/calculate_array_length_test.cc", diff --git a/test/intrinsics/arrayLength/complex_via_let.wgsl.expected.msl b/test/intrinsics/arrayLength/complex_via_let.wgsl.expected.msl index b142311dbe..d690d2497e 100644 --- a/test/intrinsics/arrayLength/complex_via_let.wgsl.expected.msl +++ b/test/intrinsics/arrayLength/complex_via_let.wgsl.expected.msl @@ -1,18 +1,15 @@ -SKIP: FAILED +#include - -[[block]] +using namespace metal; +struct tint_symbol_1 { + /* 0x0000 */ uint buffer_size[1]; +}; struct S { - a : array; + /* 0x0000 */ int a[1]; }; -[[group(0), binding(0)]] var G : S; - -[[stage(compute)]] -fn tint_symbol() { - let p = &(G); - let p2 = &((*(p)).a); - let l1 : u32 = arrayLength(p2); +kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) { + uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u); + return; } -Failed to generate: error: Unknown import method: arrayLength diff --git a/test/intrinsics/arrayLength/deprecated.wgsl.expected.msl b/test/intrinsics/arrayLength/deprecated.wgsl.expected.msl index 522865a044..c86fc59bd4 100644 --- a/test/intrinsics/arrayLength/deprecated.wgsl.expected.msl +++ b/test/intrinsics/arrayLength/deprecated.wgsl.expected.msl @@ -1,18 +1,16 @@ -SKIP: FAILED +#include - -[[block]] +using namespace metal; +struct tint_symbol_1 { + /* 0x0000 */ uint buffer_size[1]; +}; struct S { - a : array; + /* 0x0000 */ int a[1]; }; -[[group(0), binding(0)]] var G : S; - -[[stage(compute)]] -fn tint_symbol() { - let l1 : u32 = arrayLength(&(G.a)); - let p = &(G.a); - let l2 : u32 = arrayLength(p); +kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) { + uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u); + uint const l2 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u); + return; } -Failed to generate: error: Unknown import method: arrayLength diff --git a/test/intrinsics/arrayLength/simple.wgsl.expected.msl b/test/intrinsics/arrayLength/simple.wgsl.expected.msl index 61e65daf72..d690d2497e 100644 --- a/test/intrinsics/arrayLength/simple.wgsl.expected.msl +++ b/test/intrinsics/arrayLength/simple.wgsl.expected.msl @@ -1,16 +1,15 @@ -SKIP: FAILED +#include - -[[block]] +using namespace metal; +struct tint_symbol_1 { + /* 0x0000 */ uint buffer_size[1]; +}; struct S { - a : array; + /* 0x0000 */ int a[1]; }; -[[group(0), binding(0)]] var G : S; - -[[stage(compute)]] -fn tint_symbol() { - let l1 : u32 = arrayLength(&(G.a)); +kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) { + uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u); + return; } -Failed to generate: error: Unknown import method: arrayLength diff --git a/test/intrinsics/arrayLength/via_let.wgsl.expected.msl b/test/intrinsics/arrayLength/via_let.wgsl.expected.msl index e87fc055cd..d690d2497e 100644 --- a/test/intrinsics/arrayLength/via_let.wgsl.expected.msl +++ b/test/intrinsics/arrayLength/via_let.wgsl.expected.msl @@ -1,18 +1,15 @@ -SKIP: FAILED +#include - -[[block]] +using namespace metal; +struct tint_symbol_1 { + /* 0x0000 */ uint buffer_size[1]; +}; struct S { - a : array; + /* 0x0000 */ int a[1]; }; -[[group(0), binding(0)]] var G : S; - -[[stage(compute)]] -fn tint_symbol() { - let p = &(G.a); - let p2 = p; - let l1 : u32 = arrayLength(p2); +kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) { + uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u); + return; } -Failed to generate: error: Unknown import method: arrayLength diff --git a/test/intrinsics/arrayLength/via_let_complex.wgsl.expected.msl b/test/intrinsics/arrayLength/via_let_complex.wgsl.expected.msl index 9fba102d83..d690d2497e 100644 --- a/test/intrinsics/arrayLength/via_let_complex.wgsl.expected.msl +++ b/test/intrinsics/arrayLength/via_let_complex.wgsl.expected.msl @@ -1,19 +1,15 @@ -SKIP: FAILED +#include - -[[block]] +using namespace metal; +struct tint_symbol_1 { + /* 0x0000 */ uint buffer_size[1]; +}; struct S { - a : array; + /* 0x0000 */ int a[1]; }; -[[group(0), binding(0)]] var G : S; - -[[stage(compute)]] -fn tint_symbol() { - let p = &(*(&(G))); - let p2 = &(*(p)); - let p3 = &((*(p)).a); - let l1 : u32 = arrayLength(&(*(p3))); +kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) { + uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u); + return; } -Failed to generate: error: Unknown import method: arrayLength