blob: 771b95411bc07467329e56eecd67b300571a6a25 [file] [log] [blame]
Austin Engcc2516a2023-10-17 20:57:54 +00001// Copyright 2021 The Dawn & Tint Authors
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002//
Austin Engcc2516a2023-10-17 20:57:54 +00003// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
Ryan Harrisondbc13af2022-02-21 15:19:07 +00005//
Austin Engcc2516a2023-10-17 20:57:54 +00006// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00008//
Austin Engcc2516a2023-10-17 20:57:54 +00009// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Ryan Harrisondbc13af2022-02-21 15:19:07 +000027
dan sinclair99181d82023-07-20 01:14:15 +000028#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000029
30#include <memory>
31#include <unordered_map>
32#include <utility>
33#include <vector>
34
dan sinclair99181d82023-07-20 01:14:15 +000035#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
Ben Claytonae18c412023-07-29 13:00:40 +000036#include "src/tint/lang/wgsl/program/clone_context.h"
dan sinclair96db5542023-07-20 09:21:10 +000037#include "src/tint/lang/wgsl/program/program_builder.h"
Ben Clayton915ceca2023-07-29 13:12:58 +000038#include "src/tint/lang/wgsl/resolver/resolve.h"
dan sinclaird3b13692023-07-20 01:14:15 +000039#include "src/tint/lang/wgsl/sem/block_statement.h"
40#include "src/tint/lang/wgsl/sem/function.h"
41#include "src/tint/lang/wgsl/sem/statement.h"
42#include "src/tint/lang/wgsl/sem/variable.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000043#include "src/tint/utils/rtti/switch.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000044
James Priceb4acbb82023-05-11 21:27:16 +000045TINT_INSTANTIATE_TYPEINFO(tint::ast::transform::SimplifyPointers);
Ryan Harrisondbc13af2022-02-21 15:19:07 +000046
James Priceb4acbb82023-05-11 21:27:16 +000047namespace tint::ast::transform {
Ryan Harrisondbc13af2022-02-21 15:19:07 +000048
49namespace {
50
51/// PointerOp describes either possible indirection or address-of action on an
52/// expression.
53struct PointerOp {
dan sinclair41e4d9a2022-05-01 14:40:55 +000054 /// Positive: Number of times the `expr` was dereferenced (*expr)
55 /// Negative: Number of times the `expr` was 'addressed-of' (&expr)
56 /// Zero: no pointer op on `expr`
57 int indirections = 0;
58 /// The expression being operated on
James Price2b7406a2023-05-12 01:43:50 +000059 const Expression* expr = nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +000060};
61
62} // namespace
63
Ben Claytonc6b38142022-11-03 08:41:19 +000064/// PIMPL state for the transform
Ryan Harrisondbc13af2022-02-21 15:19:07 +000065struct SimplifyPointers::State {
Ben Claytonc6b38142022-11-03 08:41:19 +000066 /// The source program
Ben Clayton5ed5cc42023-09-22 10:31:04 +000067 const Program& src;
Ben Claytonc6b38142022-11-03 08:41:19 +000068 /// The target program builder
69 ProgramBuilder b;
dan sinclair41e4d9a2022-05-01 14:40:55 +000070 /// The clone context
Ben Clayton5ed5cc42023-09-22 10:31:04 +000071 program::CloneContext ctx = {&b, &src, /* auto_clone_symbols */ true};
Ryan Harrisondbc13af2022-02-21 15:19:07 +000072
dan sinclair41e4d9a2022-05-01 14:40:55 +000073 /// Constructor
Ben Claytonc6b38142022-11-03 08:41:19 +000074 /// @param program the source program
Ben Clayton5ed5cc42023-09-22 10:31:04 +000075 explicit State(const Program& program) : src(program) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +000076
dan sinclair41e4d9a2022-05-01 14:40:55 +000077 /// Traverses the expression `expr` looking for non-literal array indexing
78 /// expressions that would affect the computed address of a pointer
79 /// expression. The function-like argument `cb` is called for each found.
80 /// @param expr the expression to traverse
81 /// @param cb a function-like object with the signature
James Price2b7406a2023-05-12 01:43:50 +000082 /// `void(const Expression*)`, which is called for each array index
dan sinclair41e4d9a2022-05-01 14:40:55 +000083 /// expression
84 template <typename F>
James Price2b7406a2023-05-12 01:43:50 +000085 static void CollectSavedArrayIndices(const Expression* expr, F&& cb) {
86 if (auto* a = expr->As<IndexAccessorExpression>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +000087 CollectSavedArrayIndices(a->object, cb);
James Price2b7406a2023-05-12 01:43:50 +000088 if (!a->index->Is<LiteralExpression>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +000089 cb(a->index);
90 }
91 return;
Ryan Harrisondbc13af2022-02-21 15:19:07 +000092 }
93
James Price2b7406a2023-05-12 01:43:50 +000094 if (auto* m = expr->As<MemberAccessorExpression>()) {
Ben Claytonad315652023-02-05 12:36:50 +000095 CollectSavedArrayIndices(m->object, cb);
dan sinclair41e4d9a2022-05-01 14:40:55 +000096 return;
Ryan Harrisondbc13af2022-02-21 15:19:07 +000097 }
98
James Price2b7406a2023-05-12 01:43:50 +000099 if (auto* u = expr->As<UnaryOpExpression>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000100 CollectSavedArrayIndices(u->expr, cb);
101 return;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000102 }
103
James Price2b7406a2023-05-12 01:43:50 +0000104 // Note: Other Expression types can be safely ignored as they cannot be
dan sinclair41e4d9a2022-05-01 14:40:55 +0000105 // used to generate a reference or pointer.
106 // See https://gpuweb.github.io/gpuweb/wgsl/#forming-references-and-pointers
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000107 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000108
109 /// Reduce walks the expression chain, collapsing all address-of and
110 /// indirection ops into a PointerOp.
111 /// @param in the expression to walk
112 /// @returns the reduced PointerOp
James Price2b7406a2023-05-12 01:43:50 +0000113 PointerOp Reduce(const Expression* in) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000114 PointerOp op{0, in};
115 while (true) {
James Price2b7406a2023-05-12 01:43:50 +0000116 if (auto* unary = op.expr->As<UnaryOpExpression>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000117 switch (unary->op) {
Ben Clayton67b461b2023-08-08 07:58:19 +0000118 case core::UnaryOp::kIndirection:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000119 op.indirections++;
120 op.expr = unary->expr;
121 continue;
Ben Clayton67b461b2023-08-08 07:58:19 +0000122 case core::UnaryOp::kAddressOf:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000123 op.indirections--;
124 op.expr = unary->expr;
125 continue;
126 default:
127 break;
128 }
129 }
130 if (auto* user = ctx.src->Sem().Get<sem::VariableUser>(op.expr)) {
131 auto* var = user->Variable();
James Price2b7406a2023-05-12 01:43:50 +0000132 if (var->Is<sem::LocalVariable>() && //
133 var->Declaration()->Is<Let>() && //
dan sinclaircedcdf32023-08-10 02:39:48 +0000134 var->Type()->Is<core::type::Pointer>()) {
dan sinclair6e77b472022-10-20 13:38:28 +0000135 op.expr = var->Declaration()->initializer;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000136 continue;
137 }
138 }
139 return op;
140 }
141 }
142
Ben Claytonc6b38142022-11-03 08:41:19 +0000143 /// Runs the transform
144 /// @returns the new program or SkipTransform if the transform is not required
145 ApplyResult Run() {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000146 // A map of saved expressions to their saved variable name
dan sinclairbae54e72023-07-28 15:01:54 +0000147 Hashmap<const Expression*, Symbol, 8> saved_vars;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000148
Ben Clayton42363a52023-01-12 18:29:07 +0000149 bool needs_transform = false;
Ben Clayton559e5a22023-04-17 14:24:58 +0000150 for (auto* ty : ctx.src->Types()) {
dan sinclaircedcdf32023-08-10 02:39:48 +0000151 if (ty->Is<core::type::Pointer>()) {
Ben Clayton559e5a22023-04-17 14:24:58 +0000152 // Program contains pointers which need removing.
153 needs_transform = true;
154 break;
155 }
156 }
Ben Clayton42363a52023-01-12 18:29:07 +0000157
158 // Find all the pointer-typed `let` declarations.
159 // Note that these must be function-scoped, as module-scoped `let`s are not
160 // permitted.
161 for (auto* node : ctx.src->ASTNodes().Objects()) {
162 Switch(
163 node, //
James Price2b7406a2023-05-12 01:43:50 +0000164 [&](const VariableDeclStatement* let) {
165 if (!let->variable->Is<Let>()) {
Ben Clayton42363a52023-01-12 18:29:07 +0000166 return; // Not a `let` declaration. Ignore.
167 }
168
169 auto* var = ctx.src->Sem().Get(let->variable);
dan sinclaircedcdf32023-08-10 02:39:48 +0000170 if (!var->Type()->Is<core::type::Pointer>()) {
Ben Clayton42363a52023-01-12 18:29:07 +0000171 return; // Not a pointer type. Ignore.
172 }
173
174 // We're dealing with a pointer-typed `let` declaration.
175
176 // Scan the initializer expression for array index expressions that need
177 // to be hoist to temporary "saved" variables.
dan sinclairbae54e72023-07-28 15:01:54 +0000178 tint::Vector<const VariableDeclStatement*, 8> saved;
Ben Clayton42363a52023-01-12 18:29:07 +0000179 CollectSavedArrayIndices(
James Price2b7406a2023-05-12 01:43:50 +0000180 var->Declaration()->initializer, [&](const Expression* idx_expr) {
Ben Clayton42363a52023-01-12 18:29:07 +0000181 // We have a sub-expression that needs to be saved.
182 // Create a new variable
183 auto saved_name = ctx.dst->Symbols().New(
dan sinclaird026e132023-04-18 19:38:25 +0000184 var->Declaration()->name->symbol.Name() + "_save");
Ben Clayton42363a52023-01-12 18:29:07 +0000185 auto* decl =
186 ctx.dst->Decl(ctx.dst->Let(saved_name, ctx.Clone(idx_expr)));
187 saved.Push(decl);
188 // Record the substitution of `idx_expr` to the saved variable
189 // with the symbol `saved_name`. This will be used by the
190 // ReplaceAll() handler above.
191 saved_vars.Add(idx_expr, saved_name);
192 });
193
194 // Find the place to insert the saved declarations.
195 // Special care needs to be made for lets declared as the initializer
196 // part of for-loops. In this case the block will hold the for-loop
197 // statement, not the let.
198 if (!saved.IsEmpty()) {
199 auto* stmt = ctx.src->Sem().Get(let);
200 auto* block = stmt->Block();
201 // Find the statement owned by the block (either the let decl or a
202 // for-loop)
203 while (block != stmt->Parent()) {
204 stmt = stmt->Parent();
205 }
206 // Declare the stored variables just before stmt. Order here is
207 // important as order-of-operations needs to be preserved.
208 // CollectSavedArrayIndices() visits the LHS of an index accessor
209 // before the index expression.
210 for (auto* decl : saved) {
211 // Note that repeated calls to InsertBefore() with the same `before`
212 // argument will result in nodes to inserted in the order the
213 // calls are made (last call is inserted last).
214 ctx.InsertBefore(block->Declaration()->statements, stmt->Declaration(),
215 decl);
216 }
217 }
218
219 // As the original `let` declaration will be fully inlined, there's no
220 // need for the original declaration to exist. Remove it.
221 RemoveStatement(ctx, let);
222 },
James Price2b7406a2023-05-12 01:43:50 +0000223 [&](const UnaryOpExpression* op) {
Ben Clayton67b461b2023-08-08 07:58:19 +0000224 if (op->op == core::UnaryOp::kAddressOf) {
Ben Clayton42363a52023-01-12 18:29:07 +0000225 // Transform can be skipped if no address-of operator is used, as there
226 // will be no pointers that can be inlined.
227 needs_transform = true;
228 }
229 });
230 }
231
232 if (!needs_transform) {
233 return SkipTransform;
234 }
235
James Price2b7406a2023-05-12 01:43:50 +0000236 // Register the Expression transform handler.
dan sinclair41e4d9a2022-05-01 14:40:55 +0000237 // This performs two different transformations:
238 // * Identifiers that resolve to the pointer-typed `let` declarations are
239 // replaced with the recursively inlined initializer expression for the
240 // `let` declaration.
241 // * Sub-expressions inside the pointer-typed `let` initializer expression
242 // that have been hoisted to a saved variable are replaced with the saved
243 // variable identifier.
James Price2b7406a2023-05-12 01:43:50 +0000244 ctx.ReplaceAll([&](const Expression* expr) -> const Expression* {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000245 // Look to see if we need to swap this Expression with a saved variable.
Ben Clayton7c6e2292022-11-23 21:04:25 +0000246 if (auto saved_var = saved_vars.Find(expr)) {
Ben Claytonc6b38142022-11-03 08:41:19 +0000247 return ctx.dst->Expr(*saved_var);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000248 }
249
250 // Reduce the expression, folding away chains of address-of / indirections
251 auto op = Reduce(expr);
252
253 // Clone the reduced root expression
254 expr = ctx.CloneWithoutTransform(op.expr);
255
256 // And reapply the minimum number of address-of / indirections
257 for (int i = 0; i < op.indirections; i++) {
258 expr = ctx.dst->Deref(expr);
259 }
260 for (int i = 0; i > op.indirections; i--) {
261 expr = ctx.dst->AddressOf(expr);
262 }
263 return expr;
264 });
265
dan sinclair41e4d9a2022-05-01 14:40:55 +0000266 ctx.Clone();
Ben Clayton915ceca2023-07-29 13:12:58 +0000267 return resolver::Resolve(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000268 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000269};
270
271SimplifyPointers::SimplifyPointers() = default;
272
273SimplifyPointers::~SimplifyPointers() = default;
274
Ben Clayton5ed5cc42023-09-22 10:31:04 +0000275Transform::ApplyResult SimplifyPointers::Apply(const Program& src, const DataMap&, DataMap&) const {
Ben Claytonc6b38142022-11-03 08:41:19 +0000276 return State(src).Run();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000277}
278
James Priceb4acbb82023-05-11 21:27:16 +0000279} // namespace tint::ast::transform