• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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