v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
maglev-post-hoc-optimizations-processors.h
Go to the documentation of this file.
1// Copyright 2024 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_MAGLEV_MAGLEV_POST_HOC_OPTIMIZATIONS_PROCESSORS_H_
6#define V8_MAGLEV_MAGLEV_POST_HOC_OPTIMIZATIONS_PROCESSORS_H_
7
17
18namespace v8::internal::maglev {
19
21 public:
22 void PreProcessGraph(Graph* graph) {}
23 void PostProcessGraph(Graph* graph) {}
30 for (int i = 0; i < node->input_count(); i++) {
31 Input& input = node->input(i);
32 while (input.node() && input.node()->Is<Identity>()) {
33 node->change_input(i, input.node()->input(0).node());
34 }
35 }
37 }
38};
39
40// Optimizations involving loops which cannot be done at graph building time.
41// Currently mainly loop invariant code motion.
43 public:
45 : zone(builder->zone()) {
47 builder->compilation_unit()->feedback().was_once_deoptimized();
48 }
49
50 void PreProcessGraph(Graph* graph) {}
52
56 if (current_block->is_loop()) {
59 } else {
60 // TODO(olivf): Some dominance analysis would allow us to keep loop
61 // effects longer than just the first block of the loop.
62 loop_effects = nullptr;
63 }
65 }
66
67 bool IsLoopPhi(Node* input) {
69 if (auto phi = input->TryCast<Phi>()) {
70 if (phi->is_loop_phi() && phi->merge_state() == current_block->state()) {
71 return true;
72 }
73 }
74 return false;
75 }
76
77 bool CanHoist(Node* candidate) {
78 DCHECK_EQ(candidate->input_count(), 1);
80 ValueNode* input = candidate->input(0).node();
81 DCHECK(!IsLoopPhi(input));
82 // For hoisting an instruction we need:
83 // * A unique loop entry block.
84 // * Inputs live before the loop (i.e., not defined inside the loop).
85 // * No hoisting over checks (done eagerly by clearing loop_effects).
86 // TODO(olivf): We should enforce loops having a unique entry block at graph
87 // building time.
88 if (current_block->predecessor_count() != 2) return false;
89 BasicBlock* loop_entry = current_block->predecessor_at(0);
90 if (loop_entry->successors().size() != 1) {
91 return false;
92 }
93 if (IsConstantNode(input->opcode())) return true;
94 return input->owner() != current_block;
95 }
96
98 const ProcessingState& state) {
100 ValueNode* object = ltf->object_input().node();
101 if (IsLoopPhi(object)) {
103 }
104 auto key = std::tuple{object, ltf->offset()};
105 if (!loop_effects->may_have_aliasing_contexts &&
106 !loop_effects->unstable_aspects_cleared &&
107 !loop_effects->context_slot_written.count(key) && CanHoist(ltf)) {
109 }
111 }
112
114 const ProcessingState& state) {
115 return ProcessNamedLoad(ltf, ltf->object_input().node(), ltf->name());
116 }
117
123
130
133 DCHECK(!load->properties().can_deopt());
135 if (IsLoopPhi(object)) {
137 }
138 if (!loop_effects->unstable_aspects_cleared &&
139 !loop_effects->keys_cleared.count(name) &&
140 !loop_effects->objects_written.count(object) && CanHoist(load)) {
142 }
144 }
145
148 // Hoisting a check out of a loop can cause it to trigger more than actually
149 // needed (i.e., if the loop is executed 0 times). This could lead to
150 // deoptimization loops as there is no feedback to learn here. Thus, we
151 // abort this optimization if the function deoptimized previously. Also, if
152 // hoisting of this check fails we need to abort (and not continue) to
153 // ensure we are not hoisting other instructions over it.
155 ValueNode* object = maps->receiver_input().node();
156 if (IsLoopPhi(object)) {
158 }
159 if (!loop_effects->unstable_aspects_cleared && CanHoist(maps)) {
160 if (auto j = current_block->predecessor_at(0)
161 ->control_node()
163 maps->SetEagerDeoptInfo(zone, j->eager_deopt_info()->top_frame(),
164 maps->eager_deopt_info()->feedback_to_update());
166 }
167 }
169 }
170
171 template <typename NodeT>
173 // Ensure we are not hoisting over checks.
174 if (node->properties().can_eager_deopt()) {
175 loop_effects = nullptr;
177 }
179 }
180
181 void PostProcessGraph(Graph* graph) {}
182
187};
188
189template <typename NodeT>
193
195 public:
196 void PreProcessGraph(Graph* graph) {}
202
203 template <typename NodeT>
205 if constexpr (IsValueNode(Node::opcode_of<NodeT>) &&
206 (!NodeT::kProperties.is_required_when_unused() ||
207 std::is_same_v<ArgumentsElements, NodeT>)) {
208 if (!node->is_used()) {
209 if (!node->unused_inputs_were_visited()) {
210 DropInputUses(node);
211 }
213 }
214 }
215
216 if constexpr (CanBeStoreToNonEscapedObject<NodeT>()) {
217 if (node->input(0).node()->template Is<InlinedAllocation>()) {
218 stores_to_allocations_.push_back(node);
219 }
220 }
221
223 }
224
225#ifdef DEBUG
226 ProcessResult Process(Dead* node, const ProcessingState& state) {
227 if (!v8_flags.maglev_untagged_phis) {
228 // These nodes are removed in the phi representation selector, if we are
229 // running without it. Just remove it here.
231 }
232 UNREACHABLE();
233 }
234#endif // DEBUG
235
240
241 private:
242 std::vector<Node*> stores_to_allocations_;
243
246 if (alloc->HasBeenAnalysed() && alloc->HasEscaped()) return;
247 alloc->SetEscaped();
248 for (auto dep : deps) {
249 EscapeAllocation(graph, dep,
250 graph->allocations_escape_map().find(dep)->second);
251 }
252 }
253
255#ifdef DEBUG
256 for (const auto& it : graph->allocations_escape_map()) {
257 auto* alloc = it.first;
258 DCHECK(alloc->HasBeenAnalysed());
259 if (alloc->HasEscaped()) {
260 for (auto* dep : it.second) {
261 DCHECK(dep->HasEscaped());
262 }
263 }
264 }
265#endif // DEBUG
266 }
267
269 for (auto& it : graph->allocations_escape_map()) {
270 auto* alloc = it.first;
271 if (alloc->HasBeenAnalysed()) continue;
272 // Check if all its uses are non escaping.
273 if (alloc->IsEscaping()) {
274 // Escape this allocation and all its dependencies.
275 EscapeAllocation(graph, alloc, it.second);
276 } else {
277 // Try to capture the allocation. This can still change if a escaped
278 // allocation has this value as one of its dependencies.
279 alloc->SetElided();
280 }
281 }
282 // Check that we've reached a fixpoint.
284 }
285
287 for (Node* node : stores_to_allocations_) {
288 InlinedAllocation* alloc =
289 node->input(0).node()->Cast<InlinedAllocation>();
290 // Since we don't analyze if allocations will escape until a fixpoint,
291 // this could drop an use of an allocation and turn it non-escaping.
292 if (alloc->HasBeenElided()) {
293 // Skip first input.
294 for (int i = 1; i < node->input_count(); i++) {
295 DropInputUses(node->input(i));
296 }
297 }
298 }
299 }
300
301 void DropInputUses(Input& input) {
302 ValueNode* input_node = input.node();
303 if (input_node->properties().is_required_when_unused() &&
304 !input_node->Is<ArgumentsElements>())
305 return;
306 input_node->remove_use();
307 if (!input_node->is_used() && !input_node->unused_inputs_were_visited()) {
308 DropInputUses(input_node);
309 }
310 }
311
313 for (Input& input : *node) {
314 DropInputUses(input);
315 }
316 DCHECK(!node->properties().can_eager_deopt());
317 DCHECK(!node->properties().can_lazy_deopt());
318 node->mark_unused_inputs_visited();
319 }
320};
321
323 public:
325 if (V8_UNLIKELY(compilation_info->has_graph_labeller())) {
326 labeller_ = compilation_info->graph_labeller();
327 }
328 }
329
330 void PreProcessGraph(Graph* graph) {}
331 void PostProcessGraph(Graph* graph) {}
337
339 // Note: this need to be done before ValueLocationConstraintProcessor, since
340 // it access the allocation offsets.
341 int size = 0;
342 for (auto alloc : node->allocation_list()) {
343 if (alloc->HasEscaped()) {
344 alloc->set_offset(size);
345 size += alloc->size();
346 }
347 }
348 // ... and update its size.
349 node->set_size(size);
350 // If size is zero, then none of the inlined allocations have escaped, we
351 // can remove the allocation block.
352 if (size == 0) return ProcessResult::kRemove;
354 }
355
357 // Remove inlined allocation that became non-escaping.
358 if (!node->HasEscaped()) {
359 if (v8_flags.trace_maglev_escape_analysis) {
360 std::cout << "* Removing allocation node "
361 << PrintNodeLabel(labeller_, node) << std::endl;
362 }
364 }
366 }
367
368 template <typename NodeT>
370 if constexpr (IsValueNode(Node::opcode_of<NodeT>) &&
371 (!NodeT::kProperties.is_required_when_unused() ||
372 std::is_same_v<ArgumentsElements, NodeT>)) {
373 if (!node->is_used()) {
375 }
377 }
378
379 if constexpr (CanBeStoreToNonEscapedObject<NodeT>()) {
380 if (InlinedAllocation* object =
381 node->input(0).node()->template TryCast<InlinedAllocation>()) {
382 if (!object->HasEscaped()) {
383 if (v8_flags.trace_maglev_escape_analysis) {
384 std::cout << "* Removing store node "
385 << PrintNodeLabel(labeller_, node) << " to allocation "
386 << PrintNodeLabel(labeller_, object) << std::endl;
387 }
389 }
390 }
391 }
393 }
394
395 private:
397};
398
399} // namespace v8::internal::maglev
400
401#endif // V8_MAGLEV_MAGLEV_POST_HOC_OPTIMIZATIONS_PROCESSORS_H_
ProcessResult Process(NodeT *node, const ProcessingState &state)
void EscapeAllocation(Graph *graph, InlinedAllocation *alloc, Graph::SmallAllocationVector &deps)
base::SmallVector< BasicBlock *, 2 > successors() const
BasicBlock * predecessor_at(int i) const
MergePointInterpreterFrameState * state() const
ProcessResult Process(NodeT *node, const ProcessingState &state)
ProcessResult Process(AllocationBlock *node, const ProcessingState &state)
ProcessResult Process(InlinedAllocation *node, const ProcessingState &state)
ValueNode * node() const
Definition maglev-ir.h:1300
ProcessResult ProcessNamedLoad(Node *load, ValueNode *object, KnownNodeAspects::LoadedPropertyMapKey name)
ProcessResult Process(StringLength *len, const ProcessingState &state)
ProcessResult Process(CheckMaps *maps, const ProcessingState &state)
ProcessResult Process(LoadTaggedFieldForProperty *ltf, const ProcessingState &state)
ProcessResult Process(NodeT *node, const ProcessingState &state)
ProcessResult Process(LoadTaggedFieldForContextSlot *ltf, const ProcessingState &state)
ProcessResult Process(LoadTypedArrayLength *len, const ProcessingState &state)
MaglevCompilationUnit * compilation_unit() const
constexpr bool Is() const
Definition maglev-ir.h:2362
static constexpr Opcode opcode_of
Definition maglev-ir.h:1909
constexpr Input & input(int index)
Definition maglev-ir.h:1978
constexpr int input_count() const
Definition maglev-ir.h:1973
constexpr OpProperties properties() const
Definition maglev-ir.h:1940
constexpr bool is_required_when_unused() const
Definition maglev-ir.h:1065
ProcessResult Process(NodeBase *node, const ProcessingState &state)
bool unused_inputs_were_visited() const
Definition maglev-ir.h:2435
RpoNumber block
constexpr bool IsConstantNode(Opcode opcode)
Definition maglev-ir.h:491
constexpr bool IsValueNode(Opcode opcode)
Definition maglev-ir.h:488
bool TryCast(Tagged< From > value, Tagged< To > *out)
Definition casting.h:77
bool Is(IndirectHandle< U > value)
Definition handles-inl.h:51
V8_EXPORT_PRIVATE FlagValues v8_flags
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define V8_UNLIKELY(condition)
Definition v8config.h:660