| // 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/wrap_arrays_in_structs.h" |
| |
| #include <utility> |
| |
| #include "src/program_builder.h" |
| #include "src/sem/array.h" |
| #include "src/sem/call.h" |
| #include "src/sem/expression.h" |
| #include "src/sem/type_constructor.h" |
| #include "src/utils/map.h" |
| #include "src/utils/transform.h" |
| |
| TINT_INSTANTIATE_TYPEINFO(tint::transform::WrapArraysInStructs); |
| |
| namespace tint { |
| namespace 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)) { |
| 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::DecorationList decos; |
| if (!array->IsStrideImplicit()) { |
| decos.emplace_back( |
| c.dst->create<ast::StrideDecoration>(array->Stride())); |
| } |
| return c.dst->ty.array(el_type(c), array->Count(), std::move(decos)); |
| }; |
| |
| // 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 transform |
| } // namespace tint |