Line data Source code
1 : // Copyright 2016 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 : #include "src/compiler/memory-optimizer.h"
6 :
7 : #include "src/compiler/js-graph.h"
8 : #include "src/compiler/linkage.h"
9 : #include "src/compiler/node-matchers.h"
10 : #include "src/compiler/node-properties.h"
11 : #include "src/compiler/node.h"
12 : #include "src/compiler/simplified-operator.h"
13 : #include "src/interface-descriptors.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 : namespace compiler {
18 :
19 530980 : MemoryOptimizer::MemoryOptimizer(JSGraph* jsgraph, Zone* zone,
20 : PoisoningMitigationLevel poisoning_level,
21 : AllocationFolding allocation_folding)
22 : : jsgraph_(jsgraph),
23 : empty_state_(AllocationState::Empty(zone)),
24 : pending_(zone),
25 : tokens_(zone),
26 : zone_(zone),
27 : graph_assembler_(jsgraph, nullptr, nullptr, zone),
28 : poisoning_level_(poisoning_level),
29 1592940 : allocation_folding_(allocation_folding) {}
30 :
31 530977 : void MemoryOptimizer::Optimize() {
32 530977 : EnqueueUses(graph()->start(), empty_state());
33 27689534 : while (!tokens_.empty()) {
34 13579277 : Token const token = tokens_.front();
35 : tokens_.pop();
36 13579279 : VisitNode(token.node, token.state);
37 : }
38 : DCHECK(pending_.empty());
39 : DCHECK(tokens_.empty());
40 530982 : }
41 :
42 14019 : MemoryOptimizer::AllocationGroup::AllocationGroup(Node* node,
43 : AllocationType allocation,
44 : Zone* zone)
45 14019 : : node_ids_(zone), allocation_(allocation), size_(nullptr) {
46 28038 : node_ids_.insert(node->id());
47 14019 : }
48 :
49 0 : MemoryOptimizer::AllocationGroup::AllocationGroup(Node* node,
50 : AllocationType allocation,
51 : Node* size, Zone* zone)
52 178775 : : node_ids_(zone), allocation_(allocation), size_(size) {
53 357550 : node_ids_.insert(node->id());
54 0 : }
55 :
56 0 : void MemoryOptimizer::AllocationGroup::Add(Node* node) {
57 87112 : node_ids_.insert(node->id());
58 0 : }
59 :
60 0 : bool MemoryOptimizer::AllocationGroup::Contains(Node* node) const {
61 0 : return node_ids_.find(node->id()) != node_ids_.end();
62 : }
63 :
64 0 : MemoryOptimizer::AllocationState::AllocationState()
65 530980 : : group_(nullptr), size_(std::numeric_limits<int>::max()), top_(nullptr) {}
66 :
67 0 : MemoryOptimizer::AllocationState::AllocationState(AllocationGroup* group)
68 14343 : : group_(group), size_(std::numeric_limits<int>::max()), top_(nullptr) {}
69 :
70 0 : MemoryOptimizer::AllocationState::AllocationState(AllocationGroup* group,
71 : intptr_t size, Node* top)
72 222331 : : group_(group), size_(size), top_(top) {}
73 :
74 0 : bool MemoryOptimizer::AllocationState::IsYoungGenerationAllocation() const {
75 2783662 : return group() && group()->IsYoungGenerationAllocation();
76 : }
77 :
78 : namespace {
79 :
80 4997864 : bool CanAllocate(const Node* node) {
81 4997864 : switch (node->opcode()) {
82 : case IrOpcode::kBitcastTaggedToWord:
83 : case IrOpcode::kBitcastWordToTagged:
84 : case IrOpcode::kComment:
85 : case IrOpcode::kDebugAbort:
86 : case IrOpcode::kDebugBreak:
87 : case IrOpcode::kDeoptimizeIf:
88 : case IrOpcode::kDeoptimizeUnless:
89 : case IrOpcode::kIfException:
90 : case IrOpcode::kLoad:
91 : case IrOpcode::kLoadElement:
92 : case IrOpcode::kLoadField:
93 : case IrOpcode::kPoisonedLoad:
94 : case IrOpcode::kProtectedLoad:
95 : case IrOpcode::kProtectedStore:
96 : case IrOpcode::kRetain:
97 : case IrOpcode::kStoreElement:
98 : case IrOpcode::kStoreField:
99 : case IrOpcode::kTaggedPoisonOnSpeculation:
100 : case IrOpcode::kUnalignedLoad:
101 : case IrOpcode::kUnalignedStore:
102 : case IrOpcode::kUnsafePointerAdd:
103 : case IrOpcode::kUnreachable:
104 : case IrOpcode::kWord32AtomicAdd:
105 : case IrOpcode::kWord32AtomicAnd:
106 : case IrOpcode::kWord32AtomicCompareExchange:
107 : case IrOpcode::kWord32AtomicExchange:
108 : case IrOpcode::kWord32AtomicLoad:
109 : case IrOpcode::kWord32AtomicOr:
110 : case IrOpcode::kWord32AtomicPairAdd:
111 : case IrOpcode::kWord32AtomicPairAnd:
112 : case IrOpcode::kWord32AtomicPairCompareExchange:
113 : case IrOpcode::kWord32AtomicPairExchange:
114 : case IrOpcode::kWord32AtomicPairLoad:
115 : case IrOpcode::kWord32AtomicPairOr:
116 : case IrOpcode::kWord32AtomicPairStore:
117 : case IrOpcode::kWord32AtomicPairSub:
118 : case IrOpcode::kWord32AtomicPairXor:
119 : case IrOpcode::kWord32AtomicStore:
120 : case IrOpcode::kWord32AtomicSub:
121 : case IrOpcode::kWord32AtomicXor:
122 : case IrOpcode::kWord32PoisonOnSpeculation:
123 : case IrOpcode::kWord64AtomicAdd:
124 : case IrOpcode::kWord64AtomicAnd:
125 : case IrOpcode::kWord64AtomicCompareExchange:
126 : case IrOpcode::kWord64AtomicExchange:
127 : case IrOpcode::kWord64AtomicLoad:
128 : case IrOpcode::kWord64AtomicOr:
129 : case IrOpcode::kWord64AtomicStore:
130 : case IrOpcode::kWord64AtomicSub:
131 : case IrOpcode::kWord64AtomicXor:
132 : case IrOpcode::kWord64PoisonOnSpeculation:
133 : return false;
134 :
135 : case IrOpcode::kCall:
136 : case IrOpcode::kCallWithCallerSavedRegisters:
137 23265 : return !(CallDescriptorOf(node->op())->flags() &
138 : CallDescriptor::kNoAllocate);
139 :
140 : case IrOpcode::kStore:
141 : // Store is not safe because it could be part of CSA's bump pointer
142 : // allocation(?).
143 31464 : return true;
144 :
145 : default:
146 : break;
147 : }
148 1148004 : return true;
149 : }
150 :
151 134852 : bool CanLoopAllocate(Node* loop_effect_phi, Zone* temp_zone) {
152 134852 : Node* const control = NodeProperties::GetControlInput(loop_effect_phi);
153 :
154 134853 : ZoneQueue<Node*> queue(temp_zone);
155 : ZoneSet<Node*> visited(temp_zone);
156 : visited.insert(loop_effect_phi);
157 :
158 : // Start the effect chain walk from the loop back edges.
159 404556 : for (int i = 1; i < control->InputCount(); ++i) {
160 404554 : queue.push(loop_effect_phi->InputAt(i));
161 : }
162 :
163 359289 : while (!queue.empty()) {
164 225343 : Node* const current = queue.front();
165 : queue.pop();
166 225341 : if (visited.find(current) == visited.end()) {
167 : visited.insert(current);
168 :
169 203613 : if (CanAllocate(current)) return true;
170 :
171 452450 : for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
172 180980 : queue.push(NodeProperties::GetEffectInput(current, i));
173 : }
174 : }
175 : }
176 : return false;
177 : }
178 :
179 : } // namespace
180 :
181 13579306 : void MemoryOptimizer::VisitNode(Node* node, AllocationState const* state) {
182 : DCHECK(!node->IsDead());
183 : DCHECK_LT(0, node->op()->EffectInputCount());
184 13579306 : switch (node->opcode()) {
185 : case IrOpcode::kAllocate:
186 : // Allocate nodes were purged from the graph in effect-control
187 : // linearization.
188 0 : UNREACHABLE();
189 : case IrOpcode::kAllocateRaw:
190 236350 : return VisitAllocateRaw(node, state);
191 : case IrOpcode::kCall:
192 3896811 : return VisitCall(node, state);
193 : case IrOpcode::kCallWithCallerSavedRegisters:
194 452 : return VisitCallWithCallerSavedRegisters(node, state);
195 : case IrOpcode::kLoadElement:
196 27062 : return VisitLoadElement(node, state);
197 : case IrOpcode::kLoadField:
198 1840719 : return VisitLoadField(node, state);
199 : case IrOpcode::kStoreElement:
200 44149 : return VisitStoreElement(node, state);
201 : case IrOpcode::kStoreField:
202 2487158 : return VisitStoreField(node, state);
203 : case IrOpcode::kStore:
204 252355 : return VisitStore(node, state);
205 : default:
206 4794250 : if (!CanAllocate(node)) {
207 : // These operations cannot trigger GC.
208 : return VisitOtherEffect(node, state);
209 : }
210 : }
211 : DCHECK_EQ(0, node->op()->EffectOutputCount());
212 : }
213 :
214 : #define __ gasm()->
215 :
216 236349 : void MemoryOptimizer::VisitAllocateRaw(Node* node,
217 : AllocationState const* state) {
218 : DCHECK_EQ(IrOpcode::kAllocateRaw, node->opcode());
219 : Node* value;
220 : Node* size = node->InputAt(0);
221 : Node* effect = node->InputAt(1);
222 : Node* control = node->InputAt(2);
223 :
224 236349 : gasm()->Reset(effect, control);
225 :
226 236348 : AllocationType allocation = AllocationTypeOf(node->op());
227 :
228 : // Propagate tenuring from outer allocations to inner allocations, i.e.
229 : // when we allocate an object in old space and store a newly allocated
230 : // child object into the pretenured object, then the newly allocated
231 : // child object also should get pretenured to old space.
232 236350 : if (allocation == AllocationType::kOld) {
233 13025 : for (Edge const edge : node->use_edges()) {
234 : Node* const user = edge.from();
235 12427 : if (user->opcode() == IrOpcode::kStoreField && edge.index() == 0) {
236 : Node* const child = user->InputAt(1);
237 3658 : if (child->opcode() == IrOpcode::kAllocateRaw &&
238 0 : AllocationTypeOf(child->op()) == AllocationType::kYoung) {
239 0 : NodeProperties::ChangeOp(child, node->op());
240 0 : break;
241 : }
242 : }
243 : }
244 : } else {
245 : DCHECK_EQ(AllocationType::kYoung, allocation);
246 5680758 : for (Edge const edge : node->use_edges()) {
247 : Node* const user = edge.from();
248 5445006 : if (user->opcode() == IrOpcode::kStoreField && edge.index() == 1) {
249 : Node* const parent = user->InputAt(0);
250 119594 : if (parent->opcode() == IrOpcode::kAllocateRaw &&
251 45881 : AllocationTypeOf(parent->op()) == AllocationType::kOld) {
252 : allocation = AllocationType::kOld;
253 : break;
254 : }
255 : }
256 : }
257 : }
258 :
259 : // Determine the top/limit addresses.
260 236350 : Node* top_address = __ ExternalConstant(
261 : allocation == AllocationType::kYoung
262 : ? ExternalReference::new_space_allocation_top_address(isolate())
263 236347 : : ExternalReference::old_space_allocation_top_address(isolate()));
264 236348 : Node* limit_address = __ ExternalConstant(
265 : allocation == AllocationType::kYoung
266 : ? ExternalReference::new_space_allocation_limit_address(isolate())
267 236348 : : ExternalReference::old_space_allocation_limit_address(isolate()));
268 :
269 : // Check if we can fold this allocation into a previous allocation represented
270 : // by the incoming {state}.
271 : IntPtrMatcher m(size);
272 236349 : if (m.IsInRange(0, kMaxRegularHeapObjectSize)) {
273 : intptr_t const object_size = m.Value();
274 620635 : if (allocation_folding_ == AllocationFolding::kDoAllocationFolding &&
275 265886 : state->size() <= kMaxRegularHeapObjectSize - object_size &&
276 : state->group()->allocation() == allocation) {
277 : // We can fold this Allocate {node} into the allocation {group}
278 : // represented by the given {state}. Compute the upper bound for
279 : // the new {state}.
280 43556 : intptr_t const state_size = state->size() + object_size;
281 :
282 : // Update the reservation check to the actual maximum upper bound.
283 : AllocationGroup* const group = state->group();
284 43556 : if (machine()->Is64()) {
285 43556 : if (OpParameter<int64_t>(group->size()->op()) < state_size) {
286 43556 : NodeProperties::ChangeOp(group->size(),
287 43556 : common()->Int64Constant(state_size));
288 : }
289 : } else {
290 0 : if (OpParameter<int32_t>(group->size()->op()) < state_size) {
291 0 : NodeProperties::ChangeOp(
292 : group->size(),
293 0 : common()->Int32Constant(static_cast<int32_t>(state_size)));
294 : }
295 : }
296 :
297 : // Update the allocation top with the new object allocation.
298 : // TODO(bmeurer): Defer writing back top as much as possible.
299 43556 : Node* top = __ IntAdd(state->top(), size);
300 87112 : __ Store(StoreRepresentation(MachineType::PointerRepresentation(),
301 : kNoWriteBarrier),
302 43556 : top_address, __ IntPtrConstant(0), top);
303 :
304 : // Compute the effective inner allocated address.
305 43556 : value = __ BitcastWordToTagged(
306 43556 : __ IntAdd(state->top(), __ IntPtrConstant(kHeapObjectTag)));
307 :
308 : // Extend the allocation {group}.
309 : group->Add(value);
310 : state = AllocationState::Open(group, state_size, top, zone());
311 : } else {
312 178774 : auto call_runtime = __ MakeDeferredLabel();
313 357548 : auto done = __ MakeLabel(MachineType::PointerRepresentation());
314 :
315 : // Setup a mutable reservation size node; will be patched as we fold
316 : // additional allocations into this new group.
317 178774 : Node* size = __ UniqueIntPtrConstant(object_size);
318 :
319 : // Load allocation top and limit.
320 : Node* top =
321 178775 : __ Load(MachineType::Pointer(), top_address, __ IntPtrConstant(0));
322 : Node* limit =
323 178775 : __ Load(MachineType::Pointer(), limit_address, __ IntPtrConstant(0));
324 :
325 : // Check if we need to collect garbage before we can start bump pointer
326 : // allocation (always done for folded allocations).
327 178775 : Node* check = __ UintLessThan(__ IntAdd(top, size), limit);
328 :
329 178775 : __ GotoIfNot(check, &call_runtime);
330 : __ Goto(&done, top);
331 :
332 : __ Bind(&call_runtime);
333 : {
334 : Node* target = allocation == AllocationType::kYoung
335 : ? __
336 : AllocateInYoungGenerationStubConstant()
337 : : __
338 178774 : AllocateInOldGenerationStubConstant();
339 178774 : if (!allocate_operator_.is_set()) {
340 : auto descriptor = AllocateDescriptor{};
341 108317 : auto call_descriptor = Linkage::GetStubCallDescriptor(
342 : graph()->zone(), descriptor, descriptor.GetStackParameterCount(),
343 108317 : CallDescriptor::kCanUseRoots, Operator::kNoThrow);
344 108317 : allocate_operator_.set(common()->Call(call_descriptor));
345 : }
346 178775 : Node* vfalse = __ BitcastTaggedToWord(
347 178775 : __ Call(allocate_operator_.get(), target, size));
348 178775 : vfalse = __ IntSub(vfalse, __ IntPtrConstant(kHeapObjectTag));
349 : __ Goto(&done, vfalse);
350 : }
351 :
352 : __ Bind(&done);
353 :
354 : // Compute the new top and write it back.
355 357550 : top = __ IntAdd(done.PhiAt(0), __ IntPtrConstant(object_size));
356 357550 : __ Store(StoreRepresentation(MachineType::PointerRepresentation(),
357 : kNoWriteBarrier),
358 178775 : top_address, __ IntPtrConstant(0), top);
359 :
360 : // Compute the initial object address.
361 357550 : value = __ BitcastWordToTagged(
362 178775 : __ IntAdd(done.PhiAt(0), __ IntPtrConstant(kHeapObjectTag)));
363 :
364 : // Start a new allocation group.
365 : AllocationGroup* group =
366 : new (zone()) AllocationGroup(value, allocation, size, zone());
367 : state = AllocationState::Open(group, object_size, top, zone());
368 : }
369 : } else {
370 14019 : auto call_runtime = __ MakeDeferredLabel();
371 28038 : auto done = __ MakeLabel(MachineRepresentation::kTaggedPointer);
372 :
373 : // Load allocation top and limit.
374 : Node* top =
375 14019 : __ Load(MachineType::Pointer(), top_address, __ IntPtrConstant(0));
376 : Node* limit =
377 14019 : __ Load(MachineType::Pointer(), limit_address, __ IntPtrConstant(0));
378 :
379 : // Compute the new top.
380 14019 : Node* new_top = __ IntAdd(top, size);
381 :
382 : // Check if we can do bump pointer allocation here.
383 14019 : Node* check = __ UintLessThan(new_top, limit);
384 14019 : __ GotoIfNot(check, &call_runtime);
385 28038 : __ Store(StoreRepresentation(MachineType::PointerRepresentation(),
386 : kNoWriteBarrier),
387 14019 : top_address, __ IntPtrConstant(0), new_top);
388 14019 : __ Goto(&done, __ BitcastWordToTagged(
389 : __ IntAdd(top, __ IntPtrConstant(kHeapObjectTag))));
390 :
391 : __ Bind(&call_runtime);
392 : Node* target = allocation == AllocationType::kYoung
393 : ? __
394 : AllocateInYoungGenerationStubConstant()
395 : : __
396 14019 : AllocateInOldGenerationStubConstant();
397 14019 : if (!allocate_operator_.is_set()) {
398 : auto descriptor = AllocateDescriptor{};
399 4155 : auto call_descriptor = Linkage::GetStubCallDescriptor(
400 : graph()->zone(), descriptor, descriptor.GetStackParameterCount(),
401 4155 : CallDescriptor::kCanUseRoots, Operator::kNoThrow);
402 4155 : allocate_operator_.set(common()->Call(call_descriptor));
403 : }
404 14019 : __ Goto(&done, __ Call(allocate_operator_.get(), target, size));
405 :
406 : __ Bind(&done);
407 : value = done.PhiAt(0);
408 :
409 : // Create an unfoldable allocation group.
410 : AllocationGroup* group =
411 14019 : new (zone()) AllocationGroup(value, allocation, zone());
412 : state = AllocationState::Closed(group, zone());
413 : }
414 :
415 236350 : effect = __ ExtractCurrentEffect();
416 236350 : control = __ ExtractCurrentControl();
417 :
418 : // Replace all effect uses of {node} with the {effect}, enqueue the
419 : // effect uses for further processing, and replace all value uses of
420 : // {node} with the {value}.
421 11151246 : for (Edge edge : node->use_edges()) {
422 5457448 : if (NodeProperties::IsEffectEdge(edge)) {
423 236466 : EnqueueUse(edge.from(), edge.index(), state);
424 236466 : edge.UpdateTo(effect);
425 5220983 : } else if (NodeProperties::IsValueEdge(edge)) {
426 2908445 : edge.UpdateTo(value);
427 : } else {
428 : DCHECK(NodeProperties::IsControlEdge(edge));
429 2312538 : edge.UpdateTo(control);
430 : }
431 : }
432 :
433 : // Kill the {node} to make sure we don't leave dangling dead uses.
434 236350 : node->Kill();
435 236349 : }
436 :
437 : #undef __
438 :
439 3896808 : void MemoryOptimizer::VisitCall(Node* node, AllocationState const* state) {
440 : DCHECK_EQ(IrOpcode::kCall, node->opcode());
441 : // If the call can allocate, we start with a fresh state.
442 7793615 : if (!(CallDescriptorOf(node->op())->flags() & CallDescriptor::kNoAllocate)) {
443 : state = empty_state();
444 : }
445 3896807 : EnqueueUses(node, state);
446 3896815 : }
447 :
448 452 : void MemoryOptimizer::VisitCallWithCallerSavedRegisters(
449 : Node* node, AllocationState const* state) {
450 : DCHECK_EQ(IrOpcode::kCallWithCallerSavedRegisters, node->opcode());
451 : // If the call can allocate, we start with a fresh state.
452 904 : if (!(CallDescriptorOf(node->op())->flags() & CallDescriptor::kNoAllocate)) {
453 : state = empty_state();
454 : }
455 452 : EnqueueUses(node, state);
456 452 : }
457 :
458 27062 : void MemoryOptimizer::VisitLoadElement(Node* node,
459 : AllocationState const* state) {
460 : DCHECK_EQ(IrOpcode::kLoadElement, node->opcode());
461 27062 : ElementAccess const& access = ElementAccessOf(node->op());
462 : Node* index = node->InputAt(1);
463 27062 : node->ReplaceInput(1, ComputeIndex(access, index));
464 27062 : if (NeedsPoisoning(access.load_sensitivity) &&
465 : access.machine_type.representation() !=
466 : MachineRepresentation::kTaggedPointer) {
467 0 : NodeProperties::ChangeOp(node,
468 0 : machine()->PoisonedLoad(access.machine_type));
469 : } else {
470 27062 : NodeProperties::ChangeOp(node, machine()->Load(access.machine_type));
471 : }
472 27062 : EnqueueUses(node, state);
473 27062 : }
474 :
475 1840719 : void MemoryOptimizer::VisitLoadField(Node* node, AllocationState const* state) {
476 : DCHECK_EQ(IrOpcode::kLoadField, node->opcode());
477 1840719 : FieldAccess const& access = FieldAccessOf(node->op());
478 3681438 : Node* offset = jsgraph()->IntPtrConstant(access.offset - access.tag());
479 1840719 : node->InsertInput(graph()->zone(), 1, offset);
480 1840719 : if (NeedsPoisoning(access.load_sensitivity) &&
481 : access.machine_type.representation() !=
482 : MachineRepresentation::kTaggedPointer) {
483 0 : NodeProperties::ChangeOp(node,
484 0 : machine()->PoisonedLoad(access.machine_type));
485 : } else {
486 1840719 : NodeProperties::ChangeOp(node, machine()->Load(access.machine_type));
487 : }
488 1840719 : EnqueueUses(node, state);
489 1840719 : }
490 :
491 44149 : void MemoryOptimizer::VisitStoreElement(Node* node,
492 : AllocationState const* state) {
493 : DCHECK_EQ(IrOpcode::kStoreElement, node->opcode());
494 44149 : ElementAccess const& access = ElementAccessOf(node->op());
495 : Node* object = node->InputAt(0);
496 : Node* index = node->InputAt(1);
497 : WriteBarrierKind write_barrier_kind =
498 44149 : ComputeWriteBarrierKind(object, state, access.write_barrier_kind);
499 44149 : node->ReplaceInput(1, ComputeIndex(access, index));
500 88298 : NodeProperties::ChangeOp(
501 : node, machine()->Store(StoreRepresentation(
502 44149 : access.machine_type.representation(), write_barrier_kind)));
503 44149 : EnqueueUses(node, state);
504 44149 : }
505 :
506 2487158 : void MemoryOptimizer::VisitStoreField(Node* node,
507 : AllocationState const* state) {
508 : DCHECK_EQ(IrOpcode::kStoreField, node->opcode());
509 2487158 : FieldAccess const& access = FieldAccessOf(node->op());
510 : Node* object = node->InputAt(0);
511 : WriteBarrierKind write_barrier_kind =
512 2487159 : ComputeWriteBarrierKind(object, state, access.write_barrier_kind);
513 4974318 : Node* offset = jsgraph()->IntPtrConstant(access.offset - access.tag());
514 2487164 : node->InsertInput(graph()->zone(), 1, offset);
515 4974347 : NodeProperties::ChangeOp(
516 : node, machine()->Store(StoreRepresentation(
517 2487172 : access.machine_type.representation(), write_barrier_kind)));
518 2487172 : EnqueueUses(node, state);
519 2487157 : }
520 :
521 252355 : void MemoryOptimizer::VisitStore(Node* node, AllocationState const* state) {
522 : DCHECK_EQ(IrOpcode::kStore, node->opcode());
523 252355 : StoreRepresentation representation = StoreRepresentationOf(node->op());
524 : Node* object = node->InputAt(0);
525 252355 : WriteBarrierKind write_barrier_kind = ComputeWriteBarrierKind(
526 252355 : object, state, representation.write_barrier_kind());
527 252355 : if (write_barrier_kind != representation.write_barrier_kind()) {
528 688 : NodeProperties::ChangeOp(
529 : node, machine()->Store(StoreRepresentation(
530 344 : representation.representation(), write_barrier_kind)));
531 : }
532 252355 : EnqueueUses(node, state);
533 252355 : }
534 :
535 0 : void MemoryOptimizer::VisitOtherEffect(Node* node,
536 : AllocationState const* state) {
537 3704649 : EnqueueUses(node, state);
538 0 : }
539 :
540 71211 : Node* MemoryOptimizer::ComputeIndex(ElementAccess const& access, Node* index) {
541 : int const element_size_shift =
542 71211 : ElementSizeLog2Of(access.machine_type.representation());
543 71211 : if (element_size_shift) {
544 65961 : index = graph()->NewNode(machine()->WordShl(), index,
545 : jsgraph()->IntPtrConstant(element_size_shift));
546 : }
547 142422 : int const fixed_offset = access.header_size - access.tag();
548 71211 : if (fixed_offset) {
549 62279 : index = graph()->NewNode(machine()->IntAdd(), index,
550 : jsgraph()->IntPtrConstant(fixed_offset));
551 : }
552 71211 : return index;
553 : }
554 :
555 2783662 : WriteBarrierKind MemoryOptimizer::ComputeWriteBarrierKind(
556 : Node* object, AllocationState const* state,
557 : WriteBarrierKind write_barrier_kind) {
558 4750992 : if (state->IsYoungGenerationAllocation() &&
559 : state->group()->Contains(object)) {
560 : write_barrier_kind = kNoWriteBarrier;
561 : }
562 2783662 : return write_barrier_kind;
563 : }
564 :
565 1716511 : MemoryOptimizer::AllocationState const* MemoryOptimizer::MergeStates(
566 : AllocationStates const& states) {
567 : // Check if all states are the same; or at least if all allocation
568 : // states belong to the same allocation group.
569 1716511 : AllocationState const* state = states.front();
570 : AllocationGroup* group = state->group();
571 7156725 : for (size_t i = 1; i < states.size(); ++i) {
572 2720107 : if (states[i] != state) state = nullptr;
573 2720107 : if (states[i]->group() != group) group = nullptr;
574 : }
575 1716511 : if (state == nullptr) {
576 83335 : if (group != nullptr) {
577 : // We cannot fold any more allocations into this group, but we can still
578 : // eliminate write barriers on stores to this group.
579 : // TODO(bmeurer): We could potentially just create a Phi here to merge
580 : // the various tops; but we need to pay special attention not to create
581 : // an unschedulable graph.
582 : state = AllocationState::Closed(group, zone());
583 : } else {
584 : // The states are from different allocation groups.
585 : state = empty_state();
586 : }
587 : }
588 1716511 : return state;
589 : }
590 :
591 4706324 : void MemoryOptimizer::EnqueueMerge(Node* node, int index,
592 : AllocationState const* state) {
593 : DCHECK_EQ(IrOpcode::kEffectPhi, node->opcode());
594 4706324 : int const input_count = node->InputCount() - 1;
595 : DCHECK_LT(0, input_count);
596 : Node* const control = node->InputAt(input_count);
597 4706324 : if (control->opcode() == IrOpcode::kLoop) {
598 269705 : if (index == 0) {
599 134853 : if (CanLoopAllocate(node, zone())) {
600 : // If the loop can allocate, we start with an empty state at the
601 : // beginning.
602 113124 : EnqueueUses(node, empty_state());
603 : } else {
604 : // If the loop cannot allocate, we can just propagate the state from
605 : // before the loop.
606 21728 : EnqueueUses(node, state);
607 : }
608 : } else {
609 : // Do not revisit backedges.
610 : }
611 : } else {
612 : DCHECK_EQ(IrOpcode::kMerge, control->opcode());
613 : // Check if we already know about this pending merge.
614 : NodeId const id = node->id();
615 : auto it = pending_.find(id);
616 4436619 : if (it == pending_.end()) {
617 : // Insert a new pending merge.
618 1716512 : it = pending_.insert(std::make_pair(id, AllocationStates(zone()))).first;
619 : }
620 : // Add the next input state.
621 4436622 : it->second.push_back(state);
622 : // Check if states for all inputs are available by now.
623 4436626 : if (it->second.size() == static_cast<size_t>(input_count)) {
624 : // All inputs to this effect merge are done, merge the states given all
625 : // input constraints, drop the pending merge and enqueue uses of the
626 : // EffectPhi {node}.
627 1716516 : state = MergeStates(it->second);
628 1716512 : EnqueueUses(node, state);
629 : pending_.erase(it);
630 : }
631 : }
632 4706325 : }
633 :
634 14636339 : void MemoryOptimizer::EnqueueUses(Node* node, AllocationState const* state) {
635 102909315 : for (Edge const edge : node->use_edges()) {
636 44136820 : if (NodeProperties::IsEffectEdge(edge)) {
637 18049142 : EnqueueUse(edge.from(), edge.index(), state);
638 : }
639 : }
640 14635675 : }
641 :
642 18285614 : void MemoryOptimizer::EnqueueUse(Node* node, int index,
643 : AllocationState const* state) {
644 18285614 : if (node->opcode() == IrOpcode::kEffectPhi) {
645 : // An EffectPhi represents a merge of different effect chains, which
646 : // needs special handling depending on whether the merge is part of a
647 : // loop or just a normal control join.
648 4706328 : EnqueueMerge(node, index, state);
649 : } else {
650 13579286 : Token token = {node, state};
651 : tokens_.push(token);
652 : }
653 18285601 : }
654 :
655 0 : Graph* MemoryOptimizer::graph() const { return jsgraph()->graph(); }
656 :
657 0 : Isolate* MemoryOptimizer::isolate() const { return jsgraph()->isolate(); }
658 :
659 0 : CommonOperatorBuilder* MemoryOptimizer::common() const {
660 0 : return jsgraph()->common();
661 : }
662 :
663 0 : MachineOperatorBuilder* MemoryOptimizer::machine() const {
664 0 : return jsgraph()->machine();
665 : }
666 :
667 1867781 : bool MemoryOptimizer::NeedsPoisoning(LoadSensitivity load_sensitivity) const {
668 : // Safe loads do not need poisoning.
669 1867781 : if (load_sensitivity == LoadSensitivity::kSafe) return false;
670 :
671 1867781 : switch (poisoning_level_) {
672 : case PoisoningMitigationLevel::kDontPoison:
673 : return false;
674 : case PoisoningMitigationLevel::kPoisonAll:
675 0 : return true;
676 : case PoisoningMitigationLevel::kPoisonCriticalOnly:
677 0 : return load_sensitivity == LoadSensitivity::kCritical;
678 : }
679 0 : UNREACHABLE();
680 : }
681 :
682 : } // namespace compiler
683 : } // namespace internal
684 122028 : } // namespace v8
|