blob: b385e035d3bd83156fe23cce40c522acb654bce1 [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
Vladimír Marko434d9682022-11-04 14:04:17 +0000322namespace art HIDDEN {
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
Santiago Aboy Solanes2e2c94d2022-10-14 16:55:47 +0100977 void HandleAcquireLoad(HInstruction* instruction) {
978 DCHECK((instruction->IsInstanceFieldGet() && instruction->AsInstanceFieldGet()->IsVolatile()) ||
979 (instruction->IsStaticFieldGet() && instruction->AsStaticFieldGet()->IsVolatile()) ||
980 (instruction->IsMonitorOperation() && instruction->AsMonitorOperation()->IsEnter()))
981 << "Unexpected instruction " << instruction->GetId() << ": " << instruction->DebugName();
982
983 // Acquire operations e.g. MONITOR_ENTER change the thread's view of the memory, so we must
984 // invalidate all current values.
985 ScopedArenaVector<ValueRecord>& heap_values =
986 heap_values_for_[instruction->GetBlock()->GetBlockId()];
987 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
988 KeepStores(heap_values[i].stored_by);
989 heap_values[i].stored_by = Value::PureUnknown();
990 heap_values[i].value = Value::PartialUnknown(heap_values[i].value);
991 }
992
993 // Note that there's no need to record the load as subsequent acquire loads shouldn't be
994 // eliminated either.
995 }
996
997 void HandleReleaseStore(HInstruction* instruction) {
998 DCHECK((instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->IsVolatile()) ||
999 (instruction->IsStaticFieldSet() && instruction->AsStaticFieldSet()->IsVolatile()) ||
1000 (instruction->IsMonitorOperation() && !instruction->AsMonitorOperation()->IsEnter()))
1001 << "Unexpected instruction " << instruction->GetId() << ": " << instruction->DebugName();
1002
1003 // Release operations e.g. MONITOR_EXIT do not affect this thread's view of the memory, but
1004 // they will push the modifications for other threads to see. Therefore, we must keep the
1005 // stores but there's no need to clobber the value.
1006 ScopedArenaVector<ValueRecord>& heap_values =
1007 heap_values_for_[instruction->GetBlock()->GetBlockId()];
1008 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
1009 KeepStores(heap_values[i].stored_by);
1010 heap_values[i].stored_by = Value::PureUnknown();
1011 }
1012
1013 // Note that there's no need to record the store as subsequent release store shouldn't be
1014 // eliminated either.
1015 }
1016
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001017 void VisitInstanceFieldGet(HInstanceFieldGet* instruction) override {
Santiago Aboy Solanes2e2c94d2022-10-14 16:55:47 +01001018 if (instruction->IsVolatile()) {
1019 HandleAcquireLoad(instruction);
1020 return;
1021 }
1022
Aart Bikb765a3f2018-05-10 14:47:48 -07001023 HInstruction* object = instruction->InputAt(0);
1024 const FieldInfo& field = instruction->GetFieldInfo();
1025 VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(object, &field));
Mingyao Yang8df69d42015-10-22 15:40:58 -07001026 }
1027
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001028 void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
Santiago Aboy Solanes2e2c94d2022-10-14 16:55:47 +01001029 if (instruction->IsVolatile()) {
1030 HandleReleaseStore(instruction);
1031 return;
1032 }
1033
Aart Bikb765a3f2018-05-10 14:47:48 -07001034 HInstruction* object = instruction->InputAt(0);
1035 const FieldInfo& field = instruction->GetFieldInfo();
Mingyao Yang8df69d42015-10-22 15:40:58 -07001036 HInstruction* value = instruction->InputAt(1);
Aart Bikb765a3f2018-05-10 14:47:48 -07001037 size_t idx = heap_location_collector_.GetFieldHeapLocation(object, &field);
1038 VisitSetLocation(instruction, idx, value);
Mingyao Yang8df69d42015-10-22 15:40:58 -07001039 }
1040
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001041 void VisitStaticFieldGet(HStaticFieldGet* instruction) override {
Santiago Aboy Solanes2e2c94d2022-10-14 16:55:47 +01001042 if (instruction->IsVolatile()) {
1043 HandleAcquireLoad(instruction);
1044 return;
1045 }
1046
Mingyao Yang8df69d42015-10-22 15:40:58 -07001047 HInstruction* cls = instruction->InputAt(0);
Aart Bikb765a3f2018-05-10 14:47:48 -07001048 const FieldInfo& field = instruction->GetFieldInfo();
1049 VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(cls, &field));
Mingyao Yang8df69d42015-10-22 15:40:58 -07001050 }
1051
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001052 void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
Santiago Aboy Solanes2e2c94d2022-10-14 16:55:47 +01001053 if (instruction->IsVolatile()) {
1054 HandleReleaseStore(instruction);
1055 return;
1056 }
1057
Mingyao Yang8df69d42015-10-22 15:40:58 -07001058 HInstruction* cls = instruction->InputAt(0);
Aart Bikb765a3f2018-05-10 14:47:48 -07001059 const FieldInfo& field = instruction->GetFieldInfo();
Vladimir Marko3224f382020-06-23 14:19:53 +01001060 HInstruction* value = instruction->InputAt(1);
Aart Bikb765a3f2018-05-10 14:47:48 -07001061 size_t idx = heap_location_collector_.GetFieldHeapLocation(cls, &field);
Vladimir Marko3224f382020-06-23 14:19:53 +01001062 VisitSetLocation(instruction, idx, value);
Mingyao Yang8df69d42015-10-22 15:40:58 -07001063 }
1064
Santiago Aboy Solanes2e2c94d2022-10-14 16:55:47 +01001065 void VisitMonitorOperation(HMonitorOperation* monitor_op) override {
1066 if (monitor_op->IsEnter()) {
1067 HandleAcquireLoad(monitor_op);
1068 } else {
1069 HandleReleaseStore(monitor_op);
1070 }
1071 }
1072
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001073 void VisitArrayGet(HArrayGet* instruction) override {
Aart Bikb765a3f2018-05-10 14:47:48 -07001074 VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction));
Mingyao Yang8df69d42015-10-22 15:40:58 -07001075 }
1076
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001077 void VisitArraySet(HArraySet* instruction) override {
Aart Bikb765a3f2018-05-10 14:47:48 -07001078 size_t idx = heap_location_collector_.GetArrayHeapLocation(instruction);
xueliang.zhongd71f1dc2018-01-24 17:24:16 +00001079 VisitSetLocation(instruction, idx, instruction->GetValue());
1080 }
1081
1082 void VisitVecLoad(HVecLoad* instruction) override {
1083 VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction));
1084 }
1085
1086 void VisitVecStore(HVecStore* instruction) override {
1087 size_t idx = heap_location_collector_.GetArrayHeapLocation(instruction);
1088 VisitSetLocation(instruction, idx, instruction->GetValue());
Mingyao Yang8df69d42015-10-22 15:40:58 -07001089 }
1090
Andreas Gampefa6a1b02018-09-07 08:11:55 -07001091 void VisitDeoptimize(HDeoptimize* instruction) override {
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001092 // If we are in a try, even singletons are observable.
Santiago Aboy Solanes968db0c2022-11-03 14:19:45 +00001093 const bool inside_a_try = instruction->GetBlock()->IsTryBlock();
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001094 HBasicBlock* block = instruction->GetBlock();
1095 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01001096 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
1097 Value* stored_by = &heap_values[i].stored_by;
1098 if (stored_by->IsUnknown()) {
1099 continue;
1100 }
1101 // Stores are generally observeable after deoptimization, except
1102 // for singletons that don't escape in the deoptimization environment.
1103 bool observable = true;
1104 ReferenceInfo* info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001105 if (!inside_a_try && info->IsSingleton()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001106 HInstruction* reference = info->GetReference();
1107 // Finalizable objects always escape.
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001108 const bool finalizable_object =
1109 reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable();
1110 if (!finalizable_object && !IsEscapingObject(info, block, i)) {
Mingyao Yanga3540532018-01-25 12:17:28 -08001111 // Check whether the reference for a store is used by an environment local of
Vladimir Marko3224f382020-06-23 14:19:53 +01001112 // the HDeoptimize. If not, the singleton is not observed after deoptimization.
1113 const HUseList<HEnvironment*>& env_uses = reference->GetEnvUses();
1114 observable = std::any_of(
1115 env_uses.begin(),
1116 env_uses.end(),
1117 [instruction](const HUseListNode<HEnvironment*>& use) {
1118 return use.GetUser()->GetHolder() == instruction;
1119 });
Mingyao Yangeb2d2d346e2017-03-02 13:26:17 -08001120 }
1121 }
Vladimir Marko3224f382020-06-23 14:19:53 +01001122 if (observable) {
1123 KeepStores(*stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -08001124 *stored_by = Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01001125 }
Mingyao Yangeb2d2d346e2017-03-02 13:26:17 -08001126 }
1127 }
1128
Mingyao Yang46721ef2017-10-05 14:45:17 -07001129 // Keep necessary stores before exiting a method via return/throw.
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001130 void HandleExit(HBasicBlock* block, bool must_keep_stores = false) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001131 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
1132 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
Mingyao Yang46721ef2017-10-05 14:45:17 -07001133 ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001134 if (must_keep_stores || IsEscapingObject(ref_info, block, i)) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001135 KeepStores(heap_values[i].stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -08001136 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang46721ef2017-10-05 14:45:17 -07001137 }
1138 }
1139 }
1140
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001141 void VisitReturn(HReturn* instruction) override {
Mingyao Yang46721ef2017-10-05 14:45:17 -07001142 HandleExit(instruction->GetBlock());
1143 }
1144
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001145 void VisitReturnVoid(HReturnVoid* return_void) override {
Mingyao Yang46721ef2017-10-05 14:45:17 -07001146 HandleExit(return_void->GetBlock());
1147 }
1148
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001149 void HandleThrowingInstruction(HInstruction* instruction) {
1150 DCHECK(instruction->CanThrow());
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001151 // If we are inside of a try, singletons can become visible since we may not exit the method.
Santiago Aboy Solanes968db0c2022-11-03 14:19:45 +00001152 HandleExit(instruction->GetBlock(), instruction->GetBlock()->IsTryBlock());
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001153 }
1154
1155 void VisitMethodEntryHook(HMethodEntryHook* method_entry) override {
1156 HandleThrowingInstruction(method_entry);
1157 }
1158
1159 void VisitMethodExitHook(HMethodExitHook* method_exit) override {
1160 HandleThrowingInstruction(method_exit);
1161 }
1162
1163 void VisitDivZeroCheck(HDivZeroCheck* div_zero_check) override {
1164 HandleThrowingInstruction(div_zero_check);
1165 }
1166
1167 void VisitNullCheck(HNullCheck* null_check) override {
1168 HandleThrowingInstruction(null_check);
1169 }
1170
1171 void VisitBoundsCheck(HBoundsCheck* bounds_check) override {
1172 HandleThrowingInstruction(bounds_check);
1173 }
1174
1175 void VisitLoadClass(HLoadClass* load_class) override {
1176 if (load_class->CanThrow()) {
1177 HandleThrowingInstruction(load_class);
1178 }
1179 }
1180
1181 void VisitLoadString(HLoadString* load_string) override {
1182 if (load_string->CanThrow()) {
1183 HandleThrowingInstruction(load_string);
1184 }
1185 }
1186
Nicolas Geoffrayaafb81b2022-06-29 09:36:23 +01001187 void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) override {
1188 HandleThrowingInstruction(load_method_handle);
1189 }
1190
1191 void VisitLoadMethodType(HLoadMethodType* load_method_type) override {
1192 HandleThrowingInstruction(load_method_type);
1193 }
1194
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001195 void VisitStringBuilderAppend(HStringBuilderAppend* sb_append) override {
1196 HandleThrowingInstruction(sb_append);
1197 }
1198
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001199 void VisitThrow(HThrow* throw_instruction) override {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001200 HandleThrowingInstruction(throw_instruction);
1201 }
1202
1203 void VisitCheckCast(HCheckCast* check_cast) override {
1204 HandleThrowingInstruction(check_cast);
1205 }
1206
Mingyao Yang293f1c02017-11-08 15:22:17 -08001207 void HandleInvoke(HInstruction* instruction) {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001208 // If `instruction` can throw we have to presume all stores are visible.
1209 const bool can_throw = instruction->CanThrow();
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001210 // If we are in a try, even singletons are observable.
Santiago Aboy Solanes968db0c2022-11-03 14:19:45 +00001211 const bool can_throw_inside_a_try = can_throw && instruction->GetBlock()->IsTryBlock();
Mingyao Yang293f1c02017-11-08 15:22:17 -08001212 SideEffects side_effects = instruction->GetSideEffects();
Vladimir Marko3224f382020-06-23 14:19:53 +01001213 ScopedArenaVector<ValueRecord>& heap_values =
Mingyao Yang293f1c02017-11-08 15:22:17 -08001214 heap_values_for_[instruction->GetBlock()->GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01001215 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001216 ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
Alex Light86fe9b82020-11-16 16:54:01 +00001217 HBasicBlock* blk = instruction->GetBlock();
1218 // We don't need to do anything if the reference has not escaped at this point.
1219 // This is true if either we (1) never escape or (2) sometimes escape but
1220 // there is no possible execution where we have done so at this time. NB
1221 // We count being in the excluded cohort as escaping. Technically, this is
1222 // a bit over-conservative (since we can have multiple non-escaping calls
1223 // before a single escaping one) but this simplifies everything greatly.
Vladimir Marko5c824932021-06-02 15:54:17 +01001224 auto partial_singleton_did_not_escape = [](ReferenceInfo* ref_info, HBasicBlock* blk) {
1225 DCHECK(ref_info->IsPartialSingleton());
1226 if (!ref_info->GetNoEscapeSubgraph()->ContainsBlock(blk)) {
1227 return false;
1228 }
1229 ArrayRef<const ExecutionSubgraph::ExcludedCohort> cohorts =
1230 ref_info->GetNoEscapeSubgraph()->GetExcludedCohorts();
1231 return std::none_of(cohorts.begin(),
1232 cohorts.end(),
1233 [&](const ExecutionSubgraph::ExcludedCohort& cohort) {
1234 return cohort.PrecedesBlock(blk);
1235 });
1236 };
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001237 if (!can_throw_inside_a_try &&
Santiago Aboy Solanes614638b2022-04-22 16:51:59 +01001238 (ref_info->IsSingleton() ||
1239 // partial and we aren't currently escaping and we haven't escaped yet.
1240 (ref_info->IsPartialSingleton() && partial_singleton_did_not_escape(ref_info, blk)))) {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001241 // Singleton references cannot be seen by the callee.
1242 } else {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001243 if (can_throw || side_effects.DoesAnyRead() || side_effects.DoesAnyWrite()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001244 // Previous stores may become visible (read) and/or impossible for LSE to track (write).
1245 KeepStores(heap_values[i].stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -08001246 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang293f1c02017-11-08 15:22:17 -08001247 }
1248 if (side_effects.DoesAnyWrite()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001249 // The value may be clobbered.
Alex Light3a73ffb2021-01-25 14:11:05 +00001250 heap_values[i].value = Value::PartialUnknown(heap_values[i].value);
Mingyao Yang293f1c02017-11-08 15:22:17 -08001251 }
Mingyao Yang8df69d42015-10-22 15:40:58 -07001252 }
1253 }
1254 }
1255
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001256 void VisitInvoke(HInvoke* invoke) override {
Orion Hodsonac141392017-01-13 11:53:47 +00001257 HandleInvoke(invoke);
1258 }
1259
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001260 void VisitClinitCheck(HClinitCheck* clinit) override {
Vladimir Marko3224f382020-06-23 14:19:53 +01001261 // Class initialization check can result in class initializer calling arbitrary methods.
Mingyao Yang8df69d42015-10-22 15:40:58 -07001262 HandleInvoke(clinit);
1263 }
1264
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001265 void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001266 // Conservatively treat it as an invocation.
1267 HandleInvoke(instruction);
1268 }
1269
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001270 void VisitUnresolvedInstanceFieldSet(HUnresolvedInstanceFieldSet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001271 // Conservatively treat it as an invocation.
1272 HandleInvoke(instruction);
1273 }
1274
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001275 void VisitUnresolvedStaticFieldGet(HUnresolvedStaticFieldGet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001276 // Conservatively treat it as an invocation.
1277 HandleInvoke(instruction);
1278 }
1279
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001280 void VisitUnresolvedStaticFieldSet(HUnresolvedStaticFieldSet* instruction) override {
Mingyao Yang8df69d42015-10-22 15:40:58 -07001281 // Conservatively treat it as an invocation.
1282 HandleInvoke(instruction);
1283 }
1284
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001285 void VisitNewInstance(HNewInstance* new_instance) override {
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001286 // If we are in a try, even singletons are observable.
Santiago Aboy Solanes968db0c2022-11-03 14:19:45 +00001287 const bool inside_a_try = new_instance->GetBlock()->IsTryBlock();
Mingyao Yang8df69d42015-10-22 15:40:58 -07001288 ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_instance);
1289 if (ref_info == nullptr) {
1290 // new_instance isn't used for field accesses. No need to process it.
1291 return;
1292 }
Mingyao Yang025c1a62017-10-30 11:19:57 -07001293 if (ref_info->IsSingletonAndRemovable() && !new_instance->NeedsChecks()) {
1294 DCHECK(!new_instance->IsFinalizable());
Mingyao Yang7cf9af22018-02-06 15:02:42 -08001295 // new_instance can potentially be eliminated.
Mingyao Yang062157f2016-03-02 10:15:36 -08001296 singleton_new_instances_.push_back(new_instance);
Mingyao Yang8df69d42015-10-22 15:40:58 -07001297 }
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001298 HBasicBlock* block = new_instance->GetBlock();
1299 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01001300 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001301 ReferenceInfo* info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
1302 HInstruction* ref = info->GetReference();
Mingyao Yang8df69d42015-10-22 15:40:58 -07001303 size_t offset = heap_location_collector_.GetHeapLocation(i)->GetOffset();
Alex Light2610dfe2020-12-07 16:26:43 -08001304 if (ref == new_instance) {
Alex Lightc6da1be2021-01-22 06:58:44 -08001305 if (offset >= mirror::kObjectHeaderSize ||
1306 MemberOffset(offset) == mirror::Object::MonitorOffset()) {
Alex Light2610dfe2020-12-07 16:26:43 -08001307 // Instance fields except the header fields are set to default heap values.
Alex Lightc6da1be2021-01-22 06:58:44 -08001308 // The shadow$_monitor_ field is set to the default value however.
Alex Light2610dfe2020-12-07 16:26:43 -08001309 heap_values[i].value = Value::Default();
Alex Lightf5a84cb2021-01-15 08:35:38 -08001310 heap_values[i].stored_by = Value::PureUnknown();
Alex Light2610dfe2020-12-07 16:26:43 -08001311 } else if (MemberOffset(offset) == mirror::Object::ClassOffset()) {
1312 // The shadow$_klass_ field is special and has an actual value however.
1313 heap_values[i].value = Value::ForInstruction(new_instance->GetLoadClass());
Alex Lightf5a84cb2021-01-15 08:35:38 -08001314 heap_values[i].stored_by = Value::PureUnknown();
Alex Light2610dfe2020-12-07 16:26:43 -08001315 }
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001316 } else if (inside_a_try || IsEscapingObject(info, block, i)) {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001317 // Since NewInstance can throw, we presume all previous stores could be visible.
1318 KeepStores(heap_values[i].stored_by);
1319 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang8df69d42015-10-22 15:40:58 -07001320 }
1321 }
1322 }
1323
Roland Levillainbbc6e7e2018-08-24 16:58:47 +01001324 void VisitNewArray(HNewArray* new_array) override {
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001325 // If we are in a try, even singletons are observable.
Santiago Aboy Solanes968db0c2022-11-03 14:19:45 +00001326 const bool inside_a_try = new_array->GetBlock()->IsTryBlock();
Mingyao Yang86974902017-03-01 14:03:51 -08001327 ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_array);
1328 if (ref_info == nullptr) {
1329 // new_array isn't used for array accesses. No need to process it.
1330 return;
1331 }
1332 if (ref_info->IsSingletonAndRemovable()) {
Mingyao Yang7cf9af22018-02-06 15:02:42 -08001333 if (new_array->GetLength()->IsIntConstant() &&
1334 new_array->GetLength()->AsIntConstant()->GetValue() >= 0) {
1335 // new_array can potentially be eliminated.
1336 singleton_new_instances_.push_back(new_array);
1337 } else {
1338 // new_array may throw NegativeArraySizeException. Keep it.
1339 }
Mingyao Yang86974902017-03-01 14:03:51 -08001340 }
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001341 HBasicBlock* block = new_array->GetBlock();
1342 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01001343 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
Mingyao Yang86974902017-03-01 14:03:51 -08001344 HeapLocation* location = heap_location_collector_.GetHeapLocation(i);
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001345 ReferenceInfo* info = location->GetReferenceInfo();
1346 HInstruction* ref = info->GetReference();
Mingyao Yang86974902017-03-01 14:03:51 -08001347 if (ref == new_array && location->GetIndex() != nullptr) {
1348 // Array elements are set to default heap values.
Vladimir Marko3224f382020-06-23 14:19:53 +01001349 heap_values[i].value = Value::Default();
Alex Lightf5a84cb2021-01-15 08:35:38 -08001350 heap_values[i].stored_by = Value::PureUnknown();
Santiago Aboy Solanesec11c672022-09-16 11:27:03 +01001351 } else if (inside_a_try || IsEscapingObject(info, block, i)) {
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001352 // Since NewArray can throw, we presume all previous stores could be visible.
1353 KeepStores(heap_values[i].stored_by);
1354 heap_values[i].stored_by = Value::PureUnknown();
Mingyao Yang86974902017-03-01 14:03:51 -08001355 }
1356 }
1357 }
1358
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001359 void VisitInstruction(HInstruction* instruction) override {
1360 // Throwing instructions must be handled specially.
1361 DCHECK(!instruction->CanThrow());
1362 }
1363
Alex Light3a73ffb2021-01-25 14:11:05 +00001364 bool ShouldPerformPartialLSE() const {
1365 return perform_partial_lse_ && !GetGraph()->IsCompilingOsr();
1366 }
1367
1368 bool perform_partial_lse_;
1369
Mingyao Yang8df69d42015-10-22 15:40:58 -07001370 const HeapLocationCollector& heap_location_collector_;
Mingyao Yang8df69d42015-10-22 15:40:58 -07001371
Vladimir Marko009d1662017-10-10 13:21:15 +01001372 // Use local allocator for allocating memory.
1373 ScopedArenaAllocator allocator_;
1374
Alex Lightef28d242020-11-17 20:21:51 -08001375 // The number of unique phi_placeholders there possibly are
1376 size_t num_phi_placeholders_;
Vladimir Marko3224f382020-06-23 14:19:53 +01001377
1378 // One array of heap value records for each block.
1379 ScopedArenaVector<ScopedArenaVector<ValueRecord>> heap_values_for_;
Mingyao Yang8df69d42015-10-22 15:40:58 -07001380
Vladimir Marko3224f382020-06-23 14:19:53 +01001381 // We record loads and stores for re-processing when we find a loop Phi placeholder
1382 // with unknown value from a predecessor, and also for removing stores that are
1383 // found to be dead, i.e. not marked in `kept_stores_` at the end.
1384 struct LoadStoreRecord {
1385 HInstruction* load_or_store;
1386 size_t heap_location_index;
1387 };
1388 ScopedArenaVector<LoadStoreRecord> loads_and_stores_;
1389
Vladimir Marko9e3fe992020-08-25 16:17:51 +01001390 // We record the substitute instructions for loads that should be
1391 // eliminated but may be used by heap locations. They'll be removed
1392 // in the end. These are indexed by the load's id.
1393 ScopedArenaVector<HInstruction*> substitute_instructions_for_loads_;
1394
Alex Light3a73ffb2021-01-25 14:11:05 +00001395 // Value at the start of the given instruction for instructions which directly
1396 // read from a heap-location (i.e. FieldGet). The mapping to heap-location is
1397 // implicit through the fact that each instruction can only directly refer to
1398 // a single heap-location.
1399 ScopedArenaHashMap<HInstruction*, Value> intermediate_values_;
1400
Vladimir Marko3224f382020-06-23 14:19:53 +01001401 // Record stores to keep in a bit vector indexed by instruction ID.
1402 ArenaBitVector kept_stores_;
1403 // When we need to keep all stores that feed a Phi placeholder, we just record the
1404 // index of that placeholder for processing after graph traversal.
1405 ArenaBitVector phi_placeholders_to_search_for_kept_stores_;
1406
1407 // Loads that would require a loop Phi to replace are recorded for processing
1408 // later as we do not have enough information from back-edges to determine if
1409 // a suitable Phi can be found or created when we visit these loads.
1410 ScopedArenaHashMap<HInstruction*, ValueRecord> loads_requiring_loop_phi_;
1411
1412 // For stores, record the old value records that were replaced and the stored values.
1413 struct StoreRecord {
1414 ValueRecord old_value_record;
1415 HInstruction* stored_value;
1416 };
Vladimir Markoa718d642021-03-10 15:36:40 +00001417 // Small pre-allocated initial buffer avoids initializing a large one until it's really needed.
1418 static constexpr size_t kStoreRecordsInitialBufferSize = 16;
Vladimir Markoc9f4a372021-03-11 10:38:34 +00001419 std::pair<HInstruction*, StoreRecord> store_records_buffer_[kStoreRecordsInitialBufferSize];
Vladimir Marko3224f382020-06-23 14:19:53 +01001420 ScopedArenaHashMap<HInstruction*, StoreRecord> store_records_;
1421
1422 // Replacements for Phi placeholders.
Vladimir Marko06fb7fa2021-05-18 15:53:17 +00001423 // The invalid heap value is used to mark Phi placeholders that cannot be replaced.
Vladimir Marko3224f382020-06-23 14:19:53 +01001424 ScopedArenaVector<Value> phi_placeholder_replacements_;
Mingyao Yangfb8464a2015-11-02 10:56:59 -08001425
Alex Light86fe9b82020-11-16 16:54:01 +00001426 // Merged-unknowns that must have their predecessor values kept to ensure
1427 // partially escaped values are written
1428 ArenaBitVector kept_merged_unknowns_;
1429
Vladimir Marko009d1662017-10-10 13:21:15 +01001430 ScopedArenaVector<HInstruction*> singleton_new_instances_;
Mingyao Yang8df69d42015-10-22 15:40:58 -07001431
Alex Light3a73ffb2021-01-25 14:11:05 +00001432 // The field infos for each heap location (if relevant).
1433 ScopedArenaVector<const FieldInfo*> field_infos_;
1434
Alex Light09e23372021-01-15 08:42:11 -08001435 Phase current_phase_;
1436
Alex Light3a73ffb2021-01-25 14:11:05 +00001437 friend class PartialLoadStoreEliminationHelper;
1438 friend struct ScopedRestoreHeapValues;
1439
Alex Light86fe9b82020-11-16 16:54:01 +00001440 friend std::ostream& operator<<(std::ostream& os, const Value& v);
Alex Light3a73ffb2021-01-25 14:11:05 +00001441 friend std::ostream& operator<<(std::ostream& os, const PriorValueHolder& v);
1442 friend std::ostream& operator<<(std::ostream& oss, const LSEVisitor::Phase& phase);
Alex Light86fe9b82020-11-16 16:54:01 +00001443
Mingyao Yang8df69d42015-10-22 15:40:58 -07001444 DISALLOW_COPY_AND_ASSIGN(LSEVisitor);
1445};
1446
Alex Light3a73ffb2021-01-25 14:11:05 +00001447std::ostream& operator<<(std::ostream& oss, const LSEVisitor::PriorValueHolder& p) {
1448 p.Dump(oss);
1449 return oss;
1450}
1451
Alex Light09e23372021-01-15 08:42:11 -08001452std::ostream& operator<<(std::ostream& oss, const LSEVisitor::Phase& phase) {
1453 switch (phase) {
1454 case LSEVisitor::Phase::kLoadElimination:
1455 return oss << "kLoadElimination";
1456 case LSEVisitor::Phase::kStoreElimination:
1457 return oss << "kStoreElimination";
Alex Light3a73ffb2021-01-25 14:11:05 +00001458 case LSEVisitor::Phase::kPartialElimination:
1459 return oss << "kPartialElimination";
Alex Light09e23372021-01-15 08:42:11 -08001460 }
1461}
1462
Alex Light3a73ffb2021-01-25 14:11:05 +00001463void LSEVisitor::PriorValueHolder::Dump(std::ostream& oss) const {
1464 if (IsDefault()) {
1465 oss << "Default";
1466 } else if (IsPhi()) {
1467 oss << "Phi: " << GetPhiPlaceholder();
1468 } else {
1469 oss << "Instruction: " << *GetInstruction();
1470 }
1471}
1472
1473constexpr LSEVisitor::PriorValueHolder::PriorValueHolder(Value val)
1474 : value_(Marker{}) {
1475 DCHECK(!val.IsInvalid() && !val.IsPureUnknown());
1476 if (val.IsPartialUnknown()) {
1477 value_ = val.GetPriorValue().value_;
1478 } else if (val.IsMergedUnknown() || val.NeedsPhi()) {
1479 value_ = val.GetPhiPlaceholder();
1480 } else if (val.IsInstruction()) {
1481 value_ = val.GetInstruction();
1482 } else {
1483 DCHECK(val.IsDefault());
1484 }
1485}
1486
1487constexpr bool operator==(const LSEVisitor::Marker&, const LSEVisitor::Marker&) {
1488 return true;
1489}
1490
1491constexpr bool operator==(const LSEVisitor::PriorValueHolder& p1,
1492 const LSEVisitor::PriorValueHolder& p2) {
1493 return p1.Equals(p2);
1494}
1495
Alex Lightef28d242020-11-17 20:21:51 -08001496constexpr bool operator==(const LSEVisitor::PhiPlaceholder& p1,
1497 const LSEVisitor::PhiPlaceholder& p2) {
1498 return p1.Equals(p2);
1499}
1500
1501constexpr bool operator==(const LSEVisitor::Value::NeedsLoopPhiMarker& p1,
1502 const LSEVisitor::Value::NeedsLoopPhiMarker& p2) {
1503 return p1.phi_ == p2.phi_;
1504}
1505
1506constexpr bool operator==(const LSEVisitor::Value::NeedsNonLoopPhiMarker& p1,
1507 const LSEVisitor::Value::NeedsNonLoopPhiMarker& p2) {
1508 return p1.phi_ == p2.phi_;
1509}
1510
1511constexpr bool operator==(const LSEVisitor::Value::MergedUnknownMarker& p1,
1512 const LSEVisitor::Value::MergedUnknownMarker& p2) {
1513 return p1.phi_ == p2.phi_;
1514}
1515
1516std::ostream& operator<<(std::ostream& oss, const LSEVisitor::PhiPlaceholder& p) {
1517 p.Dump(oss);
1518 return oss;
1519}
1520
Alex Light3a73ffb2021-01-25 14:11:05 +00001521LSEVisitor::Value LSEVisitor::PriorValueHolder::ToValue() const {
1522 if (IsDefault()) {
1523 return Value::Default();
1524 } else if (IsPhi()) {
1525 return Value::ForLoopPhiPlaceholder(GetPhiPlaceholder());
1526 } else {
1527 return Value::ForInstruction(GetInstruction());
1528 }
1529}
1530
1531constexpr bool LSEVisitor::Value::ExactEquals(LSEVisitor::Value other) const {
1532 return value_ == other.value_;
1533}
1534
Alex Lightef28d242020-11-17 20:21:51 -08001535constexpr bool LSEVisitor::Value::Equals(LSEVisitor::Value other) const {
1536 // Only valid values can be compared.
1537 DCHECK(IsValid());
1538 DCHECK(other.IsValid());
1539 if (value_ == other.value_) {
1540 // Note: Two unknown values are considered different.
1541 return !IsUnknown();
1542 } else {
1543 // Default is considered equal to zero-bit-pattern instructions.
1544 return (IsDefault() && other.IsInstruction() && IsZeroBitPattern(other.GetInstruction())) ||
1545 (other.IsDefault() && IsInstruction() && IsZeroBitPattern(GetInstruction()));
1546 }
1547}
1548
Alex Light86fe9b82020-11-16 16:54:01 +00001549std::ostream& LSEVisitor::Value::Dump(std::ostream& os) const {
Alex Lightef28d242020-11-17 20:21:51 -08001550 if (std::holds_alternative<LSEVisitor::Value::ValuelessType>(value_)) {
1551 switch (GetValuelessType()) {
1552 case ValuelessType::kDefault:
1553 return os << "Default";
Alex Lightf5a84cb2021-01-15 08:35:38 -08001554 case ValuelessType::kPureUnknown:
1555 return os << "PureUnknown";
Alex Lightef28d242020-11-17 20:21:51 -08001556 case ValuelessType::kInvalid:
1557 return os << "Invalid";
1558 }
Alex Light3a73ffb2021-01-25 14:11:05 +00001559 } else if (IsPartialUnknown()) {
1560 return os << "PartialUnknown[" << GetPriorValue() << "]";
Alex Lightef28d242020-11-17 20:21:51 -08001561 } else if (IsInstruction()) {
1562 return os << "Instruction[id: " << GetInstruction()->GetId()
1563 << ", block: " << GetInstruction()->GetBlock()->GetBlockId() << "]";
1564 } else if (IsMergedUnknown()) {
1565 return os << "MergedUnknown[block: " << GetPhiPlaceholder().GetBlockId()
1566 << ", heap_loc: " << GetPhiPlaceholder().GetHeapLocation() << "]";
1567
1568 } else if (NeedsLoopPhi()) {
1569 return os << "NeedsLoopPhi[block: " << GetPhiPlaceholder().GetBlockId()
1570 << ", heap_loc: " << GetPhiPlaceholder().GetHeapLocation() << "]";
1571 } else {
1572 return os << "NeedsNonLoopPhi[block: " << GetPhiPlaceholder().GetBlockId()
1573 << ", heap_loc: " << GetPhiPlaceholder().GetHeapLocation() << "]";
Alex Light86fe9b82020-11-16 16:54:01 +00001574 }
1575}
1576
1577std::ostream& operator<<(std::ostream& os, const LSEVisitor::Value& v) {
1578 return v.Dump(os);
1579}
1580
Vladimir Marko3224f382020-06-23 14:19:53 +01001581LSEVisitor::LSEVisitor(HGraph* graph,
1582 const HeapLocationCollector& heap_location_collector,
Alex Light3a73ffb2021-01-25 14:11:05 +00001583 bool perform_partial_lse,
Vladimir Marko3224f382020-06-23 14:19:53 +01001584 OptimizingCompilerStats* stats)
1585 : HGraphDelegateVisitor(graph, stats),
Alex Light3a73ffb2021-01-25 14:11:05 +00001586 perform_partial_lse_(perform_partial_lse),
Vladimir Marko3224f382020-06-23 14:19:53 +01001587 heap_location_collector_(heap_location_collector),
1588 allocator_(graph->GetArenaStack()),
Alex Lightef28d242020-11-17 20:21:51 -08001589 num_phi_placeholders_(GetGraph()->GetBlocks().size() *
1590 heap_location_collector_.GetNumberOfHeapLocations()),
Vladimir Marko3224f382020-06-23 14:19:53 +01001591 heap_values_for_(graph->GetBlocks().size(),
1592 ScopedArenaVector<ValueRecord>(allocator_.Adapter(kArenaAllocLSE)),
1593 allocator_.Adapter(kArenaAllocLSE)),
Vladimir Marko3224f382020-06-23 14:19:53 +01001594 loads_and_stores_(allocator_.Adapter(kArenaAllocLSE)),
Vladimir Marko9e3fe992020-08-25 16:17:51 +01001595 // We may add new instructions (default values, Phis) but we're not adding loads
1596 // or stores, so we shall not need to resize following vector and BitVector.
1597 substitute_instructions_for_loads_(graph->GetCurrentInstructionId(),
1598 nullptr,
1599 allocator_.Adapter(kArenaAllocLSE)),
Alex Light3a73ffb2021-01-25 14:11:05 +00001600 intermediate_values_(allocator_.Adapter(kArenaAllocLSE)),
Vladimir Marko3224f382020-06-23 14:19:53 +01001601 kept_stores_(&allocator_,
Alex Light3a73ffb2021-01-25 14:11:05 +00001602 /*start_bits=*/graph->GetCurrentInstructionId(),
1603 /*expandable=*/false,
Vladimir Marko3224f382020-06-23 14:19:53 +01001604 kArenaAllocLSE),
1605 phi_placeholders_to_search_for_kept_stores_(&allocator_,
Alex Lightef28d242020-11-17 20:21:51 -08001606 num_phi_placeholders_,
Alex Light3a73ffb2021-01-25 14:11:05 +00001607 /*expandable=*/false,
Vladimir Marko3224f382020-06-23 14:19:53 +01001608 kArenaAllocLSE),
1609 loads_requiring_loop_phi_(allocator_.Adapter(kArenaAllocLSE)),
Vladimir Markoc9f4a372021-03-11 10:38:34 +00001610 store_records_(store_records_buffer_,
Vladimir Markoa718d642021-03-10 15:36:40 +00001611 kStoreRecordsInitialBufferSize,
1612 allocator_.Adapter(kArenaAllocLSE)),
Alex Lightef28d242020-11-17 20:21:51 -08001613 phi_placeholder_replacements_(num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01001614 Value::Invalid(),
1615 allocator_.Adapter(kArenaAllocLSE)),
Alex Light86fe9b82020-11-16 16:54:01 +00001616 kept_merged_unknowns_(&allocator_,
Alex Light3a73ffb2021-01-25 14:11:05 +00001617 /*start_bits=*/num_phi_placeholders_,
1618 /*expandable=*/false,
Alex Light86fe9b82020-11-16 16:54:01 +00001619 kArenaAllocLSE),
Alex Light09e23372021-01-15 08:42:11 -08001620 singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)),
Alex Light3a73ffb2021-01-25 14:11:05 +00001621 field_infos_(heap_location_collector_.GetNumberOfHeapLocations(),
1622 allocator_.Adapter(kArenaAllocLSE)),
Alex Light09e23372021-01-15 08:42:11 -08001623 current_phase_(Phase::kLoadElimination) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001624 // Clear bit vectors.
1625 phi_placeholders_to_search_for_kept_stores_.ClearAllBits();
1626 kept_stores_.ClearAllBits();
1627}
1628
1629LSEVisitor::Value LSEVisitor::PrepareLoopValue(HBasicBlock* block, size_t idx) {
1630 // If the pre-header value is known (which implies that the reference dominates this
1631 // block), use a Phi placeholder for the value in the loop header. If all predecessors
1632 // are later found to have a known value, we can replace loads from this location,
1633 // either with the pre-header value or with a new Phi. For array locations, the index
1634 // may be defined inside the loop but the only known value in that case should be the
1635 // default value or a Phi placeholder that can be replaced only with the default value.
1636 HLoopInformation* loop_info = block->GetLoopInformation();
1637 uint32_t pre_header_block_id = loop_info->GetPreHeader()->GetBlockId();
1638 Value pre_header_value = ReplacementOrValue(heap_values_for_[pre_header_block_id][idx].value);
1639 if (pre_header_value.IsUnknown()) {
Alex Light86fe9b82020-11-16 16:54:01 +00001640 return pre_header_value;
Vladimir Marko3224f382020-06-23 14:19:53 +01001641 }
1642 if (kIsDebugBuild) {
1643 // Check that the reference indeed dominates this loop.
1644 HeapLocation* location = heap_location_collector_.GetHeapLocation(idx);
1645 HInstruction* ref = location->GetReferenceInfo()->GetReference();
Alex Light86fe9b82020-11-16 16:54:01 +00001646 CHECK(ref->GetBlock() != block && ref->GetBlock()->Dominates(block))
1647 << GetGraph()->PrettyMethod();
Vladimir Marko3224f382020-06-23 14:19:53 +01001648 // Check that the index, if defined inside the loop, tracks a default value
1649 // or a Phi placeholder requiring a loop Phi.
1650 HInstruction* index = location->GetIndex();
1651 if (index != nullptr && loop_info->Contains(*index->GetBlock())) {
Alex Light86fe9b82020-11-16 16:54:01 +00001652 CHECK(pre_header_value.NeedsLoopPhi() || pre_header_value.Equals(Value::Default()))
1653 << GetGraph()->PrettyMethod() << " blk: " << block->GetBlockId() << " "
1654 << pre_header_value;
Vladimir Marko3224f382020-06-23 14:19:53 +01001655 }
1656 }
Alex Lightef28d242020-11-17 20:21:51 -08001657 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Vladimir Marko3224f382020-06-23 14:19:53 +01001658 return ReplacementOrValue(Value::ForLoopPhiPlaceholder(phi_placeholder));
1659}
1660
1661LSEVisitor::Value LSEVisitor::PrepareLoopStoredBy(HBasicBlock* block, size_t idx) {
1662 // Use the Phi placeholder for `stored_by` to make sure all incoming stores are kept
1663 // if the value in the location escapes. This is not applicable to singletons that are
1664 // defined inside the loop as they shall be dead in the loop header.
Santiago Aboy Solanes4ebfcac2022-03-31 11:19:25 +01001665 const ReferenceInfo* ref_info = heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo();
1666 const HInstruction* reference = ref_info->GetReference();
1667 // Finalizable objects always escape.
1668 const bool is_finalizable =
1669 reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable();
Vladimir Marko3224f382020-06-23 14:19:53 +01001670 if (ref_info->IsSingleton() &&
Santiago Aboy Solanes4ebfcac2022-03-31 11:19:25 +01001671 block->GetLoopInformation()->Contains(*reference->GetBlock()) &&
1672 !is_finalizable) {
Alex Lightf5a84cb2021-01-15 08:35:38 -08001673 return Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01001674 }
Alex Lightef28d242020-11-17 20:21:51 -08001675 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Vladimir Marko3224f382020-06-23 14:19:53 +01001676 return Value::ForLoopPhiPlaceholder(phi_placeholder);
1677}
1678
1679void LSEVisitor::PrepareLoopRecords(HBasicBlock* block) {
1680 DCHECK(block->IsLoopHeader());
1681 int block_id = block->GetBlockId();
1682 HBasicBlock* pre_header = block->GetLoopInformation()->GetPreHeader();
1683 ScopedArenaVector<ValueRecord>& pre_header_heap_values =
1684 heap_values_for_[pre_header->GetBlockId()];
1685 size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
1686 DCHECK_EQ(num_heap_locations, pre_header_heap_values.size());
1687 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block_id];
1688 DCHECK(heap_values.empty());
1689
1690 // Don't eliminate loads in irreducible loops.
1691 if (block->GetLoopInformation()->IsIrreducible()) {
1692 heap_values.resize(num_heap_locations,
Alex Light3a73ffb2021-01-25 14:11:05 +00001693 {/*value=*/Value::Invalid(), /*stored_by=*/Value::PureUnknown()});
Vladimir Marko3224f382020-06-23 14:19:53 +01001694 // Also keep the stores before the loop header, including in blocks that were not visited yet.
Alex Light3a73ffb2021-01-25 14:11:05 +00001695 bool is_osr = GetGraph()->IsCompilingOsr();
Vladimir Marko3224f382020-06-23 14:19:53 +01001696 for (size_t idx = 0u; idx != num_heap_locations; ++idx) {
Alex Light3a73ffb2021-01-25 14:11:05 +00001697 heap_values[idx].value =
1698 is_osr ? Value::PureUnknown()
1699 : Value::MergedUnknown(GetPhiPlaceholder(block->GetBlockId(), idx));
Vladimir Marko3224f382020-06-23 14:19:53 +01001700 KeepStores(Value::ForLoopPhiPlaceholder(GetPhiPlaceholder(block->GetBlockId(), idx)));
1701 }
1702 return;
1703 }
1704
1705 // Fill `heap_values` based on values from pre-header.
1706 heap_values.reserve(num_heap_locations);
1707 for (size_t idx = 0u; idx != num_heap_locations; ++idx) {
1708 heap_values.push_back({ PrepareLoopValue(block, idx), PrepareLoopStoredBy(block, idx) });
1709 }
1710}
1711
1712LSEVisitor::Value LSEVisitor::MergePredecessorValues(HBasicBlock* block, size_t idx) {
1713 ArrayRef<HBasicBlock* const> predecessors(block->GetPredecessors());
1714 DCHECK(!predecessors.empty());
1715 Value merged_value =
1716 ReplacementOrValue(heap_values_for_[predecessors[0]->GetBlockId()][idx].value);
1717 for (size_t i = 1u, size = predecessors.size(); i != size; ++i) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001718 Value pred_value =
1719 ReplacementOrValue(heap_values_for_[predecessors[i]->GetBlockId()][idx].value);
Alex Light86fe9b82020-11-16 16:54:01 +00001720 if (pred_value.Equals(merged_value)) {
1721 // Value is the same. No need to update our merged value.
1722 continue;
1723 } else if (pred_value.IsUnknown() || merged_value.IsUnknown()) {
1724 // If one is unknown and the other is a different type of unknown
Alex Lightef28d242020-11-17 20:21:51 -08001725 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Alex Light86fe9b82020-11-16 16:54:01 +00001726 merged_value = Value::MergedUnknown(phi_placeholder);
1727 // We know that at least one of the merge points is unknown (and both are
1728 // not pure-unknowns since that's captured above). This means that the
1729 // overall value needs to be a MergedUnknown. Just return that.
1730 break;
1731 } else {
Vladimir Marko3224f382020-06-23 14:19:53 +01001732 // There are conflicting known values. We may still be able to replace loads with a Phi.
Alex Lightef28d242020-11-17 20:21:51 -08001733 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Vladimir Marko3224f382020-06-23 14:19:53 +01001734 // Propagate the need for a new loop Phi from all predecessors.
1735 bool needs_loop_phi = merged_value.NeedsLoopPhi() || pred_value.NeedsLoopPhi();
1736 merged_value = ReplacementOrValue(Value::ForPhiPlaceholder(phi_placeholder, needs_loop_phi));
1737 }
1738 }
Santiago Aboy Solanes872ec722022-02-18 14:10:25 +00001739 DCHECK_IMPLIES(merged_value.IsPureUnknown(), block->GetPredecessors().size() <= 1)
Alex Light86fe9b82020-11-16 16:54:01 +00001740 << merged_value << " in " << GetGraph()->PrettyMethod();
Vladimir Marko3224f382020-06-23 14:19:53 +01001741 return merged_value;
1742}
1743
1744void LSEVisitor::MergePredecessorRecords(HBasicBlock* block) {
1745 if (block->IsExitBlock()) {
1746 // Exit block doesn't really merge values since the control flow ends in
1747 // its predecessors. Each predecessor needs to make sure stores are kept
1748 // if necessary.
1749 return;
1750 }
1751
1752 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
1753 DCHECK(heap_values.empty());
1754 size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
Santiago Aboy Solanes6df70e92022-11-04 11:22:45 +00001755 if (block->GetPredecessors().empty() || block->IsCatchBlock()) {
Santiago Aboy Solanes9e3c3712022-04-08 13:24:05 +00001756 DCHECK_IMPLIES(block->GetPredecessors().empty(), block->IsEntryBlock());
Vladimir Marko3224f382020-06-23 14:19:53 +01001757 heap_values.resize(num_heap_locations,
Alex Lightf5a84cb2021-01-15 08:35:38 -08001758 {/*value=*/Value::PureUnknown(), /*stored_by=*/Value::PureUnknown()});
Vladimir Marko3224f382020-06-23 14:19:53 +01001759 return;
1760 }
1761
1762 heap_values.reserve(num_heap_locations);
1763 for (size_t idx = 0u; idx != num_heap_locations; ++idx) {
1764 Value merged_value = MergePredecessorValues(block, idx);
1765 if (kIsDebugBuild) {
1766 if (merged_value.NeedsPhi()) {
Alex Lightef28d242020-11-17 20:21:51 -08001767 uint32_t block_id = merged_value.GetPhiPlaceholder().GetBlockId();
Vladimir Marko3224f382020-06-23 14:19:53 +01001768 CHECK(GetGraph()->GetBlocks()[block_id]->Dominates(block));
1769 } else if (merged_value.IsInstruction()) {
1770 CHECK(merged_value.GetInstruction()->GetBlock()->Dominates(block));
1771 }
1772 }
1773 ArrayRef<HBasicBlock* const> predecessors(block->GetPredecessors());
1774 Value merged_stored_by = heap_values_for_[predecessors[0]->GetBlockId()][idx].stored_by;
Vladimir Markocbeedc82020-08-25 14:31:10 +01001775 for (size_t predecessor_idx = 1u; predecessor_idx != predecessors.size(); ++predecessor_idx) {
1776 uint32_t predecessor_block_id = predecessors[predecessor_idx]->GetBlockId();
1777 Value stored_by = heap_values_for_[predecessor_block_id][idx].stored_by;
1778 if ((!stored_by.IsUnknown() || !merged_stored_by.IsUnknown()) &&
1779 !merged_stored_by.Equals(stored_by)) {
1780 // Use the Phi placeholder to track that we need to keep stores from all predecessors.
Alex Lightef28d242020-11-17 20:21:51 -08001781 PhiPlaceholder phi_placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Vladimir Markocbeedc82020-08-25 14:31:10 +01001782 merged_stored_by = Value::ForNonLoopPhiPlaceholder(phi_placeholder);
1783 break;
1784 }
Vladimir Marko3224f382020-06-23 14:19:53 +01001785 }
1786 heap_values.push_back({ merged_value, merged_stored_by });
1787 }
1788}
1789
1790static HInstruction* FindOrConstructNonLoopPhi(
1791 HBasicBlock* block,
1792 const ScopedArenaVector<HInstruction*>& phi_inputs,
1793 DataType::Type type) {
1794 for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
1795 HInstruction* phi = phi_it.Current();
1796 DCHECK_EQ(phi->InputCount(), phi_inputs.size());
1797 auto cmp = [](HInstruction* lhs, const HUserRecord<HInstruction*>& rhs) {
1798 return lhs == rhs.GetInstruction();
1799 };
1800 if (std::equal(phi_inputs.begin(), phi_inputs.end(), phi->GetInputRecords().begin(), cmp)) {
1801 return phi;
1802 }
1803 }
1804 ArenaAllocator* allocator = block->GetGraph()->GetAllocator();
1805 HPhi* phi = new (allocator) HPhi(allocator, kNoRegNumber, phi_inputs.size(), type);
1806 for (size_t i = 0, size = phi_inputs.size(); i != size; ++i) {
1807 DCHECK_NE(phi_inputs[i]->GetType(), DataType::Type::kVoid) << phi_inputs[i]->DebugName();
1808 phi->SetRawInputAt(i, phi_inputs[i]);
1809 }
1810 block->AddPhi(phi);
1811 if (type == DataType::Type::kReference) {
1812 // Update reference type information. Pass invalid handles, these are not used for Phis.
1813 ReferenceTypePropagation rtp_fixup(block->GetGraph(),
Vladimir Marko3224f382020-06-23 14:19:53 +01001814 Handle<mirror::DexCache>(),
1815 /* is_first_run= */ false);
1816 rtp_fixup.Visit(phi);
1817 }
1818 return phi;
1819}
1820
Alex Lightef28d242020-11-17 20:21:51 -08001821void LSEVisitor::MaterializeNonLoopPhis(PhiPlaceholder phi_placeholder, DataType::Type type) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001822 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid());
1823 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
Alex Lightef28d242020-11-17 20:21:51 -08001824 size_t idx = phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01001825
1826 // Use local allocator to reduce peak memory usage.
1827 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
1828 // Reuse the same vector for collecting phi inputs.
1829 ScopedArenaVector<HInstruction*> phi_inputs(allocator.Adapter(kArenaAllocLSE));
1830
Alex Lightef28d242020-11-17 20:21:51 -08001831 ScopedArenaVector<PhiPlaceholder> work_queue(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01001832 work_queue.push_back(phi_placeholder);
1833 while (!work_queue.empty()) {
Alex Lightef28d242020-11-17 20:21:51 -08001834 PhiPlaceholder current_phi_placeholder = work_queue.back();
Vladimir Marko3224f382020-06-23 14:19:53 +01001835 if (phi_placeholder_replacements_[PhiPlaceholderIndex(current_phi_placeholder)].IsValid()) {
1836 // This Phi placeholder was pushed to the `work_queue` followed by another Phi placeholder
1837 // that directly or indirectly depends on it, so it was already processed as part of the
1838 // other Phi placeholder's dependencies before this one got back to the top of the stack.
1839 work_queue.pop_back();
1840 continue;
1841 }
Alex Lightef28d242020-11-17 20:21:51 -08001842 uint32_t current_block_id = current_phi_placeholder.GetBlockId();
Vladimir Marko3224f382020-06-23 14:19:53 +01001843 HBasicBlock* current_block = blocks[current_block_id];
1844 DCHECK_GE(current_block->GetPredecessors().size(), 2u);
1845
1846 // Non-loop Phis cannot depend on a loop Phi, so we should not see any loop header here.
1847 // And the only way for such merged value to reach a different heap location is through
1848 // a load at which point we materialize the Phi. Therefore all non-loop Phi placeholders
1849 // seen here are tied to one heap location.
Alex Light09e23372021-01-15 08:42:11 -08001850 DCHECK(!current_block->IsLoopHeader())
1851 << current_phi_placeholder << " phase: " << current_phase_;
Alex Lightef28d242020-11-17 20:21:51 -08001852 DCHECK_EQ(current_phi_placeholder.GetHeapLocation(), idx);
Vladimir Marko3224f382020-06-23 14:19:53 +01001853
1854 phi_inputs.clear();
1855 for (HBasicBlock* predecessor : current_block->GetPredecessors()) {
1856 Value pred_value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
Alex Light3a73ffb2021-01-25 14:11:05 +00001857 DCHECK(!pred_value.IsPureUnknown()) << pred_value << " block " << current_block->GetBlockId()
1858 << " pred: " << predecessor->GetBlockId();
1859 if (pred_value.NeedsNonLoopPhi() ||
1860 (current_phase_ == Phase::kPartialElimination && pred_value.IsMergedUnknown())) {
Vladimir Marko3224f382020-06-23 14:19:53 +01001861 // We need to process the Phi placeholder first.
1862 work_queue.push_back(pred_value.GetPhiPlaceholder());
1863 } else if (pred_value.IsDefault()) {
1864 phi_inputs.push_back(GetDefaultValue(type));
1865 } else {
Alex Light09e23372021-01-15 08:42:11 -08001866 DCHECK(pred_value.IsInstruction()) << pred_value << " block " << current_block->GetBlockId()
1867 << " pred: " << predecessor->GetBlockId();
Vladimir Marko3224f382020-06-23 14:19:53 +01001868 phi_inputs.push_back(pred_value.GetInstruction());
1869 }
1870 }
1871 if (phi_inputs.size() == current_block->GetPredecessors().size()) {
1872 // All inputs are available. Find or construct the Phi replacement.
1873 phi_placeholder_replacements_[PhiPlaceholderIndex(current_phi_placeholder)] =
1874 Value::ForInstruction(FindOrConstructNonLoopPhi(current_block, phi_inputs, type));
1875 // Remove the block from the queue.
1876 DCHECK_EQ(current_phi_placeholder, work_queue.back());
1877 work_queue.pop_back();
1878 }
1879 }
1880}
1881
1882void LSEVisitor::VisitGetLocation(HInstruction* instruction, size_t idx) {
1883 DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound);
1884 uint32_t block_id = instruction->GetBlock()->GetBlockId();
1885 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block_id];
1886 ValueRecord& record = heap_values[idx];
Alex Light3a73ffb2021-01-25 14:11:05 +00001887 if (instruction->IsFieldAccess()) {
1888 RecordFieldInfo(&instruction->GetFieldInfo(), idx);
1889 }
Vladimir Marko3224f382020-06-23 14:19:53 +01001890 DCHECK(record.value.IsUnknown() || record.value.Equals(ReplacementOrValue(record.value)));
Alex Light3a73ffb2021-01-25 14:11:05 +00001891 // If we are unknown, we either come from somewhere untracked or we can reconstruct the partial
1892 // value.
1893 DCHECK(!record.value.IsPureUnknown() ||
1894 heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo() == nullptr ||
1895 !heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo()->IsPartialSingleton())
1896 << "In " << GetGraph()->PrettyMethod() << ": " << record.value << " for " << *instruction;
1897 intermediate_values_.insert({instruction, record.value});
Vladimir Marko3224f382020-06-23 14:19:53 +01001898 loads_and_stores_.push_back({ instruction, idx });
1899 if ((record.value.IsDefault() || record.value.NeedsNonLoopPhi()) &&
1900 !IsDefaultOrPhiAllowedForLoad(instruction)) {
Alex Lightf5a84cb2021-01-15 08:35:38 -08001901 record.value = Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01001902 }
1903 if (record.value.IsDefault()) {
Vladimir Markocbeedc82020-08-25 14:31:10 +01001904 KeepStores(record.stored_by);
Vladimir Marko3224f382020-06-23 14:19:53 +01001905 HInstruction* constant = GetDefaultValue(instruction->GetType());
1906 AddRemovedLoad(instruction, constant);
1907 record.value = Value::ForInstruction(constant);
1908 } else if (record.value.IsUnknown()) {
1909 // Load isn't eliminated. Put the load as the value into the HeapLocation.
1910 // This acts like GVN but with better aliasing analysis.
Alex Light86fe9b82020-11-16 16:54:01 +00001911 Value old_value = record.value;
Vladimir Marko3224f382020-06-23 14:19:53 +01001912 record.value = Value::ForInstruction(instruction);
1913 KeepStoresIfAliasedToLocation(heap_values, idx);
Alex Light86fe9b82020-11-16 16:54:01 +00001914 KeepStores(old_value);
Vladimir Marko3224f382020-06-23 14:19:53 +01001915 } else if (record.value.NeedsLoopPhi()) {
1916 // We do not know yet if the value is known for all back edges. Record for future processing.
1917 loads_requiring_loop_phi_.insert(std::make_pair(instruction, record));
1918 } else {
1919 // This load can be eliminated but we may need to construct non-loop Phis.
1920 if (record.value.NeedsNonLoopPhi()) {
1921 MaterializeNonLoopPhis(record.value.GetPhiPlaceholder(), instruction->GetType());
1922 record.value = Replacement(record.value);
1923 }
1924 HInstruction* heap_value = FindSubstitute(record.value.GetInstruction());
1925 AddRemovedLoad(instruction, heap_value);
Vladimir Marko3224f382020-06-23 14:19:53 +01001926 }
1927}
1928
1929void LSEVisitor::VisitSetLocation(HInstruction* instruction, size_t idx, HInstruction* value) {
1930 DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound);
1931 DCHECK(!IsStore(value)) << value->DebugName();
Alex Light3a73ffb2021-01-25 14:11:05 +00001932 if (instruction->IsFieldAccess()) {
1933 RecordFieldInfo(&instruction->GetFieldInfo(), idx);
1934 }
Vladimir Marko3224f382020-06-23 14:19:53 +01001935 // value may already have a substitute.
1936 value = FindSubstitute(value);
1937 HBasicBlock* block = instruction->GetBlock();
1938 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
1939 ValueRecord& record = heap_values[idx];
Santiago Aboy Solanes872ec722022-02-18 14:10:25 +00001940 DCHECK_IMPLIES(record.value.IsInstruction(),
1941 FindSubstitute(record.value.GetInstruction()) == record.value.GetInstruction());
Vladimir Marko3224f382020-06-23 14:19:53 +01001942
1943 if (record.value.Equals(value)) {
1944 // Store into the heap location with the same value.
1945 // This store can be eliminated right away.
1946 block->RemoveInstruction(instruction);
1947 return;
1948 }
1949
1950 store_records_.insert(std::make_pair(instruction, StoreRecord{record, value}));
1951 loads_and_stores_.push_back({ instruction, idx });
1952
1953 // If the `record.stored_by` specified a store from this block, it shall be removed
1954 // at the end, except for throwing ArraySet; it cannot be marked for keeping in
1955 // `kept_stores_` anymore after we update the `record.stored_by` below.
1956 DCHECK(!record.stored_by.IsInstruction() ||
1957 record.stored_by.GetInstruction()->GetBlock() != block ||
1958 record.stored_by.GetInstruction()->CanThrow() ||
1959 !kept_stores_.IsBitSet(record.stored_by.GetInstruction()->GetId()));
1960
1961 if (instruction->CanThrow()) {
1962 // Previous stores can become visible.
Santiago Aboy Solanesea24a142022-04-22 16:20:39 +01001963 HandleThrowingInstruction(instruction);
Vladimir Marko3224f382020-06-23 14:19:53 +01001964 // We cannot remove a possibly throwing store.
1965 // After marking it as kept, it does not matter if we track it in `stored_by` or not.
1966 kept_stores_.SetBit(instruction->GetId());
1967 }
1968
1969 // Update the record.
1970 auto it = loads_requiring_loop_phi_.find(value);
1971 if (it != loads_requiring_loop_phi_.end()) {
1972 // Propapate the Phi placeholder to the record.
1973 record.value = it->second.value;
1974 DCHECK(record.value.NeedsLoopPhi());
1975 } else {
1976 record.value = Value::ForInstruction(value);
1977 }
1978 // Track the store in the value record. If the value is loaded or needed after
1979 // return/deoptimization later, this store isn't really redundant.
1980 record.stored_by = Value::ForInstruction(instruction);
1981
1982 // This store may kill values in other heap locations due to aliasing.
1983 for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
1984 if (i == idx ||
1985 heap_values[i].value.IsUnknown() ||
1986 CanValueBeKeptIfSameAsNew(heap_values[i].value, value, instruction) ||
1987 !heap_location_collector_.MayAlias(i, idx)) {
1988 continue;
1989 }
1990 // Kill heap locations that may alias and keep previous stores to these locations.
1991 KeepStores(heap_values[i].stored_by);
Alex Lightf5a84cb2021-01-15 08:35:38 -08001992 heap_values[i].stored_by = Value::PureUnknown();
Alex Light3a73ffb2021-01-25 14:11:05 +00001993 heap_values[i].value = Value::PartialUnknown(heap_values[i].value);
Vladimir Marko3224f382020-06-23 14:19:53 +01001994 }
1995}
1996
1997void LSEVisitor::VisitBasicBlock(HBasicBlock* block) {
1998 // Populate the heap_values array for this block.
1999 // TODO: try to reuse the heap_values array from one predecessor if possible.
2000 if (block->IsLoopHeader()) {
2001 PrepareLoopRecords(block);
2002 } else {
2003 MergePredecessorRecords(block);
2004 }
2005 // Visit instructions.
2006 HGraphVisitor::VisitBasicBlock(block);
2007}
2008
Vladimir Markodac82392021-05-10 15:44:24 +00002009bool LSEVisitor::MayAliasOnBackEdge(HBasicBlock* loop_header, size_t idx1, size_t idx2) const {
2010 DCHECK_NE(idx1, idx2);
2011 DCHECK(loop_header->IsLoopHeader());
2012 if (heap_location_collector_.MayAlias(idx1, idx2)) {
2013 return true;
2014 }
2015 // For array locations with index defined inside the loop, include
2016 // all other locations in the array, even those that LSA declares
2017 // non-aliasing, such as `a[i]` and `a[i + 1]`, as they may actually
2018 // refer to the same locations for different iterations. (LSA's
2019 // `ComputeMayAlias()` does not consider different loop iterations.)
2020 HeapLocation* loc1 = heap_location_collector_.GetHeapLocation(idx1);
2021 HeapLocation* loc2 = heap_location_collector_.GetHeapLocation(idx2);
2022 if (loc1->IsArray() &&
2023 loc2->IsArray() &&
2024 HeapLocationCollector::CanReferencesAlias(loc1->GetReferenceInfo(),
2025 loc2->GetReferenceInfo())) {
2026 HLoopInformation* loop_info = loop_header->GetLoopInformation();
2027 if (loop_info->Contains(*loc1->GetIndex()->GetBlock()) ||
2028 loop_info->Contains(*loc2->GetIndex()->GetBlock())) {
2029 // Consider the locations aliasing. Do not optimize the case where both indexes
2030 // are loop invariants defined inside the loop, rely on LICM to pull them out.
2031 return true;
2032 }
2033 }
2034 return false;
2035}
2036
Vladimir Marko3224f382020-06-23 14:19:53 +01002037bool LSEVisitor::TryReplacingLoopPhiPlaceholderWithDefault(
Alex Lightef28d242020-11-17 20:21:51 -08002038 PhiPlaceholder phi_placeholder,
Vladimir Marko3224f382020-06-23 14:19:53 +01002039 DataType::Type type,
Alex Lightef28d242020-11-17 20:21:51 -08002040 /*inout*/ ArenaBitVector* phi_placeholders_to_materialize) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002041 // Use local allocator to reduce peak memory usage.
2042 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2043 ArenaBitVector visited(&allocator,
Alex Lightef28d242020-11-17 20:21:51 -08002044 /*start_bits=*/ num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01002045 /*expandable=*/ false,
2046 kArenaAllocLSE);
2047 visited.ClearAllBits();
Alex Lightef28d242020-11-17 20:21:51 -08002048 ScopedArenaVector<PhiPlaceholder> work_queue(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01002049
2050 // Use depth first search to check if any non-Phi input is unknown.
2051 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
2052 size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
2053 visited.SetBit(PhiPlaceholderIndex(phi_placeholder));
2054 work_queue.push_back(phi_placeholder);
2055 while (!work_queue.empty()) {
Alex Lightef28d242020-11-17 20:21:51 -08002056 PhiPlaceholder current_phi_placeholder = work_queue.back();
Vladimir Marko3224f382020-06-23 14:19:53 +01002057 work_queue.pop_back();
Alex Lightef28d242020-11-17 20:21:51 -08002058 HBasicBlock* block = blocks[current_phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002059 DCHECK_GE(block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002060 size_t idx = current_phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002061 for (HBasicBlock* predecessor : block->GetPredecessors()) {
2062 Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
2063 if (value.NeedsPhi()) {
2064 // Visit the predecessor Phi placeholder if it's not visited yet.
2065 if (!visited.IsBitSet(PhiPlaceholderIndex(value))) {
2066 visited.SetBit(PhiPlaceholderIndex(value));
2067 work_queue.push_back(value.GetPhiPlaceholder());
2068 }
2069 } else if (!value.Equals(Value::Default())) {
2070 return false; // Report failure.
2071 }
2072 }
2073 if (block->IsLoopHeader()) {
2074 // For back-edges we need to check all locations that write to the same array,
2075 // even those that LSA declares non-aliasing, such as `a[i]` and `a[i + 1]`
2076 // as they may actually refer to the same locations for different iterations.
2077 for (size_t i = 0; i != num_heap_locations; ++i) {
2078 if (i == idx ||
2079 heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo() !=
2080 heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo()) {
2081 continue;
2082 }
2083 for (HBasicBlock* predecessor : block->GetPredecessors()) {
2084 // Check if there were any writes to this location.
2085 // Note: We could simply process the values but due to the vector operation
2086 // carve-out (see `IsDefaultOrPhiAllowedForLoad()`), a vector load can cause
2087 // the value to change and not be equal to default. To work around this and
2088 // allow replacing the non-vector load of loop-invariant default values
2089 // anyway, skip over paths that do not have any writes.
2090 ValueRecord record = heap_values_for_[predecessor->GetBlockId()][i];
2091 while (record.stored_by.NeedsLoopPhi() &&
Alex Lightef28d242020-11-17 20:21:51 -08002092 blocks[record.stored_by.GetPhiPlaceholder().GetBlockId()]->IsLoopHeader()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002093 HLoopInformation* loop_info =
Alex Lightef28d242020-11-17 20:21:51 -08002094 blocks[record.stored_by.GetPhiPlaceholder().GetBlockId()]->GetLoopInformation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002095 record = heap_values_for_[loop_info->GetPreHeader()->GetBlockId()][i];
2096 }
2097 Value value = ReplacementOrValue(record.value);
2098 if (value.NeedsPhi()) {
2099 // Visit the predecessor Phi placeholder if it's not visited yet.
2100 if (!visited.IsBitSet(PhiPlaceholderIndex(value))) {
2101 visited.SetBit(PhiPlaceholderIndex(value));
2102 work_queue.push_back(value.GetPhiPlaceholder());
2103 }
2104 } else if (!value.Equals(Value::Default())) {
2105 return false; // Report failure.
2106 }
2107 }
2108 }
2109 }
2110 }
2111
2112 // Record replacement and report success.
2113 HInstruction* replacement = GetDefaultValue(type);
2114 for (uint32_t phi_placeholder_index : visited.Indexes()) {
2115 DCHECK(phi_placeholder_replacements_[phi_placeholder_index].IsInvalid());
Santiago Aboy Solanes9ec5df72023-02-03 16:02:27 +00002116 phi_placeholder_replacements_[phi_placeholder_index] = Value::ForInstruction(replacement);
Vladimir Marko3224f382020-06-23 14:19:53 +01002117 }
Santiago Aboy Solanes9ec5df72023-02-03 16:02:27 +00002118 phi_placeholders_to_materialize->Subtract(&visited);
Vladimir Marko3224f382020-06-23 14:19:53 +01002119 return true;
2120}
2121
2122bool LSEVisitor::TryReplacingLoopPhiPlaceholderWithSingleInput(
Alex Lightef28d242020-11-17 20:21:51 -08002123 PhiPlaceholder phi_placeholder,
2124 /*inout*/ ArenaBitVector* phi_placeholders_to_materialize) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002125 // Use local allocator to reduce peak memory usage.
2126 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2127 ArenaBitVector visited(&allocator,
Alex Lightef28d242020-11-17 20:21:51 -08002128 /*start_bits=*/ num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01002129 /*expandable=*/ false,
2130 kArenaAllocLSE);
2131 visited.ClearAllBits();
Alex Lightef28d242020-11-17 20:21:51 -08002132 ScopedArenaVector<PhiPlaceholder> work_queue(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01002133
2134 // Use depth first search to check if any non-Phi input is unknown.
2135 HInstruction* replacement = nullptr;
2136 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
2137 visited.SetBit(PhiPlaceholderIndex(phi_placeholder));
2138 work_queue.push_back(phi_placeholder);
2139 while (!work_queue.empty()) {
Alex Lightef28d242020-11-17 20:21:51 -08002140 PhiPlaceholder current_phi_placeholder = work_queue.back();
Vladimir Marko3224f382020-06-23 14:19:53 +01002141 work_queue.pop_back();
Alex Lightef28d242020-11-17 20:21:51 -08002142 HBasicBlock* current_block = blocks[current_phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002143 DCHECK_GE(current_block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002144 size_t idx = current_phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002145 for (HBasicBlock* predecessor : current_block->GetPredecessors()) {
2146 Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
2147 if (value.NeedsPhi()) {
2148 // Visit the predecessor Phi placeholder if it's not visited yet.
2149 if (!visited.IsBitSet(PhiPlaceholderIndex(value))) {
2150 visited.SetBit(PhiPlaceholderIndex(value));
2151 work_queue.push_back(value.GetPhiPlaceholder());
2152 }
2153 } else {
2154 if (!value.IsInstruction() ||
2155 (replacement != nullptr && replacement != value.GetInstruction())) {
2156 return false; // Report failure.
2157 }
2158 replacement = value.GetInstruction();
2159 }
2160 }
Vladimir Markodac82392021-05-10 15:44:24 +00002161 // While `TryReplacingLoopPhiPlaceholderWithDefault()` has special treatment
2162 // for back-edges, it is not needed here. When looking for a single input
2163 // instruction coming from before the loop, the array index must also be
2164 // defined before the loop and the aliasing analysis done by LSA is sufficient.
2165 // Any writes of a different value with an index that is not loop invariant
2166 // would invalidate the heap location in `VisitSetLocation()`.
Vladimir Marko3224f382020-06-23 14:19:53 +01002167 }
2168
2169 // Record replacement and report success.
2170 DCHECK(replacement != nullptr);
2171 for (uint32_t phi_placeholder_index : visited.Indexes()) {
2172 DCHECK(phi_placeholder_replacements_[phi_placeholder_index].IsInvalid());
Santiago Aboy Solanes9ec5df72023-02-03 16:02:27 +00002173 phi_placeholder_replacements_[phi_placeholder_index] = Value::ForInstruction(replacement);
Vladimir Marko3224f382020-06-23 14:19:53 +01002174 }
Santiago Aboy Solanes9ec5df72023-02-03 16:02:27 +00002175 phi_placeholders_to_materialize->Subtract(&visited);
Vladimir Marko3224f382020-06-23 14:19:53 +01002176 return true;
2177}
2178
Alex Lightef28d242020-11-17 20:21:51 -08002179std::optional<LSEVisitor::PhiPlaceholder> LSEVisitor::FindLoopPhisToMaterialize(
2180 PhiPlaceholder phi_placeholder,
2181 /*inout*/ ArenaBitVector* phi_placeholders_to_materialize,
Vladimir Marko3224f382020-06-23 14:19:53 +01002182 DataType::Type type,
2183 bool can_use_default_or_phi) {
2184 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid());
2185
2186 // Use local allocator to reduce peak memory usage.
2187 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
Alex Lightef28d242020-11-17 20:21:51 -08002188 ScopedArenaVector<PhiPlaceholder> work_queue(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01002189
2190 // Use depth first search to check if any non-Phi input is unknown.
2191 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
2192 phi_placeholders_to_materialize->ClearAllBits();
2193 phi_placeholders_to_materialize->SetBit(PhiPlaceholderIndex(phi_placeholder));
2194 work_queue.push_back(phi_placeholder);
2195 while (!work_queue.empty()) {
Alex Lightef28d242020-11-17 20:21:51 -08002196 PhiPlaceholder current_phi_placeholder = work_queue.back();
Vladimir Marko3224f382020-06-23 14:19:53 +01002197 work_queue.pop_back();
2198 if (!phi_placeholders_to_materialize->IsBitSet(PhiPlaceholderIndex(current_phi_placeholder))) {
2199 // Replaced by `TryReplacingLoopPhiPlaceholderWith{Default,SingleInput}()`.
2200 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(current_phi_placeholder)].Equals(
2201 Value::Default()));
2202 continue;
2203 }
Alex Lightef28d242020-11-17 20:21:51 -08002204 HBasicBlock* current_block = blocks[current_phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002205 DCHECK_GE(current_block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002206 size_t idx = current_phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002207 if (current_block->IsLoopHeader()) {
2208 // If the index is defined inside the loop, it may reference different elements of the
2209 // array on each iteration. Since we do not track if all elements of an array are set
2210 // to the same value explicitly, the only known value in pre-header can be the default
2211 // value from NewArray or a Phi placeholder depending on a default value from some outer
2212 // loop pre-header. This Phi placeholder can be replaced only by the default value.
2213 HInstruction* index = heap_location_collector_.GetHeapLocation(idx)->GetIndex();
2214 if (index != nullptr && current_block->GetLoopInformation()->Contains(*index->GetBlock())) {
2215 if (can_use_default_or_phi &&
2216 TryReplacingLoopPhiPlaceholderWithDefault(current_phi_placeholder,
2217 type,
2218 phi_placeholders_to_materialize)) {
2219 continue;
2220 } else {
2221 return current_phi_placeholder; // Report the loop Phi placeholder.
2222 }
2223 }
2224 // A similar situation arises with the index defined outside the loop if we cannot use
2225 // default values or Phis, i.e. for vector loads, as we can only replace the Phi
2226 // placeholder with a single instruction defined before the loop.
2227 if (!can_use_default_or_phi) {
Vladimir Markodac82392021-05-10 15:44:24 +00002228 DCHECK(index != nullptr); // Vector operations are array operations.
Vladimir Marko3224f382020-06-23 14:19:53 +01002229 if (TryReplacingLoopPhiPlaceholderWithSingleInput(current_phi_placeholder,
2230 phi_placeholders_to_materialize)) {
2231 continue;
2232 } else {
2233 return current_phi_placeholder; // Report the loop Phi placeholder.
2234 }
2235 }
2236 }
2237 for (HBasicBlock* predecessor : current_block->GetPredecessors()) {
Vladimir Markodac82392021-05-10 15:44:24 +00002238 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[predecessor->GetBlockId()];
2239 Value value = ReplacementOrValue(heap_values[idx].value);
Vladimir Marko3224f382020-06-23 14:19:53 +01002240 if (value.IsUnknown()) {
2241 // We cannot create a Phi for this loop Phi placeholder.
2242 return current_phi_placeholder; // Report the loop Phi placeholder.
2243 }
Vladimir Markodac82392021-05-10 15:44:24 +00002244 // For arrays, the location may have been clobbered by writes to other locations
2245 // in a loop that LSA does not consider aliasing, such as `a[i]` and `a[i + 1]`.
2246 if (current_block->IsLoopHeader() &&
2247 predecessor != current_block->GetLoopInformation()->GetPreHeader() &&
2248 heap_location_collector_.GetHeapLocation(idx)->GetIndex() != nullptr) {
2249 for (size_t i = 0, size = heap_values.size(); i != size; ++i) {
2250 if (i != idx &&
2251 !heap_values[i].stored_by.IsUnknown() &&
2252 MayAliasOnBackEdge(current_block, idx, i)) {
2253 // We cannot create a Phi for this loop Phi placeholder.
2254 return current_phi_placeholder;
2255 }
2256 }
2257 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002258 if (value.NeedsLoopPhi()) {
2259 // Visit the predecessor Phi placeholder if it's not visited yet.
2260 if (!phi_placeholders_to_materialize->IsBitSet(PhiPlaceholderIndex(value))) {
2261 phi_placeholders_to_materialize->SetBit(PhiPlaceholderIndex(value));
2262 work_queue.push_back(value.GetPhiPlaceholder());
Alex Light3a73ffb2021-01-25 14:11:05 +00002263 LSE_VLOG << "For materialization of " << phi_placeholder
2264 << " we need to materialize " << value;
Vladimir Marko3224f382020-06-23 14:19:53 +01002265 }
2266 }
2267 }
2268 }
2269
2270 // There are no unknown values feeding this Phi, so we can construct the Phis if needed.
Alex Lightef28d242020-11-17 20:21:51 -08002271 return std::nullopt;
Vladimir Marko3224f382020-06-23 14:19:53 +01002272}
2273
2274bool LSEVisitor::MaterializeLoopPhis(const ScopedArenaVector<size_t>& phi_placeholder_indexes,
Alex Light09e23372021-01-15 08:42:11 -08002275 DataType::Type type) {
Alex Light3a73ffb2021-01-25 14:11:05 +00002276 return MaterializeLoopPhis(ArrayRef<const size_t>(phi_placeholder_indexes), type);
2277}
2278
2279bool LSEVisitor::MaterializeLoopPhis(ArrayRef<const size_t> phi_placeholder_indexes,
2280 DataType::Type type) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002281 // Materialize all predecessors that do not need a loop Phi and determine if all inputs
2282 // other than loop Phis are the same.
2283 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
Alex Light1e414eb2021-01-15 08:38:18 -08002284 std::optional<Value> other_value = std::nullopt;
Vladimir Marko3224f382020-06-23 14:19:53 +01002285 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
Alex Lightef28d242020-11-17 20:21:51 -08002286 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_index);
2287 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002288 DCHECK_GE(block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002289 size_t idx = phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002290 for (HBasicBlock* predecessor : block->GetPredecessors()) {
2291 Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
Vladimir Marko642c8f62021-05-21 09:24:03 +01002292 if (value.NeedsNonLoopPhi()) {
Alex Light3a73ffb2021-01-25 14:11:05 +00002293 DCHECK(current_phase_ == Phase::kLoadElimination ||
2294 current_phase_ == Phase::kPartialElimination)
2295 << current_phase_;
Vladimir Marko3224f382020-06-23 14:19:53 +01002296 MaterializeNonLoopPhis(value.GetPhiPlaceholder(), type);
2297 value = Replacement(value);
2298 }
2299 if (!value.NeedsLoopPhi()) {
Alex Light1e414eb2021-01-15 08:38:18 -08002300 if (!other_value) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002301 // The first other value we found.
2302 other_value = value;
Alex Light1e414eb2021-01-15 08:38:18 -08002303 } else if (!other_value->IsInvalid()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002304 // Check if the current `value` differs from the previous `other_value`.
Alex Light1e414eb2021-01-15 08:38:18 -08002305 if (!value.Equals(*other_value)) {
2306 other_value = Value::Invalid();
Vladimir Marko3224f382020-06-23 14:19:53 +01002307 }
2308 }
2309 }
2310 }
2311 }
2312
Alex Light1e414eb2021-01-15 08:38:18 -08002313 DCHECK(other_value.has_value());
2314 if (!other_value->IsInvalid()) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002315 HInstruction* replacement =
Alex Light1e414eb2021-01-15 08:38:18 -08002316 (other_value->IsDefault()) ? GetDefaultValue(type) : other_value->GetInstruction();
Vladimir Marko3224f382020-06-23 14:19:53 +01002317 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
2318 phi_placeholder_replacements_[phi_placeholder_index] = Value::ForInstruction(replacement);
2319 }
2320 return true;
2321 }
2322
2323 // If we're materializing only a single Phi, try to match it with an existing Phi.
2324 // (Matching multiple Phis would need investigation. It may be prohibitively slow.)
2325 // This also covers the case when after replacing a previous set of Phi placeholders,
2326 // we continue with a Phi placeholder that does not really need a loop Phi anymore.
2327 if (phi_placeholder_indexes.size() == 1u) {
Alex Lightef28d242020-11-17 20:21:51 -08002328 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_indexes[0]);
2329 size_t idx = phi_placeholder.GetHeapLocation();
2330 HBasicBlock* block = GetGraph()->GetBlocks()[phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002331 ArrayRef<HBasicBlock* const> predecessors(block->GetPredecessors());
2332 for (HInstructionIterator phi_it(block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
2333 HInstruction* phi = phi_it.Current();
2334 DCHECK_EQ(phi->InputCount(), predecessors.size());
2335 ArrayRef<HUserRecord<HInstruction*>> phi_inputs = phi->GetInputRecords();
2336 auto cmp = [=](const HUserRecord<HInstruction*>& lhs, HBasicBlock* rhs) {
2337 Value value = ReplacementOrValue(heap_values_for_[rhs->GetBlockId()][idx].value);
2338 if (value.NeedsPhi()) {
2339 DCHECK(value.GetPhiPlaceholder() == phi_placeholder);
2340 return lhs.GetInstruction() == phi;
2341 } else {
2342 DCHECK(value.IsDefault() || value.IsInstruction());
2343 return value.Equals(lhs.GetInstruction());
2344 }
2345 };
2346 if (std::equal(phi_inputs.begin(), phi_inputs.end(), predecessors.begin(), cmp)) {
2347 phi_placeholder_replacements_[phi_placeholder_indexes[0]] = Value::ForInstruction(phi);
2348 return true;
2349 }
2350 }
2351 }
2352
Alex Light09e23372021-01-15 08:42:11 -08002353 if (current_phase_ == Phase::kStoreElimination) {
Vladimir Markoed29dce2020-08-21 17:25:16 +01002354 // We're not creating Phis during the final store elimination phase.
Vladimir Marko3224f382020-06-23 14:19:53 +01002355 return false;
2356 }
2357
2358 // There are different inputs to the Phi chain. Create the Phis.
2359 ArenaAllocator* allocator = GetGraph()->GetAllocator();
2360 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
Alex Lightef28d242020-11-17 20:21:51 -08002361 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_index);
2362 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
2363 CHECK_GE(block->GetPredecessors().size(), 2u);
Vladimir Marko3224f382020-06-23 14:19:53 +01002364 phi_placeholder_replacements_[phi_placeholder_index] = Value::ForInstruction(
2365 new (allocator) HPhi(allocator, kNoRegNumber, block->GetPredecessors().size(), type));
2366 }
2367 // Fill the Phi inputs.
2368 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
Alex Lightef28d242020-11-17 20:21:51 -08002369 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_index);
2370 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
2371 size_t idx = phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002372 HInstruction* phi = phi_placeholder_replacements_[phi_placeholder_index].GetInstruction();
Alex Light1e414eb2021-01-15 08:38:18 -08002373 DCHECK(DataType::IsTypeConversionImplicit(type, phi->GetType()))
2374 << "type=" << type << " vs phi-type=" << phi->GetType();
Vladimir Marko3224f382020-06-23 14:19:53 +01002375 for (size_t i = 0, size = block->GetPredecessors().size(); i != size; ++i) {
2376 HBasicBlock* predecessor = block->GetPredecessors()[i];
2377 Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
2378 HInstruction* input = value.IsDefault() ? GetDefaultValue(type) : value.GetInstruction();
2379 DCHECK_NE(input->GetType(), DataType::Type::kVoid);
2380 phi->SetRawInputAt(i, input);
Alex Light1e414eb2021-01-15 08:38:18 -08002381 DCHECK(DataType::IsTypeConversionImplicit(input->GetType(), phi->GetType()))
2382 << " input: " << input->GetType() << value << " phi: " << phi->GetType()
2383 << " request: " << type;
Vladimir Marko3224f382020-06-23 14:19:53 +01002384 }
2385 }
2386 // Add the Phis to their blocks.
2387 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
Alex Lightef28d242020-11-17 20:21:51 -08002388 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(phi_placeholder_index);
2389 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002390 block->AddPhi(phi_placeholder_replacements_[phi_placeholder_index].GetInstruction()->AsPhi());
2391 }
2392 if (type == DataType::Type::kReference) {
2393 ScopedArenaAllocator local_allocator(allocator_.GetArenaStack());
2394 ScopedArenaVector<HInstruction*> phis(local_allocator.Adapter(kArenaAllocLSE));
2395 for (size_t phi_placeholder_index : phi_placeholder_indexes) {
2396 phis.push_back(phi_placeholder_replacements_[phi_placeholder_index].GetInstruction());
2397 }
2398 // Update reference type information. Pass invalid handles, these are not used for Phis.
2399 ReferenceTypePropagation rtp_fixup(GetGraph(),
Vladimir Marko3224f382020-06-23 14:19:53 +01002400 Handle<mirror::DexCache>(),
2401 /* is_first_run= */ false);
2402 rtp_fixup.Visit(ArrayRef<HInstruction* const>(phis));
2403 }
2404
2405 return true;
2406}
2407
2408bool LSEVisitor::MaterializeLoopPhis(const ArenaBitVector& phi_placeholders_to_materialize,
Alex Light09e23372021-01-15 08:42:11 -08002409 DataType::Type type) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002410 // Use local allocator to reduce peak memory usage.
2411 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2412
Vladimir Markoed29dce2020-08-21 17:25:16 +01002413 // We want to recognize when a subset of these loop Phis that do not need other
Vladimir Marko3224f382020-06-23 14:19:53 +01002414 // loop Phis, i.e. a transitive closure, has only one other instruction as an input,
2415 // i.e. that instruction can be used instead of each Phi in the set. See for example
2416 // Main.testLoop{5,6,7,8}() in the test 530-checker-lse. To do that, we shall
2417 // materialize these loop Phis from the smallest transitive closure.
2418
2419 // Construct a matrix of loop phi placeholder dependencies. To reduce the memory usage,
2420 // assign new indexes to the Phi placeholders, making the matrix dense.
Alex Lightef28d242020-11-17 20:21:51 -08002421 ScopedArenaVector<size_t> matrix_indexes(num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01002422 static_cast<size_t>(-1), // Invalid.
2423 allocator.Adapter(kArenaAllocLSE));
2424 ScopedArenaVector<size_t> phi_placeholder_indexes(allocator.Adapter(kArenaAllocLSE));
2425 size_t num_phi_placeholders = phi_placeholders_to_materialize.NumSetBits();
2426 phi_placeholder_indexes.reserve(num_phi_placeholders);
2427 for (uint32_t marker_index : phi_placeholders_to_materialize.Indexes()) {
2428 matrix_indexes[marker_index] = phi_placeholder_indexes.size();
2429 phi_placeholder_indexes.push_back(marker_index);
2430 }
2431 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
2432 ScopedArenaVector<ArenaBitVector*> dependencies(allocator.Adapter(kArenaAllocLSE));
2433 dependencies.reserve(num_phi_placeholders);
2434 for (size_t matrix_index = 0; matrix_index != num_phi_placeholders; ++matrix_index) {
2435 static constexpr bool kExpandable = false;
2436 dependencies.push_back(
2437 ArenaBitVector::Create(&allocator, num_phi_placeholders, kExpandable, kArenaAllocLSE));
2438 ArenaBitVector* current_dependencies = dependencies.back();
2439 current_dependencies->ClearAllBits();
2440 current_dependencies->SetBit(matrix_index); // Count the Phi placeholder as its own dependency.
Alex Lightef28d242020-11-17 20:21:51 -08002441 PhiPlaceholder current_phi_placeholder =
2442 GetPhiPlaceholderAt(phi_placeholder_indexes[matrix_index]);
2443 HBasicBlock* current_block = blocks[current_phi_placeholder.GetBlockId()];
Vladimir Marko3224f382020-06-23 14:19:53 +01002444 DCHECK_GE(current_block->GetPredecessors().size(), 2u);
Alex Lightef28d242020-11-17 20:21:51 -08002445 size_t idx = current_phi_placeholder.GetHeapLocation();
Vladimir Marko3224f382020-06-23 14:19:53 +01002446 for (HBasicBlock* predecessor : current_block->GetPredecessors()) {
2447 Value pred_value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value);
2448 if (pred_value.NeedsLoopPhi()) {
2449 size_t pred_value_index = PhiPlaceholderIndex(pred_value);
2450 DCHECK(phi_placeholder_replacements_[pred_value_index].IsInvalid());
2451 DCHECK_NE(matrix_indexes[pred_value_index], static_cast<size_t>(-1));
2452 current_dependencies->SetBit(matrix_indexes[PhiPlaceholderIndex(pred_value)]);
2453 }
2454 }
2455 }
2456
2457 // Use the Floyd-Warshall algorithm to determine all transitive dependencies.
2458 for (size_t k = 0; k != num_phi_placeholders; ++k) {
2459 for (size_t i = 0; i != num_phi_placeholders; ++i) {
2460 for (size_t j = 0; j != num_phi_placeholders; ++j) {
2461 if (dependencies[i]->IsBitSet(k) && dependencies[k]->IsBitSet(j)) {
2462 dependencies[i]->SetBit(j);
2463 }
2464 }
2465 }
2466 }
2467
2468 // Count the number of transitive dependencies for each replaceable Phi placeholder.
2469 ScopedArenaVector<size_t> num_dependencies(allocator.Adapter(kArenaAllocLSE));
2470 num_dependencies.reserve(num_phi_placeholders);
2471 for (size_t matrix_index = 0; matrix_index != num_phi_placeholders; ++matrix_index) {
2472 num_dependencies.push_back(dependencies[matrix_index]->NumSetBits());
2473 }
2474
2475 // Pick a Phi placeholder with the smallest number of transitive dependencies and
2476 // materialize it and its dependencies. Repeat until we have materialized all.
2477 ScopedArenaVector<size_t> current_subset(allocator.Adapter(kArenaAllocLSE));
2478 current_subset.reserve(num_phi_placeholders);
2479 size_t remaining_phi_placeholders = num_phi_placeholders;
2480 while (remaining_phi_placeholders != 0u) {
2481 auto it = std::min_element(num_dependencies.begin(), num_dependencies.end());
2482 DCHECK_LE(*it, remaining_phi_placeholders);
2483 size_t current_matrix_index = std::distance(num_dependencies.begin(), it);
2484 ArenaBitVector* current_dependencies = dependencies[current_matrix_index];
2485 size_t current_num_dependencies = num_dependencies[current_matrix_index];
2486 current_subset.clear();
2487 for (uint32_t matrix_index : current_dependencies->Indexes()) {
2488 current_subset.push_back(phi_placeholder_indexes[matrix_index]);
2489 }
Alex Light09e23372021-01-15 08:42:11 -08002490 if (!MaterializeLoopPhis(current_subset, type)) {
2491 DCHECK_EQ(current_phase_, Phase::kStoreElimination);
Vladimir Marko3224f382020-06-23 14:19:53 +01002492 // This is the final store elimination phase and we shall not be able to eliminate any
2493 // stores that depend on the current subset, so mark these Phi placeholders unreplaceable.
2494 for (uint32_t matrix_index = 0; matrix_index != num_phi_placeholders; ++matrix_index) {
2495 if (dependencies[matrix_index]->IsBitSet(current_matrix_index)) {
2496 DCHECK(phi_placeholder_replacements_[phi_placeholder_indexes[matrix_index]].IsInvalid());
Alex Lightf5a84cb2021-01-15 08:35:38 -08002497 phi_placeholder_replacements_[phi_placeholder_indexes[matrix_index]] =
2498 Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01002499 }
2500 }
2501 return false;
2502 }
2503 for (uint32_t matrix_index = 0; matrix_index != num_phi_placeholders; ++matrix_index) {
2504 if (current_dependencies->IsBitSet(matrix_index)) {
2505 // Mark all dependencies as done by incrementing their `num_dependencies[.]`,
2506 // so that they shall never be the minimum again.
2507 num_dependencies[matrix_index] = num_phi_placeholders;
2508 } else if (dependencies[matrix_index]->IsBitSet(current_matrix_index)) {
2509 // Remove dependencies from other Phi placeholders.
2510 dependencies[matrix_index]->Subtract(current_dependencies);
2511 num_dependencies[matrix_index] -= current_num_dependencies;
2512 }
2513 }
2514 remaining_phi_placeholders -= current_num_dependencies;
2515 }
2516 return true;
2517}
2518
Alex Light3a73ffb2021-01-25 14:11:05 +00002519bool LSEVisitor::FullyMaterializePhi(PhiPlaceholder phi_placeholder, DataType::Type type) {
2520 ScopedArenaAllocator saa(GetGraph()->GetArenaStack());
2521 ArenaBitVector abv(&saa, num_phi_placeholders_, false, ArenaAllocKind::kArenaAllocLSE);
2522 auto res =
2523 FindLoopPhisToMaterialize(phi_placeholder, &abv, type, /* can_use_default_or_phi=*/true);
2524 CHECK(!res.has_value()) << *res;
2525 return MaterializeLoopPhis(abv, type);
2526}
2527
Alex Lightef28d242020-11-17 20:21:51 -08002528std::optional<LSEVisitor::PhiPlaceholder> LSEVisitor::TryToMaterializeLoopPhis(
2529 PhiPlaceholder phi_placeholder, HInstruction* load) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002530 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid());
2531
2532 // Use local allocator to reduce peak memory usage.
2533 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2534
2535 // Find Phi placeholders to materialize.
2536 ArenaBitVector phi_placeholders_to_materialize(
Alex Lightef28d242020-11-17 20:21:51 -08002537 &allocator, num_phi_placeholders_, /*expandable=*/ false, kArenaAllocLSE);
Vladimir Marko3224f382020-06-23 14:19:53 +01002538 phi_placeholders_to_materialize.ClearAllBits();
2539 DataType::Type type = load->GetType();
2540 bool can_use_default_or_phi = IsDefaultOrPhiAllowedForLoad(load);
Alex Lightef28d242020-11-17 20:21:51 -08002541 std::optional<PhiPlaceholder> loop_phi_with_unknown_input = FindLoopPhisToMaterialize(
Vladimir Marko3224f382020-06-23 14:19:53 +01002542 phi_placeholder, &phi_placeholders_to_materialize, type, can_use_default_or_phi);
Alex Lightef28d242020-11-17 20:21:51 -08002543 if (loop_phi_with_unknown_input) {
2544 DCHECK_GE(GetGraph()
2545 ->GetBlocks()[loop_phi_with_unknown_input->GetBlockId()]
2546 ->GetPredecessors()
2547 .size(),
2548 2u);
Vladimir Marko3224f382020-06-23 14:19:53 +01002549 return loop_phi_with_unknown_input; // Return failure.
2550 }
2551
Alex Light09e23372021-01-15 08:42:11 -08002552 DCHECK_EQ(current_phase_, Phase::kLoadElimination);
2553 bool success = MaterializeLoopPhis(phi_placeholders_to_materialize, type);
Vladimir Marko3224f382020-06-23 14:19:53 +01002554 DCHECK(success);
2555
2556 // Report success.
Alex Lightef28d242020-11-17 20:21:51 -08002557 return std::nullopt;
Vladimir Marko3224f382020-06-23 14:19:53 +01002558}
2559
2560// Re-process loads and stores in successors from the `loop_phi_with_unknown_input`. This may
2561// find one or more loads from `loads_requiring_loop_phi_` which cannot be replaced by Phis and
2562// propagate the load(s) as the new value(s) to successors; this may uncover new elimination
2563// opportunities. If we find no such load, we shall at least propagate an unknown value to some
2564// heap location that is needed by another loop Phi placeholder.
Alex Lightef28d242020-11-17 20:21:51 -08002565void LSEVisitor::ProcessLoopPhiWithUnknownInput(PhiPlaceholder loop_phi_with_unknown_input) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002566 size_t loop_phi_with_unknown_input_index = PhiPlaceholderIndex(loop_phi_with_unknown_input);
2567 DCHECK(phi_placeholder_replacements_[loop_phi_with_unknown_input_index].IsInvalid());
Alex Light86fe9b82020-11-16 16:54:01 +00002568 phi_placeholder_replacements_[loop_phi_with_unknown_input_index] =
2569 Value::MergedUnknown(loop_phi_with_unknown_input);
Vladimir Marko3224f382020-06-23 14:19:53 +01002570
Alex Lightef28d242020-11-17 20:21:51 -08002571 uint32_t block_id = loop_phi_with_unknown_input.GetBlockId();
Vladimir Marko3224f382020-06-23 14:19:53 +01002572 const ArenaVector<HBasicBlock*> reverse_post_order = GetGraph()->GetReversePostOrder();
2573 size_t reverse_post_order_index = 0;
2574 size_t reverse_post_order_size = reverse_post_order.size();
2575 size_t loads_and_stores_index = 0u;
2576 size_t loads_and_stores_size = loads_and_stores_.size();
2577
2578 // Skip blocks and instructions before the block containing the loop phi with unknown input.
2579 DCHECK_NE(reverse_post_order_index, reverse_post_order_size);
2580 while (reverse_post_order[reverse_post_order_index]->GetBlockId() != block_id) {
2581 HBasicBlock* block = reverse_post_order[reverse_post_order_index];
2582 while (loads_and_stores_index != loads_and_stores_size &&
2583 loads_and_stores_[loads_and_stores_index].load_or_store->GetBlock() == block) {
2584 ++loads_and_stores_index;
2585 }
2586 ++reverse_post_order_index;
2587 DCHECK_NE(reverse_post_order_index, reverse_post_order_size);
2588 }
2589
2590 // Use local allocator to reduce peak memory usage.
Vladimir Marko9e3fe992020-08-25 16:17:51 +01002591 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
Vladimir Marko3224f382020-06-23 14:19:53 +01002592 // Reuse one temporary vector for all remaining blocks.
2593 size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
Vladimir Marko9e3fe992020-08-25 16:17:51 +01002594 ScopedArenaVector<Value> local_heap_values(allocator.Adapter(kArenaAllocLSE));
Vladimir Marko3224f382020-06-23 14:19:53 +01002595
2596 auto get_initial_value = [this](HBasicBlock* block, size_t idx) {
2597 Value value;
2598 if (block->IsLoopHeader()) {
2599 if (block->GetLoopInformation()->IsIrreducible()) {
Alex Lightef28d242020-11-17 20:21:51 -08002600 PhiPlaceholder placeholder = GetPhiPlaceholder(block->GetBlockId(), idx);
Alex Light86fe9b82020-11-16 16:54:01 +00002601 value = Value::MergedUnknown(placeholder);
Vladimir Marko3224f382020-06-23 14:19:53 +01002602 } else {
2603 value = PrepareLoopValue(block, idx);
2604 }
2605 } else {
2606 value = MergePredecessorValues(block, idx);
2607 }
2608 DCHECK(value.IsUnknown() || ReplacementOrValue(value).Equals(value));
2609 return value;
2610 };
2611
2612 // Process remaining blocks and instructions.
2613 bool found_unreplaceable_load = false;
2614 bool replaced_heap_value_with_unknown = false;
2615 for (; reverse_post_order_index != reverse_post_order_size; ++reverse_post_order_index) {
2616 HBasicBlock* block = reverse_post_order[reverse_post_order_index];
2617 if (block->IsExitBlock()) {
2618 continue;
2619 }
2620
2621 // We shall reconstruct only the heap values that we need for processing loads and stores.
2622 local_heap_values.clear();
2623 local_heap_values.resize(num_heap_locations, Value::Invalid());
2624
2625 for (; loads_and_stores_index != loads_and_stores_size; ++loads_and_stores_index) {
2626 HInstruction* load_or_store = loads_and_stores_[loads_and_stores_index].load_or_store;
2627 size_t idx = loads_and_stores_[loads_and_stores_index].heap_location_index;
2628 if (load_or_store->GetBlock() != block) {
2629 break; // End of instructions from the current block.
2630 }
2631 bool is_store = load_or_store->GetSideEffects().DoesAnyWrite();
2632 DCHECK_EQ(is_store, IsStore(load_or_store));
2633 HInstruction* stored_value = nullptr;
2634 if (is_store) {
2635 auto it = store_records_.find(load_or_store);
2636 DCHECK(it != store_records_.end());
2637 stored_value = it->second.stored_value;
2638 }
2639 auto it = loads_requiring_loop_phi_.find(
2640 stored_value != nullptr ? stored_value : load_or_store);
2641 if (it == loads_requiring_loop_phi_.end()) {
2642 continue; // This load or store never needed a loop Phi.
2643 }
2644 ValueRecord& record = it->second;
Vladimir Marko9e3fe992020-08-25 16:17:51 +01002645 if (is_store) {
2646 // Process the store by updating `local_heap_values[idx]`. The last update shall
2647 // be propagated to the `heap_values[idx].value` if it previously needed a loop Phi
2648 // at the end of the block.
2649 Value replacement = ReplacementOrValue(record.value);
2650 if (replacement.NeedsLoopPhi()) {
2651 // No replacement yet, use the Phi placeholder from the load.
2652 DCHECK(record.value.NeedsLoopPhi());
2653 local_heap_values[idx] = record.value;
2654 } else {
2655 // If the load fetched a known value, use it, otherwise use the load.
2656 local_heap_values[idx] = Value::ForInstruction(
2657 replacement.IsUnknown() ? stored_value : replacement.GetInstruction());
2658 }
2659 } else {
Vladimir Marko3224f382020-06-23 14:19:53 +01002660 // Process the load unless it has previously been marked unreplaceable.
2661 if (record.value.NeedsLoopPhi()) {
2662 if (local_heap_values[idx].IsInvalid()) {
2663 local_heap_values[idx] = get_initial_value(block, idx);
2664 }
2665 if (local_heap_values[idx].IsUnknown()) {
2666 // This load cannot be replaced. Keep stores that feed the Phi placeholder
2667 // (no aliasing since then, otherwise the Phi placeholder would not have been
2668 // propagated as a value to this load) and store the load as the new heap value.
2669 found_unreplaceable_load = true;
2670 KeepStores(record.value);
Alex Light3a73ffb2021-01-25 14:11:05 +00002671 record.value = Value::MergedUnknown(record.value.GetPhiPlaceholder());
Vladimir Marko3224f382020-06-23 14:19:53 +01002672 local_heap_values[idx] = Value::ForInstruction(load_or_store);
2673 } else if (local_heap_values[idx].NeedsLoopPhi()) {
2674 // The load may still be replaced with a Phi later.
2675 DCHECK(local_heap_values[idx].Equals(record.value));
2676 } else {
2677 // This load can be eliminated but we may need to construct non-loop Phis.
2678 if (local_heap_values[idx].NeedsNonLoopPhi()) {
2679 MaterializeNonLoopPhis(local_heap_values[idx].GetPhiPlaceholder(),
2680 load_or_store->GetType());
2681 local_heap_values[idx] = Replacement(local_heap_values[idx]);
2682 }
2683 record.value = local_heap_values[idx];
2684 HInstruction* heap_value = local_heap_values[idx].GetInstruction();
2685 AddRemovedLoad(load_or_store, heap_value);
Vladimir Marko3224f382020-06-23 14:19:53 +01002686 }
2687 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002688 }
2689 }
2690
2691 // All heap values that previously needed a loop Phi at the end of the block
2692 // need to be updated for processing successors.
2693 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
2694 for (size_t idx = 0; idx != num_heap_locations; ++idx) {
2695 if (heap_values[idx].value.NeedsLoopPhi()) {
2696 if (local_heap_values[idx].IsValid()) {
2697 heap_values[idx].value = local_heap_values[idx];
2698 } else {
2699 heap_values[idx].value = get_initial_value(block, idx);
2700 }
2701 if (heap_values[idx].value.IsUnknown()) {
2702 replaced_heap_value_with_unknown = true;
2703 }
2704 }
2705 }
2706 }
2707 DCHECK(found_unreplaceable_load || replaced_heap_value_with_unknown);
2708}
2709
2710void LSEVisitor::ProcessLoadsRequiringLoopPhis() {
2711 // Note: The vector operations carve-out (see `IsDefaultOrPhiAllowedForLoad()`) can possibly
2712 // make the result of the processing depend on the order in which we process these loads.
2713 // To make sure the result is deterministic, iterate over `loads_and_stores_` instead of the
2714 // `loads_requiring_loop_phi_` indexed by non-deterministic pointers.
2715 for (const LoadStoreRecord& load_store_record : loads_and_stores_) {
2716 auto it = loads_requiring_loop_phi_.find(load_store_record.load_or_store);
2717 if (it == loads_requiring_loop_phi_.end()) {
2718 continue;
2719 }
2720 HInstruction* load = it->first;
2721 ValueRecord& record = it->second;
2722 while (record.value.NeedsLoopPhi() &&
2723 phi_placeholder_replacements_[PhiPlaceholderIndex(record.value)].IsInvalid()) {
Alex Lightef28d242020-11-17 20:21:51 -08002724 std::optional<PhiPlaceholder> loop_phi_with_unknown_input =
Vladimir Marko3224f382020-06-23 14:19:53 +01002725 TryToMaterializeLoopPhis(record.value.GetPhiPlaceholder(), load);
Alex Lightef28d242020-11-17 20:21:51 -08002726 DCHECK_EQ(loop_phi_with_unknown_input.has_value(),
Vladimir Marko3224f382020-06-23 14:19:53 +01002727 phi_placeholder_replacements_[PhiPlaceholderIndex(record.value)].IsInvalid());
Alex Lightef28d242020-11-17 20:21:51 -08002728 if (loop_phi_with_unknown_input) {
2729 DCHECK_GE(GetGraph()
2730 ->GetBlocks()[loop_phi_with_unknown_input->GetBlockId()]
2731 ->GetPredecessors()
2732 .size(),
2733 2u);
2734 ProcessLoopPhiWithUnknownInput(*loop_phi_with_unknown_input);
Vladimir Marko3224f382020-06-23 14:19:53 +01002735 }
2736 }
2737 // The load could have been marked as unreplaceable (and stores marked for keeping)
2738 // or marked for replacement with an instruction in ProcessLoopPhiWithUnknownInput().
2739 DCHECK(record.value.IsUnknown() || record.value.IsInstruction() || record.value.NeedsLoopPhi());
2740 if (record.value.NeedsLoopPhi()) {
2741 record.value = Replacement(record.value);
2742 HInstruction* heap_value = record.value.GetInstruction();
2743 AddRemovedLoad(load, heap_value);
Vladimir Marko3224f382020-06-23 14:19:53 +01002744 }
2745 }
2746}
2747
2748void LSEVisitor::SearchPhiPlaceholdersForKeptStores() {
2749 ScopedArenaVector<uint32_t> work_queue(allocator_.Adapter(kArenaAllocLSE));
2750 size_t start_size = phi_placeholders_to_search_for_kept_stores_.NumSetBits();
2751 work_queue.reserve(((start_size * 3u) + 1u) / 2u); // Reserve 1.5x start size, rounded up.
2752 for (uint32_t index : phi_placeholders_to_search_for_kept_stores_.Indexes()) {
2753 work_queue.push_back(index);
2754 }
2755 const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
Alex Light86fe9b82020-11-16 16:54:01 +00002756 std::optional<ArenaBitVector> not_kept_stores;
2757 if (stats_) {
2758 not_kept_stores.emplace(GetGraph()->GetAllocator(),
2759 kept_stores_.GetBitSizeOf(),
2760 false,
2761 ArenaAllocKind::kArenaAllocLSE);
2762 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002763 while (!work_queue.empty()) {
Alex Light86fe9b82020-11-16 16:54:01 +00002764 uint32_t cur_phi_idx = work_queue.back();
Alex Lightef28d242020-11-17 20:21:51 -08002765 PhiPlaceholder phi_placeholder = GetPhiPlaceholderAt(cur_phi_idx);
Alex Light86fe9b82020-11-16 16:54:01 +00002766 // Only writes to partial-escapes need to be specifically kept.
2767 bool is_partial_kept_merged_unknown =
2768 kept_merged_unknowns_.IsBitSet(cur_phi_idx) &&
Alex Lightef28d242020-11-17 20:21:51 -08002769 heap_location_collector_.GetHeapLocation(phi_placeholder.GetHeapLocation())
Alex Light86fe9b82020-11-16 16:54:01 +00002770 ->GetReferenceInfo()
2771 ->IsPartialSingleton();
Vladimir Marko3224f382020-06-23 14:19:53 +01002772 work_queue.pop_back();
Alex Lightef28d242020-11-17 20:21:51 -08002773 size_t idx = phi_placeholder.GetHeapLocation();
2774 HBasicBlock* block = blocks[phi_placeholder.GetBlockId()];
2775 DCHECK(block != nullptr) << cur_phi_idx << " phi: " << phi_placeholder
2776 << " (blocks: " << blocks.size() << ")";
Vladimir Marko3224f382020-06-23 14:19:53 +01002777 for (HBasicBlock* predecessor : block->GetPredecessors()) {
2778 ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[predecessor->GetBlockId()];
Vladimir Marko0571d472020-09-22 10:14:39 +01002779 // For loop back-edges we must also preserve all stores to locations that
2780 // may alias with the location `idx`.
Vladimir Marko3224f382020-06-23 14:19:53 +01002781 // TODO: Add tests cases around this.
2782 bool is_back_edge =
2783 block->IsLoopHeader() && predecessor != block->GetLoopInformation()->GetPreHeader();
2784 size_t start = is_back_edge ? 0u : idx;
2785 size_t end = is_back_edge ? heap_values.size() : idx + 1u;
2786 for (size_t i = start; i != end; ++i) {
2787 Value stored_by = heap_values[i].stored_by;
Vladimir Markodac82392021-05-10 15:44:24 +00002788 if (!stored_by.IsUnknown() && (i == idx || MayAliasOnBackEdge(block, idx, i))) {
Vladimir Marko3224f382020-06-23 14:19:53 +01002789 if (stored_by.NeedsPhi()) {
2790 size_t phi_placeholder_index = PhiPlaceholderIndex(stored_by);
Alex Light86fe9b82020-11-16 16:54:01 +00002791 if (is_partial_kept_merged_unknown) {
2792 // Propagate merged-unknown keep since otherwise this might look
2793 // like a partial escape we can remove.
2794 kept_merged_unknowns_.SetBit(phi_placeholder_index);
2795 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002796 if (!phi_placeholders_to_search_for_kept_stores_.IsBitSet(phi_placeholder_index)) {
2797 phi_placeholders_to_search_for_kept_stores_.SetBit(phi_placeholder_index);
2798 work_queue.push_back(phi_placeholder_index);
2799 }
2800 } else {
2801 DCHECK(IsStore(stored_by.GetInstruction()));
Alex Light86fe9b82020-11-16 16:54:01 +00002802 ReferenceInfo* ri = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
2803 DCHECK(ri != nullptr) << "No heap value for " << stored_by.GetInstruction()->DebugName()
2804 << " id: " << stored_by.GetInstruction()->GetId() << " block: "
2805 << stored_by.GetInstruction()->GetBlock()->GetBlockId();
2806 if (!is_partial_kept_merged_unknown && IsPartialNoEscape(predecessor, idx)) {
2807 if (not_kept_stores) {
2808 not_kept_stores->SetBit(stored_by.GetInstruction()->GetId());
2809 }
2810 } else {
2811 kept_stores_.SetBit(stored_by.GetInstruction()->GetId());
2812 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002813 }
2814 }
2815 }
2816 }
2817 }
Alex Light86fe9b82020-11-16 16:54:01 +00002818 if (not_kept_stores) {
2819 // a - b := (a & ~b)
2820 not_kept_stores->Subtract(&kept_stores_);
2821 auto num_removed = not_kept_stores->NumSetBits();
2822 MaybeRecordStat(stats_, MethodCompilationStat::kPartialStoreRemoved, num_removed);
2823 }
Vladimir Marko3224f382020-06-23 14:19:53 +01002824}
2825
2826void LSEVisitor::UpdateValueRecordForStoreElimination(/*inout*/ValueRecord* value_record) {
2827 while (value_record->stored_by.IsInstruction() &&
2828 !kept_stores_.IsBitSet(value_record->stored_by.GetInstruction()->GetId())) {
2829 auto it = store_records_.find(value_record->stored_by.GetInstruction());
2830 DCHECK(it != store_records_.end());
2831 *value_record = it->second.old_value_record;
2832 }
2833 if (value_record->stored_by.NeedsPhi() &&
2834 !phi_placeholders_to_search_for_kept_stores_.IsBitSet(
2835 PhiPlaceholderIndex(value_record->stored_by))) {
2836 // Some stores feeding this heap location may have been eliminated. Use the `stored_by`
2837 // Phi placeholder to recalculate the actual value.
2838 value_record->value = value_record->stored_by;
2839 }
2840 value_record->value = ReplacementOrValue(value_record->value);
2841 if (value_record->value.NeedsNonLoopPhi()) {
2842 // Treat all Phi placeholders as requiring loop Phis at this point.
2843 // We do not want MaterializeLoopPhis() to call MaterializeNonLoopPhis().
2844 value_record->value = Value::ForLoopPhiPlaceholder(value_record->value.GetPhiPlaceholder());
2845 }
2846}
2847
Alex Lightef28d242020-11-17 20:21:51 -08002848void LSEVisitor::FindOldValueForPhiPlaceholder(PhiPlaceholder phi_placeholder,
Vladimir Marko3224f382020-06-23 14:19:53 +01002849 DataType::Type type) {
2850 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid());
2851
2852 // Use local allocator to reduce peak memory usage.
2853 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2854 ArenaBitVector visited(&allocator,
Alex Lightef28d242020-11-17 20:21:51 -08002855 /*start_bits=*/ num_phi_placeholders_,
Vladimir Marko3224f382020-06-23 14:19:53 +01002856 /*expandable=*/ false,
2857 kArenaAllocLSE);
2858 visited.ClearAllBits();
2859
2860 // Find Phi placeholders to try and match against existing Phis or other replacement values.
2861 ArenaBitVector phi_placeholders_to_materialize(
Alex Lightef28d242020-11-17 20:21:51 -08002862 &allocator, num_phi_placeholders_, /*expandable=*/ false, kArenaAllocLSE);
Vladimir Marko3224f382020-06-23 14:19:53 +01002863 phi_placeholders_to_materialize.ClearAllBits();
Alex Lightef28d242020-11-17 20:21:51 -08002864 std::optional<PhiPlaceholder> loop_phi_with_unknown_input = FindLoopPhisToMaterialize(
2865 phi_placeholder, &phi_placeholders_to_materialize, type, /*can_use_default_or_phi=*/true);
2866 if (loop_phi_with_unknown_input) {
2867 DCHECK_GE(GetGraph()
2868 ->GetBlocks()[loop_phi_with_unknown_input->GetBlockId()]
2869 ->GetPredecessors()
2870 .size(),
2871 2u);
Vladimir Marko3224f382020-06-23 14:19:53 +01002872 // Mark the unreplacable placeholder as well as the input Phi placeholder as unreplaceable.
Alex Lightf5a84cb2021-01-15 08:35:38 -08002873 phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)] = Value::PureUnknown();
Alex Lightef28d242020-11-17 20:21:51 -08002874 phi_placeholder_replacements_[PhiPlaceholderIndex(*loop_phi_with_unknown_input)] =
Alex Lightf5a84cb2021-01-15 08:35:38 -08002875 Value::PureUnknown();
Vladimir Marko3224f382020-06-23 14:19:53 +01002876 return;
2877 }
2878
Alex Light09e23372021-01-15 08:42:11 -08002879 DCHECK_EQ(current_phase_, Phase::kStoreElimination);
2880 bool success = MaterializeLoopPhis(phi_placeholders_to_materialize, type);
Vladimir Marko3224f382020-06-23 14:19:53 +01002881 DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsValid());
2882 DCHECK_EQ(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsUnknown(),
2883 !success);
2884}
2885
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002886struct ScopedRestoreHeapValues {
2887 public:
2888 ScopedRestoreHeapValues(ArenaStack* alloc,
2889 size_t num_heap_locs,
2890 ScopedArenaVector<ScopedArenaVector<LSEVisitor::ValueRecord>>& to_restore)
2891 : alloc_(alloc),
2892 updated_values_(alloc_.Adapter(kArenaAllocLSE)),
2893 to_restore_(to_restore) {
2894 updated_values_.reserve(num_heap_locs * to_restore_.size());
2895 }
2896
2897 ~ScopedRestoreHeapValues() {
2898 for (const auto& rec : updated_values_) {
2899 to_restore_[rec.blk_id][rec.heap_loc].value = rec.val_;
2900 }
2901 }
2902
2903 template<typename Func>
2904 void ForEachRecord(Func func) {
2905 for (size_t blk_id : Range(to_restore_.size())) {
2906 for (size_t heap_loc : Range(to_restore_[blk_id].size())) {
2907 LSEVisitor::ValueRecord* vr = &to_restore_[blk_id][heap_loc];
2908 LSEVisitor::Value initial = vr->value;
2909 func(vr);
2910 if (!vr->value.ExactEquals(initial)) {
2911 updated_values_.push_back({blk_id, heap_loc, initial});
2912 }
2913 }
2914 }
2915 }
2916
2917 private:
2918 struct UpdateRecord {
2919 size_t blk_id;
2920 size_t heap_loc;
2921 LSEVisitor::Value val_;
2922 };
2923 ScopedArenaAllocator alloc_;
2924 ScopedArenaVector<UpdateRecord> updated_values_;
2925 ScopedArenaVector<ScopedArenaVector<LSEVisitor::ValueRecord>>& to_restore_;
2926
2927 DISALLOW_COPY_AND_ASSIGN(ScopedRestoreHeapValues);
2928};
2929
Vladimir Marko3224f382020-06-23 14:19:53 +01002930void LSEVisitor::FindStoresWritingOldValues() {
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002931 // Partial LSE relies on knowing the real heap-values not the
2932 // store-replacement versions so we need to restore the map after removing
2933 // stores.
2934 ScopedRestoreHeapValues heap_vals(allocator_.GetArenaStack(),
2935 heap_location_collector_.GetNumberOfHeapLocations(),
2936 heap_values_for_);
Vladimir Marko3224f382020-06-23 14:19:53 +01002937 // The Phi placeholder replacements have so far been used for eliminating loads,
2938 // tracking values that would be stored if all stores were kept. As we want to
2939 // compare actual old values after removing unmarked stores, prune the Phi
2940 // placeholder replacements that can be fed by values we may not actually store.
2941 // Replacements marked as unknown can be kept as they are fed by some unknown
2942 // value and would end up as unknown again if we recalculated them.
2943 for (size_t i = 0, size = phi_placeholder_replacements_.size(); i != size; ++i) {
2944 if (!phi_placeholder_replacements_[i].IsUnknown() &&
2945 !phi_placeholders_to_search_for_kept_stores_.IsBitSet(i)) {
2946 phi_placeholder_replacements_[i] = Value::Invalid();
2947 }
2948 }
2949
2950 // Update heap values at end of blocks.
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002951 heap_vals.ForEachRecord([&](ValueRecord* rec) {
2952 UpdateValueRecordForStoreElimination(rec);
2953 });
2954
2955 if (kIsDebugBuild) {
2956 heap_vals.ForEachRecord([](ValueRecord* rec) {
2957 DCHECK(!rec->value.NeedsNonLoopPhi()) << rec->value;
2958 });
Vladimir Marko3224f382020-06-23 14:19:53 +01002959 }
2960
2961 // Use local allocator to reduce peak memory usage.
2962 ScopedArenaAllocator allocator(allocator_.GetArenaStack());
2963 // Mark the stores we want to eliminate in a separate bit vector.
2964 ArenaBitVector eliminated_stores(&allocator,
2965 /*start_bits=*/ GetGraph()->GetCurrentInstructionId(),
2966 /*expandable=*/ false,
2967 kArenaAllocLSE);
2968 eliminated_stores.ClearAllBits();
2969
2970 for (auto& entry : store_records_) {
2971 HInstruction* store = entry.first;
2972 StoreRecord& store_record = entry.second;
2973 if (!kept_stores_.IsBitSet(store->GetId())) {
2974 continue; // Ignore stores that are not kept.
2975 }
2976 UpdateValueRecordForStoreElimination(&store_record.old_value_record);
2977 if (store_record.old_value_record.value.NeedsPhi()) {
2978 DataType::Type type = store_record.stored_value->GetType();
2979 FindOldValueForPhiPlaceholder(store_record.old_value_record.value.GetPhiPlaceholder(), type);
2980 store_record.old_value_record.value = ReplacementOrValue(store_record.old_value_record.value);
2981 }
2982 DCHECK(!store_record.old_value_record.value.NeedsPhi());
2983 HInstruction* stored_value = FindSubstitute(store_record.stored_value);
2984 if (store_record.old_value_record.value.Equals(stored_value)) {
2985 eliminated_stores.SetBit(store->GetId());
2986 }
2987 }
2988
2989 // Commit the stores to eliminate by removing them from `kept_stores_`.
2990 kept_stores_.Subtract(&eliminated_stores);
2991}
2992
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002993void LSEVisitor::Run() {
2994 // 1. Process blocks and instructions in reverse post order.
Vladimir Marko3224f382020-06-23 14:19:53 +01002995 for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
2996 VisitBasicBlock(block);
2997 }
2998
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00002999 // 2. Process loads that require loop Phis, trying to find/create replacements.
3000 current_phase_ = Phase::kLoadElimination;
3001 ProcessLoadsRequiringLoopPhis();
Vladimir Marko3224f382020-06-23 14:19:53 +01003002
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00003003 // 3. Determine which stores to keep and which to eliminate.
3004 current_phase_ = Phase::kStoreElimination;
3005 // Finish marking stores for keeping.
3006 SearchPhiPlaceholdersForKeptStores();
Vladimir Marko3224f382020-06-23 14:19:53 +01003007
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00003008 // Find stores that write the same value as is already present in the location.
3009 FindStoresWritingOldValues();
Vladimir Marko3224f382020-06-23 14:19:53 +01003010
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00003011 // 4. Replace loads and remove unnecessary stores and singleton allocations.
3012 FinishFullLSE();
3013
3014 // 5. Move partial escapes down and fixup with PHIs.
3015 current_phase_ = Phase::kPartialElimination;
3016 MovePartialEscapes();
Alex Light3a73ffb2021-01-25 14:11:05 +00003017}
3018
3019// Clear unknown loop-phi results. Here we'll be able to use partial-unknowns so we need to
3020// retry all of them with more information about where they come from.
3021void LSEVisitor::PrepareForPartialPhiComputation() {
3022 std::replace_if(
3023 phi_placeholder_replacements_.begin(),
3024 phi_placeholder_replacements_.end(),
Alex Lightde7c9e12021-04-01 17:19:05 -07003025 [](const Value& val) { return !val.IsDefault() && !val.IsInstruction(); },
Alex Light3a73ffb2021-01-25 14:11:05 +00003026 Value::Invalid());
3027}
3028
3029class PartialLoadStoreEliminationHelper {
3030 public:
3031 PartialLoadStoreEliminationHelper(LSEVisitor* lse, ScopedArenaAllocator* alloc)
3032 : lse_(lse),
3033 alloc_(alloc),
3034 new_ref_phis_(alloc_->Adapter(kArenaAllocLSE)),
3035 heap_refs_(alloc_->Adapter(kArenaAllocLSE)),
3036 max_preds_per_block_((*std::max_element(GetGraph()->GetActiveBlocks().begin(),
3037 GetGraph()->GetActiveBlocks().end(),
3038 [](HBasicBlock* a, HBasicBlock* b) {
3039 return a->GetNumberOfPredecessors() <
3040 b->GetNumberOfPredecessors();
3041 }))
3042 ->GetNumberOfPredecessors()),
3043 materialization_blocks_(GetGraph()->GetBlocks().size() * max_preds_per_block_,
3044 nullptr,
3045 alloc_->Adapter(kArenaAllocLSE)),
3046 first_materialization_block_id_(GetGraph()->GetBlocks().size()) {
Vladimir Marko5c824932021-06-02 15:54:17 +01003047 size_t num_partial_singletons = lse_->heap_location_collector_.CountPartialSingletons();
3048 heap_refs_.reserve(num_partial_singletons);
3049 new_ref_phis_.reserve(num_partial_singletons * GetGraph()->GetBlocks().size());
Alex Light3a73ffb2021-01-25 14:11:05 +00003050 CollectInterestingHeapRefs();
3051 }
3052
3053 ~PartialLoadStoreEliminationHelper() {
3054 if (heap_refs_.empty()) {
3055 return;
3056 }
3057 ReferenceTypePropagation rtp_fixup(GetGraph(),
Alex Light3a73ffb2021-01-25 14:11:05 +00003058 Handle<mirror::DexCache>(),
3059 /* is_first_run= */ false);
3060 rtp_fixup.Visit(ArrayRef<HInstruction* const>(new_ref_phis_));
3061 GetGraph()->ClearLoopInformation();
3062 GetGraph()->ClearDominanceInformation();
3063 GetGraph()->ClearReachabilityInformation();
3064 GetGraph()->BuildDominatorTree();
3065 GetGraph()->ComputeReachabilityInformation();
3066 }
3067
3068 class IdxToHeapLoc {
3069 public:
3070 explicit IdxToHeapLoc(const HeapLocationCollector* hlc) : collector_(hlc) {}
3071 HeapLocation* operator()(size_t idx) const {
3072 return collector_->GetHeapLocation(idx);
3073 }
3074
3075 private:
3076 const HeapLocationCollector* collector_;
3077 };
3078
3079
3080 class HeapReferenceData {
3081 public:
3082 using LocIterator = IterationRange<TransformIterator<BitVector::IndexIterator, IdxToHeapLoc>>;
3083 HeapReferenceData(PartialLoadStoreEliminationHelper* helper,
3084 HNewInstance* new_inst,
3085 const ExecutionSubgraph* subgraph,
3086 ScopedArenaAllocator* alloc)
3087 : new_instance_(new_inst),
3088 helper_(helper),
3089 heap_locs_(alloc,
3090 helper->lse_->heap_location_collector_.GetNumberOfHeapLocations(),
3091 /* expandable= */ false,
3092 kArenaAllocLSE),
3093 materializations_(
3094 // We generally won't need to create too many materialization blocks and we can expand
3095 // this as needed so just start off with 2x.
3096 2 * helper->lse_->GetGraph()->GetBlocks().size(),
3097 nullptr,
3098 alloc->Adapter(kArenaAllocLSE)),
3099 collector_(helper->lse_->heap_location_collector_),
3100 subgraph_(subgraph) {}
3101
3102 LocIterator IterateLocations() {
3103 auto idxs = heap_locs_.Indexes();
3104 return MakeTransformRange(idxs, IdxToHeapLoc(&collector_));
3105 }
3106
3107 void AddHeapLocation(size_t idx) {
3108 heap_locs_.SetBit(idx);
3109 }
3110
3111 const ExecutionSubgraph* GetNoEscapeSubgraph() const {
3112 return subgraph_;
3113 }
3114
3115 bool IsPostEscape(HBasicBlock* blk) {
3116 return std::any_of(
3117 subgraph_->GetExcludedCohorts().cbegin(),
3118 subgraph_->GetExcludedCohorts().cend(),
3119 [&](const ExecutionSubgraph::ExcludedCohort& ec) { return ec.PrecedesBlock(blk); });
3120 }
3121
3122 bool InEscapeCohort(HBasicBlock* blk) {
3123 return std::any_of(
3124 subgraph_->GetExcludedCohorts().cbegin(),
3125 subgraph_->GetExcludedCohorts().cend(),
3126 [&](const ExecutionSubgraph::ExcludedCohort& ec) { return ec.ContainsBlock(blk); });
3127 }
3128
3129 bool BeforeAllEscapes(HBasicBlock* b) {
3130 return std::none_of(subgraph_->GetExcludedCohorts().cbegin(),
3131 subgraph_->GetExcludedCohorts().cend(),
3132 [&](const ExecutionSubgraph::ExcludedCohort& ec) {
3133 return ec.PrecedesBlock(b) || ec.ContainsBlock(b);
3134 });
3135 }
3136
3137 HNewInstance* OriginalNewInstance() const {
3138 return new_instance_;
3139 }
3140
3141 // Collect and replace all uses. We need to perform this twice since we will
3142 // generate PHIs and additional uses as we create the default-values for
3143 // pred-gets. These values might be other references that are also being
3144 // partially eliminated. By running just the replacement part again we are
3145 // able to avoid having to keep another whole in-progress partial map
3146 // around. Since we will have already handled all the other uses in the
3147 // first pass the second one will be quite fast.
3148 void FixupUses(bool first_pass) {
3149 ScopedArenaAllocator saa(GetGraph()->GetArenaStack());
3150 // Replace uses with materialized values.
3151 ScopedArenaVector<InstructionUse<HInstruction>> to_replace(saa.Adapter(kArenaAllocLSE));
3152 ScopedArenaVector<HInstruction*> to_remove(saa.Adapter(kArenaAllocLSE));
3153 // Do we need to add a constructor-fence.
3154 ScopedArenaVector<InstructionUse<HConstructorFence>> constructor_fences(
3155 saa.Adapter(kArenaAllocLSE));
3156 ScopedArenaVector<InstructionUse<HInstruction>> to_predicate(saa.Adapter(kArenaAllocLSE));
3157
3158 CollectReplacements(to_replace, to_remove, constructor_fences, to_predicate);
3159
3160 if (!first_pass) {
3161 // If another partial creates new references they can only be in Phis or pred-get defaults
3162 // so they must be in the to_replace group.
3163 DCHECK(to_predicate.empty());
3164 DCHECK(constructor_fences.empty());
3165 DCHECK(to_remove.empty());
3166 }
3167
3168 ReplaceInput(to_replace);
Alex Lightde7c9e12021-04-01 17:19:05 -07003169 RemoveAndReplaceInputs(to_remove);
Alex Light3a73ffb2021-01-25 14:11:05 +00003170 CreateConstructorFences(constructor_fences);
3171 PredicateInstructions(to_predicate);
3172
3173 CHECK(OriginalNewInstance()->GetUses().empty())
3174 << OriginalNewInstance()->GetUses() << ", " << OriginalNewInstance()->GetEnvUses();
3175 }
3176
3177 void AddMaterialization(HBasicBlock* blk, HInstruction* ins) {
3178 if (blk->GetBlockId() >= materializations_.size()) {
3179 // Make sure the materialization array is large enough, try to avoid
3180 // re-sizing too many times by giving extra space.
3181 materializations_.resize(blk->GetBlockId() * 2, nullptr);
3182 }
3183 DCHECK(materializations_[blk->GetBlockId()] == nullptr)
3184 << "Already have a materialization in block " << blk->GetBlockId() << ": "
3185 << *materializations_[blk->GetBlockId()] << " when trying to set materialization to "
3186 << *ins;
3187 materializations_[blk->GetBlockId()] = ins;
3188 LSE_VLOG << "In block " << blk->GetBlockId() << " materialization is " << *ins;
3189 helper_->NotifyNewMaterialization(ins);
3190 }
3191
3192 bool HasMaterialization(HBasicBlock* blk) const {
3193 return blk->GetBlockId() < materializations_.size() &&
3194 materializations_[blk->GetBlockId()] != nullptr;
3195 }
3196
3197 HInstruction* GetMaterialization(HBasicBlock* blk) const {
3198 if (materializations_.size() <= blk->GetBlockId() ||
3199 materializations_[blk->GetBlockId()] == nullptr) {
3200 // This must be a materialization block added after the partial LSE of
3201 // the current reference finished. Since every edge can only have at
3202 // most one materialization block added to it we can just check the
3203 // blocks predecessor.
3204 DCHECK(helper_->IsMaterializationBlock(blk));
3205 blk = helper_->FindDominatingNonMaterializationBlock(blk);
3206 DCHECK(!helper_->IsMaterializationBlock(blk));
3207 }
3208 DCHECK_GT(materializations_.size(), blk->GetBlockId());
3209 DCHECK(materializations_[blk->GetBlockId()] != nullptr);
3210 return materializations_[blk->GetBlockId()];
3211 }
3212
3213 void GenerateMaterializationValueFromPredecessors(HBasicBlock* blk) {
3214 DCHECK(std::none_of(GetNoEscapeSubgraph()->GetExcludedCohorts().begin(),
3215 GetNoEscapeSubgraph()->GetExcludedCohorts().end(),
3216 [&](const ExecutionSubgraph::ExcludedCohort& cohort) {
3217 return cohort.IsEntryBlock(blk);
3218 }));
3219 DCHECK(!HasMaterialization(blk));
3220 if (blk->IsExitBlock()) {
3221 return;
3222 } else if (blk->IsLoopHeader()) {
3223 // See comment in execution_subgraph.h. Currently we act as though every
3224 // allocation for partial elimination takes place in the entry block.
3225 // This simplifies the analysis by making it so any escape cohort
3226 // expands to contain any loops it is a part of. This is something that
3227 // we should rectify at some point. In either case however we can still
3228 // special case the loop-header since (1) currently the loop can't have
3229 // any merges between different cohort entries since the pre-header will
3230 // be the earliest place entry can happen and (2) even if the analysis
3231 // is improved to consider lifetime of the object WRT loops any values
3232 // which would require loop-phis would have to make the whole loop
3233 // escape anyway.
3234 // This all means we can always use value from the pre-header when the
3235 // block is the loop-header and we didn't already create a
3236 // materialization block. (NB when we do improve the analysis we will
3237 // need to modify the materialization creation code to deal with this
3238 // correctly.)
3239 HInstruction* pre_header_val =
3240 GetMaterialization(blk->GetLoopInformation()->GetPreHeader());
3241 AddMaterialization(blk, pre_header_val);
3242 return;
3243 }
3244 ScopedArenaAllocator saa(GetGraph()->GetArenaStack());
3245 ScopedArenaVector<HInstruction*> pred_vals(saa.Adapter(kArenaAllocLSE));
3246 pred_vals.reserve(blk->GetNumberOfPredecessors());
3247 for (HBasicBlock* pred : blk->GetPredecessors()) {
3248 DCHECK(HasMaterialization(pred));
3249 pred_vals.push_back(GetMaterialization(pred));
3250 }
3251 GenerateMaterializationValueFromPredecessorsDirect(blk, pred_vals);
3252 }
3253
3254 void GenerateMaterializationValueFromPredecessorsForEntry(
3255 HBasicBlock* entry, const ScopedArenaVector<HInstruction*>& pred_vals) {
3256 DCHECK(std::any_of(GetNoEscapeSubgraph()->GetExcludedCohorts().begin(),
3257 GetNoEscapeSubgraph()->GetExcludedCohorts().end(),
3258 [&](const ExecutionSubgraph::ExcludedCohort& cohort) {
3259 return cohort.IsEntryBlock(entry);
3260 }));
3261 GenerateMaterializationValueFromPredecessorsDirect(entry, pred_vals);
3262 }
3263
3264 private:
3265 template <typename InstructionType>
3266 struct InstructionUse {
3267 InstructionType* instruction_;
3268 size_t index_;
3269 };
3270
3271 void ReplaceInput(const ScopedArenaVector<InstructionUse<HInstruction>>& to_replace) {
3272 for (auto& [ins, idx] : to_replace) {
3273 HInstruction* merged_inst = GetMaterialization(ins->GetBlock());
3274 if (ins->IsPhi() && merged_inst->IsPhi() && ins->GetBlock() == merged_inst->GetBlock()) {
3275 // Phis we just pass through the appropriate inputs.
3276 ins->ReplaceInput(merged_inst->InputAt(idx), idx);
3277 } else {
3278 ins->ReplaceInput(merged_inst, idx);
3279 }
3280 }
3281 }
3282
Alex Lightde7c9e12021-04-01 17:19:05 -07003283 void RemoveAndReplaceInputs(const ScopedArenaVector<HInstruction*>& to_remove) {
Alex Light3a73ffb2021-01-25 14:11:05 +00003284 for (HInstruction* ins : to_remove) {
3285 if (ins->GetBlock() == nullptr) {
3286 // Already dealt with.
3287 continue;
3288 }
3289 DCHECK(BeforeAllEscapes(ins->GetBlock())) << *ins;
3290 if (ins->IsInstanceFieldGet() || ins->IsInstanceFieldSet()) {
Alex Lightde7c9e12021-04-01 17:19:05 -07003291 bool instruction_has_users =
3292 ins->IsInstanceFieldGet() && (!ins->GetUses().empty() || !ins->GetEnvUses().empty());
3293 if (instruction_has_users) {
3294 // Make sure any remaining users of read are replaced.
3295 HInstruction* replacement =
3296 helper_->lse_->GetPartialValueAt(OriginalNewInstance(), ins);
3297 // NB ReplaceInput will remove a use from the list so this is
3298 // guaranteed to finish eventually.
3299 while (!ins->GetUses().empty()) {
3300 const HUseListNode<HInstruction*>& use = ins->GetUses().front();
3301 use.GetUser()->ReplaceInput(replacement, use.GetIndex());
3302 }
3303 while (!ins->GetEnvUses().empty()) {
3304 const HUseListNode<HEnvironment*>& use = ins->GetEnvUses().front();
3305 use.GetUser()->ReplaceInput(replacement, use.GetIndex());
3306 }
3307 } else {
3308 DCHECK(ins->GetUses().empty())
3309 << "Instruction has users!\n"
3310 << ins->DumpWithArgs() << "\nUsers are " << ins->GetUses();
3311 DCHECK(ins->GetEnvUses().empty())
3312 << "Instruction has users!\n"
3313 << ins->DumpWithArgs() << "\nUsers are " << ins->GetEnvUses();
3314 }
Alex Light3a73ffb2021-01-25 14:11:05 +00003315 ins->GetBlock()->RemoveInstruction(ins);
3316 } else {
3317 // Can only be obj == other, obj != other, obj == obj (!?) or, obj != obj (!?)
3318 // Since PHIs are escapes as far as LSE is concerned and we are before
3319 // any escapes these are the only 4 options.
3320 DCHECK(ins->IsEqual() || ins->IsNotEqual()) << *ins;
3321 HInstruction* replacement;
3322 if (UNLIKELY(ins->InputAt(0) == ins->InputAt(1))) {
3323 replacement = ins->IsEqual() ? GetGraph()->GetIntConstant(1)
3324 : GetGraph()->GetIntConstant(0);
3325 } else {
3326 replacement = ins->IsEqual() ? GetGraph()->GetIntConstant(0)
3327 : GetGraph()->GetIntConstant(1);
3328 }
3329 ins->ReplaceWith(replacement);
3330 ins->GetBlock()->RemoveInstruction(ins);
3331 }
3332 }
3333 }
3334
3335 void CreateConstructorFences(
3336 const ScopedArenaVector<InstructionUse<HConstructorFence>>& constructor_fences) {
3337 if (!constructor_fences.empty()) {
3338 uint32_t pc = constructor_fences.front().instruction_->GetDexPc();
3339 for (auto& [cf, idx] : constructor_fences) {
3340 if (cf->GetInputs().size() == 1) {
3341 cf->GetBlock()->RemoveInstruction(cf);
3342 } else {
3343 cf->RemoveInputAt(idx);
3344 }
3345 }
3346 for (const ExecutionSubgraph::ExcludedCohort& ec :
3347 GetNoEscapeSubgraph()->GetExcludedCohorts()) {
3348 for (HBasicBlock* blk : ec.EntryBlocks()) {
3349 for (HBasicBlock* materializer :
3350 Filter(MakeIterationRange(blk->GetPredecessors()),
3351 [&](HBasicBlock* blk) { return helper_->IsMaterializationBlock(blk); })) {
3352 HInstruction* new_cf = new (GetGraph()->GetAllocator()) HConstructorFence(
3353 GetMaterialization(materializer), pc, GetGraph()->GetAllocator());
3354 materializer->InsertInstructionBefore(new_cf, materializer->GetLastInstruction());
3355 }
3356 }
3357 }
3358 }
3359 }
3360
3361 void PredicateInstructions(
3362 const ScopedArenaVector<InstructionUse<HInstruction>>& to_predicate) {
3363 for (auto& [ins, idx] : to_predicate) {
3364 if (UNLIKELY(ins->GetBlock() == nullptr)) {
3365 // Already handled due to obj == obj;
3366 continue;
3367 } else if (ins->IsInstanceFieldGet()) {
3368 // IFieldGet[obj] => PredicatedIFieldGet[PartialValue, obj]
3369 HInstruction* new_fget = new (GetGraph()->GetAllocator()) HPredicatedInstanceFieldGet(
3370 ins->AsInstanceFieldGet(),
3371 GetMaterialization(ins->GetBlock()),
3372 helper_->lse_->GetPartialValueAt(OriginalNewInstance(), ins));
3373 MaybeRecordStat(helper_->lse_->stats_, MethodCompilationStat::kPredicatedLoadAdded);
3374 ins->GetBlock()->InsertInstructionBefore(new_fget, ins);
3375 if (ins->GetType() == DataType::Type::kReference) {
3376 // Reference info is the same
3377 new_fget->SetReferenceTypeInfo(ins->GetReferenceTypeInfo());
3378 }
Vladimir Marko06fb7fa2021-05-18 15:53:17 +00003379 // In this phase, substitute instructions are used only for the predicated get
3380 // default values which are used only if the partial singleton did not escape,
3381 // so the out value of the `new_fget` for the relevant cases is the same as
3382 // the default value.
3383 // TODO: Use the default value for materializing default values used by
3384 // other predicated loads to avoid some unnecessary Phis. (This shall
3385 // complicate the search for replacement in `ReplacementOrValue()`.)
Vladimir Marko807de1e2021-04-30 15:14:18 +00003386 DCHECK(helper_->lse_->substitute_instructions_for_loads_[ins->GetId()] == nullptr);
3387 helper_->lse_->substitute_instructions_for_loads_[ins->GetId()] = new_fget;
Alex Light3a73ffb2021-01-25 14:11:05 +00003388 ins->ReplaceWith(new_fget);
3389 ins->ReplaceEnvUsesDominatedBy(ins, new_fget);
3390 CHECK(ins->GetEnvUses().empty() && ins->GetUses().empty())
3391 << "Instruction: " << *ins << " uses: " << ins->GetUses()
3392 << ", env: " << ins->GetEnvUses();
3393 ins->GetBlock()->RemoveInstruction(ins);
3394 } else if (ins->IsInstanceFieldSet()) {
3395 // Any predicated sets shouldn't require movement.
3396 ins->AsInstanceFieldSet()->SetIsPredicatedSet();
3397 MaybeRecordStat(helper_->lse_->stats_, MethodCompilationStat::kPredicatedStoreAdded);
3398 HInstruction* merged_inst = GetMaterialization(ins->GetBlock());
3399 ins->ReplaceInput(merged_inst, idx);
3400 } else {
3401 // comparisons need to be split into 2.
3402 DCHECK(ins->IsEqual() || ins->IsNotEqual()) << "bad instruction " << *ins;
3403 bool this_is_first = idx == 0;
3404 if (ins->InputAt(0) == ins->InputAt(1)) {
3405 // This is a obj == obj or obj != obj.
3406 // No idea why anyone would do this but whatever.
3407 ins->ReplaceWith(GetGraph()->GetIntConstant(ins->IsEqual() ? 1 : 0));
3408 ins->GetBlock()->RemoveInstruction(ins);
3409 continue;
3410 } else {
3411 HInstruction* is_escaped = new (GetGraph()->GetAllocator())
3412 HNotEqual(GetMaterialization(ins->GetBlock()), GetGraph()->GetNullConstant());
3413 HInstruction* combine_inst =
3414 ins->IsEqual() ? static_cast<HInstruction*>(new (GetGraph()->GetAllocator()) HAnd(
3415 DataType::Type::kBool, is_escaped, ins))
3416 : static_cast<HInstruction*>(new (GetGraph()->GetAllocator()) HOr(
3417 DataType::Type::kBool, is_escaped, ins));
3418 ins->ReplaceInput(GetMaterialization(ins->GetBlock()), this_is_first ? 0 : 1);
3419 ins->GetBlock()->InsertInstructionBefore(is_escaped, ins);
3420 ins->GetBlock()->InsertInstructionAfter(combine_inst, ins);
3421 ins->ReplaceWith(combine_inst);
3422 combine_inst->ReplaceInput(ins, 1);
3423 }
3424 }
3425 }
3426 }
3427
3428 // Figure out all the instructions we need to
3429 // fixup/replace/remove/duplicate. Since this requires an iteration of an
3430 // intrusive linked list we want to do it only once and collect all the data
3431 // here.
3432 void CollectReplacements(
3433 ScopedArenaVector<InstructionUse<HInstruction>>& to_replace,
3434 ScopedArenaVector<HInstruction*>& to_remove,
3435 ScopedArenaVector<InstructionUse<HConstructorFence>>& constructor_fences,
3436 ScopedArenaVector<InstructionUse<HInstruction>>& to_predicate) {
3437 size_t size = new_instance_->GetUses().SizeSlow();
3438 to_replace.reserve(size);
3439 to_remove.reserve(size);
3440 constructor_fences.reserve(size);
3441 to_predicate.reserve(size);
3442 for (auto& use : new_instance_->GetUses()) {
3443 HBasicBlock* blk =
3444 helper_->FindDominatingNonMaterializationBlock(use.GetUser()->GetBlock());
3445 if (InEscapeCohort(blk)) {
3446 LSE_VLOG << "Replacing " << *new_instance_ << " use in " << *use.GetUser() << " with "
3447 << *GetMaterialization(blk);
3448 to_replace.push_back({use.GetUser(), use.GetIndex()});
3449 } else if (IsPostEscape(blk)) {
3450 LSE_VLOG << "User " << *use.GetUser() << " after escapes!";
3451 // The fields + cmp are normal uses. Phi can only be here if it was
3452 // generated by full LSE so whatever store+load that created the phi
3453 // is the escape.
3454 if (use.GetUser()->IsPhi()) {
3455 to_replace.push_back({use.GetUser(), use.GetIndex()});
3456 } else {
3457 DCHECK(use.GetUser()->IsFieldAccess() ||
3458 use.GetUser()->IsEqual() ||
3459 use.GetUser()->IsNotEqual())
3460 << *use.GetUser() << "@" << use.GetIndex();
3461 to_predicate.push_back({use.GetUser(), use.GetIndex()});
3462 }
3463 } else if (use.GetUser()->IsConstructorFence()) {
3464 LSE_VLOG << "User " << *use.GetUser() << " being moved to materialization!";
3465 constructor_fences.push_back({use.GetUser()->AsConstructorFence(), use.GetIndex()});
3466 } else {
3467 LSE_VLOG << "User " << *use.GetUser() << " not contained in cohort!";
3468 to_remove.push_back(use.GetUser());
3469 }
3470 }
3471 DCHECK_EQ(
3472 to_replace.size() + to_remove.size() + constructor_fences.size() + to_predicate.size(),
3473 size);
3474 }
3475
3476 void GenerateMaterializationValueFromPredecessorsDirect(
3477 HBasicBlock* blk, const ScopedArenaVector<HInstruction*>& pred_vals) {
3478 DCHECK(!pred_vals.empty());
3479 bool all_equal = std::all_of(pred_vals.begin() + 1, pred_vals.end(), [&](HInstruction* val) {
3480 return val == pred_vals.front();
3481 });
3482 if (LIKELY(all_equal)) {
3483 AddMaterialization(blk, pred_vals.front());
3484 } else {
3485 // Make a PHI for the predecessors.
3486 HPhi* phi = new (GetGraph()->GetAllocator()) HPhi(
3487 GetGraph()->GetAllocator(), kNoRegNumber, pred_vals.size(), DataType::Type::kReference);
3488 for (const auto& [ins, off] : ZipCount(MakeIterationRange(pred_vals))) {
3489 phi->SetRawInputAt(off, ins);
3490 }
3491 blk->AddPhi(phi);
3492 AddMaterialization(blk, phi);
3493 }
3494 }
3495
3496 HGraph* GetGraph() const {
3497 return helper_->GetGraph();
3498 }
3499
3500 HNewInstance* new_instance_;
3501 PartialLoadStoreEliminationHelper* helper_;
3502 ArenaBitVector heap_locs_;
3503 ScopedArenaVector<HInstruction*> materializations_;
3504 const HeapLocationCollector& collector_;
3505 const ExecutionSubgraph* subgraph_;
3506 };
3507
3508 ArrayRef<HeapReferenceData> GetHeapRefs() {
3509 return ArrayRef<HeapReferenceData>(heap_refs_);
3510 }
3511
3512 bool IsMaterializationBlock(HBasicBlock* blk) const {
3513 return blk->GetBlockId() >= first_materialization_block_id_;
3514 }
3515
3516 HBasicBlock* GetOrCreateMaterializationBlock(HBasicBlock* entry, size_t pred_num) {
3517 size_t idx = GetMaterializationBlockIndex(entry, pred_num);
3518 HBasicBlock* blk = materialization_blocks_[idx];
3519 if (blk == nullptr) {
3520 blk = new (GetGraph()->GetAllocator()) HBasicBlock(GetGraph());
3521 GetGraph()->AddBlock(blk);
3522 LSE_VLOG << "creating materialization block " << blk->GetBlockId() << " on edge "
3523 << entry->GetPredecessors()[pred_num]->GetBlockId() << "->" << entry->GetBlockId();
3524 blk->AddInstruction(new (GetGraph()->GetAllocator()) HGoto());
3525 materialization_blocks_[idx] = blk;
3526 }
3527 return blk;
3528 }
3529
3530 HBasicBlock* GetMaterializationBlock(HBasicBlock* entry, size_t pred_num) {
3531 HBasicBlock* out = materialization_blocks_[GetMaterializationBlockIndex(entry, pred_num)];
3532 DCHECK(out != nullptr) << "No materialization block for edge " << entry->GetBlockId() << "->"
3533 << entry->GetPredecessors()[pred_num]->GetBlockId();
3534 return out;
3535 }
3536
3537 IterationRange<ArenaVector<HBasicBlock*>::const_iterator> IterateMaterializationBlocks() {
3538 return MakeIterationRange(GetGraph()->GetBlocks().begin() + first_materialization_block_id_,
3539 GetGraph()->GetBlocks().end());
3540 }
3541
3542 void FixupPartialObjectUsers() {
3543 for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : GetHeapRefs()) {
3544 // Use the materialized instances to replace original instance
3545 ref_data.FixupUses(/*first_pass=*/true);
3546 CHECK(ref_data.OriginalNewInstance()->GetUses().empty())
3547 << ref_data.OriginalNewInstance()->GetUses() << ", "
3548 << ref_data.OriginalNewInstance()->GetEnvUses();
3549 }
3550 // This can cause new uses to be created due to the creation of phis/pred-get defaults
3551 for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : GetHeapRefs()) {
3552 // Only need to handle new phis/pred-get defaults. DCHECK that's all we find.
3553 ref_data.FixupUses(/*first_pass=*/false);
3554 CHECK(ref_data.OriginalNewInstance()->GetUses().empty())
3555 << ref_data.OriginalNewInstance()->GetUses() << ", "
3556 << ref_data.OriginalNewInstance()->GetEnvUses();
3557 }
3558 }
3559
3560 // Finds the first block which either is or dominates the given block which is
3561 // not a materialization block
3562 HBasicBlock* FindDominatingNonMaterializationBlock(HBasicBlock* blk) {
3563 if (LIKELY(!IsMaterializationBlock(blk))) {
3564 // Not a materialization block so itself.
3565 return blk;
3566 } else if (blk->GetNumberOfPredecessors() != 0) {
3567 // We're far enough along that the materialization blocks have been
3568 // inserted into the graph so no need to go searching.
3569 return blk->GetSinglePredecessor();
3570 }
3571 // Search through the materialization blocks to find where it will be
3572 // inserted.
3573 for (auto [mat, idx] : ZipCount(MakeIterationRange(materialization_blocks_))) {
3574 if (mat == blk) {
3575 size_t cur_pred_idx = idx % max_preds_per_block_;
3576 HBasicBlock* entry = GetGraph()->GetBlocks()[idx / max_preds_per_block_];
3577 return entry->GetPredecessors()[cur_pred_idx];
3578 }
3579 }
3580 LOG(FATAL) << "Unable to find materialization block position for " << blk->GetBlockId() << "!";
3581 return nullptr;
3582 }
3583
3584 void InsertMaterializationBlocks() {
3585 for (auto [mat, idx] : ZipCount(MakeIterationRange(materialization_blocks_))) {
3586 if (mat == nullptr) {
3587 continue;
3588 }
3589 size_t cur_pred_idx = idx % max_preds_per_block_;
3590 HBasicBlock* entry = GetGraph()->GetBlocks()[idx / max_preds_per_block_];
3591 HBasicBlock* pred = entry->GetPredecessors()[cur_pred_idx];
3592 mat->InsertBetween(pred, entry);
3593 LSE_VLOG << "Adding materialization block " << mat->GetBlockId() << " on edge "
3594 << pred->GetBlockId() << "->" << entry->GetBlockId();
3595 }
3596 }
3597
3598 // Replace any env-uses remaining of the partial singletons with the
3599 // appropriate phis and remove the instructions.
3600 void RemoveReplacedInstructions() {
3601 for (HeapReferenceData& ref_data : GetHeapRefs()) {
3602 CHECK(ref_data.OriginalNewInstance()->GetUses().empty())
3603 << ref_data.OriginalNewInstance()->GetUses() << ", "
3604 << ref_data.OriginalNewInstance()->GetEnvUses()
3605 << " inst is: " << ref_data.OriginalNewInstance();
3606 const auto& env_uses = ref_data.OriginalNewInstance()->GetEnvUses();
3607 while (!env_uses.empty()) {
3608 const HUseListNode<HEnvironment*>& use = env_uses.front();
3609 HInstruction* merged_inst =
3610 ref_data.GetMaterialization(use.GetUser()->GetHolder()->GetBlock());
3611 LSE_VLOG << "Replacing env use of " << *use.GetUser()->GetHolder() << "@" << use.GetIndex()
3612 << " with " << *merged_inst;
3613 use.GetUser()->ReplaceInput(merged_inst, use.GetIndex());
3614 }
3615 ref_data.OriginalNewInstance()->GetBlock()->RemoveInstruction(ref_data.OriginalNewInstance());
3616 }
3617 }
3618
3619 // We need to make sure any allocations dominate their environment uses.
3620 // Technically we could probably remove the env-uses and be fine but this is easy.
3621 void ReorderMaterializationsForEnvDominance() {
3622 for (HBasicBlock* blk : IterateMaterializationBlocks()) {
3623 ScopedArenaAllocator alloc(alloc_->GetArenaStack());
3624 ArenaBitVector still_unsorted(
3625 &alloc, GetGraph()->GetCurrentInstructionId(), false, kArenaAllocLSE);
3626 // This is guaranteed to be very short (since we will abandon LSE if there
3627 // are >= kMaxNumberOfHeapLocations (32) heap locations so that is the
3628 // absolute maximum size this list can be) so doing a selection sort is
3629 // fine. This avoids the need to do a complicated recursive check to
3630 // ensure transitivity for std::sort.
3631 ScopedArenaVector<HNewInstance*> materializations(alloc.Adapter(kArenaAllocLSE));
3632 materializations.reserve(GetHeapRefs().size());
3633 for (HInstruction* ins :
3634 MakeSTLInstructionIteratorRange(HInstructionIterator(blk->GetInstructions()))) {
3635 if (ins->IsNewInstance()) {
3636 materializations.push_back(ins->AsNewInstance());
3637 still_unsorted.SetBit(ins->GetId());
3638 }
3639 }
3640 using Iter = ScopedArenaVector<HNewInstance*>::iterator;
3641 Iter unsorted_start = materializations.begin();
3642 Iter unsorted_end = materializations.end();
3643 // selection sort. Required since the only check we can easily perform a
3644 // is-before-all-unsorted check.
3645 while (unsorted_start != unsorted_end) {
3646 bool found_instruction = false;
3647 for (Iter candidate = unsorted_start; candidate != unsorted_end; ++candidate) {
3648 HNewInstance* ni = *candidate;
3649 if (std::none_of(ni->GetAllEnvironments().cbegin(),
3650 ni->GetAllEnvironments().cend(),
3651 [&](const HEnvironment* env) {
3652 return std::any_of(
3653 env->GetEnvInputs().cbegin(),
3654 env->GetEnvInputs().cend(),
3655 [&](const HInstruction* env_element) {
3656 return env_element != nullptr &&
3657 still_unsorted.IsBitSet(env_element->GetId());
3658 });
3659 })) {
3660 still_unsorted.ClearBit(ni->GetId());
3661 std::swap(*unsorted_start, *candidate);
3662 ++unsorted_start;
3663 found_instruction = true;
3664 break;
3665 }
3666 }
3667 CHECK(found_instruction) << "Unable to select next materialization instruction."
3668 << " Environments have a dependency loop!";
3669 }
3670 // Reverse so we as we prepend them we end up with the correct order.
3671 auto reverse_iter = MakeIterationRange(materializations.rbegin(), materializations.rend());
3672 for (HNewInstance* ins : reverse_iter) {
3673 if (blk->GetFirstInstruction() != ins) {
3674 // Don't do checks since that makes sure the move is safe WRT
3675 // ins->CanBeMoved which for NewInstance is false.
3676 ins->MoveBefore(blk->GetFirstInstruction(), /*do_checks=*/false);
3677 }
3678 }
3679 }
3680 }
3681
3682 private:
3683 void CollectInterestingHeapRefs() {
3684 // Get all the partials we need to move around.
3685 for (size_t i = 0; i < lse_->heap_location_collector_.GetNumberOfHeapLocations(); ++i) {
3686 ReferenceInfo* ri = lse_->heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
3687 if (ri->IsPartialSingleton() &&
3688 ri->GetReference()->GetBlock() != nullptr &&
3689 ri->GetNoEscapeSubgraph()->ContainsBlock(ri->GetReference()->GetBlock())) {
3690 RecordHeapRefField(ri->GetReference()->AsNewInstance(), i);
3691 }
3692 }
3693 }
3694
3695 void RecordHeapRefField(HNewInstance* ni, size_t loc) {
3696 DCHECK(ni != nullptr);
3697 // This is likely to be very short so just do a linear search.
3698 auto it = std::find_if(heap_refs_.begin(), heap_refs_.end(), [&](HeapReferenceData& data) {
3699 return data.OriginalNewInstance() == ni;
3700 });
3701 HeapReferenceData& cur_ref =
3702 (it == heap_refs_.end())
3703 ? heap_refs_.emplace_back(this,
3704 ni,
3705 lse_->heap_location_collector_.GetHeapLocation(loc)
3706 ->GetReferenceInfo()
3707 ->GetNoEscapeSubgraph(),
3708 alloc_)
3709 : *it;
3710 cur_ref.AddHeapLocation(loc);
3711 }
3712
3713
3714 void NotifyNewMaterialization(HInstruction* ins) {
3715 if (ins->IsPhi()) {
3716 new_ref_phis_.push_back(ins->AsPhi());
3717 }
3718 }
3719
3720 size_t GetMaterializationBlockIndex(HBasicBlock* blk, size_t pred_num) const {
3721 DCHECK_LT(blk->GetBlockId(), first_materialization_block_id_)
3722 << "block is a materialization block!";
3723 DCHECK_LT(pred_num, max_preds_per_block_);
3724 return blk->GetBlockId() * max_preds_per_block_ + pred_num;
3725 }
3726
3727 HGraph* GetGraph() const {
3728 return lse_->GetGraph();
3729 }
3730
3731 LSEVisitor* lse_;
3732 ScopedArenaAllocator* alloc_;
3733 ScopedArenaVector<HInstruction*> new_ref_phis_;
3734 ScopedArenaVector<HeapReferenceData> heap_refs_;
3735 size_t max_preds_per_block_;
3736 // An array of (# of non-materialization blocks) * max_preds_per_block
3737 // arranged in block-id major order. Since we can only have at most one
3738 // materialization block on each edge this is the maximum possible number of
3739 // materialization blocks.
3740 ScopedArenaVector<HBasicBlock*> materialization_blocks_;
3741 size_t first_materialization_block_id_;
3742
3743 friend void LSEVisitor::MovePartialEscapes();
Alex Light3a73ffb2021-01-25 14:11:05 +00003744};
3745
3746// Work around c++ type checking annoyances with not being able to forward-declare inner types.
3747class HeapRefHolder
3748 : public std::reference_wrapper<PartialLoadStoreEliminationHelper::HeapReferenceData> {};
3749
3750HInstruction* LSEVisitor::SetupPartialMaterialization(PartialLoadStoreEliminationHelper& helper,
3751 HeapRefHolder&& holder,
3752 size_t pred_idx,
3753 HBasicBlock* entry) {
3754 PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data = holder.get();
3755 HBasicBlock* old_pred = entry->GetPredecessors()[pred_idx];
3756 HInstruction* new_inst = ref_data.OriginalNewInstance();
3757 if (UNLIKELY(!new_inst->GetBlock()->Dominates(entry))) {
3758 LSE_VLOG << "Initial materialization in non-dominating block " << entry->GetBlockId()
3759 << " is null!";
3760 return GetGraph()->GetNullConstant();
3761 }
3762 HBasicBlock* bb = helper.GetOrCreateMaterializationBlock(entry, pred_idx);
3763 CHECK(bb != nullptr) << "entry " << entry->GetBlockId() << " -> " << old_pred->GetBlockId();
3764 HNewInstance* repl_create = new_inst->Clone(GetGraph()->GetAllocator())->AsNewInstance();
3765 repl_create->SetPartialMaterialization();
3766 bb->InsertInstructionBefore(repl_create, bb->GetLastInstruction());
3767 repl_create->CopyEnvironmentFrom(new_inst->GetEnvironment());
3768 MaybeRecordStat(stats_, MethodCompilationStat::kPartialAllocationMoved);
3769 LSE_VLOG << "In blk " << bb->GetBlockId() << " initial materialization is " << *repl_create;
3770 ref_data.AddMaterialization(bb, repl_create);
3771 const FieldInfo* info = nullptr;
3772 for (const HeapLocation* loc : ref_data.IterateLocations()) {
3773 size_t loc_off = heap_location_collector_.GetHeapLocationIndex(loc);
3774 info = field_infos_[loc_off];
3775 DCHECK(loc->GetIndex() == nullptr);
3776 Value value = ReplacementOrValue(heap_values_for_[old_pred->GetBlockId()][loc_off].value);
Alex Lightde7c9e12021-04-01 17:19:05 -07003777 if (value.NeedsLoopPhi() || value.IsMergedUnknown()) {
Alex Light3a73ffb2021-01-25 14:11:05 +00003778 Value repl = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())];
Alex Light3a73ffb2021-01-25 14:11:05 +00003779 DCHECK(repl.IsDefault() || repl.IsInvalid() || repl.IsInstruction())
3780 << repl << " from " << value << " pred is " << old_pred->GetBlockId();
3781 if (!repl.IsInvalid()) {
3782 value = repl;
3783 } else {
3784 FullyMaterializePhi(value.GetPhiPlaceholder(), info->GetFieldType());
3785 value = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())];
3786 }
3787 } else if (value.NeedsNonLoopPhi()) {
3788 Value repl = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())];
3789 DCHECK(repl.IsDefault() || repl.IsInvalid() || repl.IsInstruction())
3790 << repl << " from " << value << " pred is " << old_pred->GetBlockId();
3791 if (!repl.IsInvalid()) {
3792 value = repl;
3793 } else {
3794 MaterializeNonLoopPhis(value.GetPhiPlaceholder(), info->GetFieldType());
3795 value = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())];
3796 }
3797 }
3798 DCHECK(value.IsDefault() || value.IsInstruction())
3799 << GetGraph()->PrettyMethod() << ": " << value;
3800
3801 if (!value.IsDefault() &&
3802 // shadow$_klass_ doesn't need to be manually initialized.
3803 MemberOffset(loc->GetOffset()) != mirror::Object::ClassOffset()) {
3804 CHECK(info != nullptr);
3805 HInstruction* set_value =
3806 new (GetGraph()->GetAllocator()) HInstanceFieldSet(repl_create,
3807 value.GetInstruction(),
3808 field_infos_[loc_off]->GetField(),
3809 loc->GetType(),
3810 MemberOffset(loc->GetOffset()),
3811 false,
3812 field_infos_[loc_off]->GetFieldIndex(),
3813 loc->GetDeclaringClassDefIndex(),
3814 field_infos_[loc_off]->GetDexFile(),
3815 0u);
3816 bb->InsertInstructionAfter(set_value, repl_create);
3817 LSE_VLOG << "Adding " << *set_value << " for materialization setup!";
3818 }
3819 }
3820 return repl_create;
3821}
3822
3823HInstruction* LSEVisitor::GetPartialValueAt(HNewInstance* orig_new_inst, HInstruction* read) {
3824 size_t loc = heap_location_collector_.GetFieldHeapLocation(orig_new_inst, &read->GetFieldInfo());
3825 Value pred = ReplacementOrValue(intermediate_values_.find(read)->second);
3826 LSE_VLOG << "using " << pred << " as default value for " << *read;
3827 if (pred.IsInstruction()) {
3828 return pred.GetInstruction();
3829 } else if (pred.IsMergedUnknown() || pred.NeedsPhi()) {
3830 FullyMaterializePhi(pred.GetPhiPlaceholder(),
3831 heap_location_collector_.GetHeapLocation(loc)->GetType());
3832 HInstruction* res = Replacement(pred).GetInstruction();
3833 LSE_VLOG << pred << " materialized to " << res->DumpWithArgs();
3834 return res;
Alex Lighte4f7fef2021-03-30 17:17:50 -07003835 } else if (pred.IsDefault()) {
3836 HInstruction* res = GetDefaultValue(read->GetType());
3837 LSE_VLOG << pred << " materialized to " << res->DumpWithArgs();
3838 return res;
Alex Light3a73ffb2021-01-25 14:11:05 +00003839 }
3840 LOG(FATAL) << "Unable to find unescaped value at " << read->DumpWithArgs()
Alex Lighte4f7fef2021-03-30 17:17:50 -07003841 << "! This should be impossible! Value is " << pred;
Alex Light3a73ffb2021-01-25 14:11:05 +00003842 UNREACHABLE();
3843}
3844
3845void LSEVisitor::MovePartialEscapes() {
3846 if (!ShouldPerformPartialLSE()) {
3847 return;
3848 }
3849
3850 ScopedArenaAllocator saa(allocator_.GetArenaStack());
3851 PartialLoadStoreEliminationHelper helper(this, &saa);
3852
3853 // Since for PHIs we now will have more information (since we know the object
3854 // hasn't escaped) we need to clear the old phi-replacements where we weren't
3855 // able to find the value.
3856 PrepareForPartialPhiComputation();
3857
3858 for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : helper.GetHeapRefs()) {
3859 LSE_VLOG << "Creating materializations for " << *ref_data.OriginalNewInstance();
3860 // Setup entry and exit blocks.
3861 for (const auto& excluded_cohort : ref_data.GetNoEscapeSubgraph()->GetExcludedCohorts()) {
3862 // Setup materialization blocks.
3863 for (HBasicBlock* entry : excluded_cohort.EntryBlocksReversePostOrder()) {
3864 // Setup entries.
3865 // TODO Assuming we correctly break critical edges every entry block
3866 // must have only a single predecessor so we could just put all this
3867 // stuff in there. OTOH simplifier can do it for us and this is simpler
3868 // to implement - giving clean separation between the original graph and
3869 // materialization blocks - so for now we might as well have these new
3870 // blocks.
3871 ScopedArenaAllocator pred_alloc(saa.GetArenaStack());
3872 ScopedArenaVector<HInstruction*> pred_vals(pred_alloc.Adapter(kArenaAllocLSE));
3873 pred_vals.reserve(entry->GetNumberOfPredecessors());
3874 for (const auto& [pred, pred_idx] :
3875 ZipCount(MakeIterationRange(entry->GetPredecessors()))) {
3876 DCHECK(!helper.IsMaterializationBlock(pred));
3877 if (excluded_cohort.IsEntryBlock(pred)) {
3878 pred_vals.push_back(ref_data.GetMaterialization(pred));
3879 continue;
3880 } else {
3881 pred_vals.push_back(SetupPartialMaterialization(helper, {ref_data}, pred_idx, entry));
3882 }
3883 }
3884 ref_data.GenerateMaterializationValueFromPredecessorsForEntry(entry, pred_vals);
3885 }
3886
3887 // Setup exit block heap-values for later phi-generation.
3888 for (HBasicBlock* exit : excluded_cohort.ExitBlocks()) {
3889 // mark every exit of cohorts as having a value so we can easily
3890 // materialize the PHIs.
3891 // TODO By setting this we can easily use the normal MaterializeLoopPhis
3892 // (via FullyMaterializePhis) in order to generate the default-values
3893 // for predicated-gets. This has the unfortunate side effect of creating
3894 // somewhat more phis than are really needed (in some cases). We really
3895 // should try to eventually know that we can lower these PHIs to only
3896 // the non-escaping value in cases where it is possible. Currently this
3897 // is done to some extent in instruction_simplifier but we have more
3898 // information here to do the right thing.
3899 for (const HeapLocation* loc : ref_data.IterateLocations()) {
3900 size_t loc_off = heap_location_collector_.GetHeapLocationIndex(loc);
3901 // This Value::Default() is only used to fill in PHIs used as the
3902 // default value for PredicatedInstanceFieldGets. The actual value
3903 // stored there is meaningless since the Predicated-iget will use the
3904 // actual field value instead on these paths.
3905 heap_values_for_[exit->GetBlockId()][loc_off].value = Value::Default();
3906 }
3907 }
3908 }
3909
3910 // string materialization through the graph.
3911 // // Visit RPO to PHI the materialized object through the cohort.
3912 for (HBasicBlock* blk : GetGraph()->GetReversePostOrder()) {
3913 // NB This doesn't include materialization blocks.
3914 DCHECK(!helper.IsMaterializationBlock(blk))
3915 << "Materialization blocks should not be in RPO yet.";
3916 if (ref_data.HasMaterialization(blk)) {
3917 continue;
3918 } else if (ref_data.BeforeAllEscapes(blk)) {
3919 ref_data.AddMaterialization(blk, GetGraph()->GetNullConstant());
3920 continue;
3921 } else {
3922 ref_data.GenerateMaterializationValueFromPredecessors(blk);
3923 }
3924 }
3925 }
3926
3927 // Once we've generated all the materializations we can update the users.
3928 helper.FixupPartialObjectUsers();
3929
3930 // Actually put materialization blocks into the graph
3931 helper.InsertMaterializationBlocks();
3932
3933 // Get rid of the original instructions.
3934 helper.RemoveReplacedInstructions();
3935
3936 // Ensure everything is ordered correctly in the materialization blocks. This
3937 // involves moving every NewInstance to the top and ordering them so that any
3938 // required env-uses are correctly ordered.
3939 helper.ReorderMaterializationsForEnvDominance();
3940}
3941
3942void LSEVisitor::FinishFullLSE() {
Vladimir Marko3224f382020-06-23 14:19:53 +01003943 // Remove recorded load instructions that should be eliminated.
Vladimir Marko9e3fe992020-08-25 16:17:51 +01003944 for (const LoadStoreRecord& record : loads_and_stores_) {
3945 size_t id = dchecked_integral_cast<size_t>(record.load_or_store->GetId());
3946 HInstruction* substitute = substitute_instructions_for_loads_[id];
3947 if (substitute == nullptr) {
3948 continue;
3949 }
3950 HInstruction* load = record.load_or_store;
Vladimir Marko3224f382020-06-23 14:19:53 +01003951 DCHECK(load != nullptr);
3952 DCHECK(IsLoad(load));
3953 DCHECK(load->GetBlock() != nullptr) << load->DebugName() << "@" << load->GetDexPc();
Vladimir Marko3224f382020-06-23 14:19:53 +01003954 // We proactively retrieve the substitute for a removed load, so
3955 // a load that has a substitute should not be observed as a heap
3956 // location value.
3957 DCHECK_EQ(FindSubstitute(substitute), substitute);
3958
3959 load->ReplaceWith(substitute);
3960 load->GetBlock()->RemoveInstruction(load);
3961 }
3962
3963 // Remove all the stores we can.
3964 for (const LoadStoreRecord& record : loads_and_stores_) {
3965 bool is_store = record.load_or_store->GetSideEffects().DoesAnyWrite();
3966 DCHECK_EQ(is_store, IsStore(record.load_or_store));
3967 if (is_store && !kept_stores_.IsBitSet(record.load_or_store->GetId())) {
3968 record.load_or_store->GetBlock()->RemoveInstruction(record.load_or_store);
3969 }
3970 }
3971
3972 // Eliminate singleton-classified instructions:
3973 // * - Constructor fences (they never escape this thread).
3974 // * - Allocations (if they are unused).
3975 for (HInstruction* new_instance : singleton_new_instances_) {
3976 size_t removed = HConstructorFence::RemoveConstructorFences(new_instance);
3977 MaybeRecordStat(stats_,
3978 MethodCompilationStat::kConstructorFenceRemovedLSE,
3979 removed);
3980
3981 if (!new_instance->HasNonEnvironmentUses()) {
3982 new_instance->RemoveEnvironmentUsers();
3983 new_instance->GetBlock()->RemoveInstruction(new_instance);
Alex Light86fe9b82020-11-16 16:54:01 +00003984 MaybeRecordStat(stats_, MethodCompilationStat::kFullLSEAllocationRemoved);
Vladimir Marko3224f382020-06-23 14:19:53 +01003985 }
3986 }
3987}
3988
Vladimir Markoc9f4a372021-03-11 10:38:34 +00003989// The LSEVisitor is a ValueObject (indirectly through base classes) and therefore
3990// cannot be directly allocated with an arena allocator, so we need to wrap it.
3991class LSEVisitorWrapper : public DeletableArenaObject<kArenaAllocLSE> {
3992 public:
3993 LSEVisitorWrapper(HGraph* graph,
3994 const HeapLocationCollector& heap_location_collector,
3995 bool perform_partial_lse,
3996 OptimizingCompilerStats* stats)
3997 : lse_visitor_(graph, heap_location_collector, perform_partial_lse, stats) {}
3998
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00003999 void Run() {
4000 lse_visitor_.Run();
Vladimir Markoc9f4a372021-03-11 10:38:34 +00004001 }
4002
4003 private:
4004 LSEVisitor lse_visitor_;
4005};
4006
Alex Light3a73ffb2021-01-25 14:11:05 +00004007bool LoadStoreElimination::Run(bool enable_partial_lse) {
Santiago Aboy Solanes9e3c3712022-04-08 13:24:05 +00004008 if (graph_->IsDebuggable()) {
Mingyao Yang8df69d42015-10-22 15:40:58 -07004009 // Debugger may set heap values or trigger deoptimization of callers.
Mingyao Yang8df69d42015-10-22 15:40:58 -07004010 // Skip this optimization.
Aart Bik24773202018-04-26 10:28:51 -07004011 return false;
Mingyao Yang8df69d42015-10-22 15:40:58 -07004012 }
Alex Light86fe9b82020-11-16 16:54:01 +00004013 // We need to be able to determine reachability. Clear it just to be safe but
4014 // this should initially be empty.
4015 graph_->ClearReachabilityInformation();
4016 // This is O(blocks^3) time complexity. It means we can query reachability in
4017 // O(1) though.
4018 graph_->ComputeReachabilityInformation();
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00004019 ScopedArenaAllocator allocator(graph_->GetArenaStack());
4020 LoadStoreAnalysis lsa(graph_,
4021 stats_,
4022 &allocator,
4023 enable_partial_lse ? LoadStoreAnalysisType::kFull
Nicolas Geoffray2498d852021-11-16 15:12:04 +00004024 : LoadStoreAnalysisType::kBasic);
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00004025 lsa.Run();
4026 const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector();
4027 if (heap_location_collector.GetNumberOfHeapLocations() == 0) {
4028 // No HeapLocation information from LSA, skip this optimization.
4029 return false;
4030 }
xueliang.zhongc239a2b2017-04-27 15:31:37 +01004031
Nicolas Geoffraycf6a9262021-09-17 07:58:04 +00004032 std::unique_ptr<LSEVisitorWrapper> lse_visitor(new (&allocator) LSEVisitorWrapper(
4033 graph_, heap_location_collector, enable_partial_lse, stats_));
4034 lse_visitor->Run();
Aart Bik24773202018-04-26 10:28:51 -07004035 return true;
Mingyao Yang8df69d42015-10-22 15:40:58 -07004036}
4037
Alex Light3a73ffb2021-01-25 14:11:05 +00004038#undef LSE_VLOG
4039
Mingyao Yang8df69d42015-10-22 15:40:58 -07004040} // namespace art