1 // Copyright 2021 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/transform/wrap_arrays_in_structs.h"
16
17 #include <utility>
18
19 #include "src/program_builder.h"
20 #include "src/sem/array.h"
21 #include "src/sem/call.h"
22 #include "src/sem/expression.h"
23 #include "src/sem/type_constructor.h"
24 #include "src/utils/map.h"
25 #include "src/utils/transform.h"
26
27 TINT_INSTANTIATE_TYPEINFO(tint::transform::WrapArraysInStructs);
28
29 namespace tint {
30 namespace transform {
31
32 WrapArraysInStructs::WrappedArrayInfo::WrappedArrayInfo() = default;
33 WrapArraysInStructs::WrappedArrayInfo::WrappedArrayInfo(
34 const WrappedArrayInfo&) = default;
35 WrapArraysInStructs::WrappedArrayInfo::~WrappedArrayInfo() = default;
36
37 WrapArraysInStructs::WrapArraysInStructs() = default;
38
39 WrapArraysInStructs::~WrapArraysInStructs() = default;
40
Run(CloneContext & ctx,const DataMap &,DataMap &)41 void WrapArraysInStructs::Run(CloneContext& ctx, const DataMap&, DataMap&) {
42 auto& sem = ctx.src->Sem();
43
44 std::unordered_map<const sem::Array*, WrappedArrayInfo> wrapped_arrays;
45 auto wrapper = [&](const sem::Array* array) {
46 return WrapArray(ctx, wrapped_arrays, array);
47 };
48 auto wrapper_typename = [&](const sem::Array* arr) -> ast::TypeName* {
49 auto info = wrapper(arr);
50 return info ? ctx.dst->create<ast::TypeName>(info.wrapper_name) : nullptr;
51 };
52
53 // Replace all array types with their corresponding wrapper
54 ctx.ReplaceAll([&](const ast::Type* ast_type) -> const ast::Type* {
55 auto* type = ctx.src->TypeOf(ast_type);
56 if (auto* array = type->UnwrapRef()->As<sem::Array>()) {
57 return wrapper_typename(array);
58 }
59 return nullptr;
60 });
61
62 // Fix up index accessors so `a[1]` becomes `a.arr[1]`
63 ctx.ReplaceAll([&](const ast::IndexAccessorExpression* accessor)
64 -> const ast::IndexAccessorExpression* {
65 if (auto* array = ::tint::As<sem::Array>(
66 sem.Get(accessor->object)->Type()->UnwrapRef())) {
67 if (wrapper(array)) {
68 // Array is wrapped in a structure. Emit a member accessor to get
69 // to the actual array.
70 auto* arr = ctx.Clone(accessor->object);
71 auto* idx = ctx.Clone(accessor->index);
72 auto* unwrapped = ctx.dst->MemberAccessor(arr, "arr");
73 return ctx.dst->IndexAccessor(accessor->source, unwrapped, idx);
74 }
75 }
76 return nullptr;
77 });
78
79 // Fix up array constructors so `A(1,2)` becomes `tint_array_wrapper(A(1,2))`
80 ctx.ReplaceAll(
81 [&](const ast::CallExpression* expr) -> const ast::Expression* {
82 if (auto* call = sem.Get(expr)) {
83 if (auto* ctor = call->Target()->As<sem::TypeConstructor>()) {
84 if (auto* array = ctor->ReturnType()->As<sem::Array>()) {
85 if (auto w = wrapper(array)) {
86 // Wrap the array type constructor with another constructor for
87 // the wrapper
88 auto* wrapped_array_ty = ctx.dst->ty.type_name(w.wrapper_name);
89 auto* array_ty = w.array_type(ctx);
90 auto args = utils::Transform(
91 call->Arguments(), [&](const tint::sem::Expression* s) {
92 return ctx.Clone(s->Declaration());
93 });
94 auto* arr_ctor = ctx.dst->Construct(array_ty, args);
95 return ctx.dst->Construct(wrapped_array_ty, arr_ctor);
96 }
97 }
98 }
99 }
100 return nullptr;
101 });
102
103 ctx.Clone();
104 }
105
WrapArray(CloneContext & ctx,std::unordered_map<const sem::Array *,WrappedArrayInfo> & wrapped_arrays,const sem::Array * array) const106 WrapArraysInStructs::WrappedArrayInfo WrapArraysInStructs::WrapArray(
107 CloneContext& ctx,
108 std::unordered_map<const sem::Array*, WrappedArrayInfo>& wrapped_arrays,
109 const sem::Array* array) const {
110 if (array->IsRuntimeSized()) {
111 return {}; // We don't want to wrap runtime sized arrays
112 }
113
114 return utils::GetOrCreate(wrapped_arrays, array, [&] {
115 WrappedArrayInfo info;
116
117 // Generate a unique name for the array wrapper
118 info.wrapper_name = ctx.dst->Symbols().New("tint_array_wrapper");
119
120 // Examine the element type. Is it also an array?
121 std::function<const ast::Type*(CloneContext&)> el_type;
122 if (auto* el_array = array->ElemType()->As<sem::Array>()) {
123 // Array of array - call WrapArray() on the element type
124 if (auto el = WrapArray(ctx, wrapped_arrays, el_array)) {
125 el_type = [=](CloneContext& c) {
126 return c.dst->create<ast::TypeName>(el.wrapper_name);
127 };
128 }
129 }
130
131 // If the element wasn't an array, just create the typical AST type for it
132 if (!el_type) {
133 el_type = [=](CloneContext& c) {
134 return CreateASTTypeFor(c, array->ElemType());
135 };
136 }
137
138 // Construct the single structure field type
139 info.array_type = [=](CloneContext& c) {
140 ast::DecorationList decos;
141 if (!array->IsStrideImplicit()) {
142 decos.emplace_back(
143 c.dst->create<ast::StrideDecoration>(array->Stride()));
144 }
145 return c.dst->ty.array(el_type(c), array->Count(), std::move(decos));
146 };
147
148 // Structure() will create and append the ast::Struct to the
149 // global declarations of `ctx.dst`. As we haven't finished building the
150 // current module-scope statement or function, this will be placed
151 // immediately before the usage.
152 ctx.dst->Structure(info.wrapper_name,
153 {ctx.dst->Member("arr", info.array_type(ctx))});
154 return info;
155 });
156 }
157
158 } // namespace transform
159 } // namespace tint
160