msl: Add ArrayLengthFromUniform transform

Generate a uniform buffer that will receive the lengths of all storage
buffers, and use this to implement calls to arrayLength(). The
transform is provided with a set of mappings from storage buffer
binding points to the corresponding index into the array of buffer
lengths. The transform reports whether it generated the uniform
buffers or not.

Use this transform from the MSL sanitizer, using the binding number as
the index into the array. This matches the behavior of spirv-cross,
and so works with how Dawn already produces this uniform buffer.

Bug: tint:256
Change-Id: I2682d2d024e8daa30f78270b8cfb6bbb32632133
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/54480
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
This commit is contained in:
James Price 2021-06-19 00:33:35 +00:00 committed by Tint LUCI CQ
parent 75db82c96b
commit 960aa2ee75
13 changed files with 687 additions and 64 deletions

View File

@ -548,6 +548,8 @@ libtint_source_set("libtint_core_all_src") {
"symbol_table.cc", "symbol_table.cc",
"symbol_table.h", "symbol_table.h",
"traits.h", "traits.h",
"transform/array_length_from_uniform.cc",
"transform/array_length_from_uniform.h",
"transform/binding_remapper.cc", "transform/binding_remapper.cc",
"transform/binding_remapper.h", "transform/binding_remapper.h",
"transform/bound_array_accessors.cc", "transform/bound_array_accessors.cc",

View File

@ -273,6 +273,8 @@ set(TINT_LIB_SRCS
symbol.cc symbol.cc
symbol.h symbol.h
traits.h traits.h
transform/array_length_from_uniform.cc
transform/array_length_from_uniform.h
transform/binding_remapper.cc transform/binding_remapper.cc
transform/binding_remapper.h transform/binding_remapper.h
transform/bound_array_accessors.cc transform/bound_array_accessors.cc
@ -858,6 +860,7 @@ if(${TINT_BUILD_TESTS})
if(${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_WGSL_WRITER}) if(${TINT_BUILD_WGSL_READER} AND ${TINT_BUILD_WGSL_WRITER})
list(APPEND TINT_TEST_SRCS list(APPEND TINT_TEST_SRCS
transform/array_length_from_uniform.cc
transform/binding_remapper_test.cc transform/binding_remapper_test.cc
transform/bound_array_accessors_test.cc transform/bound_array_accessors_test.cc
transform/calculate_array_length_test.cc transform/calculate_array_length_test.cc

View File

@ -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 <memory>
#include <utility>
#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<Config>();
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<ast::StructBlockDecoration>()});
buffer_size_ubo = ctx.dst->Global(
ctx.dst->Sym(), ctx.dst->ty.Of(buffer_size_struct),
ast::StorageClass::kUniform,
ast::DecorationList{
ctx.dst->create<ast::GroupDecoration>(cfg->ubo_binding.group),
ctx.dst->create<ast::BindingDecoration>(
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<ast::CallExpression>();
if (!call_expr) {
continue;
}
auto* call = sem.Get(call_expr);
auto* intrinsic = call->Target()->As<sem::Intrinsic>();
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<ast::UnaryOpExpression>();
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<ast::MemberAccessorExpression>();
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<sem::VariableUser>();
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<sem::Struct>();
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<Result>(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

View File

@ -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 <unordered_map>
#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<u32, 8>;
/// };
///
/// [[group(0), binding(30)]]
/// var<uniform> 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<Data, transform::Data> {
/// 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<sem::BindingPoint, uint32_t> bindpoint_to_size_index;
};
/// Information produced about what the transform did.
struct Result : public Castable<Result, transform::Data> {
/// 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_

View File

@ -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 <utility>
#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<ArrayLengthFromUniform>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(ArrayLengthFromUniformTest, Basic) {
auto* src = R"(
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
var len : u32 = arrayLength(&sb.arr);
}
)";
auto* expect = R"(
[[block]]
struct tint_symbol {
buffer_size : array<u32, 1>;
};
[[group(0), binding(30)]] var<uniform> tint_symbol_1 : tint_symbol;
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[group(0), binding(0)]] var<storage, read> 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<ArrayLengthFromUniform::Config>(std::move(cfg));
auto got = Run<ArrayLengthFromUniform>(src, data);
EXPECT_EQ(expect, str(got));
EXPECT_TRUE(
got.data.Get<ArrayLengthFromUniform::Result>()->needs_buffer_sizes);
}
TEST_F(ArrayLengthFromUniformTest, WithStride) {
auto* src = R"(
[[block]]
struct SB {
x : i32;
y : f32;
arr : [[stride(64)]] array<i32>;
};
[[group(0), binding(0)]] var<storage, read> sb : SB;
[[stage(compute)]]
fn main() {
var len : u32 = arrayLength(&sb.arr);
}
)";
auto* expect = R"(
[[block]]
struct tint_symbol {
buffer_size : array<u32, 1>;
};
[[group(0), binding(30)]] var<uniform> tint_symbol_1 : tint_symbol;
[[block]]
struct SB {
x : i32;
y : f32;
arr : [[stride(64)]] array<i32>;
};
[[group(0), binding(0)]] var<storage, read> 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<ArrayLengthFromUniform::Config>(std::move(cfg));
auto got = Run<ArrayLengthFromUniform>(src, data);
EXPECT_EQ(expect, str(got));
EXPECT_TRUE(
got.data.Get<ArrayLengthFromUniform::Result>()->needs_buffer_sizes);
}
TEST_F(ArrayLengthFromUniformTest, MultipleStorageBuffers) {
auto* src = R"(
[[block]]
struct SB1 {
x : i32;
arr1 : array<i32>;
};
[[block]]
struct SB2 {
x : i32;
arr2 : array<vec4<f32>>;
};
[[group(0), binding(2)]] var<storage, read> sb1 : SB1;
[[group(1), binding(2)]] var<storage, read> 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<u32, 2>;
};
[[group(0), binding(30)]] var<uniform> tint_symbol_1 : tint_symbol;
[[block]]
struct SB1 {
x : i32;
arr1 : array<i32>;
};
[[block]]
struct SB2 {
x : i32;
arr2 : array<vec4<f32>>;
};
[[group(0), binding(2)]] var<storage, read> sb1 : SB1;
[[group(1), binding(2)]] var<storage, read> 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<ArrayLengthFromUniform::Config>(std::move(cfg));
auto got = Run<ArrayLengthFromUniform>(src, data);
EXPECT_EQ(expect, str(got));
EXPECT_TRUE(
got.data.Get<ArrayLengthFromUniform::Result>()->needs_buffer_sizes);
}
TEST_F(ArrayLengthFromUniformTest, NoArrayLengthCalls) {
auto* src = R"(
[[block]]
struct SB {
x : i32;
arr : array<i32>;
};
[[group(0), binding(0)]] var<storage, read> 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<ArrayLengthFromUniform::Config>(std::move(cfg));
auto got = Run<ArrayLengthFromUniform>(src, data);
EXPECT_EQ(src, str(got));
EXPECT_FALSE(
got.data.Get<ArrayLengthFromUniform::Result>()->needs_buffer_sizes);
}
TEST_F(ArrayLengthFromUniformTest, MissingBindingPointToIndexMapping) {
auto* src = R"(
[[block]]
struct SB1 {
x : i32;
arr1 : array<i32>;
};
[[block]]
struct SB2 {
x : i32;
arr2 : array<vec4<f32>>;
};
[[group(0), binding(2)]] var<storage, read> sb1 : SB1;
[[group(1), binding(2)]] var<storage, read> 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<ArrayLengthFromUniform::Config>(std::move(cfg));
auto got = Run<ArrayLengthFromUniform>(src, data);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace transform
} // namespace tint

View File

@ -14,6 +14,7 @@
#include "src/transform/msl.h" #include "src/transform/msl.h"
#include <memory>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -24,6 +25,7 @@
#include "src/sem/function.h" #include "src/sem/function.h"
#include "src/sem/statement.h" #include "src/sem/statement.h"
#include "src/sem/variable.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/canonicalize_entry_point_io.h"
#include "src/transform/external_texture_transform.h" #include "src/transform/external_texture_transform.h"
#include "src/transform/inline_pointer_lets.h" #include "src/transform/inline_pointer_lets.h"
@ -34,15 +36,38 @@
#include "src/transform/wrap_arrays_in_structs.h" #include "src/transform/wrap_arrays_in_structs.h"
#include "src/transform/zero_init_workgroup_memory.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 tint {
namespace transform { namespace transform {
Msl::Msl() = default; Msl::Msl() = default;
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; Manager manager;
DataMap data; DataMap internal_inputs;
auto* cfg = inputs.Get<Config>();
// 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 must come before CanonicalizeEntryPointIO as
// ZeroInitWorkgroupMemory may inject new builtin parameters. // ZeroInitWorkgroupMemory may inject new builtin parameters.
manager.Add<ZeroInitWorkgroupMemory>(); manager.Add<ZeroInitWorkgroupMemory>();
@ -53,9 +78,14 @@ Output Msl::Run(const Program* in, const DataMap&) {
manager.Add<PadArrayElements>(); manager.Add<PadArrayElements>();
manager.Add<InlinePointerLets>(); manager.Add<InlinePointerLets>();
manager.Add<Simplify>(); manager.Add<Simplify>();
data.Add<CanonicalizeEntryPointIO::Config>( // ArrayLengthFromUniform must come after InlinePointerLets and Simplify, as
// it assumes that the form of the array length argument is &var.array.
manager.Add<ArrayLengthFromUniform>();
internal_inputs.Add<ArrayLengthFromUniform::Config>(
std::move(array_length_from_uniform_cfg));
internal_inputs.Add<CanonicalizeEntryPointIO::Config>(
CanonicalizeEntryPointIO::BuiltinStyle::kParameter); CanonicalizeEntryPointIO::BuiltinStyle::kParameter);
auto out = manager.Run(in, data); auto out = manager.Run(in, internal_inputs);
if (!out.program.IsValid()) { if (!out.program.IsValid()) {
return out; return out;
} }
@ -66,7 +96,10 @@ Output Msl::Run(const Program* in, const DataMap&) {
// storage class(es) as transform options. // storage class(es) as transform options.
HandleModuleScopeVariables(ctx); HandleModuleScopeVariables(ctx);
ctx.Clone(); ctx.Clone();
return Output{Program(std::move(builder))};
auto result = std::make_unique<Result>(
out.data.Get<ArrayLengthFromUniform::Result>()->needs_buffer_sizes);
return Output{Program(std::move(builder)), std::move(result)};
} }
void Msl::HandleModuleScopeVariables(CloneContext& ctx) const { 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 transform
} // namespace tint } // namespace tint

View File

@ -25,6 +25,41 @@ namespace transform {
/// behavior. /// behavior.
class Msl : public Transform { class Msl : public Transform {
public: 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<Data, transform::Data> {
/// 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<Result, transform::Data> {
/// 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 /// Constructor
Msl(); Msl();
~Msl() override; ~Msl() override;

View File

@ -275,6 +275,7 @@ tint_unittests_source_set("tint_unittests_core_src") {
"../src/symbol_table_test.cc", "../src/symbol_table_test.cc",
"../src/symbol_test.cc", "../src/symbol_test.cc",
"../src/traits_test.cc", "../src/traits_test.cc",
"../src/transform/array_length_from_uniform_test.cc",
"../src/transform/binding_remapper_test.cc", "../src/transform/binding_remapper_test.cc",
"../src/transform/bound_array_accessors_test.cc", "../src/transform/bound_array_accessors_test.cc",
"../src/transform/calculate_array_length_test.cc", "../src/transform/calculate_array_length_test.cc",

View File

@ -1,18 +1,15 @@
SKIP: FAILED #include <metal_stdlib>
using namespace metal;
[[block]] struct tint_symbol_1 {
/* 0x0000 */ uint buffer_size[1];
};
struct S { struct S {
a : array<i32>; /* 0x0000 */ int a[1];
}; };
[[group(0), binding(0)]] var<storage, read> G : S; kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) {
uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u);
[[stage(compute)]] return;
fn tint_symbol() {
let p = &(G);
let p2 = &((*(p)).a);
let l1 : u32 = arrayLength(p2);
} }
Failed to generate: error: Unknown import method: arrayLength

View File

@ -1,18 +1,16 @@
SKIP: FAILED #include <metal_stdlib>
using namespace metal;
[[block]] struct tint_symbol_1 {
/* 0x0000 */ uint buffer_size[1];
};
struct S { struct S {
a : array<i32>; /* 0x0000 */ int a[1];
}; };
[[group(0), binding(0)]] var<storage, read> G : S; kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) {
uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u);
[[stage(compute)]] uint const l2 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u);
fn tint_symbol() { return;
let l1 : u32 = arrayLength(&(G.a));
let p = &(G.a);
let l2 : u32 = arrayLength(p);
} }
Failed to generate: error: Unknown import method: arrayLength

View File

@ -1,16 +1,15 @@
SKIP: FAILED #include <metal_stdlib>
using namespace metal;
[[block]] struct tint_symbol_1 {
/* 0x0000 */ uint buffer_size[1];
};
struct S { struct S {
a : array<i32>; /* 0x0000 */ int a[1];
}; };
[[group(0), binding(0)]] var<storage, read> G : S; kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) {
uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u);
[[stage(compute)]] return;
fn tint_symbol() {
let l1 : u32 = arrayLength(&(G.a));
} }
Failed to generate: error: Unknown import method: arrayLength

View File

@ -1,18 +1,15 @@
SKIP: FAILED #include <metal_stdlib>
using namespace metal;
[[block]] struct tint_symbol_1 {
/* 0x0000 */ uint buffer_size[1];
};
struct S { struct S {
a : array<i32>; /* 0x0000 */ int a[1];
}; };
[[group(0), binding(0)]] var<storage, read> G : S; kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) {
uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u);
[[stage(compute)]] return;
fn tint_symbol() {
let p = &(G.a);
let p2 = p;
let l1 : u32 = arrayLength(p2);
} }
Failed to generate: error: Unknown import method: arrayLength

View File

@ -1,19 +1,15 @@
SKIP: FAILED #include <metal_stdlib>
using namespace metal;
[[block]] struct tint_symbol_1 {
/* 0x0000 */ uint buffer_size[1];
};
struct S { struct S {
a : array<i32>; /* 0x0000 */ int a[1];
}; };
[[group(0), binding(0)]] var<storage, read> G : S; kernel void tint_symbol(constant tint_symbol_1& tint_symbol_2 [[buffer(30)]]) {
uint const l1 = ((tint_symbol_2.buffer_size[0u] - 0u) / 4u);
[[stage(compute)]] return;
fn tint_symbol() {
let p = &(*(&(G)));
let p2 = &(*(p));
let p3 = &((*(p)).a);
let l1 : u32 = arrayLength(&(*(p3)));
} }
Failed to generate: error: Unknown import method: arrayLength