tint/writer/msl: Generate an array<T,N> helper

And remove the WrapArraysInStructs transform.

Wrapping arrays in structures becomes troublesome for `const` arrays, as
currently WGSL does not allow `const` structures.

MSL 2.0+ has a builtin array<> helper, but we're targetting MSL 1.2, so
we have to emit our own. Fortunately, it can be done with a few lines of
templated code.

This produces significantly cleaner output.

Change-Id: Ifc92ef21e09befa252a07c856c4b5afdc51cc2e4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94540
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton
2022-06-24 17:01:59 +00:00
committed by Dawn LUCI CQ
parent 3c054304a8
commit f47887d207
218 changed files with 3637 additions and 2269 deletions

View File

@@ -1,158 +0,0 @@
// 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/wrap_arrays_in_structs.h"
#include <utility>
#include "src/tint/program_builder.h"
#include "src/tint/sem/array.h"
#include "src/tint/sem/call.h"
#include "src/tint/sem/expression.h"
#include "src/tint/sem/type_constructor.h"
#include "src/tint/utils/map.h"
#include "src/tint/utils/transform.h"
TINT_INSTANTIATE_TYPEINFO(tint::transform::WrapArraysInStructs);
namespace tint::transform {
WrapArraysInStructs::WrappedArrayInfo::WrappedArrayInfo() = default;
WrapArraysInStructs::WrappedArrayInfo::WrappedArrayInfo(const WrappedArrayInfo&) = default;
WrapArraysInStructs::WrappedArrayInfo::~WrappedArrayInfo() = default;
WrapArraysInStructs::WrapArraysInStructs() = default;
WrapArraysInStructs::~WrapArraysInStructs() = default;
bool WrapArraysInStructs::ShouldRun(const Program* program, const DataMap&) const {
for (auto* node : program->ASTNodes().Objects()) {
if (program->Sem().Get<sem::Array>(node->As<ast::Type>())) {
return true;
}
}
return false;
}
void WrapArraysInStructs::Run(CloneContext& ctx, const DataMap&, DataMap&) const {
auto& sem = ctx.src->Sem();
std::unordered_map<const sem::Array*, WrappedArrayInfo> wrapped_arrays;
auto wrapper = [&](const sem::Array* array) { return WrapArray(ctx, wrapped_arrays, array); };
auto wrapper_typename = [&](const sem::Array* arr) -> ast::TypeName* {
auto info = wrapper(arr);
return info ? ctx.dst->create<ast::TypeName>(info.wrapper_name) : nullptr;
};
// Replace all array types with their corresponding wrapper
ctx.ReplaceAll([&](const ast::Type* ast_type) -> const ast::Type* {
auto* type = ctx.src->TypeOf(ast_type);
if (auto* array = type->UnwrapRef()->As<sem::Array>()) {
return wrapper_typename(array);
}
return nullptr;
});
// Fix up index accessors so `a[1]` becomes `a.arr[1]`
ctx.ReplaceAll(
[&](const ast::IndexAccessorExpression* accessor) -> const ast::IndexAccessorExpression* {
if (auto* array =
::tint::As<sem::Array>(sem.Get(accessor->object)->Type()->UnwrapRef())) {
if (wrapper(array)) {
// Array is wrapped in a structure. Emit a member accessor to get
// to the actual array.
auto* arr = ctx.Clone(accessor->object);
auto* idx = ctx.Clone(accessor->index);
auto* unwrapped = ctx.dst->MemberAccessor(arr, "arr");
return ctx.dst->IndexAccessor(accessor->source, unwrapped, idx);
}
}
return nullptr;
});
// Fix up array constructors so `A(1,2)` becomes `tint_array_wrapper(A(1,2))`
ctx.ReplaceAll([&](const ast::CallExpression* expr) -> const ast::Expression* {
if (auto* call = sem.Get(expr)->UnwrapMaterialize()->As<sem::Call>()) {
if (auto* ctor = call->Target()->As<sem::TypeConstructor>()) {
if (auto* array = ctor->ReturnType()->As<sem::Array>()) {
if (auto w = wrapper(array)) {
// Wrap the array type constructor with another constructor for
// the wrapper
auto* wrapped_array_ty = ctx.dst->ty.type_name(w.wrapper_name);
auto* array_ty = w.array_type(ctx);
auto args = utils::Transform(call->Arguments(),
[&](const tint::sem::Expression* s) {
return ctx.Clone(s->Declaration());
});
auto* arr_ctor = ctx.dst->Construct(array_ty, args);
return ctx.dst->Construct(wrapped_array_ty, arr_ctor);
}
}
}
}
return nullptr;
});
ctx.Clone();
}
WrapArraysInStructs::WrappedArrayInfo WrapArraysInStructs::WrapArray(
CloneContext& ctx,
std::unordered_map<const sem::Array*, WrappedArrayInfo>& wrapped_arrays,
const sem::Array* array) const {
if (array->IsRuntimeSized()) {
return {}; // We don't want to wrap runtime sized arrays
}
return utils::GetOrCreate(wrapped_arrays, array, [&] {
WrappedArrayInfo info;
// Generate a unique name for the array wrapper
info.wrapper_name = ctx.dst->Symbols().New("tint_array_wrapper");
// Examine the element type. Is it also an array?
std::function<const ast::Type*(CloneContext&)> el_type;
if (auto* el_array = array->ElemType()->As<sem::Array>()) {
// Array of array - call WrapArray() on the element type
if (auto el = WrapArray(ctx, wrapped_arrays, el_array)) {
el_type = [=](CloneContext& c) {
return c.dst->create<ast::TypeName>(el.wrapper_name);
};
}
}
// If the element wasn't an array, just create the typical AST type for it
if (!el_type) {
el_type = [=](CloneContext& c) { return CreateASTTypeFor(c, array->ElemType()); };
}
// Construct the single structure field type
info.array_type = [=](CloneContext& c) {
ast::AttributeList attrs;
if (!array->IsStrideImplicit()) {
attrs.emplace_back(c.dst->create<ast::StrideAttribute>(array->Stride()));
}
return c.dst->ty.array(el_type(c), u32(array->Count()), std::move(attrs));
};
// Structure() will create and append the ast::Struct to the
// global declarations of `ctx.dst`. As we haven't finished building the
// current module-scope statement or function, this will be placed
// immediately before the usage.
ctx.dst->Structure(info.wrapper_name, {ctx.dst->Member("arr", info.array_type(ctx))});
return info;
});
}
} // namespace tint::transform

View File

@@ -1,88 +0,0 @@
// 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_TINT_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_
#define SRC_TINT_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_
#include <string>
#include <unordered_map>
#include "src/tint/transform/transform.h"
// Forward declarations
namespace tint::ast {
class Type;
} // namespace tint::ast
namespace tint::transform {
/// WrapArraysInStructs is a transform that replaces all array types with a
/// structure holding a single field of that array type.
/// Array index expressions and constructors are also adjusted to deal with this
/// wrapping.
/// This transform helps with backends that cannot directly return arrays or use
/// them as parameters.
class WrapArraysInStructs : public Castable<WrapArraysInStructs, Transform> {
public:
/// Constructor
WrapArraysInStructs();
/// Destructor
~WrapArraysInStructs() 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;
private:
struct WrappedArrayInfo {
WrappedArrayInfo();
WrappedArrayInfo(const WrappedArrayInfo&);
~WrappedArrayInfo();
Symbol wrapper_name;
std::function<const ast::Type*(CloneContext&)> array_type;
operator bool() { return wrapper_name.IsValid(); }
};
/// WrapArray wraps the fixed-size array type in a new structure (if it hasn't
/// already been wrapped). WrapArray will recursively wrap arrays-of-arrays.
/// The new structure will be added to module-scope type declarations of
/// `ctx.dst`.
/// @param ctx the CloneContext
/// @param wrapped_arrays a map of src array type to the wrapped structure
/// name
/// @param array the array type
/// @return the name of the structure that wraps the array, or an invalid
/// Symbol if this array should not be wrapped
WrappedArrayInfo WrapArray(
CloneContext& ctx,
std::unordered_map<const sem::Array*, WrappedArrayInfo>& wrapped_arrays,
const sem::Array* array) const;
};
} // namespace tint::transform
#endif // SRC_TINT_TRANSFORM_WRAP_ARRAYS_IN_STRUCTS_H_

View File

@@ -1,422 +0,0 @@
// 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/wrap_arrays_in_structs.h"
#include <memory>
#include <utility>
#include "src/tint/transform/test_helper.h"
namespace tint::transform {
namespace {
using WrapArraysInStructsTest = TransformTest;
TEST_F(WrapArraysInStructsTest, ShouldRunEmptyModule) {
auto* src = R"()";
EXPECT_FALSE(ShouldRun<WrapArraysInStructs>(src));
}
TEST_F(WrapArraysInStructsTest, ShouldRunHasArray) {
auto* src = R"(
var<private> arr : array<i32, 4>;
)";
EXPECT_TRUE(ShouldRun<WrapArraysInStructs>(src));
}
TEST_F(WrapArraysInStructsTest, EmptyModule) {
auto* src = R"()";
auto* expect = src;
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, ArrayAsGlobal) {
auto* src = R"(
var<private> arr : array<i32, 4>;
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 4u>,
}
var<private> arr : tint_array_wrapper;
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, ArrayAsFunctionVar) {
auto* src = R"(
fn f() {
var arr : array<i32, 4>;
let x = arr[3];
}
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 4u>,
}
fn f() {
var arr : tint_array_wrapper;
let x = arr.arr[3];
}
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, ArrayAsParam) {
auto* src = R"(
fn f(a : array<i32, 4>) -> i32 {
return a[2];
}
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 4u>,
}
fn f(a : tint_array_wrapper) -> i32 {
return a.arr[2];
}
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, ArrayAsReturn) {
auto* src = R"(
fn f() -> array<i32, 4> {
return array<i32, 4>(1, 2, 3, 4);
}
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 4u>,
}
fn f() -> tint_array_wrapper {
return tint_array_wrapper(array<i32, 4u>(1, 2, 3, 4));
}
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, ArrayAlias) {
auto* src = R"(
type Inner = array<i32, 2>;
type Array = array<Inner, 2>;
fn f() {
var arr : Array;
arr = Array();
arr = Array(Inner(1, 2), Inner(3, 4));
let vals : Array = Array(Inner(1, 2), Inner(3, 4));
arr = vals;
let x = arr[3];
}
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 2u>,
}
type Inner = tint_array_wrapper;
struct tint_array_wrapper_1 {
arr : array<tint_array_wrapper, 2u>,
}
type Array = tint_array_wrapper_1;
fn f() {
var arr : tint_array_wrapper_1;
arr = tint_array_wrapper_1(array<tint_array_wrapper, 2u>());
arr = tint_array_wrapper_1(array<tint_array_wrapper, 2u>(tint_array_wrapper(array<i32, 2u>(1, 2)), tint_array_wrapper(array<i32, 2u>(3, 4))));
let vals : tint_array_wrapper_1 = tint_array_wrapper_1(array<tint_array_wrapper, 2u>(tint_array_wrapper(array<i32, 2u>(1, 2)), tint_array_wrapper(array<i32, 2u>(3, 4))));
arr = vals;
let x = arr.arr[3];
}
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, ArrayAlias_OutOfOrder) {
auto* src = R"(
fn f() {
var arr : Array;
arr = Array();
arr = Array(Inner(1, 2), Inner(3, 4));
let vals : Array = Array(Inner(1, 2), Inner(3, 4));
arr = vals;
let x = arr[3];
}
type Array = array<Inner, 2>;
type Inner = array<i32, 2>;
)";
auto* expect = R"(
struct tint_array_wrapper_1 {
arr : array<i32, 2u>,
}
struct tint_array_wrapper {
arr : array<tint_array_wrapper_1, 2u>,
}
fn f() {
var arr : tint_array_wrapper;
arr = tint_array_wrapper(array<tint_array_wrapper_1, 2u>());
arr = tint_array_wrapper(array<tint_array_wrapper_1, 2u>(tint_array_wrapper_1(array<i32, 2u>(1, 2)), tint_array_wrapper_1(array<i32, 2u>(3, 4))));
let vals : tint_array_wrapper = tint_array_wrapper(array<tint_array_wrapper_1, 2u>(tint_array_wrapper_1(array<i32, 2u>(1, 2)), tint_array_wrapper_1(array<i32, 2u>(3, 4))));
arr = vals;
let x = arr.arr[3];
}
type Array = tint_array_wrapper;
type Inner = tint_array_wrapper_1;
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, ArraysInStruct) {
auto* src = R"(
struct S {
a : array<i32, 4>,
b : array<i32, 8>,
c : array<i32, 4>,
};
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 4u>,
}
struct tint_array_wrapper_1 {
arr : array<i32, 8u>,
}
struct S {
a : tint_array_wrapper,
b : tint_array_wrapper_1,
c : tint_array_wrapper,
}
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, ArraysOfArraysInStruct) {
auto* src = R"(
struct S {
a : array<i32, 4>,
b : array<array<i32, 4>, 4>,
c : array<array<array<i32, 4>, 4>, 4>,
};
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 4u>,
}
struct tint_array_wrapper_1 {
arr : array<tint_array_wrapper, 4u>,
}
struct tint_array_wrapper_2 {
arr : array<tint_array_wrapper_1, 4u>,
}
struct S {
a : tint_array_wrapper,
b : tint_array_wrapper_1,
c : tint_array_wrapper_2,
}
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, AccessArraysOfArraysInStruct) {
auto* src = R"(
struct S {
a : array<i32, 4>,
b : array<array<i32, 4>, 4>,
c : array<array<array<i32, 4>, 4>, 4>,
};
fn f(s : S) -> i32 {
return s.a[2] + s.b[1][2] + s.c[3][1][2];
}
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 4u>,
}
struct tint_array_wrapper_1 {
arr : array<tint_array_wrapper, 4u>,
}
struct tint_array_wrapper_2 {
arr : array<tint_array_wrapper_1, 4u>,
}
struct S {
a : tint_array_wrapper,
b : tint_array_wrapper_1,
c : tint_array_wrapper_2,
}
fn f(s : S) -> i32 {
return ((s.a.arr[2] + s.b.arr[1].arr[2]) + s.c.arr[3].arr[1].arr[2]);
}
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, DeclarationOrder) {
auto* src = R"(
type T0 = i32;
type T1 = array<i32, 1>;
type T2 = i32;
fn f1(a : array<i32, 2>) {
}
type T3 = i32;
fn f2() {
var v : array<i32, 3>;
}
)";
auto* expect = R"(
type T0 = i32;
struct tint_array_wrapper {
arr : array<i32, 1u>,
}
type T1 = tint_array_wrapper;
type T2 = i32;
struct tint_array_wrapper_1 {
arr : array<i32, 2u>,
}
fn f1(a : tint_array_wrapper_1) {
}
type T3 = i32;
struct tint_array_wrapper_2 {
arr : array<i32, 3u>,
}
fn f2() {
var v : tint_array_wrapper_2;
}
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
TEST_F(WrapArraysInStructsTest, DeclarationOrder_OutOfOrder) {
auto* src = R"(
fn f2() {
var v : array<i32, 3>;
}
type T3 = i32;
fn f1(a : array<i32, 2>) {
}
type T2 = i32;
type T1 = array<i32, 1>;
type T0 = i32;
)";
auto* expect = R"(
struct tint_array_wrapper {
arr : array<i32, 3u>,
}
fn f2() {
var v : tint_array_wrapper;
}
type T3 = i32;
struct tint_array_wrapper_1 {
arr : array<i32, 2u>,
}
fn f1(a : tint_array_wrapper_1) {
}
type T2 = i32;
struct tint_array_wrapper_2 {
arr : array<i32, 1u>,
}
type T1 = tint_array_wrapper_2;
type T0 = i32;
)";
auto got = Run<WrapArraysInStructs>(src);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace tint::transform