blob: d4ceca56ab9af81e61fe71c5317b4421636a79f5 [file] [log] [blame]
Mingyao Yang8df69d42015-10-22 15:40:58 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "load_store_elimination.h"
Aart Bik96fd51d2016-11-28 11:22:35 -080018
Alex Light09e23372021-01-15 08:42:11 -080019#include <algorithm>
20#include <optional>
21#include <sstream>
22#include <variant>
23
Alex Light86fe9b82020-11-16 16:54:01 +000024#include "base/arena_allocator.h"
Vladimir Marko3224f382020-06-23 14:19:53 +010025#include "base/arena_bit_vector.h"
Vladimir Marko009d1662017-10-10 13:21:15 +010026#include "base/array_ref.h"
Vladimir Marko3224f382020-06-23 14:19:53 +010027#include "base/bit_vector-inl.h"
Alex Light86fe9b82020-11-16 16:54:01 +000028#include "base/bit_vector.h"
Alex Lightef28d242020-11-17 20:21:51 -080029#include "base/globals.h"
Alex Light3a73ffb2021-01-25 14:11:05 +000030#include "base/indenter.h"
Alex Lightef28d242020-11-17 20:21:51 -080031#include "base/iteration_range.h"
Vladimir Marko009d1662017-10-10 13:21:15 +010032#include "base/scoped_arena_allocator.h"
33#include "base/scoped_arena_containers.h"
Alex Light3a73ffb2021-01-25 14:11:05 +000034#include "base/transform_iterator.h"
Aart Bik96fd51d2016-11-28 11:22:35 -080035#include "escape.h"
Alex Light86fe9b82020-11-16 16:54:01 +000036#include "execution_subgraph.h"
Alex Light3a73ffb2021-01-25 14:11:05 +000037#include "handle.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070038#include "load_store_analysis.h"
Alex Light3a73ffb2021-01-25 14:11:05 +000039#include "mirror/class_loader.h"
40#include "mirror/dex_cache.h"
Alex Light86fe9b82020-11-16 16:54:01 +000041#include "nodes.h"
Alex Light3a73ffb2021-01-25 14:11:05 +000042#include "optimizing/execution_subgraph.h"
Alex Light86fe9b82020-11-16 16:54:01 +000043#include "optimizing_compiler_stats.h"
Vladimir Marko3224f382020-06-23 14:19:53 +010044#include "reference_type_propagation.h"
Alex Light86fe9b82020-11-16 16:54:01 +000045#include "side_effects_analysis.h"
Alex Light3a73ffb2021-01-25 14:11:05 +000046#include "stack_map.h"
Mingyao Yang8df69d42015-10-22 15:40:58 -070047
Mingyao Yanga3540532018-01-25 12:17:28 -080048/**
49 * The general algorithm of load-store elimination (LSE).
Vladimir Marko3224f382020-06-23 14:19:53 +010050 *
51 * We use load-store analysis to collect a list of heap locations and perform
52 * alias analysis of those heap locations. LSE then keeps track of a list of
53 * heap values corresponding to the heap locations and stores that put those
Vladimir Marko9e3fe992020-08-25 16:17:51 +010054 * values in these locations.
55 * - In phase 1, we visit basic blocks in reverse post order and for each basic
56 * block, visit instructions sequentially, recording heap values and looking
57 * for loads and stores to eliminate without relying on loop Phis.
58 * - In phase 2, we look for loads that can be replaced by creating loop Phis
59 * or using a loop-invariant value.
60 * - In phase 3, we determine which stores are dead and can be eliminated and
61 * based on that information we re-evaluate whether some kept stores are
62 * storing the same value as the value in the heap location; such stores are
63 * also marked for elimination.
64 * - In phase 4, we commit the changes, replacing loads marked for elimination
65 * in previous processing and removing stores not marked for keeping. We also
66 * remove allocations that are no longer needed.
Alex Light3a73ffb2021-01-25 14:11:05 +000067 * - In phase 5, we move allocations which only escape along some executions
68 * closer to their escape points and fixup non-escaping paths with their actual
69 * values, creating PHIs when needed.
Vladimir Marko3224f382020-06-23 14:19:53 +010070 *
71 * 1. Walk over blocks and their instructions.
72 *
73 * The initial set of heap values for a basic block is
74 * - For a loop header of an irreducible loop, all heap values are unknown.
75 * - For a loop header of a normal loop, all values unknown at the end of the
76 * preheader are initialized to unknown, other heap values are set to Phi
77 * placeholders as we cannot determine yet whether these values are known on
78 * all back-edges. We use Phi placeholders also for array heap locations with
79 * index defined inside the loop but this helps only when the value remains
80 * zero from the array allocation throughout the loop.
Santiago Aboy Solanes9e3c3712022-04-08 13:24:05 +000081 * - For catch blocks, we clear all assumptions since we arrived due to an
82 * instruction throwing.
Vladimir Marko3224f382020-06-23 14:19:53 +010083 * - For other basic blocks, we merge incoming values from the end of all
84 * predecessors. If any incoming value is unknown, the start value for this
85 * block is also unknown. Otherwise, if all the incoming values are the same
86 * (including the case of a single predecessor), the incoming value is used.
87 * Otherwise, we use a Phi placeholder to indicate different incoming values.
88 * We record whether such Phi placeholder depends on a loop Phi placeholder.
89 *
90 * For each instruction in the block
91 * - If the instruction is a load from a heap location with a known value not
92 * dependent on a loop Phi placeholder, the load can be eliminated, either by
93 * using an existing instruction or by creating new Phi(s) instead. In order
94 * to maintain the validity of all heap locations during the optimization
95 * phase, we only record substitutes at this phase and the real elimination
96 * is delayed till the end of LSE. Loads that require a loop Phi placeholder
Alex Light3a73ffb2021-01-25 14:11:05 +000097 * replacement are recorded for processing later. We also keep track of the
98 * heap-value at the start load so that later partial-LSE can predicate the
99 * load.
Vladimir Marko3224f382020-06-23 14:19:53 +0100100 * - If the instruction is a store, it updates the heap value for the heap
101 * location with the stored value and records the store itself so that we can
102 * mark it for keeping if the value becomes observable. Heap values are
103 * invalidated for heap locations that may alias with the store instruction's
104 * heap location and their recorded stores are marked for keeping as they are
105 * now potentially observable. The store instruction can be eliminated unless
106 * the value stored is later needed e.g. by a load from the same/aliased heap
107 * location or the heap location persists at method return/deoptimization.
108 * - A store that stores the same value as the heap value is eliminated.
109 * - For newly instantiated instances, their heap values are initialized to
110 * language defined default values.
111 * - Finalizable objects are considered as persisting at method
112 * return/deoptimization.
113 * - Some instructions such as invokes are treated as loading and invalidating
114 * all the heap values, depending on the instruction's side effects.
115 * - SIMD graphs (with VecLoad and VecStore instructions) are also handled. Any
116 * partial overlap access among ArrayGet/ArraySet/VecLoad/Store is seen as
117 * alias and no load/store is eliminated in such case.
Vladimir Marko3224f382020-06-23 14:19:53 +0100118 *
119 * The time complexity of the initial phase has several components. The total
120 * time for the initialization of heap values for all blocks is
121 * O(heap_locations * edges)
122 * and the time complexity for simple instruction processing is
123 * O(instructions).
124 * See the description of phase 3 for additional complexity due to matching of
125 * existing Phis for replacing loads.
126 *
127 * 2. Process loads that depend on loop Phi placeholders.
128 *
129 * We go over these loads to determine whether they can be eliminated. We look
130 * for the set of all Phi placeholders that feed the load and depend on a loop
131 * Phi placeholder and, if we find no unknown value, we construct the necessary
132 * Phi(s) or, if all other inputs are identical, i.e. the location does not
133 * change in the loop, just use that input. If we do find an unknown input, this
134 * must be from a loop back-edge and we replace the loop Phi placeholder with
135 * unknown value and re-process loads and stores that previously depended on
136 * loop Phi placeholders. This shall find at least one load of an unknown value
137 * which is now known to be unreplaceable or a new unknown value on a back-edge
138 * and we repeat this process until each load is either marked for replacement
139 * or found to be unreplaceable. As we mark at least one additional loop Phi
140 * placeholder as unreplacable in each iteration, this process shall terminate.
141 *
142 * The depth-first search for Phi placeholders in FindLoopPhisToMaterialize()
143 * is limited by the number of Phi placeholders and their dependencies we need
144 * to search with worst-case time complexity
145 * O(phi_placeholder_dependencies) .
146 * The dependencies are usually just the Phi placeholders' potential inputs,
147 * but if we use TryReplacingLoopPhiPlaceholderWithDefault() for default value
148 * replacement search, there are additional dependencies to consider, see below.
149 *
Vladimir Marko0571d472020-09-22 10:14:39 +0100150 * In the successful case (no unknown inputs found) we use the Floyd-Warshall
Vladimir Markoed29dce2020-08-21 17:25:16 +0100151 * algorithm to determine transitive closures for each found Phi placeholder,
Vladimir Marko3224f382020-06-23 14:19:53 +0100152 * and then match or materialize Phis from the smallest transitive closure,
153 * so that we can determine if such subset has a single other input. This has
154 * time complexity
155 * O(phi_placeholders_found^3) .
156 * Note that successful TryReplacingLoopPhiPlaceholderWithDefault() does not
157 * contribute to this as such Phi placeholders are replaced immediately.
158 * The total time of all such successful cases has time complexity
159 * O(phi_placeholders^3)
160 * because the found sets are disjoint and `Sum(n_i^3) <= Sum(n_i)^3`. Similar
161 * argument applies to the searches used to find all successful cases, so their
162 * total contribution is also just an insignificant
163 * O(phi_placeholder_dependencies) .
164 * The materialization of Phis has an insignificant total time complexity
165 * O(phi_placeholders * edges) .
166 *
167 * If we find an unknown input, we re-process heap values and loads with a time
168 * complexity that's the same as the phase 1 in the worst case. Adding this to
169 * the depth-first search time complexity yields
170 * O(phi_placeholder_dependencies + heap_locations * edges + instructions)
171 * for a single iteration. We can ignore the middle term as it's proprotional
172 * to the number of Phi placeholder inputs included in the first term. Using
173 * the upper limit of number of such iterations, the total time complexity is
174 * O((phi_placeholder_dependencies + instructions) * phi_placeholders) .
175 *
176 * The upper bound of Phi placeholder inputs is
177 * heap_locations * edges
178 * but if we use TryReplacingLoopPhiPlaceholderWithDefault(), the dependencies
179 * include other heap locations in predecessor blocks with the upper bound of
180 * heap_locations^2 * edges .
181 * Using the estimate
182 * edges <= blocks^2
183 * and
184 * phi_placeholders <= heap_locations * blocks ,
185 * the worst-case time complexity of the
186 * O(phi_placeholder_dependencies * phi_placeholders)
187 * term from unknown input cases is actually
188 * O(heap_locations^3 * blocks^3) ,
Vladimir Marko0571d472020-09-22 10:14:39 +0100189 * exactly as the estimate for the Floyd-Warshall parts of successful cases.
Vladimir Marko3224f382020-06-23 14:19:53 +0100190 * Adding the other term from the unknown input cases (to account for the case
191 * with significantly more instructions than blocks and heap locations), the
192 * phase 2 time complexity is
193 * O(heap_locations^3 * blocks^3 + heap_locations * blocks * instructions) .
194 *
195 * See the description of phase 3 for additional complexity due to matching of
196 * existing Phis for replacing loads.
197 *
198 * 3. Determine which stores to keep and which to eliminate.
199 *
Vladimir Markoed29dce2020-08-21 17:25:16 +0100200 * During instruction processing in phase 1 and re-processing in phase 2, we are
Vladimir Marko3224f382020-06-23 14:19:53 +0100201 * keeping a record of the stores and Phi placeholders that become observable
202 * and now propagate the observable Phi placeholders to all actual stores that
203 * feed them. Having determined observable stores, we look for stores that just
204 * overwrite the old value with the same. Since ignoring non-observable stores
205 * actually changes the old values in heap locations, we need to recalculate
206 * Phi placeholder replacements but we proceed similarly to the previous phase.
207 * We look for the set of all Phis that feed the old value replaced by the store
208 * (but ignoring whether they depend on a loop Phi) and, if we find no unknown
209 * value, we try to match existing Phis (we do not create new Phis anymore) or,
210 * if all other inputs are identical, i.e. the location does not change in the
211 * loop, just use that input. If this succeeds and the old value is identical to
212 * the value we're storing, such store shall be eliminated.
213 *
Vladimir Markoed29dce2020-08-21 17:25:16 +0100214 * The work is similar to the phase 2, except that we're not re-processing loads
Vladimir Marko3224f382020-06-23 14:19:53 +0100215 * and stores anymore, so the time complexity of phase 3 is
216 * O(heap_locations^3 * blocks^3) .
217 *
218 * There is additional complexity in matching existing Phis shared between the
219 * phases 1, 2 and 3. We are never trying to match two or more Phis at the same
220 * time (this could be difficult and slow), so each matching attempt is just
221 * looking at Phis in the block (both old Phis and newly created Phis) and their
222 * inputs. As we create at most `heap_locations` Phis in each block, the upper
223 * bound on the number of Phis we look at is
224 * heap_locations * (old_phis + heap_locations)
225 * and the worst-case time complexity is
226 * O(heap_locations^2 * edges + heap_locations * old_phis * edges) .
227 * The first term is lower than one term in phase 2, so the relevant part is
228 * O(heap_locations * old_phis * edges) .
229 *
230 * 4. Replace loads and remove unnecessary stores and singleton allocations.
231 *
232 * A special type of objects called singletons are instantiated in the method
233 * and have a single name, i.e. no aliases. Singletons have exclusive heap
234 * locations since they have no aliases. Singletons are helpful in narrowing
235 * down the life span of a heap location such that they do not always need to
236 * participate in merging heap values. Allocation of a singleton can be
237 * eliminated if that singleton is not used and does not persist at method
238 * return/deoptimization.
239 *
240 * The time complexity of this phase is
241 * O(instructions + instruction_uses) .
242 *
Alex Light3a73ffb2021-01-25 14:11:05 +0000243 * 5. Partial LSE
244 *
245 * Move allocations closer to their escapes and remove/predicate loads and
246 * stores as required.
247 *
248 * Partial singletons are objects which only escape from the function or have
249 * multiple names along certain execution paths. In cases where we recognize
250 * these partial singletons we can move the allocation and initialization
251 * closer to the actual escape(s). We can then perform a simplified version of
252 * LSE step 2 to determine the unescaped value of any reads performed after the
253 * object may have escaped. These are used to replace these reads with
254 * 'predicated-read' instructions where the value is only read if the object
255 * has actually escaped. We use the existence of the object itself as the
256 * marker of whether escape has occurred.
257 *
258 * There are several steps in this sub-pass
259 *
260 * 5.1 Group references
261 *
262 * Since all heap-locations for a single reference escape at the same time, we
263 * need to group the heap-locations by reference and process them at the same
264 * time.
265 *
266 * O(heap_locations).
267 *
268 * FIXME: The time complexity above assumes we can bucket the heap-locations in
269 * O(1) which is not true since we just perform a linear-scan of the heap-ref
270 * list. Since there are generally only a small number of heap-references which
271 * are partial-singletons this is fine and lower real overhead than a hash map.
272 *
273 * 5.2 Generate materializations
274 *
275 * Once we have the references we add new 'materialization blocks' on the edges
276 * where escape becomes inevitable. This information is calculated by the
277 * execution-subgraphs created during load-store-analysis. We create new
278 * 'materialization's in these blocks and initialize them with the value of
279 * each heap-location ignoring side effects (since the object hasn't escaped
280 * yet). Worst case this is the same time-complexity as step 3 since we may
281 * need to materialize phis.
282 *
283 * O(heap_locations^2 * materialization_edges)
284 *
285 * 5.3 Propagate materializations
286 *
287 * Since we use the materialization as the marker for escape we need to
288 * propagate it throughout the graph. Since the subgraph analysis considers any
289 * lifetime that escapes a loop (and hence would require a loop-phi) to be
290 * escaping at the loop-header we do not need to create any loop-phis to do
291 * this.
292 *
293 * O(edges)
294 *
295 * NB: Currently the subgraph analysis considers all objects to have their
296 * lifetimes start at the entry block. This simplifies that analysis enormously
297 * but means that we cannot distinguish between an escape in a loop where the
298 * lifetime does not escape the loop (in which case this pass could optimize)
299 * and one where it does escape the loop (in which case the whole loop is
300 * escaping). This is a shortcoming that would be good to fix at some point.
301 *
302 * 5.4 Propagate partial values
303 *
304 * We need to replace loads and stores to the partial reference with predicated
305 * ones that have default non-escaping values. Again this is the same as step 3.
306 *
307 * O(heap_locations^2 * edges)
308 *
309 * 5.5 Final fixup
310 *
311 * Now all we need to do is replace and remove uses of the old reference with the
312 * appropriate materialization.
313 *
314 * O(instructions + uses)
315 *
316 * FIXME: The time complexities described above assumes that the
Vladimir Marko9e3fe992020-08-25 16:17:51 +0100317 * HeapLocationCollector finds a heap location for an instruction in O(1)
318 * time but it is currently O(heap_locations); this can be fixed by adding
319 * a hash map to the HeapLocationCollector.
Mingyao Yanga3540532018-01-25 12:17:28 -0800320 */
321
Vladimir Marko0a516052019-10-14 13:00:44 +0000322namespace art {
Mingyao Yang8df69d42015-10-22 15:40:58 -0700323
Alex Light3a73ffb2021-01-25 14:11:05 +0000324#define LSE_VLOG \
325 if (::art::LoadStoreElimination::kVerboseLoggingMode && VLOG_IS_ON(compiler)) LOG(INFO)
326
327class PartialLoadStoreEliminationHelper;
328class HeapRefHolder;
329
Mingyao Yangc62b7ec2017-10-25 16:42:15 -0700330// Use HGraphDelegateVisitor for which all VisitInvokeXXX() delegate to VisitInvoke().
Vladimir Marko3224f382020-06-23 14:19:53 +0100331class LSEVisitor final : private HGraphDelegateVisitor {
Mingyao Yang8df69d42015-10-22 15:40:58 -0700332 public:
333 LSEVisitor(HGraph* graph,
Vladimir Marko3224f382020-06-23 14:19:53 +0100334 const HeapLocationCollector& heap_location_collector,
Alex Light3a73ffb2021-01-25 14:11:05 +0000335 bool perform_partial_lse,
Vladimir Marko3224f382020-06-23 14:19:53 +0100336 OptimizingCompilerStats* stats);
337
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +0000338 void Run();
Vladimir Marko3224f382020-06-23 14:19:53 +0100339
340 private:
341 class PhiPlaceholder {
342 public:
Alex Lightef28d242020-11-17 20:21:51 -0800343 constexpr PhiPlaceholder() : block_id_(-1), heap_location_(-1) {}
344 constexpr PhiPlaceholder(uint32_t block_id, size_t heap_location)
345 : block_id_(block_id), heap_location_(dchecked_integral_cast<uint32_t>(heap_location)) {}
Vladimir Marko3224f382020-06-23 14:19:53 +0100346
Alex Lightef28d242020-11-17 20:21:51 -0800347 constexpr PhiPlaceholder(const PhiPlaceholder& p) = default;
348 constexpr PhiPlaceholder(PhiPlaceholder&& p) = default;
349 constexpr PhiPlaceholder& operator=(const PhiPlaceholder& p) = default;
350 constexpr PhiPlaceholder& operator=(PhiPlaceholder&& p) = default;
351
352 constexpr uint32_t GetBlockId() const {
Vladimir Marko3224f382020-06-23 14:19:53 +0100353 return block_id_;
354 }
355
Alex Lightef28d242020-11-17 20:21:51 -0800356 constexpr size_t GetHeapLocation() const {
Vladimir Marko3224f382020-06-23 14:19:53 +0100357 return heap_location_;
358 }
359
Alex Lightef28d242020-11-17 20:21:51 -0800360 constexpr bool Equals(const PhiPlaceholder& p2) const {
361 return block_id_ == p2.block_id_ && heap_location_ == p2.heap_location_;
362 }
363
364 void Dump(std::ostream& oss) const {
365 oss << "PhiPlaceholder[blk: " << block_id_ << ", heap_location_: " << heap_location_ << "]";
366 }
367
Vladimir Marko3224f382020-06-23 14:19:53 +0100368 private:
369 uint32_t block_id_;
370 uint32_t heap_location_;
371 };
372
Alex Light3a73ffb2021-01-25 14:11:05 +0000373 struct Marker {};
374
375 class Value;
376
377 class PriorValueHolder {
378 public:
379 constexpr explicit PriorValueHolder(Value prior);
380
381 constexpr bool IsInstruction() const {
382 return std::holds_alternative<HInstruction*>(value_);
383 }
384 constexpr bool IsPhi() const {
385 return std::holds_alternative<PhiPlaceholder>(value_);
386 }
387 constexpr bool IsDefault() const {
388 return std::holds_alternative<Marker>(value_);
389 }
390 constexpr PhiPlaceholder GetPhiPlaceholder() const {
391 DCHECK(IsPhi());
392 return std::get<PhiPlaceholder>(value_);
393 }
394 constexpr HInstruction* GetInstruction() const {
395 DCHECK(IsInstruction());
396 return std::get<HInstruction*>(value_);
397 }
398
399 Value ToValue() const;
400 void Dump(std::ostream& oss) const;
401
402 constexpr bool Equals(PriorValueHolder other) const {
403 return value_ == other.value_;
404 }
405
406 private:
407 std::variant<Marker, HInstruction*, PhiPlaceholder> value_;
408 };
409
410 friend constexpr bool operator==(const Marker&, const Marker&);
411 friend constexpr bool operator==(const PriorValueHolder& p1, const PriorValueHolder& p2);
Alex Lightef28d242020-11-17 20:21:51 -0800412 friend constexpr bool operator==(const PhiPlaceholder& p1, const PhiPlaceholder& p2);
413 friend std::ostream& operator<<(std::ostream& oss, const PhiPlaceholder& p2);
414
Vladimir Marko3224f382020-06-23 14:19:53 +0100415 class Value {
416 public:
Alex Lightef28d242020-11-17 20:21:51 -0800417 enum class ValuelessType {
Vladimir Marko3224f382020-06-23 14:19:53 +0100418 kInvalid,
Alex Lightf5a84cb2021-01-15 08:35:38 -0800419 kPureUnknown,
Vladimir Marko3224f382020-06-23 14:19:53 +0100420 kDefault,
Alex Lightef28d242020-11-17 20:21:51 -0800421 };
422 struct MergedUnknownMarker {
423 PhiPlaceholder phi_;
424 };
425 struct NeedsNonLoopPhiMarker {
426 PhiPlaceholder phi_;
427 };
428 struct NeedsLoopPhiMarker {
429 PhiPlaceholder phi_;
Vladimir Marko3224f382020-06-23 14:19:53 +0100430 };
431
Alex Lightef28d242020-11-17 20:21:51 -0800432 static constexpr Value Invalid() {
433 return Value(ValuelessType::kInvalid);
Vladimir Marko3224f382020-06-23 14:19:53 +0100434 }
435
436 // An unknown heap value. Loads with such a value in the heap location cannot be eliminated.
437 // A heap location can be set to an unknown heap value when:
438 // - it is coming from outside the method,
439 // - it is killed due to aliasing, or side effects, or merging with an unknown value.
Alex Lightf5a84cb2021-01-15 08:35:38 -0800440 static constexpr Value PureUnknown() {
441 return Value(ValuelessType::kPureUnknown);
Vladimir Marko3224f382020-06-23 14:19:53 +0100442 }
443
Alex Light3a73ffb2021-01-25 14:11:05 +0000444 static constexpr Value PartialUnknown(Value old_value) {
445 if (old_value.IsInvalid() || old_value.IsPureUnknown()) {
446 return PureUnknown();
447 } else {
448 return Value(PriorValueHolder(old_value));
449 }
450 }
451
Alex Lightef28d242020-11-17 20:21:51 -0800452 static constexpr Value MergedUnknown(PhiPlaceholder phi_placeholder) {
453 return Value(MergedUnknownMarker{phi_placeholder});
Alex Light86fe9b82020-11-16 16:54:01 +0000454 }
455
Vladimir Marko3224f382020-06-23 14:19:53 +0100456 // Default heap value after an allocation.
457 // A heap location can be set to that value right after an allocation.
Alex Lightef28d242020-11-17 20:21:51 -0800458 static constexpr Value Default() {
459 return Value(ValuelessType::kDefault);
Vladimir Marko3224f382020-06-23 14:19:53 +0100460 }
461
Alex Lightef28d242020-11-17 20:21:51 -0800462 static constexpr Value ForInstruction(HInstruction* instruction) {
463 return Value(instruction);
Vladimir Marko3224f382020-06-23 14:19:53 +0100464 }
465
Alex Lightef28d242020-11-17 20:21:51 -0800466 static constexpr Value ForNonLoopPhiPlaceholder(PhiPlaceholder phi_placeholder) {
467 return Value(NeedsNonLoopPhiMarker{phi_placeholder});
Vladimir Marko3224f382020-06-23 14:19:53 +0100468 }
469
Alex Lightef28d242020-11-17 20:21:51 -0800470 static constexpr Value ForLoopPhiPlaceholder(PhiPlaceholder phi_placeholder) {
471 return Value(NeedsLoopPhiMarker{phi_placeholder});
Vladimir Marko3224f382020-06-23 14:19:53 +0100472 }
473
Alex Lightef28d242020-11-17 20:21:51 -0800474 static constexpr Value ForPhiPlaceholder(PhiPlaceholder phi_placeholder, bool needs_loop_phi) {
Vladimir Marko3224f382020-06-23 14:19:53 +0100475 return needs_loop_phi ? ForLoopPhiPlaceholder(phi_placeholder)
476 : ForNonLoopPhiPlaceholder(phi_placeholder);
477 }
478
Peter Collingbournef6b9e402020-12-30 22:55:57 -0800479 constexpr bool IsValid() const {
Vladimir Marko3224f382020-06-23 14:19:53 +0100480 return !IsInvalid();
481 }
482
Peter Collingbournef6b9e402020-12-30 22:55:57 -0800483 constexpr bool IsInvalid() const {
Alex Lightef28d242020-11-17 20:21:51 -0800484 return std::holds_alternative<ValuelessType>(value_) &&
485 GetValuelessType() == ValuelessType::kInvalid;
Vladimir Marko3224f382020-06-23 14:19:53 +0100486 }
487
Alex Light3a73ffb2021-01-25 14:11:05 +0000488 bool IsPartialUnknown() const {
489 return std::holds_alternative<PriorValueHolder>(value_);
490 }
491
Alex Light86fe9b82020-11-16 16:54:01 +0000492 bool IsMergedUnknown() const {
Alex Lightef28d242020-11-17 20:21:51 -0800493 return std::holds_alternative<MergedUnknownMarker>(value_);
Alex Light86fe9b82020-11-16 16:54:01 +0000494 }
495
496 bool IsPureUnknown() const {
Alex Lightef28d242020-11-17 20:21:51 -0800497 return std::holds_alternative<ValuelessType>(value_) &&
Alex Lightf5a84cb2021-01-15 08:35:38 -0800498 GetValuelessType() == ValuelessType::kPureUnknown;
Alex Lightb6837f02020-11-12 17:05:28 +0000499 }
500
Alex Light86fe9b82020-11-16 16:54:01 +0000501 bool IsUnknown() const {
Alex Light3a73ffb2021-01-25 14:11:05 +0000502 return IsPureUnknown() || IsMergedUnknown() || IsPartialUnknown();
Alex Light86fe9b82020-11-16 16:54:01 +0000503 }
504
Vladimir Marko3224f382020-06-23 14:19:53 +0100505 bool IsDefault() const {
Alex Lightef28d242020-11-17 20:21:51 -0800506 return std::holds_alternative<ValuelessType>(value_) &&
507 GetValuelessType() == ValuelessType::kDefault;
Vladimir Marko3224f382020-06-23 14:19:53 +0100508 }
509
510 bool IsInstruction() const {
Alex Lightef28d242020-11-17 20:21:51 -0800511 return std::holds_alternative<HInstruction*>(value_);
Vladimir Marko3224f382020-06-23 14:19:53 +0100512 }
513
514 bool NeedsNonLoopPhi() const {
Alex Lightef28d242020-11-17 20:21:51 -0800515 return std::holds_alternative<NeedsNonLoopPhiMarker>(value_);
Vladimir Marko3224f382020-06-23 14:19:53 +0100516 }
517
518 bool NeedsLoopPhi() const {
Alex Lightef28d242020-11-17 20:21:51 -0800519 return std::holds_alternative<NeedsLoopPhiMarker>(value_);
Vladimir Marko3224f382020-06-23 14:19:53 +0100520 }
521
522 bool NeedsPhi() const {
523 return NeedsNonLoopPhi() || NeedsLoopPhi();
524 }
525
526 HInstruction* GetInstruction() const {
Alex Light3a73ffb2021-01-25 14:11:05 +0000527 DCHECK(IsInstruction()) << *this;
Alex Lightef28d242020-11-17 20:21:51 -0800528 return std::get<HInstruction*>(value_);
Vladimir Marko3224f382020-06-23 14:19:53 +0100529 }
530
Alex Light3a73ffb2021-01-25 14:11:05 +0000531 PriorValueHolder GetPriorValue() const {
532 DCHECK(IsPartialUnknown());
533 return std::get<PriorValueHolder>(value_);
534 }
535
Alex Lightef28d242020-11-17 20:21:51 -0800536 PhiPlaceholder GetPhiPlaceholder() const {
Alex Light86fe9b82020-11-16 16:54:01 +0000537 DCHECK(NeedsPhi() || IsMergedUnknown());
Alex Lightef28d242020-11-17 20:21:51 -0800538 if (NeedsNonLoopPhi()) {
539 return std::get<NeedsNonLoopPhiMarker>(value_).phi_;
540 } else if (NeedsLoopPhi()) {
541 return std::get<NeedsLoopPhiMarker>(value_).phi_;
542 } else {
543 return std::get<MergedUnknownMarker>(value_).phi_;
544 }
Vladimir Marko3224f382020-06-23 14:19:53 +0100545 }
546
Alex Light86fe9b82020-11-16 16:54:01 +0000547 uint32_t GetMergeBlockId() const {
548 DCHECK(IsMergedUnknown()) << this;
Alex Lightef28d242020-11-17 20:21:51 -0800549 return std::get<MergedUnknownMarker>(value_).phi_.GetBlockId();
Alex Light86fe9b82020-11-16 16:54:01 +0000550 }
551
552 HBasicBlock* GetMergeBlock(const HGraph* graph) const {
Alex Light3a73ffb2021-01-25 14:11:05 +0000553 DCHECK(IsMergedUnknown()) << *this;
Alex Light86fe9b82020-11-16 16:54:01 +0000554 return graph->GetBlocks()[GetMergeBlockId()];
555 }
556
557 size_t GetHeapLocation() const {
558 DCHECK(IsMergedUnknown() || NeedsPhi()) << this;
Alex Lightef28d242020-11-17 20:21:51 -0800559 return GetPhiPlaceholder().GetHeapLocation();
Alex Light86fe9b82020-11-16 16:54:01 +0000560 }
561
Alex Light3a73ffb2021-01-25 14:11:05 +0000562 constexpr bool ExactEquals(Value other) const;
563
Alex Lightef28d242020-11-17 20:21:51 -0800564 constexpr bool Equals(Value other) const;
Vladimir Marko3224f382020-06-23 14:19:53 +0100565
Alex Lightef28d242020-11-17 20:21:51 -0800566 constexpr bool Equals(HInstruction* instruction) const {
Vladimir Marko3224f382020-06-23 14:19:53 +0100567 return Equals(ForInstruction(instruction));
568 }
569
Alex Light86fe9b82020-11-16 16:54:01 +0000570 std::ostream& Dump(std::ostream& os) const;
571
Alex Lightef28d242020-11-17 20:21:51 -0800572 // Public for use with lists.
573 constexpr Value() : value_(ValuelessType::kInvalid) {}
574
Vladimir Marko3224f382020-06-23 14:19:53 +0100575 private:
Alex Lightef28d242020-11-17 20:21:51 -0800576 using ValueHolder = std::variant<ValuelessType,
577 HInstruction*,
578 MergedUnknownMarker,
579 NeedsNonLoopPhiMarker,
Alex Light3a73ffb2021-01-25 14:11:05 +0000580 NeedsLoopPhiMarker,
581 PriorValueHolder>;
Peter Collingbournef6b9e402020-12-30 22:55:57 -0800582 constexpr ValuelessType GetValuelessType() const {
Alex Lightef28d242020-11-17 20:21:51 -0800583 return std::get<ValuelessType>(value_);
584 }
585
586 constexpr explicit Value(ValueHolder v) : value_(v) {}
587
Alex Light86fe9b82020-11-16 16:54:01 +0000588 friend std::ostream& operator<<(std::ostream& os, const Value& v);
589
Alex Lightef28d242020-11-17 20:21:51 -0800590 ValueHolder value_;
591
592 static_assert(std::is_move_assignable<PhiPlaceholder>::value);
Vladimir Marko3224f382020-06-23 14:19:53 +0100593 };
594
Alex Lightef28d242020-11-17 20:21:51 -0800595 friend constexpr bool operator==(const Value::NeedsLoopPhiMarker& p1,
596 const Value::NeedsLoopPhiMarker& p2);
597 friend constexpr bool operator==(const Value::NeedsNonLoopPhiMarker& p1,
598 const Value::NeedsNonLoopPhiMarker& p2);
599 friend constexpr bool operator==(const Value::MergedUnknownMarker& p1,
600 const Value::MergedUnknownMarker& p2);
601
Vladimir Marko3224f382020-06-23 14:19:53 +0100602 // Get Phi placeholder index for access to `phi_placeholder_replacements_`
603 // and "visited" bit vectors during depth-first searches.
Alex Lightef28d242020-11-17 20:21:51 -0800604 size_t PhiPlaceholderIndex(PhiPlaceholder phi_placeholder) const {
605 size_t res =
606 phi_placeholder.GetBlockId() * heap_location_collector_.GetNumberOfHeapLocations() +
607 phi_placeholder.GetHeapLocation();
608 DCHECK_EQ(phi_placeholder, GetPhiPlaceholderAt(res))
609 << res << "blks: " << GetGraph()->GetBlocks().size()
610 << " hls: " << heap_location_collector_.GetNumberOfHeapLocations();
611 return res;
Mingyao Yang8df69d42015-10-22 15:40:58 -0700612 }
613
Vladimir Marko3224f382020-06-23 14:19:53 +0100614 size_t PhiPlaceholderIndex(Value phi_placeholder) const {
615 return PhiPlaceholderIndex(phi_placeholder.GetPhiPlaceholder());
Mingyao Yang8df69d42015-10-22 15:40:58 -0700616 }
617
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +0100618 bool IsEscapingObject(ReferenceInfo* info, HBasicBlock* block, size_t index) {
619 return !info->IsSingletonAndRemovable() &&
620 !(info->IsPartialSingleton() && IsPartialNoEscape(block, index));
621 }
622
Alex Light86fe9b82020-11-16 16:54:01 +0000623 bool IsPartialNoEscape(HBasicBlock* blk, size_t idx) {
624 auto* ri = heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo();
Vladimir Marko5c824932021-06-02 15:54:17 +0100625 if (!ri->IsPartialSingleton()) {
626 return false;
627 }
628 ArrayRef<const ExecutionSubgraph::ExcludedCohort> cohorts =
629 ri->GetNoEscapeSubgraph()->GetExcludedCohorts();
630 return std::none_of(cohorts.cbegin(),
631 cohorts.cend(),
Alex Light86fe9b82020-11-16 16:54:01 +0000632 [&](const ExecutionSubgraph::ExcludedCohort& ex) -> bool {
633 // Make sure we haven't yet and never will escape.
634 return ex.PrecedesBlock(blk) ||
635 ex.ContainsBlock(blk) ||
636 ex.SucceedsBlock(blk);
637 });
638 }
639
Alex Lightef28d242020-11-17 20:21:51 -0800640 PhiPlaceholder GetPhiPlaceholderAt(size_t off) const {
641 DCHECK_LT(off, num_phi_placeholders_);
642 size_t id = off % heap_location_collector_.GetNumberOfHeapLocations();
643 // Technically this should be (off - id) / NumberOfHeapLocations
644 // but due to truncation it's all the same.
645 size_t blk_id = off / heap_location_collector_.GetNumberOfHeapLocations();
646 return GetPhiPlaceholder(blk_id, id);
647 }
648
649 PhiPlaceholder GetPhiPlaceholder(uint32_t block_id, size_t idx) const {
650 DCHECK(GetGraph()->GetBlocks()[block_id] != nullptr) << block_id;
651 return PhiPlaceholder(block_id, idx);
Vladimir Marko3224f382020-06-23 14:19:53 +0100652 }
653
654 Value Replacement(Value value) const {
Alex Light3a73ffb2021-01-25 14:11:05 +0000655 DCHECK(value.NeedsPhi() ||
656 (current_phase_ == Phase::kPartialElimination && value.IsMergedUnknown()))
657 << value << " phase: " << current_phase_;
Vladimir Marko3224f382020-06-23 14:19:53 +0100658 Value replacement = phi_placeholder_replacements_[PhiPlaceholderIndex(value)];
659 DCHECK(replacement.IsUnknown() || replacement.IsInstruction());
660 DCHECK(replacement.IsUnknown() ||
661 FindSubstitute(replacement.GetInstruction()) == replacement.GetInstruction());
662 return replacement;
663 }
664
665 Value ReplacementOrValue(Value value) const {
Alex Light3a73ffb2021-01-25 14:11:05 +0000666 if (current_phase_ == Phase::kPartialElimination) {
Vladimir Marko06fb7fa2021-05-18 15:53:17 +0000667 // In this phase we are materializing the default values which are used
668 // only if the partial singleton did not escape, so we can replace
669 // a partial unknown with the prior value.
Alex Light3a73ffb2021-01-25 14:11:05 +0000670 if (value.IsPartialUnknown()) {
671 value = value.GetPriorValue().ToValue();
672 }
Vladimir Marko06fb7fa2021-05-18 15:53:17 +0000673 if ((value.IsMergedUnknown() || value.NeedsPhi()) &&
674 phi_placeholder_replacements_[PhiPlaceholderIndex(value)].IsValid()) {
675 value = phi_placeholder_replacements_[PhiPlaceholderIndex(value)];
676 DCHECK(!value.IsMergedUnknown());
677 DCHECK(!value.NeedsPhi());
678 } else if (value.IsMergedUnknown()) {
679 return Value::ForLoopPhiPlaceholder(value.GetPhiPlaceholder());
Alex Light3a73ffb2021-01-25 14:11:05 +0000680 }
Vladimir Marko807de1e2021-04-30 15:14:18 +0000681 if (value.IsInstruction() && value.GetInstruction()->IsInstanceFieldGet()) {
682 DCHECK_LT(static_cast<size_t>(value.GetInstruction()->GetId()),
683 substitute_instructions_for_loads_.size());
684 HInstruction* substitute =
685 substitute_instructions_for_loads_[value.GetInstruction()->GetId()];
686 if (substitute != nullptr) {
687 DCHECK(substitute->IsPredicatedInstanceFieldGet());
688 return Value::ForInstruction(substitute);
689 }
690 }
Santiago Aboy Solanes872ec722022-02-18 14:10:25 +0000691 DCHECK_IMPLIES(value.IsInstruction(),
692 FindSubstitute(value.GetInstruction()) == value.GetInstruction());
Vladimir Marko06fb7fa2021-05-18 15:53:17 +0000693 return value;
Alex Light3a73ffb2021-01-25 14:11:05 +0000694 }
Vladimir Marko3224f382020-06-23 14:19:53 +0100695 if (value.NeedsPhi() && phi_placeholder_replacements_[PhiPlaceholderIndex(value)].IsValid()) {
696 return Replacement(value);
697 } else {
Santiago Aboy Solanes872ec722022-02-18 14:10:25 +0000698 DCHECK_IMPLIES(value.IsInstruction(),
699 FindSubstitute(value.GetInstruction()) == value.GetInstruction());
Vladimir Marko3224f382020-06-23 14:19:53 +0100700 return value;
701 }
702 }
703
Vladimir Marko3224f382020-06-23 14:19:53 +0100704 // The record of a heap value and instruction(s) that feed that value.
705 struct ValueRecord {
706 Value value;
707 Value stored_by;
708 };
709
Vladimir Marko4307cd72020-07-17 14:35:56 +0100710 HTypeConversion* FindOrAddTypeConversionIfNecessary(HInstruction* instruction,
711 HInstruction* value,
712 DataType::Type expected_type) {
Vladimir Marko94539fd2017-11-15 17:52:46 +0000713 // Should never add type conversion into boolean value.
Vladimir Marko4307cd72020-07-17 14:35:56 +0100714 if (expected_type == DataType::Type::kBool ||
715 DataType::IsTypeConversionImplicit(value->GetType(), expected_type) ||
716 // TODO: This prevents type conversion of default values but we can still insert
717 // type conversion of other constants and there is no constant folding pass after LSE.
718 IsZeroBitPattern(value)) {
719 return nullptr;
Vladimir Marko94539fd2017-11-15 17:52:46 +0000720 }
Vladimir Marko4307cd72020-07-17 14:35:56 +0100721
722 // Check if there is already a suitable TypeConversion we can reuse.
723 for (const HUseListNode<HInstruction*>& use : value->GetUses()) {
724 if (use.GetUser()->IsTypeConversion() &&
725 use.GetUser()->GetType() == expected_type &&
726 // TODO: We could move the TypeConversion to a common dominator
727 // if it does not cross irreducible loop header.
728 use.GetUser()->GetBlock()->Dominates(instruction->GetBlock()) &&
729 // Don't share across irreducible loop headers.
730 // TODO: can be more fine-grained than this by testing each dominator.
731 (use.GetUser()->GetBlock() == instruction->GetBlock() ||
732 !GetGraph()->HasIrreducibleLoops())) {
733 if (use.GetUser()->GetBlock() == instruction->GetBlock() &&
734 use.GetUser()->GetBlock()->GetInstructions().FoundBefore(instruction, use.GetUser())) {
735 // Move the TypeConversion before the instruction.
736 use.GetUser()->MoveBefore(instruction);
737 }
738 DCHECK(use.GetUser()->StrictlyDominates(instruction));
739 return use.GetUser()->AsTypeConversion();
740 }
741 }
742
743 // We must create a new TypeConversion instruction.
744 HTypeConversion* type_conversion = new (GetGraph()->GetAllocator()) HTypeConversion(
745 expected_type, value, instruction->GetDexPc());
746 instruction->GetBlock()->InsertInstructionBefore(type_conversion, instruction);
Vladimir Marko94539fd2017-11-15 17:52:46 +0000747 return type_conversion;
748 }
749
Mingyao Yanga3540532018-01-25 12:17:28 -0800750 // Find an instruction's substitute if it's a removed load.
Mingyao Yang206070c2017-11-29 23:01:58 -0800751 // Return the same instruction if it should not be removed.
Vladimir Marko3224f382020-06-23 14:19:53 +0100752 HInstruction* FindSubstitute(HInstruction* instruction) const {
Vladimir Marko9e3fe992020-08-25 16:17:51 +0100753 size_t id = static_cast<size_t>(instruction->GetId());
754 if (id >= substitute_instructions_for_loads_.size()) {
Vladimir Marko06fb7fa2021-05-18 15:53:17 +0000755 // New Phi (may not be in the graph yet), default value or PredicatedInstanceFieldGet.
Santiago Aboy Solanes872ec722022-02-18 14:10:25 +0000756 DCHECK_IMPLIES(IsLoad(instruction), instruction->IsPredicatedInstanceFieldGet());
Mingyao Yanga3540532018-01-25 12:17:28 -0800757 return instruction;
758 }
Vladimir Marko9e3fe992020-08-25 16:17:51 +0100759 HInstruction* substitute = substitute_instructions_for_loads_[id];
760 DCHECK(substitute == nullptr || IsLoad(instruction));
761 return (substitute != nullptr) ? substitute : instruction;
Mingyao Yang206070c2017-11-29 23:01:58 -0800762 }
763
Vladimir Marko94539fd2017-11-15 17:52:46 +0000764 void AddRemovedLoad(HInstruction* load, HInstruction* heap_value) {
Mingyao Yanga3540532018-01-25 12:17:28 -0800765 DCHECK(IsLoad(load));
Vladimir Marko3224f382020-06-23 14:19:53 +0100766 DCHECK_EQ(FindSubstitute(load), load);
Vladimir Marko94539fd2017-11-15 17:52:46 +0000767 DCHECK_EQ(FindSubstitute(heap_value), heap_value) <<
768 "Unexpected heap_value that has a substitute " << heap_value->DebugName();
Vladimir Marko94539fd2017-11-15 17:52:46 +0000769
Vladimir Marko4307cd72020-07-17 14:35:56 +0100770 // The load expects to load the heap value as type load->GetType().
771 // However the tracked heap value may not be of that type. An explicit
772 // type conversion may be needed.
773 // There are actually three types involved here:
774 // (1) tracked heap value's type (type A)
775 // (2) heap location (field or element)'s type (type B)
776 // (3) load's type (type C)
777 // We guarantee that type A stored as type B and then fetched out as
778 // type C is the same as casting from type A to type C directly, since
779 // type B and type C will have the same size which is guaranteed in
780 // HInstanceFieldGet/HStaticFieldGet/HArrayGet/HVecLoad's SetType().
781 // So we only need one type conversion from type A to type C.
782 HTypeConversion* type_conversion = FindOrAddTypeConversionIfNecessary(
783 load, heap_value, load->GetType());
784
Vladimir Marko9e3fe992020-08-25 16:17:51 +0100785 substitute_instructions_for_loads_[load->GetId()] =
786 type_conversion != nullptr ? type_conversion : heap_value;
Vladimir Marko94539fd2017-11-15 17:52:46 +0000787 }
788
Vladimir Marko3224f382020-06-23 14:19:53 +0100789 static bool IsLoad(HInstruction* instruction) {
Mingyao Yanga3540532018-01-25 12:17:28 -0800790 // Unresolved load is not treated as a load.
791 return instruction->IsInstanceFieldGet() ||
Alex Light3a73ffb2021-01-25 14:11:05 +0000792 instruction->IsPredicatedInstanceFieldGet() ||
Vladimir Marko3224f382020-06-23 14:19:53 +0100793 instruction->IsStaticFieldGet() ||
794 instruction->IsVecLoad() ||
795 instruction->IsArrayGet();
Mingyao Yanga3540532018-01-25 12:17:28 -0800796 }
797
Vladimir Marko3224f382020-06-23 14:19:53 +0100798 static bool IsStore(HInstruction* instruction) {
Mingyao Yanga3540532018-01-25 12:17:28 -0800799 // Unresolved store is not treated as a store.
800 return instruction->IsInstanceFieldSet() ||
Vladimir Marko3224f382020-06-23 14:19:53 +0100801 instruction->IsArraySet() ||
802 instruction->IsVecStore() ||
803 instruction->IsStaticFieldSet();
Mingyao Yanga3540532018-01-25 12:17:28 -0800804 }
805
Vladimir Marko3224f382020-06-23 14:19:53 +0100806 // Check if it is allowed to use default values or Phis for the specified load.
807 static bool IsDefaultOrPhiAllowedForLoad(HInstruction* instruction) {
808 DCHECK(IsLoad(instruction));
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000809 // Using defaults for VecLoads requires to create additional vector operations.
810 // As there are some issues with scheduling vector operations it is better to avoid creating
811 // them.
Vladimir Marko3224f382020-06-23 14:19:53 +0100812 return !instruction->IsVecOperation();
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000813 }
814
Vladimir Marko3224f382020-06-23 14:19:53 +0100815 // Keep the store referenced by the instruction, or all stores that feed a Phi placeholder.
816 // This is necessary if the stored heap value can be observed.
817 void KeepStores(Value value) {
Alex Light3a73ffb2021-01-25 14:11:05 +0000818 if (value.IsPureUnknown() || value.IsPartialUnknown()) {
Alex Light86fe9b82020-11-16 16:54:01 +0000819 return;
820 }
821 if (value.IsMergedUnknown()) {
822 kept_merged_unknowns_.SetBit(PhiPlaceholderIndex(value));
823 phi_placeholders_to_search_for_kept_stores_.SetBit(PhiPlaceholderIndex(value));
Mingyao Yangfb8464a2015-11-02 10:56:59 -0800824 return;
825 }
Vladimir Marko3224f382020-06-23 14:19:53 +0100826 if (value.NeedsPhi()) {
827 phi_placeholders_to_search_for_kept_stores_.SetBit(PhiPlaceholderIndex(value));
828 } else {
829 HInstruction* instruction = value.GetInstruction();
830 DCHECK(IsStore(instruction));
831 kept_stores_.SetBit(instruction->GetId());
Mingyao Yangfb8464a2015-11-02 10:56:59 -0800832 }
833 }
834
Mingyao Yanga3540532018-01-25 12:17:28 -0800835 // If a heap location X may alias with heap location at `loc_index`
836 // and heap_values of that heap location X holds a store, keep that store.
837 // It's needed for a dependent load that's not eliminated since any store
838 // that may put value into the load's heap location needs to be kept.
Vladimir Marko3224f382020-06-23 14:19:53 +0100839 void KeepStoresIfAliasedToLocation(ScopedArenaVector<ValueRecord>& heap_values,
Mingyao Yanga3540532018-01-25 12:17:28 -0800840 size_t loc_index) {
Vladimir Marko3224f382020-06-23 14:19:53 +0100841 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
842 if (i == loc_index) {
843 // We use this function when reading a location with unknown value and
844 // therefore we cannot know what exact store wrote that unknown value.
845 // But we can have a phi placeholder here marking multiple stores to keep.
Alex Light86fe9b82020-11-16 16:54:01 +0000846 DCHECK(
847 !heap_values[i].stored_by.IsInstruction() ||
848 heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo()->IsPartialSingleton());
Vladimir Marko3224f382020-06-23 14:19:53 +0100849 KeepStores(heap_values[i].stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -0800850 heap_values[i].stored_by = Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +0100851 } else if (heap_location_collector_.MayAlias(i, loc_index)) {
852 KeepStores(heap_values[i].stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -0800853 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang58d9bfc2016-11-01 13:31:58 -0700854 }
Mingyao Yang8df69d42015-10-22 15:40:58 -0700855 }
856 }
857
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100858 HInstruction* GetDefaultValue(DataType::Type type) {
Mingyao Yang8df69d42015-10-22 15:40:58 -0700859 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100860 case DataType::Type::kReference:
Mingyao Yang8df69d42015-10-22 15:40:58 -0700861 return GetGraph()->GetNullConstant();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100862 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +0100863 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100864 case DataType::Type::kInt8:
865 case DataType::Type::kUint16:
866 case DataType::Type::kInt16:
867 case DataType::Type::kInt32:
Mingyao Yang8df69d42015-10-22 15:40:58 -0700868 return GetGraph()->GetIntConstant(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100869 case DataType::Type::kInt64:
Mingyao Yang8df69d42015-10-22 15:40:58 -0700870 return GetGraph()->GetLongConstant(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100871 case DataType::Type::kFloat32:
Mingyao Yang8df69d42015-10-22 15:40:58 -0700872 return GetGraph()->GetFloatConstant(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100873 case DataType::Type::kFloat64:
Mingyao Yang8df69d42015-10-22 15:40:58 -0700874 return GetGraph()->GetDoubleConstant(0);
875 default:
876 UNREACHABLE();
877 }
878 }
879
Vladimir Marko3224f382020-06-23 14:19:53 +0100880 bool CanValueBeKeptIfSameAsNew(Value value,
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000881 HInstruction* new_value,
882 HInstruction* new_value_set_instr) {
883 // For field/array set location operations, if the value is the same as the new_value
884 // it can be kept even if aliasing happens. All aliased operations will access the same memory
885 // range.
886 // For vector values, this is not true. For example:
887 // packed_data = [0xA, 0xB, 0xC, 0xD]; <-- Different values in each lane.
888 // VecStore array[i ,i+1,i+2,i+3] = packed_data;
889 // VecStore array[i+1,i+2,i+3,i+4] = packed_data; <-- We are here (partial overlap).
890 // VecLoad vx = array[i,i+1,i+2,i+3]; <-- Cannot be eliminated because the value
891 // here is not packed_data anymore.
892 //
893 // TODO: to allow such 'same value' optimization on vector data,
894 // LSA needs to report more fine-grain MAY alias information:
895 // (1) May alias due to two vector data partial overlap.
896 // e.g. a[i..i+3] and a[i+1,..,i+4].
897 // (2) May alias due to two vector data may complete overlap each other.
898 // e.g. a[i..i+3] and b[i..i+3].
899 // (3) May alias but the exact relationship between two locations is unknown.
900 // e.g. a[i..i+3] and b[j..j+3], where values of a,b,i,j are all unknown.
901 // This 'same value' optimization can apply only on case (2).
902 if (new_value_set_instr->IsVecOperation()) {
903 return false;
904 }
905
Vladimir Marko3224f382020-06-23 14:19:53 +0100906 return value.Equals(new_value);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +0000907 }
908
Vladimir Marko3224f382020-06-23 14:19:53 +0100909 Value PrepareLoopValue(HBasicBlock* block, size_t idx);
910 Value PrepareLoopStoredBy(HBasicBlock* block, size_t idx);
911 void PrepareLoopRecords(HBasicBlock* block);
912 Value MergePredecessorValues(HBasicBlock* block, size_t idx);
913 void MergePredecessorRecords(HBasicBlock* block);
Mingyao Yanga3540532018-01-25 12:17:28 -0800914
Alex Lightef28d242020-11-17 20:21:51 -0800915 void MaterializeNonLoopPhis(PhiPlaceholder phi_placeholder, DataType::Type type);
Mingyao Yange9d6e602015-10-23 17:08:42 -0700916
Vladimir Marko3224f382020-06-23 14:19:53 +0100917 void VisitGetLocation(HInstruction* instruction, size_t idx);
918 void VisitSetLocation(HInstruction* instruction, size_t idx, HInstruction* value);
Alex Light3a73ffb2021-01-25 14:11:05 +0000919 void RecordFieldInfo(const FieldInfo* info, size_t heap_loc) {
920 field_infos_[heap_loc] = info;
921 }
Mingyao Yanga3540532018-01-25 12:17:28 -0800922
Vladimir Marko3224f382020-06-23 14:19:53 +0100923 void VisitBasicBlock(HBasicBlock* block) override;
924
925 enum class Phase {
926 kLoadElimination,
Alex Light3a73ffb2021-01-25 14:11:05 +0000927 kStoreElimination,
928 kPartialElimination,
Vladimir Marko3224f382020-06-23 14:19:53 +0100929 };
930
Vladimir Markodac82392021-05-10 15:44:24 +0000931 bool MayAliasOnBackEdge(HBasicBlock* loop_header, size_t idx1, size_t idx2) const;
932
Vladimir Marko3224f382020-06-23 14:19:53 +0100933 bool TryReplacingLoopPhiPlaceholderWithDefault(
Alex Lightef28d242020-11-17 20:21:51 -0800934 PhiPlaceholder phi_placeholder,
Vladimir Marko3224f382020-06-23 14:19:53 +0100935 DataType::Type type,
Alex Lightef28d242020-11-17 20:21:51 -0800936 /*inout*/ ArenaBitVector* phi_placeholders_to_materialize);
Vladimir Marko3224f382020-06-23 14:19:53 +0100937 bool TryReplacingLoopPhiPlaceholderWithSingleInput(
Alex Lightef28d242020-11-17 20:21:51 -0800938 PhiPlaceholder phi_placeholder,
939 /*inout*/ ArenaBitVector* phi_placeholders_to_materialize);
Alex Lightdeef2002021-01-15 08:53:07 -0800940 std::optional<PhiPlaceholder> FindLoopPhisToMaterialize(
941 PhiPlaceholder phi_placeholder,
942 /*out*/ ArenaBitVector* phi_placeholders_to_materialize,
943 DataType::Type type,
944 bool can_use_default_or_phi);
Vladimir Marko3224f382020-06-23 14:19:53 +0100945 bool MaterializeLoopPhis(const ScopedArenaVector<size_t>& phi_placeholder_indexes,
Alex Light09e23372021-01-15 08:42:11 -0800946 DataType::Type type);
Alex Light3a73ffb2021-01-25 14:11:05 +0000947 bool MaterializeLoopPhis(ArrayRef<const size_t> phi_placeholder_indexes, DataType::Type type);
Vladimir Marko3224f382020-06-23 14:19:53 +0100948 bool MaterializeLoopPhis(const ArenaBitVector& phi_placeholders_to_materialize,
Alex Light09e23372021-01-15 08:42:11 -0800949 DataType::Type type);
Alex Light3a73ffb2021-01-25 14:11:05 +0000950 bool FullyMaterializePhi(PhiPlaceholder phi_placeholder, DataType::Type type);
Alex Lightef28d242020-11-17 20:21:51 -0800951 std::optional<PhiPlaceholder> TryToMaterializeLoopPhis(PhiPlaceholder phi_placeholder,
952 HInstruction* load);
953 void ProcessLoopPhiWithUnknownInput(PhiPlaceholder loop_phi_with_unknown_input);
Vladimir Marko3224f382020-06-23 14:19:53 +0100954 void ProcessLoadsRequiringLoopPhis();
955
956 void SearchPhiPlaceholdersForKeptStores();
957 void UpdateValueRecordForStoreElimination(/*inout*/ValueRecord* value_record);
Alex Lightef28d242020-11-17 20:21:51 -0800958 void FindOldValueForPhiPlaceholder(PhiPlaceholder phi_placeholder, DataType::Type type);
Vladimir Marko3224f382020-06-23 14:19:53 +0100959 void FindStoresWritingOldValues();
Alex Light3a73ffb2021-01-25 14:11:05 +0000960 void FinishFullLSE();
961 void PrepareForPartialPhiComputation();
962 // Create materialization block and materialization object for the given predecessor of entry.
963 HInstruction* SetupPartialMaterialization(PartialLoadStoreEliminationHelper& helper,
964 HeapRefHolder&& holder,
965 size_t pred_idx,
966 HBasicBlock* blk);
967 // Returns the value that would be read by the 'read' instruction on
968 // 'orig_new_inst' if 'orig_new_inst' has not escaped.
969 HInstruction* GetPartialValueAt(HNewInstance* orig_new_inst, HInstruction* read);
970 void MovePartialEscapes();
971
972 void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* instruction) override {
973 LOG(FATAL) << "Visited instruction " << instruction->DumpWithoutArgs()
974 << " but LSE should be the only source of predicated-ifield-gets!";
975 }
Mingyao Yang8df69d42015-10-22 15:40:58 -0700976
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100977 void VisitInstanceFieldGet(HInstanceFieldGet* instruction) override {
Aart Bikb765a3f2018-05-10 14:47:48 -0700978 HInstruction* object = instruction->InputAt(0);
979 const FieldInfo& field = instruction->GetFieldInfo();
980 VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(object, &field));
Mingyao Yang8df69d42015-10-22 15:40:58 -0700981 }
982
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100983 void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
Aart Bikb765a3f2018-05-10 14:47:48 -0700984 HInstruction* object = instruction->InputAt(0);
985 const FieldInfo& field = instruction->GetFieldInfo();
Mingyao Yang8df69d42015-10-22 15:40:58 -0700986 HInstruction* value = instruction->InputAt(1);
Aart Bikb765a3f2018-05-10 14:47:48 -0700987 size_t idx = heap_location_collector_.GetFieldHeapLocation(object, &field);
988 VisitSetLocation(instruction, idx, value);
Mingyao Yang8df69d42015-10-22 15:40:58 -0700989 }
990
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100991 void VisitStaticFieldGet(HStaticFieldGet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -0700992 HInstruction* cls = instruction->InputAt(0);
Aart Bikb765a3f2018-05-10 14:47:48 -0700993 const FieldInfo& field = instruction->GetFieldInfo();
994 VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(cls, &field));
Mingyao Yang8df69d42015-10-22 15:40:58 -0700995 }
996
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100997 void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -0700998 HInstruction* cls = instruction->InputAt(0);
Aart Bikb765a3f2018-05-10 14:47:48 -0700999 const FieldInfo& field = instruction->GetFieldInfo();
Vladimir Marko3224f382020-06-23 14:19:53 +01001000 HInstruction* value = instruction->InputAt(1);
Aart Bikb765a3f2018-05-10 14:47:48 -07001001 size_t idx = heap_location_collector_.GetFieldHeapLocation(cls, &field);
Vladimir Marko3224f382020-06-23 14:19:53 +01001002 VisitSetLocation(instruction, idx, value);
Mingyao Yang8df69d42015-10-22 15:40:58 -07001003 }
1004
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001005 void VisitArrayGet(HArrayGet* instruction) override {
Aart Bikb765a3f2018-05-10 14:47:48 -07001006 VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction));
Mingyao Yang8df69d42015-10-22 15:40:58 -07001007 }
1008
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001009 void VisitArraySet(HArraySet* instruction) override {
Aart Bikb765a3f2018-05-10 14:47:48 -07001010 size_t idx = heap_location_collector_.GetArrayHeapLocation(instruction);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001011 VisitSetLocation(instruction, idx, instruction->GetValue());
1012 }
1013
1014 void VisitVecLoad(HVecLoad* instruction) override {
1015 VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction));
1016 }
1017
1018 void VisitVecStore(HVecStore* instruction) override {
1019 size_t idx = heap_location_collector_.GetArrayHeapLocation(instruction);
1020 VisitSetLocation(instruction, idx, instruction->GetValue());
Mingyao Yang8df69d42015-10-22 15:40:58 -07001021 }
1022
Andreas Gampefa6a1b02018-09-07 08:11:55 -07001023 void VisitDeoptimize(HDeoptimize* instruction) override {
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001024 // If we are in a try catch, even singletons are observable.
1025 const bool in_try_catch = instruction->GetBlock()->GetTryCatchInformation() != nullptr;
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001026 HBasicBlock* block = instruction->GetBlock();
1027 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01001028 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
1029 Value* stored_by = &heap_values[i].stored_by;
1030 if (stored_by->IsUnknown()) {
1031 continue;
1032 }
1033 // Stores are generally observeable after deoptimization, except
1034 // for singletons that don't escape in the deoptimization environment.
1035 bool observable = true;
1036 ReferenceInfo* info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001037 if (!in_try_catch && info->IsSingleton()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001038 HInstruction* reference = info->GetReference();
1039 // Finalizable objects always escape.
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001040 const bool finalizable_object =
1041 reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable();
1042 if (!finalizable_object && !IsEscapingObject(info, block, i)) {
Mingyao Yanga3540532018-01-25 12:17:28 -08001043 // Check whether the reference for a store is used by an environment local of
Vladimir Marko3224f382020-06-23 14:19:53 +01001044 // the HDeoptimize. If not, the singleton is not observed after deoptimization.
1045 const HUseList<HEnvironment*>& env_uses = reference->GetEnvUses();
1046 observable = std::any_of(
1047 env_uses.begin(),
1048 env_uses.end(),
1049 [instruction](const HUseListNode<HEnvironment*>& use) {
1050 return use.GetUser()->GetHolder() == instruction;
1051 });
Mingyao Yangeb2d2d346e2017-03-02 13:26:17 -08001052 }
1053 }
Vladimir Marko3224f382020-06-23 14:19:53 +01001054 if (observable) {
1055 KeepStores(*stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -08001056 *stored_by = Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01001057 }
Mingyao Yangeb2d2d346e2017-03-02 13:26:17 -08001058 }
1059 }
1060
Mingyao Yang46721ef2017-10-05 14:45:17 -07001061 // Keep necessary stores before exiting a method via return/throw.
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001062 void HandleExit(HBasicBlock* block, bool must_keep_stores = false) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001063 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
1064 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
Mingyao Yang46721ef2017-10-05 14:45:17 -07001065 ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001066 if (must_keep_stores || IsEscapingObject(ref_info, block, i)) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001067 KeepStores(heap_values[i].stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -08001068 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang46721ef2017-10-05 14:45:17 -07001069 }
1070 }
1071 }
1072
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001073 void VisitReturn(HReturn* instruction) override {
Mingyao Yang46721ef2017-10-05 14:45:17 -07001074 HandleExit(instruction->GetBlock());
1075 }
1076
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001077 void VisitReturnVoid(HReturnVoid* return_void) override {
Mingyao Yang46721ef2017-10-05 14:45:17 -07001078 HandleExit(return_void->GetBlock());
1079 }
1080
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001081 void HandleThrowingInstruction(HInstruction* instruction) {
1082 DCHECK(instruction->CanThrow());
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001083 // If we are inside of a try catch, singletons can become visible since we may not exit the
1084 // method.
1085 HandleExit(instruction->GetBlock(),
1086 instruction->GetBlock()->GetTryCatchInformation() != nullptr);
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001087 }
1088
1089 void VisitMethodEntryHook(HMethodEntryHook* method_entry) override {
1090 HandleThrowingInstruction(method_entry);
1091 }
1092
1093 void VisitMethodExitHook(HMethodExitHook* method_exit) override {
1094 HandleThrowingInstruction(method_exit);
1095 }
1096
1097 void VisitDivZeroCheck(HDivZeroCheck* div_zero_check) override {
1098 HandleThrowingInstruction(div_zero_check);
1099 }
1100
1101 void VisitNullCheck(HNullCheck* null_check) override {
1102 HandleThrowingInstruction(null_check);
1103 }
1104
1105 void VisitBoundsCheck(HBoundsCheck* bounds_check) override {
1106 HandleThrowingInstruction(bounds_check);
1107 }
1108
1109 void VisitLoadClass(HLoadClass* load_class) override {
1110 if (load_class->CanThrow()) {
1111 HandleThrowingInstruction(load_class);
1112 }
1113 }
1114
1115 void VisitLoadString(HLoadString* load_string) override {
1116 if (load_string->CanThrow()) {
1117 HandleThrowingInstruction(load_string);
1118 }
1119 }
1120
Nicolas Geoffrayaafb81b2022-06-29 09:36:23 +01001121 void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) override {
1122 HandleThrowingInstruction(load_method_handle);
1123 }
1124
1125 void VisitLoadMethodType(HLoadMethodType* load_method_type) override {
1126 HandleThrowingInstruction(load_method_type);
1127 }
1128
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001129 void VisitStringBuilderAppend(HStringBuilderAppend* sb_append) override {
1130 HandleThrowingInstruction(sb_append);
1131 }
1132
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001133 void VisitThrow(HThrow* throw_instruction) override {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001134 HandleThrowingInstruction(throw_instruction);
1135 }
1136
1137 void VisitCheckCast(HCheckCast* check_cast) override {
1138 HandleThrowingInstruction(check_cast);
1139 }
1140
1141 void VisitMonitorOperation(HMonitorOperation* monitor_op) override {
1142 if (monitor_op->CanThrow()) {
1143 HandleThrowingInstruction(monitor_op);
1144 }
Mingyao Yang46721ef2017-10-05 14:45:17 -07001145 }
1146
Mingyao Yang293f1c02017-11-08 15:22:17 -08001147 void HandleInvoke(HInstruction* instruction) {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001148 // If `instruction` can throw we have to presume all stores are visible.
1149 const bool can_throw = instruction->CanThrow();
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001150 // If we are in a try catch, even singletons are observable.
1151 const bool can_throw_in_try_catch =
1152 can_throw && instruction->GetBlock()->GetTryCatchInformation() != nullptr;
Mingyao Yang293f1c02017-11-08 15:22:17 -08001153 SideEffects side_effects = instruction->GetSideEffects();
Vladimir Marko3224f382020-06-23 14:19:53 +01001154 ScopedArenaVector<ValueRecord>& heap_values =
Mingyao Yang293f1c02017-11-08 15:22:17 -08001155 heap_values_for_[instruction->GetBlock()->GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01001156 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001157 ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
Alex Light86fe9b82020-11-16 16:54:01 +00001158 HBasicBlock* blk = instruction->GetBlock();
1159 // We don't need to do anything if the reference has not escaped at this point.
1160 // This is true if either we (1) never escape or (2) sometimes escape but
1161 // there is no possible execution where we have done so at this time. NB
1162 // We count being in the excluded cohort as escaping. Technically, this is
1163 // a bit over-conservative (since we can have multiple non-escaping calls
1164 // before a single escaping one) but this simplifies everything greatly.
Vladimir Marko5c824932021-06-02 15:54:17 +01001165 auto partial_singleton_did_not_escape = [](ReferenceInfo* ref_info, HBasicBlock* blk) {
1166 DCHECK(ref_info->IsPartialSingleton());
1167 if (!ref_info->GetNoEscapeSubgraph()->ContainsBlock(blk)) {
1168 return false;
1169 }
1170 ArrayRef<const ExecutionSubgraph::ExcludedCohort> cohorts =
1171 ref_info->GetNoEscapeSubgraph()->GetExcludedCohorts();
1172 return std::none_of(cohorts.begin(),
1173 cohorts.end(),
1174 [&](const ExecutionSubgraph::ExcludedCohort& cohort) {
1175 return cohort.PrecedesBlock(blk);
1176 });
1177 };
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001178 if (!can_throw_in_try_catch &&
1179 (ref_info->IsSingleton() ||
1180 // partial and we aren't currently escaping and we haven't escaped yet.
1181 (ref_info->IsPartialSingleton() && partial_singleton_did_not_escape(ref_info, blk)))) {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001182 // Singleton references cannot be seen by the callee.
1183 } else {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001184 if (can_throw || side_effects.DoesAnyRead() || side_effects.DoesAnyWrite()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001185 // Previous stores may become visible (read) and/or impossible for LSE to track (write).
1186 KeepStores(heap_values[i].stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -08001187 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang293f1c02017-11-08 15:22:17 -08001188 }
1189 if (side_effects.DoesAnyWrite()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001190 // The value may be clobbered.
Alex Light3a73ffb2021-01-25 14:11:05 +00001191 heap_values[i].value = Value::PartialUnknown(heap_values[i].value);
Mingyao Yang293f1c02017-11-08 15:22:17 -08001192 }
Mingyao Yang8df69d42015-10-22 15:40:58 -07001193 }
1194 }
1195 }
1196
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001197 void VisitInvoke(HInvoke* invoke) override {
Orion Hodsonac141392017-01-13 11:53:47 +00001198 HandleInvoke(invoke);
1199 }
1200
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001201 void VisitClinitCheck(HClinitCheck* clinit) override {
Vladimir Marko3224f382020-06-23 14:19:53 +01001202 // Class initialization check can result in class initializer calling arbitrary methods.
Mingyao Yang8df69d42015-10-22 15:40:58 -07001203 HandleInvoke(clinit);
1204 }
1205
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001206 void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001207 // Conservatively treat it as an invocation.
1208 HandleInvoke(instruction);
1209 }
1210
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001211 void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001212 // Conservatively treat it as an invocation.
1213 HandleInvoke(instruction);
1214 }
1215
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001216 void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001217 // Conservatively treat it as an invocation.
1218 HandleInvoke(instruction);
1219 }
1220
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001221 void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001222 // Conservatively treat it as an invocation.
1223 HandleInvoke(instruction);
1224 }
1225
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001226 void VisitNewInstance(HNewInstance* new_instance) override {
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001227 // If we are in a try catch, even singletons are observable.
1228 const bool in_try_catch = new_instance->GetBlock()->GetTryCatchInformation() != nullptr;
Mingyao Yang8df69d42015-10-22 15:40:58 -07001229 ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_instance);
1230 if (ref_info == nullptr) {
1231 // new_instance isn't used for field accesses. No need to process it.
1232 return;
1233 }
Mingyao Yang025c1a62017-10-30 11:19:57 -07001234 if (ref_info->IsSingletonAndRemovable() && !new_instance->NeedsChecks()) {
1235 DCHECK(!new_instance->IsFinalizable());
Mingyao Yang7cf9af22018-02-06 15:02:42 -08001236 // new_instance can potentially be eliminated.
Mingyao Yang062157f2016-03-02 10:15:36 -08001237 singleton_new_instances_.push_back(new_instance);
Mingyao Yang8df69d42015-10-22 15:40:58 -07001238 }
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001239 HBasicBlock* block = new_instance->GetBlock();
1240 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01001241 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001242 ReferenceInfo* info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
1243 HInstruction* ref = info->GetReference();
Mingyao Yang8df69d42015-10-22 15:40:58 -07001244 size_t offset = heap_location_collector_.GetHeapLocation(i)->GetOffset();
Alex Light2610dfe2020-12-07 16:26:43 -08001245 if (ref == new_instance) {
Alex Lightc6da1be2021-01-22 06:58:44 -08001246 if (offset >= mirror::kObjectHeaderSize ||
1247 MemberOffset(offset) == mirror::Object::MonitorOffset()) {
Alex Light2610dfe2020-12-07 16:26:43 -08001248 // Instance fields except the header fields are set to default heap values.
Alex Lightc6da1be2021-01-22 06:58:44 -08001249 // The shadow$_monitor_ field is set to the default value however.
Alex Light2610dfe2020-12-07 16:26:43 -08001250 heap_values[i].value = Value::Default();
Alex Lightf5a84cb2021-01-15 08:35:38 -08001251 heap_values[i].stored_by = Value::PureUnknown();
Alex Light2610dfe2020-12-07 16:26:43 -08001252 } else if (MemberOffset(offset) == mirror::Object::ClassOffset()) {
1253 // The shadow$_klass_ field is special and has an actual value however.
1254 heap_values[i].value = Value::ForInstruction(new_instance->GetLoadClass());
Alex Lightf5a84cb2021-01-15 08:35:38 -08001255 heap_values[i].stored_by = Value::PureUnknown();
Alex Light2610dfe2020-12-07 16:26:43 -08001256 }
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001257 } else if (in_try_catch || IsEscapingObject(info, block, i)) {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001258 // Since NewInstance can throw, we presume all previous stores could be visible.
1259 KeepStores(heap_values[i].stored_by);
1260 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang8df69d42015-10-22 15:40:58 -07001261 }
1262 }
1263 }
1264
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001265 void VisitNewArray(HNewArray* new_array) override {
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001266 // If we are in a try catch, even singletons are observable.
1267 const bool in_try_catch = new_array->GetBlock()->GetTryCatchInformation() != nullptr;
Mingyao Yang86974902017-03-01 14:03:51 -08001268 ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_array);
1269 if (ref_info == nullptr) {
1270 // new_array isn't used for array accesses. No need to process it.
1271 return;
1272 }
1273 if (ref_info->IsSingletonAndRemovable()) {
Mingyao Yang7cf9af22018-02-06 15:02:42 -08001274 if (new_array->GetLength()->IsIntConstant() &&
1275 new_array->GetLength()->AsIntConstant()->GetValue() >= 0) {
1276 // new_array can potentially be eliminated.
1277 singleton_new_instances_.push_back(new_array);
1278 } else {
1279 // new_array may throw NegativeArraySizeException. Keep it.
1280 }
Mingyao Yang86974902017-03-01 14:03:51 -08001281 }
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001282 HBasicBlock* block = new_array->GetBlock();
1283 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01001284 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
Mingyao Yang86974902017-03-01 14:03:51 -08001285 HeapLocation* location = heap_location_collector_.GetHeapLocation(i);
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001286 ReferenceInfo* info = location->GetReferenceInfo();
1287 HInstruction* ref = info->GetReference();
Mingyao Yang86974902017-03-01 14:03:51 -08001288 if (ref == new_array && location->GetIndex() != nullptr) {
1289 // Array elements are set to default heap values.
Vladimir Marko3224f382020-06-23 14:19:53 +01001290 heap_values[i].value = Value::Default();
Alex Lightf5a84cb2021-01-15 08:35:38 -08001291 heap_values[i].stored_by = Value::PureUnknown();
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001292 } else if (in_try_catch || IsEscapingObject(info, block, i)) {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001293 // Since NewArray can throw, we presume all previous stores could be visible.
1294 KeepStores(heap_values[i].stored_by);
1295 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang86974902017-03-01 14:03:51 -08001296 }
1297 }
1298 }
1299
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001300 void VisitInstruction(HInstruction* instruction) override {
1301 // Throwing instructions must be handled specially.
1302 DCHECK(!instruction->CanThrow());
1303 }
1304
Alex Light3a73ffb2021-01-25 14:11:05 +00001305 bool ShouldPerformPartialLSE() const {
1306 return perform_partial_lse_ && !GetGraph()->IsCompilingOsr();
1307 }
1308
1309 bool perform_partial_lse_;
1310
Mingyao Yang8df69d42015-10-22 15:40:58 -07001311 const HeapLocationCollector& heap_location_collector_;
Mingyao Yang8df69d42015-10-22 15:40:58 -07001312
Vladimir Marko009d1662017-10-10 13:21:15 +01001313 // Use local allocator for allocating memory.
1314 ScopedArenaAllocator allocator_;
1315
Alex Lightef28d242020-11-17 20:21:51 -08001316 // The number of unique phi_placeholders there possibly are
1317 size_t num_phi_placeholders_;
Vladimir Marko3224f382020-06-23 14:19:53 +01001318
1319 // One array of heap value records for each block.
1320 ScopedArenaVector<ScopedArenaVector<ValueRecord>> heap_values_for_;
Mingyao Yang8df69d42015-10-22 15:40:58 -07001321
Vladimir Marko3224f382020-06-23 14:19:53 +01001322 // We record loads and stores for re-processing when we find a loop Phi placeholder
1323 // with unknown value from a predecessor, and also for removing stores that are
1324 // found to be dead, i.e. not marked in `kept_stores_` at the end.
1325 struct LoadStoreRecord {
1326 HInstruction* load_or_store;
1327 size_t heap_location_index;
1328 };
1329 ScopedArenaVector<LoadStoreRecord> loads_and_stores_;
1330
Vladimir Marko9e3fe992020-08-25 16:17:51 +01001331 // We record the substitute instructions for loads that should be
1332 // eliminated but may be used by heap locations. They'll be removed
1333 // in the end. These are indexed by the load's id.
1334 ScopedArenaVector<HInstruction*> substitute_instructions_for_loads_;
1335
Alex Light3a73ffb2021-01-25 14:11:05 +00001336 // Value at the start of the given instruction for instructions which directly
1337 // read from a heap-location (i.e. FieldGet). The mapping to heap-location is
1338 // implicit through the fact that each instruction can only directly refer to
1339 // a single heap-location.
1340 ScopedArenaHashMap<HInstruction*, Value> intermediate_values_;
1341
Vladimir Marko3224f382020-06-23 14:19:53 +01001342 // Record stores to keep in a bit vector indexed by instruction ID.
1343 ArenaBitVector kept_stores_;
1344 // When we need to keep all stores that feed a Phi placeholder, we just record the
1345 // index of that placeholder for processing after graph traversal.
1346 ArenaBitVector phi_placeholders_to_search_for_kept_stores_;
1347
1348 // Loads that would require a loop Phi to replace are recorded for processing
1349 // later as we do not have enough information from back-edges to determine if
1350 // a suitable Phi can be found or created when we visit these loads.
1351 ScopedArenaHashMap<HInstruction*, ValueRecord> loads_requiring_loop_phi_;
1352
1353 // For stores, record the old value records that were replaced and the stored values.
1354 struct StoreRecord {
1355 ValueRecord old_value_record;
1356 HInstruction* stored_value;
1357 };
Vladimir Markoa718d642021-03-10 15:36:40 +00001358 // Small pre-allocated initial buffer avoids initializing a large one until it's really needed.
1359 static constexpr size_t kStoreRecordsInitialBufferSize = 16;
Vladimir Markoc9f4a372021-03-11 10:38:34 +00001360 std::pair<HInstruction*, StoreRecord> store_records_buffer_[kStoreRecordsInitialBufferSize];
Vladimir Marko3224f382020-06-23 14:19:53 +01001361 ScopedArenaHashMap<HInstruction*, StoreRecord> store_records_;
1362
1363 // Replacements for Phi placeholders.
Vladimir Marko06fb7fa2021-05-18 15:53:17 +00001364 // The invalid heap value is used to mark Phi placeholders that cannot be replaced.
Vladimir Marko3224f382020-06-23 14:19:53 +01001365 ScopedArenaVector<Value> phi_placeholder_replacements_;
Mingyao Yangfb8464a2015-11-02 10:56:59 -08001366
Alex Light86fe9b82020-11-16 16:54:01 +00001367 // Merged-unknowns that must have their predecessor values kept to ensure
1368 // partially escaped values are written
1369 ArenaBitVector kept_merged_unknowns_;
1370
Vladimir Marko009d1662017-10-10 13:21:15 +01001371 ScopedArenaVector<HInstruction*> singleton_new_instances_;
Mingyao Yang8df69d42015-10-22 15:40:58 -07001372
Alex Light3a73ffb2021-01-25 14:11:05 +00001373 // The field infos for each heap location (if relevant).
1374 ScopedArenaVector<const FieldInfo*> field_infos_;
1375
Alex Light09e23372021-01-15 08:42:11 -08001376 Phase current_phase_;
1377
Alex Light3a73ffb2021-01-25 14:11:05 +00001378 friend class PartialLoadStoreEliminationHelper;
1379 friend struct ScopedRestoreHeapValues;
1380
Alex Light86fe9b82020-11-16 16:54:01 +00001381 friend std::ostream& operator<<(std::ostream& os, const Value& v);
Alex Light3a73ffb2021-01-25 14:11:05 +00001382 friend std::ostream& operator<<(std::ostream& os, const PriorValueHolder& v);
1383 friend std::ostream& operator<<(std::ostream& oss, const LSEVisitor::Phase& phase);
Alex Light86fe9b82020-11-16 16:54:01 +00001384
Mingyao Yang8df69d42015-10-22 15:40:58 -07001385 DISALLOW_COPY_AND_ASSIGN(LSEVisitor);
1386};
1387
Alex Light3a73ffb2021-01-25 14:11:05 +00001388std::ostream& operator<<(std::ostream& oss, const LSEVisitor::PriorValueHolder& p) {
1389 p.Dump(oss);
1390 return oss;
1391}
1392
Alex Light09e23372021-01-15 08:42:11 -08001393std::ostream& operator<<(std::ostream& oss, const LSEVisitor::Phase& phase) {
1394 switch (phase) {
1395 case LSEVisitor::Phase::kLoadElimination:
1396 return oss << "kLoadElimination";
1397 case LSEVisitor::Phase::kStoreElimination:
1398 return oss << "kStoreElimination";
Alex Light3a73ffb2021-01-25 14:11:05 +00001399 case LSEVisitor::Phase::kPartialElimination:
1400 return oss << "kPartialElimination";
Alex Light09e23372021-01-15 08:42:11 -08001401 }
1402}
1403
Alex Light3a73ffb2021-01-25 14:11:05 +00001404void LSEVisitor::PriorValueHolder::Dump(std::ostream& oss) const {
1405 if (IsDefault()) {
1406 oss << "Default";
1407 } else if (IsPhi()) {
1408 oss << "Phi: " << GetPhiPlaceholder();
1409 } else {
1410 oss << "Instruction: " << *GetInstruction();
1411 }
1412}
1413
1414constexpr LSEVisitor::PriorValueHolder::PriorValueHolder(Value val)
1415 : value_(Marker{}) {
1416 DCHECK(!val.IsInvalid() && !val.IsPureUnknown());
1417 if (val.IsPartialUnknown()) {
1418 value_ = val.GetPriorValue().value_;
1419 } else if (val.IsMergedUnknown() || val.NeedsPhi()) {
1420 value_ = val.GetPhiPlaceholder();
1421 } else if (val.IsInstruction()) {
1422 value_ = val.GetInstruction();
1423 } else {
1424 DCHECK(val.IsDefault());
1425 }
1426}
1427
1428constexpr bool operator==(const LSEVisitor::Marker&, const LSEVisitor::Marker&) {
1429 return true;
1430}
1431
1432constexpr bool operator==(const LSEVisitor::PriorValueHolder& p1,
1433 const LSEVisitor::PriorValueHolder& p2) {
1434 return p1.Equals(p2);
1435}
1436
Alex Lightef28d242020-11-17 20:21:51 -08001437constexpr bool operator==(const LSEVisitor::PhiPlaceholder& p1,
1438 const LSEVisitor::PhiPlaceholder& p2) {
1439 return p1.Equals(p2);
1440}
1441
1442constexpr bool operator==(const LSEVisitor::Value::NeedsLoopPhiMarker& p1,
1443 const LSEVisitor::Value::NeedsLoopPhiMarker& p2) {
1444 return p1.phi_ == p2.phi_;
1445}
1446
1447constexpr bool operator==(const LSEVisitor::Value::NeedsNonLoopPhiMarker& p1,
1448 const LSEVisitor::Value::NeedsNonLoopPhiMarker& p2) {
1449 return p1.phi_ == p2.phi_;
1450}
1451
1452constexpr bool operator==(const LSEVisitor::Value::MergedUnknownMarker& p1,
1453 const LSEVisitor::Value::MergedUnknownMarker& p2) {
1454 return p1.phi_ == p2.phi_;
1455}
1456
1457std::ostream& operator<<(std::ostream& oss, const LSEVisitor::PhiPlaceholder& p) {
1458 p.Dump(oss);
1459 return oss;
1460}
1461
Alex Light3a73ffb2021-01-25 14:11:05 +00001462LSEVisitor::Value LSEVisitor::PriorValueHolder::ToValue() const {
1463 if (IsDefault()) {
1464 return Value::Default();
1465 } else if (IsPhi()) {
1466 return Value::ForLoopPhiPlaceholder(GetPhiPlaceholder());
1467 } else {
1468 return Value::ForInstruction(GetInstruction());
1469 }
1470}
1471
1472constexpr bool LSEVisitor::Value::ExactEquals(LSEVisitor::Value other) const {
1473 return value_ == other.value_;
1474}
1475
Alex Lightef28d242020-11-17 20:21:51 -08001476constexpr bool LSEVisitor::Value::Equals(LSEVisitor::Value other) const {
1477 // Only valid values can be compared.
1478 DCHECK(IsValid());
1479 DCHECK(other.IsValid());
1480 if (value_ == other.value_) {
1481 // Note: Two unknown values are considered different.
1482 return !IsUnknown();
1483 } else {
1484 // Default is considered equal to zero-bit-pattern instructions.
1485 return (IsDefault() && other.IsInstruction() && IsZeroBitPattern(other.GetInstruction())) ||
1486 (other.IsDefault() && IsInstruction() && IsZeroBitPattern(GetInstruction()));
1487 }
1488}
1489
Alex Light86fe9b82020-11-16 16:54:01 +00001490std::ostream& LSEVisitor::Value::Dump(std::ostream& os) const {
Alex Lightef28d242020-11-17 20:21:51 -08001491 if (std::holds_alternative<LSEVisitor::Value::ValuelessType>(value_)) {
1492 switch (GetValuelessType()) {
1493 case ValuelessType::kDefault:
1494 return os << "Default";
Alex Lightf5a84cb2021-01-15 08:35:38 -08001495 case ValuelessType::kPureUnknown:
1496 return os << "PureUnknown";
Alex Lightef28d242020-11-17 20:21:51 -08001497 case ValuelessType::kInvalid:
1498 return os << "Invalid";
1499 }
Alex Light3a73ffb2021-01-25 14:11:05 +00001500 } else if (IsPartialUnknown()) {
1501 return os << "PartialUnknown[" << GetPriorValue() << "]";
Alex Lightef28d242020-11-17 20:21:51 -08001502 } else if (IsInstruction()) {
1503 return os << "Instruction[id: " << GetInstruction()->GetId()
1504 << ", block: " << GetInstruction()->GetBlock()->GetBlockId() << "]";
1505 } else if (IsMergedUnknown()) {
1506 return os << "MergedUnknown[block: " << GetPhiPlaceholder().GetBlockId()
1507 << ", heap_loc: " << GetPhiPlaceholder().GetHeapLocation() << "]";
1508
1509 } else if (NeedsLoopPhi()) {
1510 return os << "NeedsLoopPhi[block: " << GetPhiPlaceholder().GetBlockId()
1511 << ", heap_loc: " << GetPhiPlaceholder().GetHeapLocation() << "]";
1512 } else {
1513 return os << "NeedsNonLoopPhi[block: " << GetPhiPlaceholder().GetBlockId()
1514 << ", heap_loc: " << GetPhiPlaceholder().GetHeapLocation() << "]";
Alex Light86fe9b82020-11-16 16:54:01 +00001515 }
1516}
1517
1518std::ostream& operator<<(std::ostream& os, const LSEVisitor::Value& v) {
1519 return v.Dump(os);
1520}
1521
Vladimir Marko3224f382020-06-23 14:19:53 +01001522LSEVisitor::LSEVisitor(HGraph* graph,
1523 const HeapLocationCollector& heap_location_collector,
Alex Light3a73ffb2021-01-25 14:11:05 +00001524 bool perform_partial_lse,
Vladimir Marko3224f382020-06-23 14:19:53 +01001525 OptimizingCompilerStats* stats)
1526 : HGraphDelegateVisitor(graph, stats),
Alex Light3a73ffb2021-01-25 14:11:05 +00001527 perform_partial_lse_(perform_partial_lse),
Vladimir Marko3224f382020-06-23 14:19:53 +01001528 heap_location_collector_(heap_location_collector),
1529 allocator_(graph->GetArenaStack()),
Alex Lightef28d242020-11-17 20:21:51 -08001530 num_phi_placeholders_(GetGraph()->GetBlocks().size() *
1531 heap_location_collector_.GetNumberOfHeapLocations()),
Vladimir Marko3224f382020-06-23 14:19:53 +01001532 heap_values_for_(graph->GetBlocks().size(),
1533 ScopedArenaVector<ValueRecord>(allocator_.Adapter(kArenaAllocLSE)),
1534 allocator_.Adapter(kArenaAllocLSE)),
Vladimir Marko3224f382020-06-23 14:19:53 +01001535 loads_and_stores_(allocator_.Adapter(kArenaAllocLSE)),
Vladimir Marko9e3fe992020-08-25 16:17:51 +01001536 // We may add new instructions (default values, Phis) but we're not adding loads
1537 // or stores, so we shall not need to resize following vector and BitVector.
1538 substitute_instructions_for_loads_(graph->GetCurrentInstructionId(),
1539 nullptr,
1540 allocator_.Adapter(kArenaAllocLSE)),
Alex Light3a73ffb2021-01-25 14:11:05 +00001541 intermediate_values_(allocator_.Adapter(kArenaAllocLSE)),
Vladimir Marko3224f382020-06-23 14:19:53 +01001542 kept_stores_(&allocator_,
Alex Light3a73ffb2021-01-25 14:11:05 +00001543 /*start_bits=*/graph->GetCurrentInstructionId(),
1544 /*expandable=*/false,
Vladimir Marko3224f382020-06-23 14:19:53 +01001545 kArenaAllocLSE),
1546 phi_placeholders_to_search_for_kept_stores_(&allocator_,
Alex Lightef28d242020-11-17 20:21:51 -08001547 num_phi_placeholders_,
Alex Light3a73ffb2021-01-25 14:11:05 +00001548 /*expandable=*/false,
Vladimir Marko3224f382020-06-23 14:19:53 +01001549 kArenaAllocLSE),
1550 loads_requiring_loop_phi_(allocator_.Adapter(kArenaAllocLSE)),
Vladimir Markoc9f4a372021-03-11 10:38:34 +00001551 store_records_(store_records_buffer_,
Vladimir Markoa718d642021-03-10 15:36:40 +00001552 kStoreRecordsInitialBufferSize,
1553 allocator_.Adapter(kArenaAllocLSE)),
Alex Lightef28d242020-11-17 20:21:51 -08001554 phi_placeholder_replacements_(num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01001555 Value::Invalid(),
1556 allocator_.Adapter(kArenaAllocLSE)),
Alex Light86fe9b82020-11-16 16:54:01 +00001557 kept_merged_unknowns_(&allocator_,
Alex Light3a73ffb2021-01-25 14:11:05 +00001558 /*start_bits=*/num_phi_placeholders_,
1559 /*expandable=*/false,
Alex Light86fe9b82020-11-16 16:54:01 +00001560 kArenaAllocLSE),
Alex Light09e23372021-01-15 08:42:11 -08001561 singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)),
Alex Light3a73ffb2021-01-25 14:11:05 +00001562 field_infos_(heap_location_collector_.GetNumberOfHeapLocations(),
1563 allocator_.Adapter(kArenaAllocLSE)),
Alex Light09e23372021-01-15 08:42:11 -08001564 current_phase_(Phase::kLoadElimination) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001565 // Clear bit vectors.
1566 phi_placeholders_to_search_for_kept_stores_.ClearAllBits();
1567 kept_stores_.ClearAllBits();
1568}
1569
1570LSEVisitor::Value LSEVisitor::PrepareLoopValue(HBasicBlock* block, size_t idx) {
1571 // If the pre-header value is known (which implies that the reference dominates this
1572 // block), use a Phi placeholder for the value in the loop header. If all predecessors
1573 // are later found to have a known value, we can replace loads from this location,
1574 // either with the pre-header value or with a new Phi. For array locations, the index
1575 // may be defined inside the loop but the only known value in that case should be the
1576 // default value or a Phi placeholder that can be replaced only with the default value.
1577 HLoopInformation* loop_info = block->GetLoopInformation();
1578 uint32_t pre_header_block_id = loop_info->GetPreHeader()->GetBlockId();
1579 Value pre_header_value = ReplacementOrValue(heap_values_for_[pre_header_block_id][idx].value);
1580 if (pre_header_value.IsUnknown()) {
Alex Light86fe9b82020-11-16 16:54:01 +00001581 return pre_header_value;
Vladimir Marko3224f382020-06-23 14:19:53 +01001582 }
1583 if (kIsDebugBuild) {
1584 // Check that the reference indeed dominates this loop.
1585 HeapLocation* location = heap_location_collector_.GetHeapLocation(idx);
1586 HInstruction* ref = location->GetReferenceInfo()->GetReference();
Alex Light86fe9b82020-11-16 16:54:01 +00001587 CHECK(ref->GetBlock() != block && ref->GetBlock()->Dominates(block))
1588 << GetGraph()->PrettyMethod();
Vladimir Marko3224f382020-06-23 14:19:53 +01001589 // Check that the index, if defined inside the loop, tracks a default value
1590 // or a Phi placeholder requiring a loop Phi.
1591 HInstruction* index = location->GetIndex();
1592 if (index != nullptr && loop_info->Contains(*index->GetBlock())) {
Alex Light86fe9b82020-11-16 16:54:01 +00001593 CHECK(pre_header_value.NeedsLoopPhi() || pre_header_value.Equals(Value::Default()))
1594 << GetGraph()->PrettyMethod() << " blk: " << block->GetBlockId() << " "
1595 << pre_header_value;
Vladimir Marko3224f382020-06-23 14:19:53 +01001596 }
1597 }
Alex Lightef28d242020-11-17 20:21:51 -08001598 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Vladimir Marko3224f382020-06-23 14:19:53 +01001599 return ReplacementOrValue(Value::ForLoopPhiPlaceholder(phi_placeholder));
1600}
1601
1602LSEVisitor::Value LSEVisitor::PrepareLoopStoredBy(HBasicBlock* block, size_t idx) {
1603 // Use the Phi placeholder for `stored_by` to make sure all incoming stores are kept
1604 // if the value in the location escapes. This is not applicable to singletons that are
1605 // defined inside the loop as they shall be dead in the loop header.
Santiago Aboy Solanes4ebfcac2022-03-31 11:19:25 +01001606 const ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo();
1607 const HInstruction* reference = ref_info->GetReference();
1608 // Finalizable objects always escape.
1609 const bool is_finalizable =
1610 reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable();
Vladimir Marko3224f382020-06-23 14:19:53 +01001611 if (ref_info->IsSingleton() &&
Santiago Aboy Solanes4ebfcac2022-03-31 11:19:25 +01001612 block->GetLoopInformation()->Contains(*reference->GetBlock()) &&
1613 !is_finalizable) {
Alex Lightf5a84cb2021-01-15 08:35:38 -08001614 return Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01001615 }
Alex Lightef28d242020-11-17 20:21:51 -08001616 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Vladimir Marko3224f382020-06-23 14:19:53 +01001617 return Value::ForLoopPhiPlaceholder(phi_placeholder);
1618}
1619
1620void LSEVisitor::PrepareLoopRecords(HBasicBlock* block) {
1621 DCHECK(block->IsLoopHeader());
1622 int block_id = block->GetBlockId();
1623 HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
1624 ScopedArenaVector<ValueRecord>& pre_header_heap_values =
1625 heap_values_for_[pre_header->GetBlockId()];
1626 size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
1627 DCHECK_EQ(num_heap_locations, pre_header_heap_values.size());
1628 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block_id];
1629 DCHECK(heap_values.empty());
1630
1631 // Don't eliminate loads in irreducible loops.
1632 if (block->GetLoopInformation()->IsIrreducible()) {
1633 heap_values.resize(num_heap_locations,
Alex Light3a73ffb2021-01-25 14:11:05 +00001634 {/*value=*/Value::Invalid(), /*stored_by=*/Value::PureUnknown()});
Vladimir Marko3224f382020-06-23 14:19:53 +01001635 // Also keep the stores before the loop header, including in blocks that were not visited yet.
Alex Light3a73ffb2021-01-25 14:11:05 +00001636 bool is_osr = GetGraph()->IsCompilingOsr();
Vladimir Marko3224f382020-06-23 14:19:53 +01001637 for (size_t idx = 0u; idx != num_heap_locations; ++idx) {
Alex Light3a73ffb2021-01-25 14:11:05 +00001638 heap_values[idx].value =
1639 is_osr ? Value::PureUnknown()
1640 : Value::MergedUnknown(GetPhiPlaceholder(block->GetBlockId(), idx));
Vladimir Marko3224f382020-06-23 14:19:53 +01001641 KeepStores(Value::ForLoopPhiPlaceholder(GetPhiPlaceholder(block->GetBlockId(), idx)));
1642 }
1643 return;
1644 }
1645
1646 // Fill `heap_values` based on values from pre-header.
1647 heap_values.reserve(num_heap_locations);
1648 for (size_t idx = 0u; idx != num_heap_locations; ++idx) {
1649 heap_values.push_back({ PrepareLoopValue(block, idx), PrepareLoopStoredBy(block, idx) });
1650 }
1651}
1652
1653LSEVisitor::Value LSEVisitor::MergePredecessorValues(HBasicBlock* block, size_t idx) {
1654 ArrayRef<HBasicBlock* const> predecessors(block->GetPredecessors());
1655 DCHECK(!predecessors.empty());
1656 Value merged_value =
1657 ReplacementOrValue(heap_values_for_[predecessors[0]->GetBlockId()][idx].value);
1658 for (size_t i = 1u, size = predecessors.size(); i != size; ++i) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001659 Value pred_value =
1660 ReplacementOrValue(heap_values_for_[predecessors[i]->GetBlockId()][idx].value);
Alex Light86fe9b82020-11-16 16:54:01 +00001661 if (pred_value.Equals(merged_value)) {
1662 // Value is the same. No need to update our merged value.
1663 continue;
1664 } else if (pred_value.IsUnknown() || merged_value.IsUnknown()) {
1665 // If one is unknown and the other is a different type of unknown
Alex Lightef28d242020-11-17 20:21:51 -08001666 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Alex Light86fe9b82020-11-16 16:54:01 +00001667 merged_value = Value::MergedUnknown(phi_placeholder);
1668 // We know that at least one of the merge points is unknown (and both are
1669 // not pure-unknowns since that's captured above). This means that the
1670 // overall value needs to be a MergedUnknown. Just return that.
1671 break;
1672 } else {
Vladimir Marko3224f382020-06-23 14:19:53 +01001673 // There are conflicting known values. We may still be able to replace loads with a Phi.
Alex Lightef28d242020-11-17 20:21:51 -08001674 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Vladimir Marko3224f382020-06-23 14:19:53 +01001675 // Propagate the need for a new loop Phi from all predecessors.
1676 bool needs_loop_phi = merged_value.NeedsLoopPhi() || pred_value.NeedsLoopPhi();
1677 merged_value = ReplacementOrValue(Value::ForPhiPlaceholder(phi_placeholder, needs_loop_phi));
1678 }
1679 }
Santiago Aboy Solanes872ec722022-02-18 14:10:25 +00001680 DCHECK_IMPLIES(merged_value.IsPureUnknown(), block->GetPredecessors().size() <= 1)
Alex Light86fe9b82020-11-16 16:54:01 +00001681 << merged_value << " in " << GetGraph()->PrettyMethod();
Vladimir Marko3224f382020-06-23 14:19:53 +01001682 return merged_value;
1683}
1684
1685void LSEVisitor::MergePredecessorRecords(HBasicBlock* block) {
1686 if (block->IsExitBlock()) {
1687 // Exit block doesn't really merge values since the control flow ends in
1688 // its predecessors. Each predecessor needs to make sure stores are kept
1689 // if necessary.
1690 return;
1691 }
1692
1693 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
1694 DCHECK(heap_values.empty());
1695 size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
Santiago Aboy Solanes9e3c3712022-04-08 13:24:05 +00001696 if (block->GetPredecessors().empty() || (block->GetTryCatchInformation() != nullptr &&
1697 block->GetTryCatchInformation()->IsCatchBlock())) {
1698 DCHECK_IMPLIES(block->GetPredecessors().empty(), block->IsEntryBlock());
Vladimir Marko3224f382020-06-23 14:19:53 +01001699 heap_values.resize(num_heap_locations,
Alex Lightf5a84cb2021-01-15 08:35:38 -08001700 {/*value=*/Value::PureUnknown(), /*stored_by=*/Value::PureUnknown()});
Vladimir Marko3224f382020-06-23 14:19:53 +01001701 return;
1702 }
1703
1704 heap_values.reserve(num_heap_locations);
1705 for (size_t idx = 0u; idx != num_heap_locations; ++idx) {
1706 Value merged_value = MergePredecessorValues(block, idx);
1707 if (kIsDebugBuild) {
1708 if (merged_value.NeedsPhi()) {
Alex Lightef28d242020-11-17 20:21:51 -08001709 uint32_t block_id = merged_value.GetPhiPlaceholder().GetBlockId();
Vladimir Marko3224f382020-06-23 14:19:53 +01001710 CHECK(GetGraph()->GetBlocks()[block_id]->Dominates(block));
1711 } else if (merged_value.IsInstruction()) {
1712 CHECK(merged_value.GetInstruction()->GetBlock()->Dominates(block));
1713 }
1714 }
1715 ArrayRef<HBasicBlock* const> predecessors(block->GetPredecessors());
1716 Value merged_stored_by = heap_values_for_[predecessors[0]->GetBlockId()][idx].stored_by;
Vladimir Markocbeedc82020-08-25 14:31:10 +01001717 for (size_t predecessor_idx = 1u; predecessor_idx != predecessors.size(); ++predecessor_idx) {
1718 uint32_t predecessor_block_id = predecessors[predecessor_idx]->GetBlockId();
1719 Value stored_by = heap_values_for_[predecessor_block_id][idx].stored_by;
1720 if ((!stored_by.IsUnknown() || !merged_stored_by.IsUnknown()) &&
1721 !merged_stored_by.Equals(stored_by)) {
1722 // Use the Phi placeholder to track that we need to keep stores from all predecessors.
Alex Lightef28d242020-11-17 20:21:51 -08001723 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Vladimir Markocbeedc82020-08-25 14:31:10 +01001724 merged_stored_by = Value::ForNonLoopPhiPlaceholder(phi_placeholder);
1725 break;
1726 }
Vladimir Marko3224f382020-06-23 14:19:53 +01001727 }
1728 heap_values.push_back({ merged_value, merged_stored_by });
1729 }
1730}
1731
1732static HInstruction* FindOrConstructNonLoopPhi(
1733 HBasicBlock* block,
1734 const ScopedArenaVector<HInstruction*>& phi_inputs,
1735 DataType::Type type) {
1736 for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
1737 HInstruction* phi = phi_it.Current();
1738 DCHECK_EQ(phi->InputCount(), phi_inputs.size());
1739 auto cmp = [](HInstruction* lhs, const HUserRecord<HInstruction*>& rhs) {
1740 return lhs == rhs.GetInstruction();
1741 };
1742 if (std::equal(phi_inputs.begin(), phi_inputs.end(), phi->GetInputRecords().begin(), cmp)) {
1743 return phi;
1744 }
1745 }
1746 ArenaAllocator* allocator = block->GetGraph()->GetAllocator();
1747 HPhi* phi = new (allocator) HPhi(allocator, kNoRegNumber, phi_inputs.size(), type);
1748 for (size_t i = 0, size = phi_inputs.size(); i != size; ++i) {
1749 DCHECK_NE(phi_inputs[i]->GetType(), DataType::Type::kVoid) << phi_inputs[i]->DebugName();
1750 phi->SetRawInputAt(i, phi_inputs[i]);
1751 }
1752 block->AddPhi(phi);
1753 if (type == DataType::Type::kReference) {
1754 // Update reference type information. Pass invalid handles, these are not used for Phis.
1755 ReferenceTypePropagation rtp_fixup(block->GetGraph(),
Vladimir Marko3224f382020-06-23 14:19:53 +01001756 Handle<mirror::DexCache>(),
1757 /* is_first_run= */ false);
1758 rtp_fixup.Visit(phi);
1759 }
1760 return phi;
1761}
1762
Alex Lightef28d242020-11-17 20:21:51 -08001763void LSEVisitor::MaterializeNonLoopPhis(PhiPlaceholder phi_placeholder, DataType::Type type) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001764 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid());
1765 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
Alex Lightef28d242020-11-17 20:21:51 -08001766 size_t idx = phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01001767
1768 // Use local allocator to reduce peak memory usage.
1769 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
1770 // Reuse the same vector for collecting phi inputs.
1771 ScopedArenaVector<HInstruction*> phi_inputs(allocator.Adapter(kArenaAllocLSE));
1772
Alex Lightef28d242020-11-17 20:21:51 -08001773 ScopedArenaVector<PhiPlaceholder> work_queue(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01001774 work_queue.push_back(phi_placeholder);
1775 while (!work_queue.empty()) {
Alex Lightef28d242020-11-17 20:21:51 -08001776 PhiPlaceholder current_phi_placeholder = work_queue.back();
Vladimir Marko3224f382020-06-23 14:19:53 +01001777 if (phi_placeholder_replacements_[PhiPlaceholderIndex(current_phi_placeholder)].IsValid()) {
1778 // This Phi placeholder was pushed to the `work_queue` followed by another Phi placeholder
1779 // that directly or indirectly depends on it, so it was already processed as part of the
1780 // other Phi placeholder's dependencies before this one got back to the top of the stack.
1781 work_queue.pop_back();
1782 continue;
1783 }
Alex Lightef28d242020-11-17 20:21:51 -08001784 uint32_t current_block_id = current_phi_placeholder.GetBlockId();
Vladimir Marko3224f382020-06-23 14:19:53 +01001785 HBasicBlock* current_block = blocks[current_block_id];
1786 DCHECK_GE(current_block->GetPredecessors().size(), 2u);
1787
1788 // Non-loop Phis cannot depend on a loop Phi, so we should not see any loop header here.
1789 // And the only way for such merged value to reach a different heap location is through
1790 // a load at which point we materialize the Phi. Therefore all non-loop Phi placeholders
1791 // seen here are tied to one heap location.
Alex Light09e23372021-01-15 08:42:11 -08001792 DCHECK(!current_block->IsLoopHeader())
1793 << current_phi_placeholder << " phase: " << current_phase_;
Alex Lightef28d242020-11-17 20:21:51 -08001794 DCHECK_EQ(current_phi_placeholder.GetHeapLocation(), idx);
Vladimir Marko3224f382020-06-23 14:19:53 +01001795
1796 phi_inputs.clear();
1797 for (HBasicBlock* predecessor : current_block->GetPredecessors()) {
1798 Value pred_value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
Alex Light3a73ffb2021-01-25 14:11:05 +00001799 DCHECK(!pred_value.IsPureUnknown()) << pred_value << " block " << current_block->GetBlockId()
1800 << " pred: " << predecessor->GetBlockId();
1801 if (pred_value.NeedsNonLoopPhi() ||
1802 (current_phase_ == Phase::kPartialElimination && pred_value.IsMergedUnknown())) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001803 // We need to process the Phi placeholder first.
1804 work_queue.push_back(pred_value.GetPhiPlaceholder());
1805 } else if (pred_value.IsDefault()) {
1806 phi_inputs.push_back(GetDefaultValue(type));
1807 } else {
Alex Light09e23372021-01-15 08:42:11 -08001808 DCHECK(pred_value.IsInstruction()) << pred_value << " block " << current_block->GetBlockId()
1809 << " pred: " << predecessor->GetBlockId();
Vladimir Marko3224f382020-06-23 14:19:53 +01001810 phi_inputs.push_back(pred_value.GetInstruction());
1811 }
1812 }
1813 if (phi_inputs.size() == current_block->GetPredecessors().size()) {
1814 // All inputs are available. Find or construct the Phi replacement.
1815 phi_placeholder_replacements_[PhiPlaceholderIndex(current_phi_placeholder)] =
1816 Value::ForInstruction(FindOrConstructNonLoopPhi(current_block, phi_inputs, type));
1817 // Remove the block from the queue.
1818 DCHECK_EQ(current_phi_placeholder, work_queue.back());
1819 work_queue.pop_back();
1820 }
1821 }
1822}
1823
1824void LSEVisitor::VisitGetLocation(HInstruction* instruction, size_t idx) {
1825 DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound);
1826 uint32_t block_id = instruction->GetBlock()->GetBlockId();
1827 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block_id];
1828 ValueRecord& record = heap_values[idx];
Alex Light3a73ffb2021-01-25 14:11:05 +00001829 if (instruction->IsFieldAccess()) {
1830 RecordFieldInfo(&instruction->GetFieldInfo(), idx);
1831 }
Vladimir Marko3224f382020-06-23 14:19:53 +01001832 DCHECK(record.value.IsUnknown() || record.value.Equals(ReplacementOrValue(record.value)));
Alex Light3a73ffb2021-01-25 14:11:05 +00001833 // If we are unknown, we either come from somewhere untracked or we can reconstruct the partial
1834 // value.
1835 DCHECK(!record.value.IsPureUnknown() ||
1836 heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo() == nullptr ||
1837 !heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo()->IsPartialSingleton())
1838 << "In " << GetGraph()->PrettyMethod() << ": " << record.value << " for " << *instruction;
1839 intermediate_values_.insert({instruction, record.value});
Vladimir Marko3224f382020-06-23 14:19:53 +01001840 loads_and_stores_.push_back({ instruction, idx });
1841 if ((record.value.IsDefault() || record.value.NeedsNonLoopPhi()) &&
1842 !IsDefaultOrPhiAllowedForLoad(instruction)) {
Alex Lightf5a84cb2021-01-15 08:35:38 -08001843 record.value = Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01001844 }
1845 if (record.value.IsDefault()) {
Vladimir Markocbeedc82020-08-25 14:31:10 +01001846 KeepStores(record.stored_by);
Vladimir Marko3224f382020-06-23 14:19:53 +01001847 HInstruction* constant = GetDefaultValue(instruction->GetType());
1848 AddRemovedLoad(instruction, constant);
1849 record.value = Value::ForInstruction(constant);
1850 } else if (record.value.IsUnknown()) {
1851 // Load isn't eliminated. Put the load as the value into the HeapLocation.
1852 // This acts like GVN but with better aliasing analysis.
Alex Light86fe9b82020-11-16 16:54:01 +00001853 Value old_value = record.value;
Vladimir Marko3224f382020-06-23 14:19:53 +01001854 record.value = Value::ForInstruction(instruction);
1855 KeepStoresIfAliasedToLocation(heap_values, idx);
Alex Light86fe9b82020-11-16 16:54:01 +00001856 KeepStores(old_value);
Vladimir Marko3224f382020-06-23 14:19:53 +01001857 } else if (record.value.NeedsLoopPhi()) {
1858 // We do not know yet if the value is known for all back edges. Record for future processing.
1859 loads_requiring_loop_phi_.insert(std::make_pair(instruction, record));
1860 } else {
1861 // This load can be eliminated but we may need to construct non-loop Phis.
1862 if (record.value.NeedsNonLoopPhi()) {
1863 MaterializeNonLoopPhis(record.value.GetPhiPlaceholder(), instruction->GetType());
1864 record.value = Replacement(record.value);
1865 }
1866 HInstruction* heap_value = FindSubstitute(record.value.GetInstruction());
1867 AddRemovedLoad(instruction, heap_value);
Vladimir Marko3224f382020-06-23 14:19:53 +01001868 }
1869}
1870
1871void LSEVisitor::VisitSetLocation(HInstruction* instruction, size_t idx, HInstruction* value) {
1872 DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound);
1873 DCHECK(!IsStore(value)) << value->DebugName();
Alex Light3a73ffb2021-01-25 14:11:05 +00001874 if (instruction->IsFieldAccess()) {
1875 RecordFieldInfo(&instruction->GetFieldInfo(), idx);
1876 }
Vladimir Marko3224f382020-06-23 14:19:53 +01001877 // value may already have a substitute.
1878 value = FindSubstitute(value);
1879 HBasicBlock* block = instruction->GetBlock();
1880 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
1881 ValueRecord& record = heap_values[idx];
Santiago Aboy Solanes872ec722022-02-18 14:10:25 +00001882 DCHECK_IMPLIES(record.value.IsInstruction(),
1883 FindSubstitute(record.value.GetInstruction()) == record.value.GetInstruction());
Vladimir Marko3224f382020-06-23 14:19:53 +01001884
1885 if (record.value.Equals(value)) {
1886 // Store into the heap location with the same value.
1887 // This store can be eliminated right away.
1888 block->RemoveInstruction(instruction);
1889 return;
1890 }
1891
1892 store_records_.insert(std::make_pair(instruction, StoreRecord{record, value}));
1893 loads_and_stores_.push_back({ instruction, idx });
1894
1895 // If the `record.stored_by` specified a store from this block, it shall be removed
1896 // at the end, except for throwing ArraySet; it cannot be marked for keeping in
1897 // `kept_stores_` anymore after we update the `record.stored_by` below.
1898 DCHECK(!record.stored_by.IsInstruction() ||
1899 record.stored_by.GetInstruction()->GetBlock() != block ||
1900 record.stored_by.GetInstruction()->CanThrow() ||
1901 !kept_stores_.IsBitSet(record.stored_by.GetInstruction()->GetId()));
1902
1903 if (instruction->CanThrow()) {
1904 // Previous stores can become visible.
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001905 HandleThrowingInstruction(instruction);
Vladimir Marko3224f382020-06-23 14:19:53 +01001906 // We cannot remove a possibly throwing store.
1907 // After marking it as kept, it does not matter if we track it in `stored_by` or not.
1908 kept_stores_.SetBit(instruction->GetId());
1909 }
1910
1911 // Update the record.
1912 auto it = loads_requiring_loop_phi_.find(value);
1913 if (it != loads_requiring_loop_phi_.end()) {
1914 // Propapate the Phi placeholder to the record.
1915 record.value = it->second.value;
1916 DCHECK(record.value.NeedsLoopPhi());
1917 } else {
1918 record.value = Value::ForInstruction(value);
1919 }
1920 // Track the store in the value record. If the value is loaded or needed after
1921 // return/deoptimization later, this store isn't really redundant.
1922 record.stored_by = Value::ForInstruction(instruction);
1923
1924 // This store may kill values in other heap locations due to aliasing.
1925 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
1926 if (i == idx ||
1927 heap_values[i].value.IsUnknown() ||
1928 CanValueBeKeptIfSameAsNew(heap_values[i].value, value, instruction) ||
1929 !heap_location_collector_.MayAlias(i, idx)) {
1930 continue;
1931 }
1932 // Kill heap locations that may alias and keep previous stores to these locations.
1933 KeepStores(heap_values[i].stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -08001934 heap_values[i].stored_by = Value::PureUnknown();
Alex Light3a73ffb2021-01-25 14:11:05 +00001935 heap_values[i].value = Value::PartialUnknown(heap_values[i].value);
Vladimir Marko3224f382020-06-23 14:19:53 +01001936 }
1937}
1938
1939void LSEVisitor::VisitBasicBlock(HBasicBlock* block) {
1940 // Populate the heap_values array for this block.
1941 // TODO: try to reuse the heap_values array from one predecessor if possible.
1942 if (block->IsLoopHeader()) {
1943 PrepareLoopRecords(block);
1944 } else {
1945 MergePredecessorRecords(block);
1946 }
1947 // Visit instructions.
1948 HGraphVisitor::VisitBasicBlock(block);
1949}
1950
Vladimir Markodac82392021-05-10 15:44:24 +00001951bool LSEVisitor::MayAliasOnBackEdge(HBasicBlock* loop_header, size_t idx1, size_t idx2) const {
1952 DCHECK_NE(idx1, idx2);
1953 DCHECK(loop_header->IsLoopHeader());
1954 if (heap_location_collector_.MayAlias(idx1, idx2)) {
1955 return true;
1956 }
1957 // For array locations with index defined inside the loop, include
1958 // all other locations in the array, even those that LSA declares
1959 // non-aliasing, such as `a[i]` and `a[i + 1]`, as they may actually
1960 // refer to the same locations for different iterations. (LSA's
1961 // `ComputeMayAlias()` does not consider different loop iterations.)
1962 HeapLocation* loc1 = heap_location_collector_.GetHeapLocation(idx1);
1963 HeapLocation* loc2 = heap_location_collector_.GetHeapLocation(idx2);
1964 if (loc1->IsArray() &&
1965 loc2->IsArray() &&
1966 HeapLocationCollector::CanReferencesAlias(loc1->GetReferenceInfo(),
1967 loc2->GetReferenceInfo())) {
1968 HLoopInformation* loop_info = loop_header->GetLoopInformation();
1969 if (loop_info->Contains(*loc1->GetIndex()->GetBlock()) ||
1970 loop_info->Contains(*loc2->GetIndex()->GetBlock())) {
1971 // Consider the locations aliasing. Do not optimize the case where both indexes
1972 // are loop invariants defined inside the loop, rely on LICM to pull them out.
1973 return true;
1974 }
1975 }
1976 return false;
1977}
1978
Vladimir Marko3224f382020-06-23 14:19:53 +01001979bool LSEVisitor::TryReplacingLoopPhiPlaceholderWithDefault(
Alex Lightef28d242020-11-17 20:21:51 -08001980 PhiPlaceholder phi_placeholder,
Vladimir Marko3224f382020-06-23 14:19:53 +01001981 DataType::Type type,
Alex Lightef28d242020-11-17 20:21:51 -08001982 /*inout*/ ArenaBitVector* phi_placeholders_to_materialize) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001983 // Use local allocator to reduce peak memory usage.
1984 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
1985 ArenaBitVector visited(&allocator,
Alex Lightef28d242020-11-17 20:21:51 -08001986 /*start_bits=*/ num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01001987 /*expandable=*/ false,
1988 kArenaAllocLSE);
1989 visited.ClearAllBits();
Alex Lightef28d242020-11-17 20:21:51 -08001990 ScopedArenaVector<PhiPlaceholder> work_queue(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01001991
1992 // Use depth first search to check if any non-Phi input is unknown.
1993 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
1994 size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
1995 visited.SetBit(PhiPlaceholderIndex(phi_placeholder));
1996 work_queue.push_back(phi_placeholder);
1997 while (!work_queue.empty()) {
Alex Lightef28d242020-11-17 20:21:51 -08001998 PhiPlaceholder current_phi_placeholder = work_queue.back();
Vladimir Marko3224f382020-06-23 14:19:53 +01001999 work_queue.pop_back();
Alex Lightef28d242020-11-17 20:21:51 -08002000 HBasicBlock* block = blocks[current_phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002001 DCHECK_GE(block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002002 size_t idx = current_phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002003 for (HBasicBlock* predecessor : block->GetPredecessors()) {
2004 Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
2005 if (value.NeedsPhi()) {
2006 // Visit the predecessor Phi placeholder if it's not visited yet.
2007 if (!visited.IsBitSet(PhiPlaceholderIndex(value))) {
2008 visited.SetBit(PhiPlaceholderIndex(value));
2009 work_queue.push_back(value.GetPhiPlaceholder());
2010 }
2011 } else if (!value.Equals(Value::Default())) {
2012 return false; // Report failure.
2013 }
2014 }
2015 if (block->IsLoopHeader()) {
2016 // For back-edges we need to check all locations that write to the same array,
2017 // even those that LSA declares non-aliasing, such as `a[i]` and `a[i + 1]`
2018 // as they may actually refer to the same locations for different iterations.
2019 for (size_t i = 0; i != num_heap_locations; ++i) {
2020 if (i == idx ||
2021 heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo() !=
2022 heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo()) {
2023 continue;
2024 }
2025 for (HBasicBlock* predecessor : block->GetPredecessors()) {
2026 // Check if there were any writes to this location.
2027 // Note: We could simply process the values but due to the vector operation
2028 // carve-out (see `IsDefaultOrPhiAllowedForLoad()`), a vector load can cause
2029 // the value to change and not be equal to default. To work around this and
2030 // allow replacing the non-vector load of loop-invariant default values
2031 // anyway, skip over paths that do not have any writes.
2032 ValueRecord record = heap_values_for_[predecessor->GetBlockId()][i];
2033 while (record.stored_by.NeedsLoopPhi() &&
Alex Lightef28d242020-11-17 20:21:51 -08002034 blocks[record.stored_by.GetPhiPlaceholder().GetBlockId()]->IsLoopHeader()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002035 HLoopInformation* loop_info =
Alex Lightef28d242020-11-17 20:21:51 -08002036 blocks[record.stored_by.GetPhiPlaceholder().GetBlockId()]->GetLoopInformation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002037 record = heap_values_for_[loop_info->GetPreHeader()->GetBlockId()][i];
2038 }
2039 Value value = ReplacementOrValue(record.value);
2040 if (value.NeedsPhi()) {
2041 // Visit the predecessor Phi placeholder if it's not visited yet.
2042 if (!visited.IsBitSet(PhiPlaceholderIndex(value))) {
2043 visited.SetBit(PhiPlaceholderIndex(value));
2044 work_queue.push_back(value.GetPhiPlaceholder());
2045 }
2046 } else if (!value.Equals(Value::Default())) {
2047 return false; // Report failure.
2048 }
2049 }
2050 }
2051 }
2052 }
2053
2054 // Record replacement and report success.
2055 HInstruction* replacement = GetDefaultValue(type);
2056 for (uint32_t phi_placeholder_index : visited.Indexes()) {
2057 DCHECK(phi_placeholder_replacements_[phi_placeholder_index].IsInvalid());
2058 phi_placeholder_replacements_[phi_placeholder_index] = Value::ForInstruction(replacement);
2059 }
2060 phi_placeholders_to_materialize->Subtract(&visited);
2061 return true;
2062}
2063
2064bool LSEVisitor::TryReplacingLoopPhiPlaceholderWithSingleInput(
Alex Lightef28d242020-11-17 20:21:51 -08002065 PhiPlaceholder phi_placeholder,
2066 /*inout*/ ArenaBitVector* phi_placeholders_to_materialize) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002067 // Use local allocator to reduce peak memory usage.
2068 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2069 ArenaBitVector visited(&allocator,
Alex Lightef28d242020-11-17 20:21:51 -08002070 /*start_bits=*/ num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01002071 /*expandable=*/ false,
2072 kArenaAllocLSE);
2073 visited.ClearAllBits();
Alex Lightef28d242020-11-17 20:21:51 -08002074 ScopedArenaVector<PhiPlaceholder> work_queue(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01002075
2076 // Use depth first search to check if any non-Phi input is unknown.
2077 HInstruction* replacement = nullptr;
2078 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
2079 visited.SetBit(PhiPlaceholderIndex(phi_placeholder));
2080 work_queue.push_back(phi_placeholder);
2081 while (!work_queue.empty()) {
Alex Lightef28d242020-11-17 20:21:51 -08002082 PhiPlaceholder current_phi_placeholder = work_queue.back();
Vladimir Marko3224f382020-06-23 14:19:53 +01002083 work_queue.pop_back();
Alex Lightef28d242020-11-17 20:21:51 -08002084 HBasicBlock* current_block = blocks[current_phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002085 DCHECK_GE(current_block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002086 size_t idx = current_phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002087 for (HBasicBlock* predecessor : current_block->GetPredecessors()) {
2088 Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
2089 if (value.NeedsPhi()) {
2090 // Visit the predecessor Phi placeholder if it's not visited yet.
2091 if (!visited.IsBitSet(PhiPlaceholderIndex(value))) {
2092 visited.SetBit(PhiPlaceholderIndex(value));
2093 work_queue.push_back(value.GetPhiPlaceholder());
2094 }
2095 } else {
2096 if (!value.IsInstruction() ||
2097 (replacement != nullptr && replacement != value.GetInstruction())) {
2098 return false; // Report failure.
2099 }
2100 replacement = value.GetInstruction();
2101 }
2102 }
Vladimir Markodac82392021-05-10 15:44:24 +00002103 // While `TryReplacingLoopPhiPlaceholderWithDefault()` has special treatment
2104 // for back-edges, it is not needed here. When looking for a single input
2105 // instruction coming from before the loop, the array index must also be
2106 // defined before the loop and the aliasing analysis done by LSA is sufficient.
2107 // Any writes of a different value with an index that is not loop invariant
2108 // would invalidate the heap location in `VisitSetLocation()`.
Vladimir Marko3224f382020-06-23 14:19:53 +01002109 }
2110
2111 // Record replacement and report success.
2112 DCHECK(replacement != nullptr);
2113 for (uint32_t phi_placeholder_index : visited.Indexes()) {
2114 DCHECK(phi_placeholder_replacements_[phi_placeholder_index].IsInvalid());
2115 phi_placeholder_replacements_[phi_placeholder_index] = Value::ForInstruction(replacement);
2116 }
2117 phi_placeholders_to_materialize->Subtract(&visited);
2118 return true;
2119}
2120
Alex Lightef28d242020-11-17 20:21:51 -08002121std::optional<LSEVisitor::PhiPlaceholder> LSEVisitor::FindLoopPhisToMaterialize(
2122 PhiPlaceholder phi_placeholder,
2123 /*inout*/ ArenaBitVector* phi_placeholders_to_materialize,
Vladimir Marko3224f382020-06-23 14:19:53 +01002124 DataType::Type type,
2125 bool can_use_default_or_phi) {
2126 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid());
2127
2128 // Use local allocator to reduce peak memory usage.
2129 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
Alex Lightef28d242020-11-17 20:21:51 -08002130 ScopedArenaVector<PhiPlaceholder> work_queue(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01002131
2132 // Use depth first search to check if any non-Phi input is unknown.
2133 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
2134 phi_placeholders_to_materialize->ClearAllBits();
2135 phi_placeholders_to_materialize->SetBit(PhiPlaceholderIndex(phi_placeholder));
2136 work_queue.push_back(phi_placeholder);
2137 while (!work_queue.empty()) {
Alex Lightef28d242020-11-17 20:21:51 -08002138 PhiPlaceholder current_phi_placeholder = work_queue.back();
Vladimir Marko3224f382020-06-23 14:19:53 +01002139 work_queue.pop_back();
2140 if (!phi_placeholders_to_materialize->IsBitSet(PhiPlaceholderIndex(current_phi_placeholder))) {
2141 // Replaced by `TryReplacingLoopPhiPlaceholderWith{Default,SingleInput}()`.
2142 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(current_phi_placeholder)].Equals(
2143 Value::Default()));
2144 continue;
2145 }
Alex Lightef28d242020-11-17 20:21:51 -08002146 HBasicBlock* current_block = blocks[current_phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002147 DCHECK_GE(current_block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002148 size_t idx = current_phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002149 if (current_block->IsLoopHeader()) {
2150 // If the index is defined inside the loop, it may reference different elements of the
2151 // array on each iteration. Since we do not track if all elements of an array are set
2152 // to the same value explicitly, the only known value in pre-header can be the default
2153 // value from NewArray or a Phi placeholder depending on a default value from some outer
2154 // loop pre-header. This Phi placeholder can be replaced only by the default value.
2155 HInstruction* index = heap_location_collector_.GetHeapLocation(idx)->GetIndex();
2156 if (index != nullptr && current_block->GetLoopInformation()->Contains(*index->GetBlock())) {
2157 if (can_use_default_or_phi &&
2158 TryReplacingLoopPhiPlaceholderWithDefault(current_phi_placeholder,
2159 type,
2160 phi_placeholders_to_materialize)) {
2161 continue;
2162 } else {
2163 return current_phi_placeholder; // Report the loop Phi placeholder.
2164 }
2165 }
2166 // A similar situation arises with the index defined outside the loop if we cannot use
2167 // default values or Phis, i.e. for vector loads, as we can only replace the Phi
2168 // placeholder with a single instruction defined before the loop.
2169 if (!can_use_default_or_phi) {
Vladimir Markodac82392021-05-10 15:44:24 +00002170 DCHECK(index != nullptr); // Vector operations are array operations.
Vladimir Marko3224f382020-06-23 14:19:53 +01002171 if (TryReplacingLoopPhiPlaceholderWithSingleInput(current_phi_placeholder,
2172 phi_placeholders_to_materialize)) {
2173 continue;
2174 } else {
2175 return current_phi_placeholder; // Report the loop Phi placeholder.
2176 }
2177 }
2178 }
2179 for (HBasicBlock* predecessor : current_block->GetPredecessors()) {
Vladimir Markodac82392021-05-10 15:44:24 +00002180 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[predecessor->GetBlockId()];
2181 Value value = ReplacementOrValue(heap_values[idx].value);
Vladimir Marko3224f382020-06-23 14:19:53 +01002182 if (value.IsUnknown()) {
2183 // We cannot create a Phi for this loop Phi placeholder.
2184 return current_phi_placeholder; // Report the loop Phi placeholder.
2185 }
Vladimir Markodac82392021-05-10 15:44:24 +00002186 // For arrays, the location may have been clobbered by writes to other locations
2187 // in a loop that LSA does not consider aliasing, such as `a[i]` and `a[i + 1]`.
2188 if (current_block->IsLoopHeader() &&
2189 predecessor != current_block->GetLoopInformation()->GetPreHeader() &&
2190 heap_location_collector_.GetHeapLocation(idx)->GetIndex() != nullptr) {
2191 for (size_t i = 0, size = heap_values.size(); i != size; ++i) {
2192 if (i != idx &&
2193 !heap_values[i].stored_by.IsUnknown() &&
2194 MayAliasOnBackEdge(current_block, idx, i)) {
2195 // We cannot create a Phi for this loop Phi placeholder.
2196 return current_phi_placeholder;
2197 }
2198 }
2199 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002200 if (value.NeedsLoopPhi()) {
2201 // Visit the predecessor Phi placeholder if it's not visited yet.
2202 if (!phi_placeholders_to_materialize->IsBitSet(PhiPlaceholderIndex(value))) {
2203 phi_placeholders_to_materialize->SetBit(PhiPlaceholderIndex(value));
2204 work_queue.push_back(value.GetPhiPlaceholder());
Alex Light3a73ffb2021-01-25 14:11:05 +00002205 LSE_VLOG << "For materialization of " << phi_placeholder
2206 << " we need to materialize " << value;
Vladimir Marko3224f382020-06-23 14:19:53 +01002207 }
2208 }
2209 }
2210 }
2211
2212 // There are no unknown values feeding this Phi, so we can construct the Phis if needed.
Alex Lightef28d242020-11-17 20:21:51 -08002213 return std::nullopt;
Vladimir Marko3224f382020-06-23 14:19:53 +01002214}
2215
2216bool LSEVisitor::MaterializeLoopPhis(const ScopedArenaVector<size_t>& phi_placeholder_indexes,
Alex Light09e23372021-01-15 08:42:11 -08002217 DataType::Type type) {
Alex Light3a73ffb2021-01-25 14:11:05 +00002218 return MaterializeLoopPhis(ArrayRef<const size_t>(phi_placeholder_indexes), type);
2219}
2220
2221bool LSEVisitor::MaterializeLoopPhis(ArrayRef<const size_t> phi_placeholder_indexes,
2222 DataType::Type type) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002223 // Materialize all predecessors that do not need a loop Phi and determine if all inputs
2224 // other than loop Phis are the same.
2225 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
Alex Light1e414eb2021-01-15 08:38:18 -08002226 std::optional<Value> other_value = std::nullopt;
Vladimir Marko3224f382020-06-23 14:19:53 +01002227 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
Alex Lightef28d242020-11-17 20:21:51 -08002228 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_index);
2229 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002230 DCHECK_GE(block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002231 size_t idx = phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002232 for (HBasicBlock* predecessor : block->GetPredecessors()) {
2233 Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
Vladimir Marko642c8f62021-05-21 09:24:03 +01002234 if (value.NeedsNonLoopPhi()) {
Alex Light3a73ffb2021-01-25 14:11:05 +00002235 DCHECK(current_phase_ == Phase::kLoadElimination ||
2236 current_phase_ == Phase::kPartialElimination)
2237 << current_phase_;
Vladimir Marko3224f382020-06-23 14:19:53 +01002238 MaterializeNonLoopPhis(value.GetPhiPlaceholder(), type);
2239 value = Replacement(value);
2240 }
2241 if (!value.NeedsLoopPhi()) {
Alex Light1e414eb2021-01-15 08:38:18 -08002242 if (!other_value) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002243 // The first other value we found.
2244 other_value = value;
Alex Light1e414eb2021-01-15 08:38:18 -08002245 } else if (!other_value->IsInvalid()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002246 // Check if the current `value` differs from the previous `other_value`.
Alex Light1e414eb2021-01-15 08:38:18 -08002247 if (!value.Equals(*other_value)) {
2248 other_value = Value::Invalid();
Vladimir Marko3224f382020-06-23 14:19:53 +01002249 }
2250 }
2251 }
2252 }
2253 }
2254
Alex Light1e414eb2021-01-15 08:38:18 -08002255 DCHECK(other_value.has_value());
2256 if (!other_value->IsInvalid()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002257 HInstruction* replacement =
Alex Light1e414eb2021-01-15 08:38:18 -08002258 (other_value->IsDefault()) ? GetDefaultValue(type) : other_value->GetInstruction();
Vladimir Marko3224f382020-06-23 14:19:53 +01002259 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
2260 phi_placeholder_replacements_[phi_placeholder_index] = Value::ForInstruction(replacement);
2261 }
2262 return true;
2263 }
2264
2265 // If we're materializing only a single Phi, try to match it with an existing Phi.
2266 // (Matching multiple Phis would need investigation. It may be prohibitively slow.)
2267 // This also covers the case when after replacing a previous set of Phi placeholders,
2268 // we continue with a Phi placeholder that does not really need a loop Phi anymore.
2269 if (phi_placeholder_indexes.size() == 1u) {
Alex Lightef28d242020-11-17 20:21:51 -08002270 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_indexes[0]);
2271 size_t idx = phi_placeholder.GetHeapLocation();
2272 HBasicBlock* block = GetGraph()->GetBlocks()[phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002273 ArrayRef<HBasicBlock* const> predecessors(block->GetPredecessors());
2274 for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
2275 HInstruction* phi = phi_it.Current();
2276 DCHECK_EQ(phi->InputCount(), predecessors.size());
2277 ArrayRef<HUserRecord<HInstruction*>> phi_inputs = phi->GetInputRecords();
2278 auto cmp = [=](const HUserRecord<HInstruction*>& lhs, HBasicBlock* rhs) {
2279 Value value = ReplacementOrValue(heap_values_for_[rhs->GetBlockId()][idx].value);
2280 if (value.NeedsPhi()) {
2281 DCHECK(value.GetPhiPlaceholder() == phi_placeholder);
2282 return lhs.GetInstruction() == phi;
2283 } else {
2284 DCHECK(value.IsDefault() || value.IsInstruction());
2285 return value.Equals(lhs.GetInstruction());
2286 }
2287 };
2288 if (std::equal(phi_inputs.begin(), phi_inputs.end(), predecessors.begin(), cmp)) {
2289 phi_placeholder_replacements_[phi_placeholder_indexes[0]] = Value::ForInstruction(phi);
2290 return true;
2291 }
2292 }
2293 }
2294
Alex Light09e23372021-01-15 08:42:11 -08002295 if (current_phase_ == Phase::kStoreElimination) {
Vladimir Markoed29dce2020-08-21 17:25:16 +01002296 // We're not creating Phis during the final store elimination phase.
Vladimir Marko3224f382020-06-23 14:19:53 +01002297 return false;
2298 }
2299
2300 // There are different inputs to the Phi chain. Create the Phis.
2301 ArenaAllocator* allocator = GetGraph()->GetAllocator();
2302 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
Alex Lightef28d242020-11-17 20:21:51 -08002303 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_index);
2304 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
2305 CHECK_GE(block->GetPredecessors().size(), 2u);
Vladimir Marko3224f382020-06-23 14:19:53 +01002306 phi_placeholder_replacements_[phi_placeholder_index] = Value::ForInstruction(
2307 new (allocator) HPhi(allocator, kNoRegNumber, block->GetPredecessors().size(), type));
2308 }
2309 // Fill the Phi inputs.
2310 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
Alex Lightef28d242020-11-17 20:21:51 -08002311 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_index);
2312 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
2313 size_t idx = phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002314 HInstruction* phi = phi_placeholder_replacements_[phi_placeholder_index].GetInstruction();
Alex Light1e414eb2021-01-15 08:38:18 -08002315 DCHECK(DataType::IsTypeConversionImplicit(type, phi->GetType()))
2316 << "type=" << type << " vs phi-type=" << phi->GetType();
Vladimir Marko3224f382020-06-23 14:19:53 +01002317 for (size_t i = 0, size = block->GetPredecessors().size(); i != size; ++i) {
2318 HBasicBlock* predecessor = block->GetPredecessors()[i];
2319 Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
2320 HInstruction* input = value.IsDefault() ? GetDefaultValue(type) : value.GetInstruction();
2321 DCHECK_NE(input->GetType(), DataType::Type::kVoid);
2322 phi->SetRawInputAt(i, input);
Alex Light1e414eb2021-01-15 08:38:18 -08002323 DCHECK(DataType::IsTypeConversionImplicit(input->GetType(), phi->GetType()))
2324 << " input: " << input->GetType() << value << " phi: " << phi->GetType()
2325 << " request: " << type;
Vladimir Marko3224f382020-06-23 14:19:53 +01002326 }
2327 }
2328 // Add the Phis to their blocks.
2329 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
Alex Lightef28d242020-11-17 20:21:51 -08002330 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_index);
2331 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002332 block->AddPhi(phi_placeholder_replacements_[phi_placeholder_index].GetInstruction()->AsPhi());
2333 }
2334 if (type == DataType::Type::kReference) {
2335 ScopedArenaAllocator local_allocator(allocator_.GetArenaStack());
2336 ScopedArenaVector<HInstruction*> phis(local_allocator.Adapter(kArenaAllocLSE));
2337 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
2338 phis.push_back(phi_placeholder_replacements_[phi_placeholder_index].GetInstruction());
2339 }
2340 // Update reference type information. Pass invalid handles, these are not used for Phis.
2341 ReferenceTypePropagation rtp_fixup(GetGraph(),
Vladimir Marko3224f382020-06-23 14:19:53 +01002342 Handle<mirror::DexCache>(),
2343 /* is_first_run= */ false);
2344 rtp_fixup.Visit(ArrayRef<HInstruction* const>(phis));
2345 }
2346
2347 return true;
2348}
2349
2350bool LSEVisitor::MaterializeLoopPhis(const ArenaBitVector& phi_placeholders_to_materialize,
Alex Light09e23372021-01-15 08:42:11 -08002351 DataType::Type type) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002352 // Use local allocator to reduce peak memory usage.
2353 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2354
Vladimir Markoed29dce2020-08-21 17:25:16 +01002355 // We want to recognize when a subset of these loop Phis that do not need other
Vladimir Marko3224f382020-06-23 14:19:53 +01002356 // loop Phis, i.e. a transitive closure, has only one other instruction as an input,
2357 // i.e. that instruction can be used instead of each Phi in the set. See for example
2358 // Main.testLoop{5,6,7,8}() in the test 530-checker-lse. To do that, we shall
2359 // materialize these loop Phis from the smallest transitive closure.
2360
2361 // Construct a matrix of loop phi placeholder dependencies. To reduce the memory usage,
2362 // assign new indexes to the Phi placeholders, making the matrix dense.
Alex Lightef28d242020-11-17 20:21:51 -08002363 ScopedArenaVector<size_t> matrix_indexes(num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01002364 static_cast<size_t>(-1), // Invalid.
2365 allocator.Adapter(kArenaAllocLSE));
2366 ScopedArenaVector<size_t> phi_placeholder_indexes(allocator.Adapter(kArenaAllocLSE));
2367 size_t num_phi_placeholders = phi_placeholders_to_materialize.NumSetBits();
2368 phi_placeholder_indexes.reserve(num_phi_placeholders);
2369 for (uint32_t marker_index : phi_placeholders_to_materialize.Indexes()) {
2370 matrix_indexes[marker_index] = phi_placeholder_indexes.size();
2371 phi_placeholder_indexes.push_back(marker_index);
2372 }
2373 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
2374 ScopedArenaVector<ArenaBitVector*> dependencies(allocator.Adapter(kArenaAllocLSE));
2375 dependencies.reserve(num_phi_placeholders);
2376 for (size_t matrix_index = 0; matrix_index != num_phi_placeholders; ++matrix_index) {
2377 static constexpr bool kExpandable = false;
2378 dependencies.push_back(
2379 ArenaBitVector::Create(&allocator, num_phi_placeholders, kExpandable, kArenaAllocLSE));
2380 ArenaBitVector* current_dependencies = dependencies.back();
2381 current_dependencies->ClearAllBits();
2382 current_dependencies->SetBit(matrix_index); // Count the Phi placeholder as its own dependency.
Alex Lightef28d242020-11-17 20:21:51 -08002383 PhiPlaceholder current_phi_placeholder =
2384 GetPhiPlaceholderAt(phi_placeholder_indexes[matrix_index]);
2385 HBasicBlock* current_block = blocks[current_phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002386 DCHECK_GE(current_block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002387 size_t idx = current_phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002388 for (HBasicBlock* predecessor : current_block->GetPredecessors()) {
2389 Value pred_value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
2390 if (pred_value.NeedsLoopPhi()) {
2391 size_t pred_value_index = PhiPlaceholderIndex(pred_value);
2392 DCHECK(phi_placeholder_replacements_[pred_value_index].IsInvalid());
2393 DCHECK_NE(matrix_indexes[pred_value_index], static_cast<size_t>(-1));
2394 current_dependencies->SetBit(matrix_indexes[PhiPlaceholderIndex(pred_value)]);
2395 }
2396 }
2397 }
2398
2399 // Use the Floyd-Warshall algorithm to determine all transitive dependencies.
2400 for (size_t k = 0; k != num_phi_placeholders; ++k) {
2401 for (size_t i = 0; i != num_phi_placeholders; ++i) {
2402 for (size_t j = 0; j != num_phi_placeholders; ++j) {
2403 if (dependencies[i]->IsBitSet(k) && dependencies[k]->IsBitSet(j)) {
2404 dependencies[i]->SetBit(j);
2405 }
2406 }
2407 }
2408 }
2409
2410 // Count the number of transitive dependencies for each replaceable Phi placeholder.
2411 ScopedArenaVector<size_t> num_dependencies(allocator.Adapter(kArenaAllocLSE));
2412 num_dependencies.reserve(num_phi_placeholders);
2413 for (size_t matrix_index = 0; matrix_index != num_phi_placeholders; ++matrix_index) {
2414 num_dependencies.push_back(dependencies[matrix_index]->NumSetBits());
2415 }
2416
2417 // Pick a Phi placeholder with the smallest number of transitive dependencies and
2418 // materialize it and its dependencies. Repeat until we have materialized all.
2419 ScopedArenaVector<size_t> current_subset(allocator.Adapter(kArenaAllocLSE));
2420 current_subset.reserve(num_phi_placeholders);
2421 size_t remaining_phi_placeholders = num_phi_placeholders;
2422 while (remaining_phi_placeholders != 0u) {
2423 auto it = std::min_element(num_dependencies.begin(), num_dependencies.end());
2424 DCHECK_LE(*it, remaining_phi_placeholders);
2425 size_t current_matrix_index = std::distance(num_dependencies.begin(), it);
2426 ArenaBitVector* current_dependencies = dependencies[current_matrix_index];
2427 size_t current_num_dependencies = num_dependencies[current_matrix_index];
2428 current_subset.clear();
2429 for (uint32_t matrix_index : current_dependencies->Indexes()) {
2430 current_subset.push_back(phi_placeholder_indexes[matrix_index]);
2431 }
Alex Light09e23372021-01-15 08:42:11 -08002432 if (!MaterializeLoopPhis(current_subset, type)) {
2433 DCHECK_EQ(current_phase_, Phase::kStoreElimination);
Vladimir Marko3224f382020-06-23 14:19:53 +01002434 // This is the final store elimination phase and we shall not be able to eliminate any
2435 // stores that depend on the current subset, so mark these Phi placeholders unreplaceable.
2436 for (uint32_t matrix_index = 0; matrix_index != num_phi_placeholders; ++matrix_index) {
2437 if (dependencies[matrix_index]->IsBitSet(current_matrix_index)) {
2438 DCHECK(phi_placeholder_replacements_[phi_placeholder_indexes[matrix_index]].IsInvalid());
Alex Lightf5a84cb2021-01-15 08:35:38 -08002439 phi_placeholder_replacements_[phi_placeholder_indexes[matrix_index]] =
2440 Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01002441 }
2442 }
2443 return false;
2444 }
2445 for (uint32_t matrix_index = 0; matrix_index != num_phi_placeholders; ++matrix_index) {
2446 if (current_dependencies->IsBitSet(matrix_index)) {
2447 // Mark all dependencies as done by incrementing their `num_dependencies[.]`,
2448 // so that they shall never be the minimum again.
2449 num_dependencies[matrix_index] = num_phi_placeholders;
2450 } else if (dependencies[matrix_index]->IsBitSet(current_matrix_index)) {
2451 // Remove dependencies from other Phi placeholders.
2452 dependencies[matrix_index]->Subtract(current_dependencies);
2453 num_dependencies[matrix_index] -= current_num_dependencies;
2454 }
2455 }
2456 remaining_phi_placeholders -= current_num_dependencies;
2457 }
2458 return true;
2459}
2460
Alex Light3a73ffb2021-01-25 14:11:05 +00002461bool LSEVisitor::FullyMaterializePhi(PhiPlaceholder phi_placeholder, DataType::Type type) {
2462 ScopedArenaAllocator saa(GetGraph()->GetArenaStack());
2463 ArenaBitVector abv(&saa, num_phi_placeholders_, false, ArenaAllocKind::kArenaAllocLSE);
2464 auto res =
2465 FindLoopPhisToMaterialize(phi_placeholder, &abv, type, /* can_use_default_or_phi=*/true);
2466 CHECK(!res.has_value()) << *res;
2467 return MaterializeLoopPhis(abv, type);
2468}
2469
Alex Lightef28d242020-11-17 20:21:51 -08002470std::optional<LSEVisitor::PhiPlaceholder> LSEVisitor::TryToMaterializeLoopPhis(
2471 PhiPlaceholder phi_placeholder, HInstruction* load) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002472 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid());
2473
2474 // Use local allocator to reduce peak memory usage.
2475 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2476
2477 // Find Phi placeholders to materialize.
2478 ArenaBitVector phi_placeholders_to_materialize(
Alex Lightef28d242020-11-17 20:21:51 -08002479 &allocator, num_phi_placeholders_, /*expandable=*/ false, kArenaAllocLSE);
Vladimir Marko3224f382020-06-23 14:19:53 +01002480 phi_placeholders_to_materialize.ClearAllBits();
2481 DataType::Type type = load->GetType();
2482 bool can_use_default_or_phi = IsDefaultOrPhiAllowedForLoad(load);
Alex Lightef28d242020-11-17 20:21:51 -08002483 std::optional<PhiPlaceholder> loop_phi_with_unknown_input = FindLoopPhisToMaterialize(
Vladimir Marko3224f382020-06-23 14:19:53 +01002484 phi_placeholder, &phi_placeholders_to_materialize, type, can_use_default_or_phi);
Alex Lightef28d242020-11-17 20:21:51 -08002485 if (loop_phi_with_unknown_input) {
2486 DCHECK_GE(GetGraph()
2487 ->GetBlocks()[loop_phi_with_unknown_input->GetBlockId()]
2488 ->GetPredecessors()
2489 .size(),
2490 2u);
Vladimir Marko3224f382020-06-23 14:19:53 +01002491 return loop_phi_with_unknown_input; // Return failure.
2492 }
2493
Alex Light09e23372021-01-15 08:42:11 -08002494 DCHECK_EQ(current_phase_, Phase::kLoadElimination);
2495 bool success = MaterializeLoopPhis(phi_placeholders_to_materialize, type);
Vladimir Marko3224f382020-06-23 14:19:53 +01002496 DCHECK(success);
2497
2498 // Report success.
Alex Lightef28d242020-11-17 20:21:51 -08002499 return std::nullopt;
Vladimir Marko3224f382020-06-23 14:19:53 +01002500}
2501
2502// Re-process loads and stores in successors from the `loop_phi_with_unknown_input`. This may
2503// find one or more loads from `loads_requiring_loop_phi_` which cannot be replaced by Phis and
2504// propagate the load(s) as the new value(s) to successors; this may uncover new elimination
2505// opportunities. If we find no such load, we shall at least propagate an unknown value to some
2506// heap location that is needed by another loop Phi placeholder.
Alex Lightef28d242020-11-17 20:21:51 -08002507void LSEVisitor::ProcessLoopPhiWithUnknownInput(PhiPlaceholder loop_phi_with_unknown_input) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002508 size_t loop_phi_with_unknown_input_index = PhiPlaceholderIndex(loop_phi_with_unknown_input);
2509 DCHECK(phi_placeholder_replacements_[loop_phi_with_unknown_input_index].IsInvalid());
Alex Light86fe9b82020-11-16 16:54:01 +00002510 phi_placeholder_replacements_[loop_phi_with_unknown_input_index] =
2511 Value::MergedUnknown(loop_phi_with_unknown_input);
Vladimir Marko3224f382020-06-23 14:19:53 +01002512
Alex Lightef28d242020-11-17 20:21:51 -08002513 uint32_t block_id = loop_phi_with_unknown_input.GetBlockId();
Vladimir Marko3224f382020-06-23 14:19:53 +01002514 const ArenaVector<HBasicBlock*> reverse_post_order = GetGraph()->GetReversePostOrder();
2515 size_t reverse_post_order_index = 0;
2516 size_t reverse_post_order_size = reverse_post_order.size();
2517 size_t loads_and_stores_index = 0u;
2518 size_t loads_and_stores_size = loads_and_stores_.size();
2519
2520 // Skip blocks and instructions before the block containing the loop phi with unknown input.
2521 DCHECK_NE(reverse_post_order_index, reverse_post_order_size);
2522 while (reverse_post_order[reverse_post_order_index]->GetBlockId() != block_id) {
2523 HBasicBlock* block = reverse_post_order[reverse_post_order_index];
2524 while (loads_and_stores_index != loads_and_stores_size &&
2525 loads_and_stores_[loads_and_stores_index].load_or_store->GetBlock() == block) {
2526 ++loads_and_stores_index;
2527 }
2528 ++reverse_post_order_index;
2529 DCHECK_NE(reverse_post_order_index, reverse_post_order_size);
2530 }
2531
2532 // Use local allocator to reduce peak memory usage.
Vladimir Marko9e3fe992020-08-25 16:17:51 +01002533 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
Vladimir Marko3224f382020-06-23 14:19:53 +01002534 // Reuse one temporary vector for all remaining blocks.
2535 size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
Vladimir Marko9e3fe992020-08-25 16:17:51 +01002536 ScopedArenaVector<Value> local_heap_values(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01002537
2538 auto get_initial_value = [this](HBasicBlock* block, size_t idx) {
2539 Value value;
2540 if (block->IsLoopHeader()) {
2541 if (block->GetLoopInformation()->IsIrreducible()) {
Alex Lightef28d242020-11-17 20:21:51 -08002542 PhiPlaceholder placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Alex Light86fe9b82020-11-16 16:54:01 +00002543 value = Value::MergedUnknown(placeholder);
Vladimir Marko3224f382020-06-23 14:19:53 +01002544 } else {
2545 value = PrepareLoopValue(block, idx);
2546 }
2547 } else {
2548 value = MergePredecessorValues(block, idx);
2549 }
2550 DCHECK(value.IsUnknown() || ReplacementOrValue(value).Equals(value));
2551 return value;
2552 };
2553
2554 // Process remaining blocks and instructions.
2555 bool found_unreplaceable_load = false;
2556 bool replaced_heap_value_with_unknown = false;
2557 for (; reverse_post_order_index != reverse_post_order_size; ++reverse_post_order_index) {
2558 HBasicBlock* block = reverse_post_order[reverse_post_order_index];
2559 if (block->IsExitBlock()) {
2560 continue;
2561 }
2562
2563 // We shall reconstruct only the heap values that we need for processing loads and stores.
2564 local_heap_values.clear();
2565 local_heap_values.resize(num_heap_locations, Value::Invalid());
2566
2567 for (; loads_and_stores_index != loads_and_stores_size; ++loads_and_stores_index) {
2568 HInstruction* load_or_store = loads_and_stores_[loads_and_stores_index].load_or_store;
2569 size_t idx = loads_and_stores_[loads_and_stores_index].heap_location_index;
2570 if (load_or_store->GetBlock() != block) {
2571 break; // End of instructions from the current block.
2572 }
2573 bool is_store = load_or_store->GetSideEffects().DoesAnyWrite();
2574 DCHECK_EQ(is_store, IsStore(load_or_store));
2575 HInstruction* stored_value = nullptr;
2576 if (is_store) {
2577 auto it = store_records_.find(load_or_store);
2578 DCHECK(it != store_records_.end());
2579 stored_value = it->second.stored_value;
2580 }
2581 auto it = loads_requiring_loop_phi_.find(
2582 stored_value != nullptr ? stored_value : load_or_store);
2583 if (it == loads_requiring_loop_phi_.end()) {
2584 continue; // This load or store never needed a loop Phi.
2585 }
2586 ValueRecord& record = it->second;
Vladimir Marko9e3fe992020-08-25 16:17:51 +01002587 if (is_store) {
2588 // Process the store by updating `local_heap_values[idx]`. The last update shall
2589 // be propagated to the `heap_values[idx].value` if it previously needed a loop Phi
2590 // at the end of the block.
2591 Value replacement = ReplacementOrValue(record.value);
2592 if (replacement.NeedsLoopPhi()) {
2593 // No replacement yet, use the Phi placeholder from the load.
2594 DCHECK(record.value.NeedsLoopPhi());
2595 local_heap_values[idx] = record.value;
2596 } else {
2597 // If the load fetched a known value, use it, otherwise use the load.
2598 local_heap_values[idx] = Value::ForInstruction(
2599 replacement.IsUnknown() ? stored_value : replacement.GetInstruction());
2600 }
2601 } else {
Vladimir Marko3224f382020-06-23 14:19:53 +01002602 // Process the load unless it has previously been marked unreplaceable.
2603 if (record.value.NeedsLoopPhi()) {
2604 if (local_heap_values[idx].IsInvalid()) {
2605 local_heap_values[idx] = get_initial_value(block, idx);
2606 }
2607 if (local_heap_values[idx].IsUnknown()) {
2608 // This load cannot be replaced. Keep stores that feed the Phi placeholder
2609 // (no aliasing since then, otherwise the Phi placeholder would not have been
2610 // propagated as a value to this load) and store the load as the new heap value.
2611 found_unreplaceable_load = true;
2612 KeepStores(record.value);
Alex Light3a73ffb2021-01-25 14:11:05 +00002613 record.value = Value::MergedUnknown(record.value.GetPhiPlaceholder());
Vladimir Marko3224f382020-06-23 14:19:53 +01002614 local_heap_values[idx] = Value::ForInstruction(load_or_store);
2615 } else if (local_heap_values[idx].NeedsLoopPhi()) {
2616 // The load may still be replaced with a Phi later.
2617 DCHECK(local_heap_values[idx].Equals(record.value));
2618 } else {
2619 // This load can be eliminated but we may need to construct non-loop Phis.
2620 if (local_heap_values[idx].NeedsNonLoopPhi()) {
2621 MaterializeNonLoopPhis(local_heap_values[idx].GetPhiPlaceholder(),
2622 load_or_store->GetType());
2623 local_heap_values[idx] = Replacement(local_heap_values[idx]);
2624 }
2625 record.value = local_heap_values[idx];
2626 HInstruction* heap_value = local_heap_values[idx].GetInstruction();
2627 AddRemovedLoad(load_or_store, heap_value);
Vladimir Marko3224f382020-06-23 14:19:53 +01002628 }
2629 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002630 }
2631 }
2632
2633 // All heap values that previously needed a loop Phi at the end of the block
2634 // need to be updated for processing successors.
2635 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
2636 for (size_t idx = 0; idx != num_heap_locations; ++idx) {
2637 if (heap_values[idx].value.NeedsLoopPhi()) {
2638 if (local_heap_values[idx].IsValid()) {
2639 heap_values[idx].value = local_heap_values[idx];
2640 } else {
2641 heap_values[idx].value = get_initial_value(block, idx);
2642 }
2643 if (heap_values[idx].value.IsUnknown()) {
2644 replaced_heap_value_with_unknown = true;
2645 }
2646 }
2647 }
2648 }
2649 DCHECK(found_unreplaceable_load || replaced_heap_value_with_unknown);
2650}
2651
2652void LSEVisitor::ProcessLoadsRequiringLoopPhis() {
2653 // Note: The vector operations carve-out (see `IsDefaultOrPhiAllowedForLoad()`) can possibly
2654 // make the result of the processing depend on the order in which we process these loads.
2655 // To make sure the result is deterministic, iterate over `loads_and_stores_` instead of the
2656 // `loads_requiring_loop_phi_` indexed by non-deterministic pointers.
2657 for (const LoadStoreRecord& load_store_record : loads_and_stores_) {
2658 auto it = loads_requiring_loop_phi_.find(load_store_record.load_or_store);
2659 if (it == loads_requiring_loop_phi_.end()) {
2660 continue;
2661 }
2662 HInstruction* load = it->first;
2663 ValueRecord& record = it->second;
2664 while (record.value.NeedsLoopPhi() &&
2665 phi_placeholder_replacements_[PhiPlaceholderIndex(record.value)].IsInvalid()) {
Alex Lightef28d242020-11-17 20:21:51 -08002666 std::optional<PhiPlaceholder> loop_phi_with_unknown_input =
Vladimir Marko3224f382020-06-23 14:19:53 +01002667 TryToMaterializeLoopPhis(record.value.GetPhiPlaceholder(), load);
Alex Lightef28d242020-11-17 20:21:51 -08002668 DCHECK_EQ(loop_phi_with_unknown_input.has_value(),
Vladimir Marko3224f382020-06-23 14:19:53 +01002669 phi_placeholder_replacements_[PhiPlaceholderIndex(record.value)].IsInvalid());
Alex Lightef28d242020-11-17 20:21:51 -08002670 if (loop_phi_with_unknown_input) {
2671 DCHECK_GE(GetGraph()
2672 ->GetBlocks()[loop_phi_with_unknown_input->GetBlockId()]
2673 ->GetPredecessors()
2674 .size(),
2675 2u);
2676 ProcessLoopPhiWithUnknownInput(*loop_phi_with_unknown_input);
Vladimir Marko3224f382020-06-23 14:19:53 +01002677 }
2678 }
2679 // The load could have been marked as unreplaceable (and stores marked for keeping)
2680 // or marked for replacement with an instruction in ProcessLoopPhiWithUnknownInput().
2681 DCHECK(record.value.IsUnknown() || record.value.IsInstruction() || record.value.NeedsLoopPhi());
2682 if (record.value.NeedsLoopPhi()) {
2683 record.value = Replacement(record.value);
2684 HInstruction* heap_value = record.value.GetInstruction();
2685 AddRemovedLoad(load, heap_value);
Vladimir Marko3224f382020-06-23 14:19:53 +01002686 }
2687 }
2688}
2689
2690void LSEVisitor::SearchPhiPlaceholdersForKeptStores() {
2691 ScopedArenaVector<uint32_t> work_queue(allocator_.Adapter(kArenaAllocLSE));
2692 size_t start_size = phi_placeholders_to_search_for_kept_stores_.NumSetBits();
2693 work_queue.reserve(((start_size * 3u) + 1u) / 2u); // Reserve 1.5x start size, rounded up.
2694 for (uint32_t index : phi_placeholders_to_search_for_kept_stores_.Indexes()) {
2695 work_queue.push_back(index);
2696 }
2697 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
Alex Light86fe9b82020-11-16 16:54:01 +00002698 std::optional<ArenaBitVector> not_kept_stores;
2699 if (stats_) {
2700 not_kept_stores.emplace(GetGraph()->GetAllocator(),
2701 kept_stores_.GetBitSizeOf(),
2702 false,
2703 ArenaAllocKind::kArenaAllocLSE);
2704 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002705 while (!work_queue.empty()) {
Alex Light86fe9b82020-11-16 16:54:01 +00002706 uint32_t cur_phi_idx = work_queue.back();
Alex Lightef28d242020-11-17 20:21:51 -08002707 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(cur_phi_idx);
Alex Light86fe9b82020-11-16 16:54:01 +00002708 // Only writes to partial-escapes need to be specifically kept.
2709 bool is_partial_kept_merged_unknown =
2710 kept_merged_unknowns_.IsBitSet(cur_phi_idx) &&
Alex Lightef28d242020-11-17 20:21:51 -08002711 heap_location_collector_.GetHeapLocation(phi_placeholder.GetHeapLocation())
Alex Light86fe9b82020-11-16 16:54:01 +00002712 ->GetReferenceInfo()
2713 ->IsPartialSingleton();
Vladimir Marko3224f382020-06-23 14:19:53 +01002714 work_queue.pop_back();
Alex Lightef28d242020-11-17 20:21:51 -08002715 size_t idx = phi_placeholder.GetHeapLocation();
2716 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
2717 DCHECK(block != nullptr) << cur_phi_idx << " phi: " << phi_placeholder
2718 << " (blocks: " << blocks.size() << ")";
Vladimir Marko3224f382020-06-23 14:19:53 +01002719 for (HBasicBlock* predecessor : block->GetPredecessors()) {
2720 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[predecessor->GetBlockId()];
Vladimir Marko0571d472020-09-22 10:14:39 +01002721 // For loop back-edges we must also preserve all stores to locations that
2722 // may alias with the location `idx`.
Vladimir Marko3224f382020-06-23 14:19:53 +01002723 // TODO: Add tests cases around this.
2724 bool is_back_edge =
2725 block->IsLoopHeader() && predecessor != block->GetLoopInformation()->GetPreHeader();
2726 size_t start = is_back_edge ? 0u : idx;
2727 size_t end = is_back_edge ? heap_values.size() : idx + 1u;
2728 for (size_t i = start; i != end; ++i) {
2729 Value stored_by = heap_values[i].stored_by;
Vladimir Markodac82392021-05-10 15:44:24 +00002730 if (!stored_by.IsUnknown() && (i == idx || MayAliasOnBackEdge(block, idx, i))) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002731 if (stored_by.NeedsPhi()) {
2732 size_t phi_placeholder_index = PhiPlaceholderIndex(stored_by);
Alex Light86fe9b82020-11-16 16:54:01 +00002733 if (is_partial_kept_merged_unknown) {
2734 // Propagate merged-unknown keep since otherwise this might look
2735 // like a partial escape we can remove.
2736 kept_merged_unknowns_.SetBit(phi_placeholder_index);
2737 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002738 if (!phi_placeholders_to_search_for_kept_stores_.IsBitSet(phi_placeholder_index)) {
2739 phi_placeholders_to_search_for_kept_stores_.SetBit(phi_placeholder_index);
2740 work_queue.push_back(phi_placeholder_index);
2741 }
2742 } else {
2743 DCHECK(IsStore(stored_by.GetInstruction()));
Alex Light86fe9b82020-11-16 16:54:01 +00002744 ReferenceInfo* ri = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
2745 DCHECK(ri != nullptr) << "No heap value for " << stored_by.GetInstruction()->DebugName()
2746 << " id: " << stored_by.GetInstruction()->GetId() << " block: "
2747 << stored_by.GetInstruction()->GetBlock()->GetBlockId();
2748 if (!is_partial_kept_merged_unknown && IsPartialNoEscape(predecessor, idx)) {
2749 if (not_kept_stores) {
2750 not_kept_stores->SetBit(stored_by.GetInstruction()->GetId());
2751 }
2752 } else {
2753 kept_stores_.SetBit(stored_by.GetInstruction()->GetId());
2754 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002755 }
2756 }
2757 }
2758 }
2759 }
Alex Light86fe9b82020-11-16 16:54:01 +00002760 if (not_kept_stores) {
2761 // a - b := (a & ~b)
2762 not_kept_stores->Subtract(&kept_stores_);
2763 auto num_removed = not_kept_stores->NumSetBits();
2764 MaybeRecordStat(stats_, MethodCompilationStat::kPartialStoreRemoved, num_removed);
2765 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002766}
2767
2768void LSEVisitor::UpdateValueRecordForStoreElimination(/*inout*/ValueRecord* value_record) {
2769 while (value_record->stored_by.IsInstruction() &&
2770 !kept_stores_.IsBitSet(value_record->stored_by.GetInstruction()->GetId())) {
2771 auto it = store_records_.find(value_record->stored_by.GetInstruction());
2772 DCHECK(it != store_records_.end());
2773 *value_record = it->second.old_value_record;
2774 }
2775 if (value_record->stored_by.NeedsPhi() &&
2776 !phi_placeholders_to_search_for_kept_stores_.IsBitSet(
2777 PhiPlaceholderIndex(value_record->stored_by))) {
2778 // Some stores feeding this heap location may have been eliminated. Use the `stored_by`
2779 // Phi placeholder to recalculate the actual value.
2780 value_record->value = value_record->stored_by;
2781 }
2782 value_record->value = ReplacementOrValue(value_record->value);
2783 if (value_record->value.NeedsNonLoopPhi()) {
2784 // Treat all Phi placeholders as requiring loop Phis at this point.
2785 // We do not want MaterializeLoopPhis() to call MaterializeNonLoopPhis().
2786 value_record->value = Value::ForLoopPhiPlaceholder(value_record->value.GetPhiPlaceholder());
2787 }
2788}
2789
Alex Lightef28d242020-11-17 20:21:51 -08002790void LSEVisitor::FindOldValueForPhiPlaceholder(PhiPlaceholder phi_placeholder,
Vladimir Marko3224f382020-06-23 14:19:53 +01002791 DataType::Type type) {
2792 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid());
2793
2794 // Use local allocator to reduce peak memory usage.
2795 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2796 ArenaBitVector visited(&allocator,
Alex Lightef28d242020-11-17 20:21:51 -08002797 /*start_bits=*/ num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01002798 /*expandable=*/ false,
2799 kArenaAllocLSE);
2800 visited.ClearAllBits();
2801
2802 // Find Phi placeholders to try and match against existing Phis or other replacement values.
2803 ArenaBitVector phi_placeholders_to_materialize(
Alex Lightef28d242020-11-17 20:21:51 -08002804 &allocator, num_phi_placeholders_, /*expandable=*/ false, kArenaAllocLSE);
Vladimir Marko3224f382020-06-23 14:19:53 +01002805 phi_placeholders_to_materialize.ClearAllBits();
Alex Lightef28d242020-11-17 20:21:51 -08002806 std::optional<PhiPlaceholder> loop_phi_with_unknown_input = FindLoopPhisToMaterialize(
2807 phi_placeholder, &phi_placeholders_to_materialize, type, /*can_use_default_or_phi=*/true);
2808 if (loop_phi_with_unknown_input) {
2809 DCHECK_GE(GetGraph()
2810 ->GetBlocks()[loop_phi_with_unknown_input->GetBlockId()]
2811 ->GetPredecessors()
2812 .size(),
2813 2u);
Vladimir Marko3224f382020-06-23 14:19:53 +01002814 // Mark the unreplacable placeholder as well as the input Phi placeholder as unreplaceable.
Alex Lightf5a84cb2021-01-15 08:35:38 -08002815 phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)] = Value::PureUnknown();
Alex Lightef28d242020-11-17 20:21:51 -08002816 phi_placeholder_replacements_[PhiPlaceholderIndex(*loop_phi_with_unknown_input)] =
Alex Lightf5a84cb2021-01-15 08:35:38 -08002817 Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01002818 return;
2819 }
2820
Alex Light09e23372021-01-15 08:42:11 -08002821 DCHECK_EQ(current_phase_, Phase::kStoreElimination);
2822 bool success = MaterializeLoopPhis(phi_placeholders_to_materialize, type);
Vladimir Marko3224f382020-06-23 14:19:53 +01002823 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsValid());
2824 DCHECK_EQ(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsUnknown(),
2825 !success);
2826}
2827
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002828struct ScopedRestoreHeapValues {
2829 public:
2830 ScopedRestoreHeapValues(ArenaStack* alloc,
2831 size_t num_heap_locs,
2832 ScopedArenaVector<ScopedArenaVector<LSEVisitor::ValueRecord>>& to_restore)
2833 : alloc_(alloc),
2834 updated_values_(alloc_.Adapter(kArenaAllocLSE)),
2835 to_restore_(to_restore) {
2836 updated_values_.reserve(num_heap_locs * to_restore_.size());
2837 }
2838
2839 ~ScopedRestoreHeapValues() {
2840 for (const auto& rec : updated_values_) {
2841 to_restore_[rec.blk_id][rec.heap_loc].value = rec.val_;
2842 }
2843 }
2844
2845 template<typename Func>
2846 void ForEachRecord(Func func) {
2847 for (size_t blk_id : Range(to_restore_.size())) {
2848 for (size_t heap_loc : Range(to_restore_[blk_id].size())) {
2849 LSEVisitor::ValueRecord* vr = &to_restore_[blk_id][heap_loc];
2850 LSEVisitor::Value initial = vr->value;
2851 func(vr);
2852 if (!vr->value.ExactEquals(initial)) {
2853 updated_values_.push_back({blk_id, heap_loc, initial});
2854 }
2855 }
2856 }
2857 }
2858
2859 private:
2860 struct UpdateRecord {
2861 size_t blk_id;
2862 size_t heap_loc;
2863 LSEVisitor::Value val_;
2864 };
2865 ScopedArenaAllocator alloc_;
2866 ScopedArenaVector<UpdateRecord> updated_values_;
2867 ScopedArenaVector<ScopedArenaVector<LSEVisitor::ValueRecord>>& to_restore_;
2868
2869 DISALLOW_COPY_AND_ASSIGN(ScopedRestoreHeapValues);
2870};
2871
Vladimir Marko3224f382020-06-23 14:19:53 +01002872void LSEVisitor::FindStoresWritingOldValues() {
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002873 // Partial LSE relies on knowing the real heap-values not the
2874 // store-replacement versions so we need to restore the map after removing
2875 // stores.
2876 ScopedRestoreHeapValues heap_vals(allocator_.GetArenaStack(),
2877 heap_location_collector_.GetNumberOfHeapLocations(),
2878 heap_values_for_);
Vladimir Marko3224f382020-06-23 14:19:53 +01002879 // The Phi placeholder replacements have so far been used for eliminating loads,
2880 // tracking values that would be stored if all stores were kept. As we want to
2881 // compare actual old values after removing unmarked stores, prune the Phi
2882 // placeholder replacements that can be fed by values we may not actually store.
2883 // Replacements marked as unknown can be kept as they are fed by some unknown
2884 // value and would end up as unknown again if we recalculated them.
2885 for (size_t i = 0, size = phi_placeholder_replacements_.size(); i != size; ++i) {
2886 if (!phi_placeholder_replacements_[i].IsUnknown() &&
2887 !phi_placeholders_to_search_for_kept_stores_.IsBitSet(i)) {
2888 phi_placeholder_replacements_[i] = Value::Invalid();
2889 }
2890 }
2891
2892 // Update heap values at end of blocks.
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002893 heap_vals.ForEachRecord([&](ValueRecord* rec) {
2894 UpdateValueRecordForStoreElimination(rec);
2895 });
2896
2897 if (kIsDebugBuild) {
2898 heap_vals.ForEachRecord([](ValueRecord* rec) {
2899 DCHECK(!rec->value.NeedsNonLoopPhi()) << rec->value;
2900 });
Vladimir Marko3224f382020-06-23 14:19:53 +01002901 }
2902
2903 // Use local allocator to reduce peak memory usage.
2904 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2905 // Mark the stores we want to eliminate in a separate bit vector.
2906 ArenaBitVector eliminated_stores(&allocator,
2907 /*start_bits=*/ GetGraph()->GetCurrentInstructionId(),
2908 /*expandable=*/ false,
2909 kArenaAllocLSE);
2910 eliminated_stores.ClearAllBits();
2911
2912 for (auto& entry : store_records_) {
2913 HInstruction* store = entry.first;
2914 StoreRecord& store_record = entry.second;
2915 if (!kept_stores_.IsBitSet(store->GetId())) {
2916 continue; // Ignore stores that are not kept.
2917 }
2918 UpdateValueRecordForStoreElimination(&store_record.old_value_record);
2919 if (store_record.old_value_record.value.NeedsPhi()) {
2920 DataType::Type type = store_record.stored_value->GetType();
2921 FindOldValueForPhiPlaceholder(store_record.old_value_record.value.GetPhiPlaceholder(), type);
2922 store_record.old_value_record.value = ReplacementOrValue(store_record.old_value_record.value);
2923 }
2924 DCHECK(!store_record.old_value_record.value.NeedsPhi());
2925 HInstruction* stored_value = FindSubstitute(store_record.stored_value);
2926 if (store_record.old_value_record.value.Equals(stored_value)) {
2927 eliminated_stores.SetBit(store->GetId());
2928 }
2929 }
2930
2931 // Commit the stores to eliminate by removing them from `kept_stores_`.
2932 kept_stores_.Subtract(&eliminated_stores);
2933}
2934
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002935void LSEVisitor::Run() {
2936 // 1. Process blocks and instructions in reverse post order.
Vladimir Marko3224f382020-06-23 14:19:53 +01002937 for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
2938 VisitBasicBlock(block);
2939 }
2940
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002941 // 2. Process loads that require loop Phis, trying to find/create replacements.
2942 current_phase_ = Phase::kLoadElimination;
2943 ProcessLoadsRequiringLoopPhis();
Vladimir Marko3224f382020-06-23 14:19:53 +01002944
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002945 // 3. Determine which stores to keep and which to eliminate.
2946 current_phase_ = Phase::kStoreElimination;
2947 // Finish marking stores for keeping.
2948 SearchPhiPlaceholdersForKeptStores();
Vladimir Marko3224f382020-06-23 14:19:53 +01002949
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002950 // Find stores that write the same value as is already present in the location.
2951 FindStoresWritingOldValues();
Vladimir Marko3224f382020-06-23 14:19:53 +01002952
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002953 // 4. Replace loads and remove unnecessary stores and singleton allocations.
2954 FinishFullLSE();
2955
2956 // 5. Move partial escapes down and fixup with PHIs.
2957 current_phase_ = Phase::kPartialElimination;
2958 MovePartialEscapes();
Alex Light3a73ffb2021-01-25 14:11:05 +00002959}
2960
2961// Clear unknown loop-phi results. Here we'll be able to use partial-unknowns so we need to
2962// retry all of them with more information about where they come from.
2963void LSEVisitor::PrepareForPartialPhiComputation() {
2964 std::replace_if(
2965 phi_placeholder_replacements_.begin(),
2966 phi_placeholder_replacements_.end(),
Alex Lightde7c9e12021-04-01 17:19:05 -07002967 [](const Value& val) { return !val.IsDefault() && !val.IsInstruction(); },
Alex Light3a73ffb2021-01-25 14:11:05 +00002968 Value::Invalid());
2969}
2970
2971class PartialLoadStoreEliminationHelper {
2972 public:
2973 PartialLoadStoreEliminationHelper(LSEVisitor* lse, ScopedArenaAllocator* alloc)
2974 : lse_(lse),
2975 alloc_(alloc),
2976 new_ref_phis_(alloc_->Adapter(kArenaAllocLSE)),
2977 heap_refs_(alloc_->Adapter(kArenaAllocLSE)),
2978 max_preds_per_block_((*std::max_element(GetGraph()->GetActiveBlocks().begin(),
2979 GetGraph()->GetActiveBlocks().end(),
2980 [](HBasicBlock* a, HBasicBlock* b) {
2981 return a->GetNumberOfPredecessors() <
2982 b->GetNumberOfPredecessors();
2983 }))
2984 ->GetNumberOfPredecessors()),
2985 materialization_blocks_(GetGraph()->GetBlocks().size() * max_preds_per_block_,
2986 nullptr,
2987 alloc_->Adapter(kArenaAllocLSE)),
2988 first_materialization_block_id_(GetGraph()->GetBlocks().size()) {
Vladimir Marko5c824932021-06-02 15:54:17 +01002989 size_t num_partial_singletons = lse_->heap_location_collector_.CountPartialSingletons();
2990 heap_refs_.reserve(num_partial_singletons);
2991 new_ref_phis_.reserve(num_partial_singletons * GetGraph()->GetBlocks().size());
Alex Light3a73ffb2021-01-25 14:11:05 +00002992 CollectInterestingHeapRefs();
2993 }
2994
2995 ~PartialLoadStoreEliminationHelper() {
2996 if (heap_refs_.empty()) {
2997 return;
2998 }
2999 ReferenceTypePropagation rtp_fixup(GetGraph(),
Alex Light3a73ffb2021-01-25 14:11:05 +00003000 Handle<mirror::DexCache>(),
3001 /* is_first_run= */ false);
3002 rtp_fixup.Visit(ArrayRef<HInstruction* const>(new_ref_phis_));
3003 GetGraph()->ClearLoopInformation();
3004 GetGraph()->ClearDominanceInformation();
3005 GetGraph()->ClearReachabilityInformation();
3006 GetGraph()->BuildDominatorTree();
3007 GetGraph()->ComputeReachabilityInformation();
3008 }
3009
3010 class IdxToHeapLoc {
3011 public:
3012 explicit IdxToHeapLoc(const HeapLocationCollector* hlc) : collector_(hlc) {}
3013 HeapLocation* operator()(size_t idx) const {
3014 return collector_->GetHeapLocation(idx);
3015 }
3016
3017 private:
3018 const HeapLocationCollector* collector_;
3019 };
3020
3021
3022 class HeapReferenceData {
3023 public:
3024 using LocIterator = IterationRange<TransformIterator<BitVector::IndexIterator, IdxToHeapLoc>>;
3025 HeapReferenceData(PartialLoadStoreEliminationHelper* helper,
3026 HNewInstance* new_inst,
3027 const ExecutionSubgraph* subgraph,
3028 ScopedArenaAllocator* alloc)
3029 : new_instance_(new_inst),
3030 helper_(helper),
3031 heap_locs_(alloc,
3032 helper->lse_->heap_location_collector_.GetNumberOfHeapLocations(),
3033 /* expandable= */ false,
3034 kArenaAllocLSE),
3035 materializations_(
3036 // We generally won't need to create too many materialization blocks and we can expand
3037 // this as needed so just start off with 2x.
3038 2 * helper->lse_->GetGraph()->GetBlocks().size(),
3039 nullptr,
3040 alloc->Adapter(kArenaAllocLSE)),
3041 collector_(helper->lse_->heap_location_collector_),
3042 subgraph_(subgraph) {}
3043
3044 LocIterator IterateLocations() {
3045 auto idxs = heap_locs_.Indexes();
3046 return MakeTransformRange(idxs, IdxToHeapLoc(&collector_));
3047 }
3048
3049 void AddHeapLocation(size_t idx) {
3050 heap_locs_.SetBit(idx);
3051 }
3052
3053 const ExecutionSubgraph* GetNoEscapeSubgraph() const {
3054 return subgraph_;
3055 }
3056
3057 bool IsPostEscape(HBasicBlock* blk) {
3058 return std::any_of(
3059 subgraph_->GetExcludedCohorts().cbegin(),
3060 subgraph_->GetExcludedCohorts().cend(),
3061 [&](const ExecutionSubgraph::ExcludedCohort& ec) { return ec.PrecedesBlock(blk); });
3062 }
3063
3064 bool InEscapeCohort(HBasicBlock* blk) {
3065 return std::any_of(
3066 subgraph_->GetExcludedCohorts().cbegin(),
3067 subgraph_->GetExcludedCohorts().cend(),
3068 [&](const ExecutionSubgraph::ExcludedCohort& ec) { return ec.ContainsBlock(blk); });
3069 }
3070
3071 bool BeforeAllEscapes(HBasicBlock* b) {
3072 return std::none_of(subgraph_->GetExcludedCohorts().cbegin(),
3073 subgraph_->GetExcludedCohorts().cend(),
3074 [&](const ExecutionSubgraph::ExcludedCohort& ec) {
3075 return ec.PrecedesBlock(b) || ec.ContainsBlock(b);
3076 });
3077 }
3078
3079 HNewInstance* OriginalNewInstance() const {
3080 return new_instance_;
3081 }
3082
3083 // Collect and replace all uses. We need to perform this twice since we will
3084 // generate PHIs and additional uses as we create the default-values for
3085 // pred-gets. These values might be other references that are also being
3086 // partially eliminated. By running just the replacement part again we are
3087 // able to avoid having to keep another whole in-progress partial map
3088 // around. Since we will have already handled all the other uses in the
3089 // first pass the second one will be quite fast.
3090 void FixupUses(bool first_pass) {
3091 ScopedArenaAllocator saa(GetGraph()->GetArenaStack());
3092 // Replace uses with materialized values.
3093 ScopedArenaVector<InstructionUse<HInstruction>> to_replace(saa.Adapter(kArenaAllocLSE));
3094 ScopedArenaVector<HInstruction*> to_remove(saa.Adapter(kArenaAllocLSE));
3095 // Do we need to add a constructor-fence.
3096 ScopedArenaVector<InstructionUse<HConstructorFence>> constructor_fences(
3097 saa.Adapter(kArenaAllocLSE));
3098 ScopedArenaVector<InstructionUse<HInstruction>> to_predicate(saa.Adapter(kArenaAllocLSE));
3099
3100 CollectReplacements(to_replace, to_remove, constructor_fences, to_predicate);
3101
3102 if (!first_pass) {
3103 // If another partial creates new references they can only be in Phis or pred-get defaults
3104 // so they must be in the to_replace group.
3105 DCHECK(to_predicate.empty());
3106 DCHECK(constructor_fences.empty());
3107 DCHECK(to_remove.empty());
3108 }
3109
3110 ReplaceInput(to_replace);
Alex Lightde7c9e12021-04-01 17:19:05 -07003111 RemoveAndReplaceInputs(to_remove);
Alex Light3a73ffb2021-01-25 14:11:05 +00003112 CreateConstructorFences(constructor_fences);
3113 PredicateInstructions(to_predicate);
3114
3115 CHECK(OriginalNewInstance()->GetUses().empty())
3116 << OriginalNewInstance()->GetUses() << ", " << OriginalNewInstance()->GetEnvUses();
3117 }
3118
3119 void AddMaterialization(HBasicBlock* blk, HInstruction* ins) {
3120 if (blk->GetBlockId() >= materializations_.size()) {
3121 // Make sure the materialization array is large enough, try to avoid
3122 // re-sizing too many times by giving extra space.
3123 materializations_.resize(blk->GetBlockId() * 2, nullptr);
3124 }
3125 DCHECK(materializations_[blk->GetBlockId()] == nullptr)
3126 << "Already have a materialization in block " << blk->GetBlockId() << ": "
3127 << *materializations_[blk->GetBlockId()] << " when trying to set materialization to "
3128 << *ins;
3129 materializations_[blk->GetBlockId()] = ins;
3130 LSE_VLOG << "In block " << blk->GetBlockId() << " materialization is " << *ins;
3131 helper_->NotifyNewMaterialization(ins);
3132 }
3133
3134 bool HasMaterialization(HBasicBlock* blk) const {
3135 return blk->GetBlockId() < materializations_.size() &&
3136 materializations_[blk->GetBlockId()] != nullptr;
3137 }
3138
3139 HInstruction* GetMaterialization(HBasicBlock* blk) const {
3140 if (materializations_.size() <= blk->GetBlockId() ||
3141 materializations_[blk->GetBlockId()] == nullptr) {
3142 // This must be a materialization block added after the partial LSE of
3143 // the current reference finished. Since every edge can only have at
3144 // most one materialization block added to it we can just check the
3145 // blocks predecessor.
3146 DCHECK(helper_->IsMaterializationBlock(blk));
3147 blk = helper_->FindDominatingNonMaterializationBlock(blk);
3148 DCHECK(!helper_->IsMaterializationBlock(blk));
3149 }
3150 DCHECK_GT(materializations_.size(), blk->GetBlockId());
3151 DCHECK(materializations_[blk->GetBlockId()] != nullptr);
3152 return materializations_[blk->GetBlockId()];
3153 }
3154
3155 void GenerateMaterializationValueFromPredecessors(HBasicBlock* blk) {
3156 DCHECK(std::none_of(GetNoEscapeSubgraph()->GetExcludedCohorts().begin(),
3157 GetNoEscapeSubgraph()->GetExcludedCohorts().end(),
3158 [&](const ExecutionSubgraph::ExcludedCohort& cohort) {
3159 return cohort.IsEntryBlock(blk);
3160 }));
3161 DCHECK(!HasMaterialization(blk));
3162 if (blk->IsExitBlock()) {
3163 return;
3164 } else if (blk->IsLoopHeader()) {
3165 // See comment in execution_subgraph.h. Currently we act as though every
3166 // allocation for partial elimination takes place in the entry block.
3167 // This simplifies the analysis by making it so any escape cohort
3168 // expands to contain any loops it is a part of. This is something that
3169 // we should rectify at some point. In either case however we can still
3170 // special case the loop-header since (1) currently the loop can't have
3171 // any merges between different cohort entries since the pre-header will
3172 // be the earliest place entry can happen and (2) even if the analysis
3173 // is improved to consider lifetime of the object WRT loops any values
3174 // which would require loop-phis would have to make the whole loop
3175 // escape anyway.
3176 // This all means we can always use value from the pre-header when the
3177 // block is the loop-header and we didn't already create a
3178 // materialization block. (NB when we do improve the analysis we will
3179 // need to modify the materialization creation code to deal with this
3180 // correctly.)
3181 HInstruction* pre_header_val =
3182 GetMaterialization(blk->GetLoopInformation()->GetPreHeader());
3183 AddMaterialization(blk, pre_header_val);
3184 return;
3185 }
3186 ScopedArenaAllocator saa(GetGraph()->GetArenaStack());
3187 ScopedArenaVector<HInstruction*> pred_vals(saa.Adapter(kArenaAllocLSE));
3188 pred_vals.reserve(blk->GetNumberOfPredecessors());
3189 for (HBasicBlock* pred : blk->GetPredecessors()) {
3190 DCHECK(HasMaterialization(pred));
3191 pred_vals.push_back(GetMaterialization(pred));
3192 }
3193 GenerateMaterializationValueFromPredecessorsDirect(blk, pred_vals);
3194 }
3195
3196 void GenerateMaterializationValueFromPredecessorsForEntry(
3197 HBasicBlock* entry, const ScopedArenaVector<HInstruction*>& pred_vals) {
3198 DCHECK(std::any_of(GetNoEscapeSubgraph()->GetExcludedCohorts().begin(),
3199 GetNoEscapeSubgraph()->GetExcludedCohorts().end(),
3200 [&](const ExecutionSubgraph::ExcludedCohort& cohort) {
3201 return cohort.IsEntryBlock(entry);
3202 }));
3203 GenerateMaterializationValueFromPredecessorsDirect(entry, pred_vals);
3204 }
3205
3206 private:
3207 template <typename InstructionType>
3208 struct InstructionUse {
3209 InstructionType* instruction_;
3210 size_t index_;
3211 };
3212
3213 void ReplaceInput(const ScopedArenaVector<InstructionUse<HInstruction>>& to_replace) {
3214 for (auto& [ins, idx] : to_replace) {
3215 HInstruction* merged_inst = GetMaterialization(ins->GetBlock());
3216 if (ins->IsPhi() && merged_inst->IsPhi() && ins->GetBlock() == merged_inst->GetBlock()) {
3217 // Phis we just pass through the appropriate inputs.
3218 ins->ReplaceInput(merged_inst->InputAt(idx), idx);
3219 } else {
3220 ins->ReplaceInput(merged_inst, idx);
3221 }
3222 }
3223 }
3224
Alex Lightde7c9e12021-04-01 17:19:05 -07003225 void RemoveAndReplaceInputs(const ScopedArenaVector<HInstruction*>& to_remove) {
Alex Light3a73ffb2021-01-25 14:11:05 +00003226 for (HInstruction* ins : to_remove) {
3227 if (ins->GetBlock() == nullptr) {
3228 // Already dealt with.
3229 continue;
3230 }
3231 DCHECK(BeforeAllEscapes(ins->GetBlock())) << *ins;
3232 if (ins->IsInstanceFieldGet() || ins->IsInstanceFieldSet()) {
Alex Lightde7c9e12021-04-01 17:19:05 -07003233 bool instruction_has_users =
3234 ins->IsInstanceFieldGet() && (!ins->GetUses().empty() || !ins->GetEnvUses().empty());
3235 if (instruction_has_users) {
3236 // Make sure any remaining users of read are replaced.
3237 HInstruction* replacement =
3238 helper_->lse_->GetPartialValueAt(OriginalNewInstance(), ins);
3239 // NB ReplaceInput will remove a use from the list so this is
3240 // guaranteed to finish eventually.
3241 while (!ins->GetUses().empty()) {
3242 const HUseListNode<HInstruction*>& use = ins->GetUses().front();
3243 use.GetUser()->ReplaceInput(replacement, use.GetIndex());
3244 }
3245 while (!ins->GetEnvUses().empty()) {
3246 const HUseListNode<HEnvironment*>& use = ins->GetEnvUses().front();
3247 use.GetUser()->ReplaceInput(replacement, use.GetIndex());
3248 }
3249 } else {
3250 DCHECK(ins->GetUses().empty())
3251 << "Instruction has users!\n"
3252 << ins->DumpWithArgs() << "\nUsers are " << ins->GetUses();
3253 DCHECK(ins->GetEnvUses().empty())
3254 << "Instruction has users!\n"
3255 << ins->DumpWithArgs() << "\nUsers are " << ins->GetEnvUses();
3256 }
Alex Light3a73ffb2021-01-25 14:11:05 +00003257 ins->GetBlock()->RemoveInstruction(ins);
3258 } else {
3259 // Can only be obj == other, obj != other, obj == obj (!?) or, obj != obj (!?)
3260 // Since PHIs are escapes as far as LSE is concerned and we are before
3261 // any escapes these are the only 4 options.
3262 DCHECK(ins->IsEqual() || ins->IsNotEqual()) << *ins;
3263 HInstruction* replacement;
3264 if (UNLIKELY(ins->InputAt(0) == ins->InputAt(1))) {
3265 replacement = ins->IsEqual() ? GetGraph()->GetIntConstant(1)
3266 : GetGraph()->GetIntConstant(0);
3267 } else {
3268 replacement = ins->IsEqual() ? GetGraph()->GetIntConstant(0)
3269 : GetGraph()->GetIntConstant(1);
3270 }
3271 ins->ReplaceWith(replacement);
3272 ins->GetBlock()->RemoveInstruction(ins);
3273 }
3274 }
3275 }
3276
3277 void CreateConstructorFences(
3278 const ScopedArenaVector<InstructionUse<HConstructorFence>>& constructor_fences) {
3279 if (!constructor_fences.empty()) {
3280 uint32_t pc = constructor_fences.front().instruction_->GetDexPc();
3281 for (auto& [cf, idx] : constructor_fences) {
3282 if (cf->GetInputs().size() == 1) {
3283 cf->GetBlock()->RemoveInstruction(cf);
3284 } else {
3285 cf->RemoveInputAt(idx);
3286 }
3287 }
3288 for (const ExecutionSubgraph::ExcludedCohort& ec :
3289 GetNoEscapeSubgraph()->GetExcludedCohorts()) {
3290 for (HBasicBlock* blk : ec.EntryBlocks()) {
3291 for (HBasicBlock* materializer :
3292 Filter(MakeIterationRange(blk->GetPredecessors()),
3293 [&](HBasicBlock* blk) { return helper_->IsMaterializationBlock(blk); })) {
3294 HInstruction* new_cf = new (GetGraph()->GetAllocator()) HConstructorFence(
3295 GetMaterialization(materializer), pc, GetGraph()->GetAllocator());
3296 materializer->InsertInstructionBefore(new_cf, materializer->GetLastInstruction());
3297 }
3298 }
3299 }
3300 }
3301 }
3302
3303 void PredicateInstructions(
3304 const ScopedArenaVector<InstructionUse<HInstruction>>& to_predicate) {
3305 for (auto& [ins, idx] : to_predicate) {
3306 if (UNLIKELY(ins->GetBlock() == nullptr)) {
3307 // Already handled due to obj == obj;
3308 continue;
3309 } else if (ins->IsInstanceFieldGet()) {
3310 // IFieldGet[obj] => PredicatedIFieldGet[PartialValue, obj]
3311 HInstruction* new_fget = new (GetGraph()->GetAllocator()) HPredicatedInstanceFieldGet(
3312 ins->AsInstanceFieldGet(),
3313 GetMaterialization(ins->GetBlock()),
3314 helper_->lse_->GetPartialValueAt(OriginalNewInstance(), ins));
3315 MaybeRecordStat(helper_->lse_->stats_, MethodCompilationStat::kPredicatedLoadAdded);
3316 ins->GetBlock()->InsertInstructionBefore(new_fget, ins);
3317 if (ins->GetType() == DataType::Type::kReference) {
3318 // Reference info is the same
3319 new_fget->SetReferenceTypeInfo(ins->GetReferenceTypeInfo());
3320 }
Vladimir Marko06fb7fa2021-05-18 15:53:17 +00003321 // In this phase, substitute instructions are used only for the predicated get
3322 // default values which are used only if the partial singleton did not escape,
3323 // so the out value of the `new_fget` for the relevant cases is the same as
3324 // the default value.
3325 // TODO: Use the default value for materializing default values used by
3326 // other predicated loads to avoid some unnecessary Phis. (This shall
3327 // complicate the search for replacement in `ReplacementOrValue()`.)
Vladimir Marko807de1e2021-04-30 15:14:18 +00003328 DCHECK(helper_->lse_->substitute_instructions_for_loads_[ins->GetId()] == nullptr);
3329 helper_->lse_->substitute_instructions_for_loads_[ins->GetId()] = new_fget;
Alex Light3a73ffb2021-01-25 14:11:05 +00003330 ins->ReplaceWith(new_fget);
3331 ins->ReplaceEnvUsesDominatedBy(ins, new_fget);
3332 CHECK(ins->GetEnvUses().empty() && ins->GetUses().empty())
3333 << "Instruction: " << *ins << " uses: " << ins->GetUses()
3334 << ", env: " << ins->GetEnvUses();
3335 ins->GetBlock()->RemoveInstruction(ins);
3336 } else if (ins->IsInstanceFieldSet()) {
3337 // Any predicated sets shouldn't require movement.
3338 ins->AsInstanceFieldSet()->SetIsPredicatedSet();
3339 MaybeRecordStat(helper_->lse_->stats_, MethodCompilationStat::kPredicatedStoreAdded);
3340 HInstruction* merged_inst = GetMaterialization(ins->GetBlock());
3341 ins->ReplaceInput(merged_inst, idx);
3342 } else {
3343 // comparisons need to be split into 2.
3344 DCHECK(ins->IsEqual() || ins->IsNotEqual()) << "bad instruction " << *ins;
3345 bool this_is_first = idx == 0;
3346 if (ins->InputAt(0) == ins->InputAt(1)) {
3347 // This is a obj == obj or obj != obj.
3348 // No idea why anyone would do this but whatever.
3349 ins->ReplaceWith(GetGraph()->GetIntConstant(ins->IsEqual() ? 1 : 0));
3350 ins->GetBlock()->RemoveInstruction(ins);
3351 continue;
3352 } else {
3353 HInstruction* is_escaped = new (GetGraph()->GetAllocator())
3354 HNotEqual(GetMaterialization(ins->GetBlock()), GetGraph()->GetNullConstant());
3355 HInstruction* combine_inst =
3356 ins->IsEqual() ? static_cast<HInstruction*>(new (GetGraph()->GetAllocator()) HAnd(
3357 DataType::Type::kBool, is_escaped, ins))
3358 : static_cast<HInstruction*>(new (GetGraph()->GetAllocator()) HOr(
3359 DataType::Type::kBool, is_escaped, ins));
3360 ins->ReplaceInput(GetMaterialization(ins->GetBlock()), this_is_first ? 0 : 1);
3361 ins->GetBlock()->InsertInstructionBefore(is_escaped, ins);
3362 ins->GetBlock()->InsertInstructionAfter(combine_inst, ins);
3363 ins->ReplaceWith(combine_inst);
3364 combine_inst->ReplaceInput(ins, 1);
3365 }
3366 }
3367 }
3368 }
3369
3370 // Figure out all the instructions we need to
3371 // fixup/replace/remove/duplicate. Since this requires an iteration of an
3372 // intrusive linked list we want to do it only once and collect all the data
3373 // here.
3374 void CollectReplacements(
3375 ScopedArenaVector<InstructionUse<HInstruction>>& to_replace,
3376 ScopedArenaVector<HInstruction*>& to_remove,
3377 ScopedArenaVector<InstructionUse<HConstructorFence>>& constructor_fences,
3378 ScopedArenaVector<InstructionUse<HInstruction>>& to_predicate) {
3379 size_t size = new_instance_->GetUses().SizeSlow();
3380 to_replace.reserve(size);
3381 to_remove.reserve(size);
3382 constructor_fences.reserve(size);
3383 to_predicate.reserve(size);
3384 for (auto& use : new_instance_->GetUses()) {
3385 HBasicBlock* blk =
3386 helper_->FindDominatingNonMaterializationBlock(use.GetUser()->GetBlock());
3387 if (InEscapeCohort(blk)) {
3388 LSE_VLOG << "Replacing " << *new_instance_ << " use in " << *use.GetUser() << " with "
3389 << *GetMaterialization(blk);
3390 to_replace.push_back({use.GetUser(), use.GetIndex()});
3391 } else if (IsPostEscape(blk)) {
3392 LSE_VLOG << "User " << *use.GetUser() << " after escapes!";
3393 // The fields + cmp are normal uses. Phi can only be here if it was
3394 // generated by full LSE so whatever store+load that created the phi
3395 // is the escape.
3396 if (use.GetUser()->IsPhi()) {
3397 to_replace.push_back({use.GetUser(), use.GetIndex()});
3398 } else {
3399 DCHECK(use.GetUser()->IsFieldAccess() ||
3400 use.GetUser()->IsEqual() ||
3401 use.GetUser()->IsNotEqual())
3402 << *use.GetUser() << "@" << use.GetIndex();
3403 to_predicate.push_back({use.GetUser(), use.GetIndex()});
3404 }
3405 } else if (use.GetUser()->IsConstructorFence()) {
3406 LSE_VLOG << "User " << *use.GetUser() << " being moved to materialization!";
3407 constructor_fences.push_back({use.GetUser()->AsConstructorFence(), use.GetIndex()});
3408 } else {
3409 LSE_VLOG << "User " << *use.GetUser() << " not contained in cohort!";
3410 to_remove.push_back(use.GetUser());
3411 }
3412 }
3413 DCHECK_EQ(
3414 to_replace.size() + to_remove.size() + constructor_fences.size() + to_predicate.size(),
3415 size);
3416 }
3417
3418 void GenerateMaterializationValueFromPredecessorsDirect(
3419 HBasicBlock* blk, const ScopedArenaVector<HInstruction*>& pred_vals) {
3420 DCHECK(!pred_vals.empty());
3421 bool all_equal = std::all_of(pred_vals.begin() + 1, pred_vals.end(), [&](HInstruction* val) {
3422 return val == pred_vals.front();
3423 });
3424 if (LIKELY(all_equal)) {
3425 AddMaterialization(blk, pred_vals.front());
3426 } else {
3427 // Make a PHI for the predecessors.
3428 HPhi* phi = new (GetGraph()->GetAllocator()) HPhi(
3429 GetGraph()->GetAllocator(), kNoRegNumber, pred_vals.size(), DataType::Type::kReference);
3430 for (const auto& [ins, off] : ZipCount(MakeIterationRange(pred_vals))) {
3431 phi->SetRawInputAt(off, ins);
3432 }
3433 blk->AddPhi(phi);
3434 AddMaterialization(blk, phi);
3435 }
3436 }
3437
3438 HGraph* GetGraph() const {
3439 return helper_->GetGraph();
3440 }
3441
3442 HNewInstance* new_instance_;
3443 PartialLoadStoreEliminationHelper* helper_;
3444 ArenaBitVector heap_locs_;
3445 ScopedArenaVector<HInstruction*> materializations_;
3446 const HeapLocationCollector& collector_;
3447 const ExecutionSubgraph* subgraph_;
3448 };
3449
3450 ArrayRef<HeapReferenceData> GetHeapRefs() {
3451 return ArrayRef<HeapReferenceData>(heap_refs_);
3452 }
3453
3454 bool IsMaterializationBlock(HBasicBlock* blk) const {
3455 return blk->GetBlockId() >= first_materialization_block_id_;
3456 }
3457
3458 HBasicBlock* GetOrCreateMaterializationBlock(HBasicBlock* entry, size_t pred_num) {
3459 size_t idx = GetMaterializationBlockIndex(entry, pred_num);
3460 HBasicBlock* blk = materialization_blocks_[idx];
3461 if (blk == nullptr) {
3462 blk = new (GetGraph()->GetAllocator()) HBasicBlock(GetGraph());
3463 GetGraph()->AddBlock(blk);
3464 LSE_VLOG << "creating materialization block " << blk->GetBlockId() << " on edge "
3465 << entry->GetPredecessors()[pred_num]->GetBlockId() << "->" << entry->GetBlockId();
3466 blk->AddInstruction(new (GetGraph()->GetAllocator()) HGoto());
3467 materialization_blocks_[idx] = blk;
3468 }
3469 return blk;
3470 }
3471
3472 HBasicBlock* GetMaterializationBlock(HBasicBlock* entry, size_t pred_num) {
3473 HBasicBlock* out = materialization_blocks_[GetMaterializationBlockIndex(entry, pred_num)];
3474 DCHECK(out != nullptr) << "No materialization block for edge " << entry->GetBlockId() << "->"
3475 << entry->GetPredecessors()[pred_num]->GetBlockId();
3476 return out;
3477 }
3478
3479 IterationRange<ArenaVector<HBasicBlock*>::const_iterator> IterateMaterializationBlocks() {
3480 return MakeIterationRange(GetGraph()->GetBlocks().begin() + first_materialization_block_id_,
3481 GetGraph()->GetBlocks().end());
3482 }
3483
3484 void FixupPartialObjectUsers() {
3485 for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : GetHeapRefs()) {
3486 // Use the materialized instances to replace original instance
3487 ref_data.FixupUses(/*first_pass=*/true);
3488 CHECK(ref_data.OriginalNewInstance()->GetUses().empty())
3489 << ref_data.OriginalNewInstance()->GetUses() << ", "
3490 << ref_data.OriginalNewInstance()->GetEnvUses();
3491 }
3492 // This can cause new uses to be created due to the creation of phis/pred-get defaults
3493 for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : GetHeapRefs()) {
3494 // Only need to handle new phis/pred-get defaults. DCHECK that's all we find.
3495 ref_data.FixupUses(/*first_pass=*/false);
3496 CHECK(ref_data.OriginalNewInstance()->GetUses().empty())
3497 << ref_data.OriginalNewInstance()->GetUses() << ", "
3498 << ref_data.OriginalNewInstance()->GetEnvUses();
3499 }
3500 }
3501
3502 // Finds the first block which either is or dominates the given block which is
3503 // not a materialization block
3504 HBasicBlock* FindDominatingNonMaterializationBlock(HBasicBlock* blk) {
3505 if (LIKELY(!IsMaterializationBlock(blk))) {
3506 // Not a materialization block so itself.
3507 return blk;
3508 } else if (blk->GetNumberOfPredecessors() != 0) {
3509 // We're far enough along that the materialization blocks have been
3510 // inserted into the graph so no need to go searching.
3511 return blk->GetSinglePredecessor();
3512 }
3513 // Search through the materialization blocks to find where it will be
3514 // inserted.
3515 for (auto [mat, idx] : ZipCount(MakeIterationRange(materialization_blocks_))) {
3516 if (mat == blk) {
3517 size_t cur_pred_idx = idx % max_preds_per_block_;
3518 HBasicBlock* entry = GetGraph()->GetBlocks()[idx / max_preds_per_block_];
3519 return entry->GetPredecessors()[cur_pred_idx];
3520 }
3521 }
3522 LOG(FATAL) << "Unable to find materialization block position for " << blk->GetBlockId() << "!";
3523 return nullptr;
3524 }
3525
3526 void InsertMaterializationBlocks() {
3527 for (auto [mat, idx] : ZipCount(MakeIterationRange(materialization_blocks_))) {
3528 if (mat == nullptr) {
3529 continue;
3530 }
3531 size_t cur_pred_idx = idx % max_preds_per_block_;
3532 HBasicBlock* entry = GetGraph()->GetBlocks()[idx / max_preds_per_block_];
3533 HBasicBlock* pred = entry->GetPredecessors()[cur_pred_idx];
3534 mat->InsertBetween(pred, entry);
3535 LSE_VLOG << "Adding materialization block " << mat->GetBlockId() << " on edge "
3536 << pred->GetBlockId() << "->" << entry->GetBlockId();
3537 }
3538 }
3539
3540 // Replace any env-uses remaining of the partial singletons with the
3541 // appropriate phis and remove the instructions.
3542 void RemoveReplacedInstructions() {
3543 for (HeapReferenceData& ref_data : GetHeapRefs()) {
3544 CHECK(ref_data.OriginalNewInstance()->GetUses().empty())
3545 << ref_data.OriginalNewInstance()->GetUses() << ", "
3546 << ref_data.OriginalNewInstance()->GetEnvUses()
3547 << " inst is: " << ref_data.OriginalNewInstance();
3548 const auto& env_uses = ref_data.OriginalNewInstance()->GetEnvUses();
3549 while (!env_uses.empty()) {
3550 const HUseListNode<HEnvironment*>& use = env_uses.front();
3551 HInstruction* merged_inst =
3552 ref_data.GetMaterialization(use.GetUser()->GetHolder()->GetBlock());
3553 LSE_VLOG << "Replacing env use of " << *use.GetUser()->GetHolder() << "@" << use.GetIndex()
3554 << " with " << *merged_inst;
3555 use.GetUser()->ReplaceInput(merged_inst, use.GetIndex());
3556 }
3557 ref_data.OriginalNewInstance()->GetBlock()->RemoveInstruction(ref_data.OriginalNewInstance());
3558 }
3559 }
3560
3561 // We need to make sure any allocations dominate their environment uses.
3562 // Technically we could probably remove the env-uses and be fine but this is easy.
3563 void ReorderMaterializationsForEnvDominance() {
3564 for (HBasicBlock* blk : IterateMaterializationBlocks()) {
3565 ScopedArenaAllocator alloc(alloc_->GetArenaStack());
3566 ArenaBitVector still_unsorted(
3567 &alloc, GetGraph()->GetCurrentInstructionId(), false, kArenaAllocLSE);
3568 // This is guaranteed to be very short (since we will abandon LSE if there
3569 // are >= kMaxNumberOfHeapLocations (32) heap locations so that is the
3570 // absolute maximum size this list can be) so doing a selection sort is
3571 // fine. This avoids the need to do a complicated recursive check to
3572 // ensure transitivity for std::sort.
3573 ScopedArenaVector<HNewInstance*> materializations(alloc.Adapter(kArenaAllocLSE));
3574 materializations.reserve(GetHeapRefs().size());
3575 for (HInstruction* ins :
3576 MakeSTLInstructionIteratorRange(HInstructionIterator(blk->GetInstructions()))) {
3577 if (ins->IsNewInstance()) {
3578 materializations.push_back(ins->AsNewInstance());
3579 still_unsorted.SetBit(ins->GetId());
3580 }
3581 }
3582 using Iter = ScopedArenaVector<HNewInstance*>::iterator;
3583 Iter unsorted_start = materializations.begin();
3584 Iter unsorted_end = materializations.end();
3585 // selection sort. Required since the only check we can easily perform a
3586 // is-before-all-unsorted check.
3587 while (unsorted_start != unsorted_end) {
3588 bool found_instruction = false;
3589 for (Iter candidate = unsorted_start; candidate != unsorted_end; ++candidate) {
3590 HNewInstance* ni = *candidate;
3591 if (std::none_of(ni->GetAllEnvironments().cbegin(),
3592 ni->GetAllEnvironments().cend(),
3593 [&](const HEnvironment* env) {
3594 return std::any_of(
3595 env->GetEnvInputs().cbegin(),
3596 env->GetEnvInputs().cend(),
3597 [&](const HInstruction* env_element) {
3598 return env_element != nullptr &&
3599 still_unsorted.IsBitSet(env_element->GetId());
3600 });
3601 })) {
3602 still_unsorted.ClearBit(ni->GetId());
3603 std::swap(*unsorted_start, *candidate);
3604 ++unsorted_start;
3605 found_instruction = true;
3606 break;
3607 }
3608 }
3609 CHECK(found_instruction) << "Unable to select next materialization instruction."
3610 << " Environments have a dependency loop!";
3611 }
3612 // Reverse so we as we prepend them we end up with the correct order.
3613 auto reverse_iter = MakeIterationRange(materializations.rbegin(), materializations.rend());
3614 for (HNewInstance* ins : reverse_iter) {
3615 if (blk->GetFirstInstruction() != ins) {
3616 // Don't do checks since that makes sure the move is safe WRT
3617 // ins->CanBeMoved which for NewInstance is false.
3618 ins->MoveBefore(blk->GetFirstInstruction(), /*do_checks=*/false);
3619 }
3620 }
3621 }
3622 }
3623
3624 private:
3625 void CollectInterestingHeapRefs() {
3626 // Get all the partials we need to move around.
3627 for (size_t i = 0; i < lse_->heap_location_collector_.GetNumberOfHeapLocations(); ++i) {
3628 ReferenceInfo* ri = lse_->heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
3629 if (ri->IsPartialSingleton() &&
3630 ri->GetReference()->GetBlock() != nullptr &&
3631 ri->GetNoEscapeSubgraph()->ContainsBlock(ri->GetReference()->GetBlock())) {
3632 RecordHeapRefField(ri->GetReference()->AsNewInstance(), i);
3633 }
3634 }
3635 }
3636
3637 void RecordHeapRefField(HNewInstance* ni, size_t loc) {
3638 DCHECK(ni != nullptr);
3639 // This is likely to be very short so just do a linear search.
3640 auto it = std::find_if(heap_refs_.begin(), heap_refs_.end(), [&](HeapReferenceData& data) {
3641 return data.OriginalNewInstance() == ni;
3642 });
3643 HeapReferenceData& cur_ref =
3644 (it == heap_refs_.end())
3645 ? heap_refs_.emplace_back(this,
3646 ni,
3647 lse_->heap_location_collector_.GetHeapLocation(loc)
3648 ->GetReferenceInfo()
3649 ->GetNoEscapeSubgraph(),
3650 alloc_)
3651 : *it;
3652 cur_ref.AddHeapLocation(loc);
3653 }
3654
3655
3656 void NotifyNewMaterialization(HInstruction* ins) {
3657 if (ins->IsPhi()) {
3658 new_ref_phis_.push_back(ins->AsPhi());
3659 }
3660 }
3661
3662 size_t GetMaterializationBlockIndex(HBasicBlock* blk, size_t pred_num) const {
3663 DCHECK_LT(blk->GetBlockId(), first_materialization_block_id_)
3664 << "block is a materialization block!";
3665 DCHECK_LT(pred_num, max_preds_per_block_);
3666 return blk->GetBlockId() * max_preds_per_block_ + pred_num;
3667 }
3668
3669 HGraph* GetGraph() const {
3670 return lse_->GetGraph();
3671 }
3672
3673 LSEVisitor* lse_;
3674 ScopedArenaAllocator* alloc_;
3675 ScopedArenaVector<HInstruction*> new_ref_phis_;
3676 ScopedArenaVector<HeapReferenceData> heap_refs_;
3677 size_t max_preds_per_block_;
3678 // An array of (# of non-materialization blocks) * max_preds_per_block
3679 // arranged in block-id major order. Since we can only have at most one
3680 // materialization block on each edge this is the maximum possible number of
3681 // materialization blocks.
3682 ScopedArenaVector<HBasicBlock*> materialization_blocks_;
3683 size_t first_materialization_block_id_;
3684
3685 friend void LSEVisitor::MovePartialEscapes();
Alex Light3a73ffb2021-01-25 14:11:05 +00003686};
3687
3688// Work around c++ type checking annoyances with not being able to forward-declare inner types.
3689class HeapRefHolder
3690 : public std::reference_wrapper<PartialLoadStoreEliminationHelper::HeapReferenceData> {};
3691
3692HInstruction* LSEVisitor::SetupPartialMaterialization(PartialLoadStoreEliminationHelper& helper,
3693 HeapRefHolder&& holder,
3694 size_t pred_idx,
3695 HBasicBlock* entry) {
3696 PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data = holder.get();
3697 HBasicBlock* old_pred = entry->GetPredecessors()[pred_idx];
3698 HInstruction* new_inst = ref_data.OriginalNewInstance();
3699 if (UNLIKELY(!new_inst->GetBlock()->Dominates(entry))) {
3700 LSE_VLOG << "Initial materialization in non-dominating block " << entry->GetBlockId()
3701 << " is null!";
3702 return GetGraph()->GetNullConstant();
3703 }
3704 HBasicBlock* bb = helper.GetOrCreateMaterializationBlock(entry, pred_idx);
3705 CHECK(bb != nullptr) << "entry " << entry->GetBlockId() << " -> " << old_pred->GetBlockId();
3706 HNewInstance* repl_create = new_inst->Clone(GetGraph()->GetAllocator())->AsNewInstance();
3707 repl_create->SetPartialMaterialization();
3708 bb->InsertInstructionBefore(repl_create, bb->GetLastInstruction());
3709 repl_create->CopyEnvironmentFrom(new_inst->GetEnvironment());
3710 MaybeRecordStat(stats_, MethodCompilationStat::kPartialAllocationMoved);
3711 LSE_VLOG << "In blk " << bb->GetBlockId() << " initial materialization is " << *repl_create;
3712 ref_data.AddMaterialization(bb, repl_create);
3713 const FieldInfo* info = nullptr;
3714 for (const HeapLocation* loc : ref_data.IterateLocations()) {
3715 size_t loc_off = heap_location_collector_.GetHeapLocationIndex(loc);
3716 info = field_infos_[loc_off];
3717 DCHECK(loc->GetIndex() == nullptr);
3718 Value value = ReplacementOrValue(heap_values_for_[old_pred->GetBlockId()][loc_off].value);
Alex Lightde7c9e12021-04-01 17:19:05 -07003719 if (value.NeedsLoopPhi() || value.IsMergedUnknown()) {
Alex Light3a73ffb2021-01-25 14:11:05 +00003720 Value repl = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())];
Alex Light3a73ffb2021-01-25 14:11:05 +00003721 DCHECK(repl.IsDefault() || repl.IsInvalid() || repl.IsInstruction())
3722 << repl << " from " << value << " pred is " << old_pred->GetBlockId();
3723 if (!repl.IsInvalid()) {
3724 value = repl;
3725 } else {
3726 FullyMaterializePhi(value.GetPhiPlaceholder(), info->GetFieldType());
3727 value = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())];
3728 }
3729 } else if (value.NeedsNonLoopPhi()) {
3730 Value repl = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())];
3731 DCHECK(repl.IsDefault() || repl.IsInvalid() || repl.IsInstruction())
3732 << repl << " from " << value << " pred is " << old_pred->GetBlockId();
3733 if (!repl.IsInvalid()) {
3734 value = repl;
3735 } else {
3736 MaterializeNonLoopPhis(value.GetPhiPlaceholder(), info->GetFieldType());
3737 value = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())];
3738 }
3739 }
3740 DCHECK(value.IsDefault() || value.IsInstruction())
3741 << GetGraph()->PrettyMethod() << ": " << value;
3742
3743 if (!value.IsDefault() &&
3744 // shadow$_klass_ doesn't need to be manually initialized.
3745 MemberOffset(loc->GetOffset()) != mirror::Object::ClassOffset()) {
3746 CHECK(info != nullptr);
3747 HInstruction* set_value =
3748 new (GetGraph()->GetAllocator()) HInstanceFieldSet(repl_create,
3749 value.GetInstruction(),
3750 field_infos_[loc_off]->GetField(),
3751 loc->GetType(),
3752 MemberOffset(loc->GetOffset()),
3753 false,
3754 field_infos_[loc_off]->GetFieldIndex(),
3755 loc->GetDeclaringClassDefIndex(),
3756 field_infos_[loc_off]->GetDexFile(),
3757 0u);
3758 bb->InsertInstructionAfter(set_value, repl_create);
3759 LSE_VLOG << "Adding " << *set_value << " for materialization setup!";
3760 }
3761 }
3762 return repl_create;
3763}
3764
3765HInstruction* LSEVisitor::GetPartialValueAt(HNewInstance* orig_new_inst, HInstruction* read) {
3766 size_t loc = heap_location_collector_.GetFieldHeapLocation(orig_new_inst, &read->GetFieldInfo());
3767 Value pred = ReplacementOrValue(intermediate_values_.find(read)->second);
3768 LSE_VLOG << "using " << pred << " as default value for " << *read;
3769 if (pred.IsInstruction()) {
3770 return pred.GetInstruction();
3771 } else if (pred.IsMergedUnknown() || pred.NeedsPhi()) {
3772 FullyMaterializePhi(pred.GetPhiPlaceholder(),
3773 heap_location_collector_.GetHeapLocation(loc)->GetType());
3774 HInstruction* res = Replacement(pred).GetInstruction();
3775 LSE_VLOG << pred << " materialized to " << res->DumpWithArgs();
3776 return res;
Alex Lighte4f7fef2021-03-30 17:17:50 -07003777 } else if (pred.IsDefault()) {
3778 HInstruction* res = GetDefaultValue(read->GetType());
3779 LSE_VLOG << pred << " materialized to " << res->DumpWithArgs();
3780 return res;
Alex Light3a73ffb2021-01-25 14:11:05 +00003781 }
3782 LOG(FATAL) << "Unable to find unescaped value at " << read->DumpWithArgs()
Alex Lighte4f7fef2021-03-30 17:17:50 -07003783 << "! This should be impossible! Value is " << pred;
Alex Light3a73ffb2021-01-25 14:11:05 +00003784 UNREACHABLE();
3785}
3786
3787void LSEVisitor::MovePartialEscapes() {
3788 if (!ShouldPerformPartialLSE()) {
3789 return;
3790 }
3791
3792 ScopedArenaAllocator saa(allocator_.GetArenaStack());
3793 PartialLoadStoreEliminationHelper helper(this, &saa);
3794
3795 // Since for PHIs we now will have more information (since we know the object
3796 // hasn't escaped) we need to clear the old phi-replacements where we weren't
3797 // able to find the value.
3798 PrepareForPartialPhiComputation();
3799
3800 for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : helper.GetHeapRefs()) {
3801 LSE_VLOG << "Creating materializations for " << *ref_data.OriginalNewInstance();
3802 // Setup entry and exit blocks.
3803 for (const auto& excluded_cohort : ref_data.GetNoEscapeSubgraph()->GetExcludedCohorts()) {
3804 // Setup materialization blocks.
3805 for (HBasicBlock* entry : excluded_cohort.EntryBlocksReversePostOrder()) {
3806 // Setup entries.
3807 // TODO Assuming we correctly break critical edges every entry block
3808 // must have only a single predecessor so we could just put all this
3809 // stuff in there. OTOH simplifier can do it for us and this is simpler
3810 // to implement - giving clean separation between the original graph and
3811 // materialization blocks - so for now we might as well have these new
3812 // blocks.
3813 ScopedArenaAllocator pred_alloc(saa.GetArenaStack());
3814 ScopedArenaVector<HInstruction*> pred_vals(pred_alloc.Adapter(kArenaAllocLSE));
3815 pred_vals.reserve(entry->GetNumberOfPredecessors());
3816 for (const auto& [pred, pred_idx] :
3817 ZipCount(MakeIterationRange(entry->GetPredecessors()))) {
3818 DCHECK(!helper.IsMaterializationBlock(pred));
3819 if (excluded_cohort.IsEntryBlock(pred)) {
3820 pred_vals.push_back(ref_data.GetMaterialization(pred));
3821 continue;
3822 } else {
3823 pred_vals.push_back(SetupPartialMaterialization(helper, {ref_data}, pred_idx, entry));
3824 }
3825 }
3826 ref_data.GenerateMaterializationValueFromPredecessorsForEntry(entry, pred_vals);
3827 }
3828
3829 // Setup exit block heap-values for later phi-generation.
3830 for (HBasicBlock* exit : excluded_cohort.ExitBlocks()) {
3831 // mark every exit of cohorts as having a value so we can easily
3832 // materialize the PHIs.
3833 // TODO By setting this we can easily use the normal MaterializeLoopPhis
3834 // (via FullyMaterializePhis) in order to generate the default-values
3835 // for predicated-gets. This has the unfortunate side effect of creating
3836 // somewhat more phis than are really needed (in some cases). We really
3837 // should try to eventually know that we can lower these PHIs to only
3838 // the non-escaping value in cases where it is possible. Currently this
3839 // is done to some extent in instruction_simplifier but we have more
3840 // information here to do the right thing.
3841 for (const HeapLocation* loc : ref_data.IterateLocations()) {
3842 size_t loc_off = heap_location_collector_.GetHeapLocationIndex(loc);
3843 // This Value::Default() is only used to fill in PHIs used as the
3844 // default value for PredicatedInstanceFieldGets. The actual value
3845 // stored there is meaningless since the Predicated-iget will use the
3846 // actual field value instead on these paths.
3847 heap_values_for_[exit->GetBlockId()][loc_off].value = Value::Default();
3848 }
3849 }
3850 }
3851
3852 // string materialization through the graph.
3853 // // Visit RPO to PHI the materialized object through the cohort.
3854 for (HBasicBlock* blk : GetGraph()->GetReversePostOrder()) {
3855 // NB This doesn't include materialization blocks.
3856 DCHECK(!helper.IsMaterializationBlock(blk))
3857 << "Materialization blocks should not be in RPO yet.";
3858 if (ref_data.HasMaterialization(blk)) {
3859 continue;
3860 } else if (ref_data.BeforeAllEscapes(blk)) {
3861 ref_data.AddMaterialization(blk, GetGraph()->GetNullConstant());
3862 continue;
3863 } else {
3864 ref_data.GenerateMaterializationValueFromPredecessors(blk);
3865 }
3866 }
3867 }
3868
3869 // Once we've generated all the materializations we can update the users.
3870 helper.FixupPartialObjectUsers();
3871
3872 // Actually put materialization blocks into the graph
3873 helper.InsertMaterializationBlocks();
3874
3875 // Get rid of the original instructions.
3876 helper.RemoveReplacedInstructions();
3877
3878 // Ensure everything is ordered correctly in the materialization blocks. This
3879 // involves moving every NewInstance to the top and ordering them so that any
3880 // required env-uses are correctly ordered.
3881 helper.ReorderMaterializationsForEnvDominance();
3882}
3883
3884void LSEVisitor::FinishFullLSE() {
Vladimir Marko3224f382020-06-23 14:19:53 +01003885 // Remove recorded load instructions that should be eliminated.
Vladimir Marko9e3fe992020-08-25 16:17:51 +01003886 for (const LoadStoreRecord& record : loads_and_stores_) {
3887 size_t id = dchecked_integral_cast<size_t>(record.load_or_store->GetId());
3888 HInstruction* substitute = substitute_instructions_for_loads_[id];
3889 if (substitute == nullptr) {
3890 continue;
3891 }
3892 HInstruction* load = record.load_or_store;
Vladimir Marko3224f382020-06-23 14:19:53 +01003893 DCHECK(load != nullptr);
3894 DCHECK(IsLoad(load));
3895 DCHECK(load->GetBlock() != nullptr) << load->DebugName() << "@" << load->GetDexPc();
Vladimir Marko3224f382020-06-23 14:19:53 +01003896 // We proactively retrieve the substitute for a removed load, so
3897 // a load that has a substitute should not be observed as a heap
3898 // location value.
3899 DCHECK_EQ(FindSubstitute(substitute), substitute);
3900
3901 load->ReplaceWith(substitute);
3902 load->GetBlock()->RemoveInstruction(load);
3903 }
3904
3905 // Remove all the stores we can.
3906 for (const LoadStoreRecord& record : loads_and_stores_) {
3907 bool is_store = record.load_or_store->GetSideEffects().DoesAnyWrite();
3908 DCHECK_EQ(is_store, IsStore(record.load_or_store));
3909 if (is_store && !kept_stores_.IsBitSet(record.load_or_store->GetId())) {
3910 record.load_or_store->GetBlock()->RemoveInstruction(record.load_or_store);
3911 }
3912 }
3913
3914 // Eliminate singleton-classified instructions:
3915 // * - Constructor fences (they never escape this thread).
3916 // * - Allocations (if they are unused).
3917 for (HInstruction* new_instance : singleton_new_instances_) {
3918 size_t removed = HConstructorFence::RemoveConstructorFences(new_instance);
3919 MaybeRecordStat(stats_,
3920 MethodCompilationStat::kConstructorFenceRemovedLSE,
3921 removed);
3922
3923 if (!new_instance->HasNonEnvironmentUses()) {
3924 new_instance->RemoveEnvironmentUsers();
3925 new_instance->GetBlock()->RemoveInstruction(new_instance);
Alex Light86fe9b82020-11-16 16:54:01 +00003926 MaybeRecordStat(stats_, MethodCompilationStat::kFullLSEAllocationRemoved);
Vladimir Marko3224f382020-06-23 14:19:53 +01003927 }
3928 }
3929}
3930
Vladimir Markoc9f4a372021-03-11 10:38:34 +00003931// The LSEVisitor is a ValueObject (indirectly through base classes) and therefore
3932// cannot be directly allocated with an arena allocator, so we need to wrap it.
3933class LSEVisitorWrapper : public DeletableArenaObject<kArenaAllocLSE> {
3934 public:
3935 LSEVisitorWrapper(HGraph* graph,
3936 const HeapLocationCollector& heap_location_collector,
3937 bool perform_partial_lse,
3938 OptimizingCompilerStats* stats)
3939 : lse_visitor_(graph, heap_location_collector, perform_partial_lse, stats) {}
3940
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00003941 void Run() {
3942 lse_visitor_.Run();
Vladimir Markoc9f4a372021-03-11 10:38:34 +00003943 }
3944
3945 private:
3946 LSEVisitor lse_visitor_;
3947};
3948
Alex Light3a73ffb2021-01-25 14:11:05 +00003949bool LoadStoreElimination::Run(bool enable_partial_lse) {
Santiago Aboy Solanes9e3c3712022-04-08 13:24:05 +00003950 if (graph_->IsDebuggable()) {
Mingyao Yang8df69d42015-10-22 15:40:58 -07003951 // Debugger may set heap values or trigger deoptimization of callers.
Mingyao Yang8df69d42015-10-22 15:40:58 -07003952 // Skip this optimization.
Aart Bik24773202018-04-26 10:28:51 -07003953 return false;
Mingyao Yang8df69d42015-10-22 15:40:58 -07003954 }
Alex Light86fe9b82020-11-16 16:54:01 +00003955 // We need to be able to determine reachability. Clear it just to be safe but
3956 // this should initially be empty.
3957 graph_->ClearReachabilityInformation();
3958 // This is O(blocks^3) time complexity. It means we can query reachability in
3959 // O(1) though.
3960 graph_->ComputeReachabilityInformation();
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00003961 ScopedArenaAllocator allocator(graph_->GetArenaStack());
3962 LoadStoreAnalysis lsa(graph_,
3963 stats_,
3964 &allocator,
3965 enable_partial_lse ? LoadStoreAnalysisType::kFull
Nicolas Geoffray2498d852021-11-16 15:12:04 +00003966 : LoadStoreAnalysisType::kBasic);
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00003967 lsa.Run();
3968 const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector();
3969 if (heap_location_collector.GetNumberOfHeapLocations() == 0) {
3970 // No HeapLocation information from LSA, skip this optimization.
3971 return false;
3972 }
xueliang.zhongc239a2b2017-04-27 15:31:37 +01003973
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00003974 std::unique_ptr<LSEVisitorWrapper> lse_visitor(new (&allocator) LSEVisitorWrapper(
3975 graph_, heap_location_collector, enable_partial_lse, stats_));
3976 lse_visitor->Run();
Aart Bik24773202018-04-26 10:28:51 -07003977 return true;
Mingyao Yang8df69d42015-10-22 15:40:58 -07003978}
3979
Alex Light3a73ffb2021-01-25 14:11:05 +00003980#undef LSE_VLOG
3981
Mingyao Yang8df69d42015-10-22 15:40:58 -07003982} // namespace art