1 // Copyright 2019 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/csa-load-elimination.h"
6 
7 #include "src/compiler/common-operator.h"
8 #include "src/compiler/node-matchers.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/simplified-operator.h"
11 
12 namespace v8 {
13 namespace internal {
14 namespace compiler {
15 
Reduce(Node * node)16 Reduction CsaLoadElimination::Reduce(Node* node) {
17   if (FLAG_trace_turbo_load_elimination) {
18     if (node->op()->EffectInputCount() > 0) {
19       PrintF(" visit #%d:%s", node->id(), node->op()->mnemonic());
20       if (node->op()->ValueInputCount() > 0) {
21         PrintF("(");
22         for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
23           if (i > 0) PrintF(", ");
24           Node* const value = NodeProperties::GetValueInput(node, i);
25           PrintF("#%d:%s", value->id(), value->op()->mnemonic());
26         }
27         PrintF(")");
28       }
29       PrintF("\n");
30       for (int i = 0; i < node->op()->EffectInputCount(); ++i) {
31         Node* const effect = NodeProperties::GetEffectInput(node, i);
32         if (AbstractState const* const state = node_states_.Get(effect)) {
33           PrintF("  state[%i]: #%d:%s\n", i, effect->id(),
34                  effect->op()->mnemonic());
35           state->Print();
36         } else {
37           PrintF("  no state[%i]: #%d:%s\n", i, effect->id(),
38                  effect->op()->mnemonic());
39         }
40       }
41     }
42   }
43   switch (node->opcode()) {
44     case IrOpcode::kLoadFromObject:
45       return ReduceLoadFromObject(node, ObjectAccessOf(node->op()));
46     case IrOpcode::kStoreToObject:
47       return ReduceStoreToObject(node, ObjectAccessOf(node->op()));
48     case IrOpcode::kDebugBreak:
49     case IrOpcode::kAbortCSADcheck:
50       // Avoid changing optimizations in the presence of debug instructions.
51       return PropagateInputState(node);
52     case IrOpcode::kCall:
53       return ReduceCall(node);
54     case IrOpcode::kEffectPhi:
55       return ReduceEffectPhi(node);
56     case IrOpcode::kDead:
57       return NoChange();
58     case IrOpcode::kStart:
59       return ReduceStart(node);
60     default:
61       return ReduceOtherNode(node);
62   }
63   UNREACHABLE();
64 }
65 
66 namespace CsaLoadEliminationHelpers {
67 
Subsumes(MachineRepresentation from,MachineRepresentation to)68 bool Subsumes(MachineRepresentation from, MachineRepresentation to) {
69   if (from == to) return true;
70   if (IsAnyTagged(from)) return IsAnyTagged(to);
71   if (IsIntegral(from)) {
72     return IsIntegral(to) && ElementSizeInBytes(from) >= ElementSizeInBytes(to);
73   }
74   return false;
75 }
76 
IsConstantObject(Node * object)77 bool IsConstantObject(Node* object) {
78   return object->opcode() == IrOpcode::kParameter ||
79          object->opcode() == IrOpcode::kLoadImmutable ||
80          NodeProperties::IsConstant(object);
81 }
82 
IsFreshObject(Node * object)83 bool IsFreshObject(Node* object) {
84   DCHECK_IMPLIES(NodeProperties::IsFreshObject(object),
85                  !IsConstantObject(object));
86   return NodeProperties::IsFreshObject(object);
87 }
88 
89 }  // namespace CsaLoadEliminationHelpers
90 
91 namespace Helpers = CsaLoadEliminationHelpers;
92 
93 // static
94 template <typename OuterKey>
IntersectWith(OuterMap<OuterKey> & to,const OuterMap<OuterKey> & from)95 void CsaLoadElimination::AbstractState::IntersectWith(
96     OuterMap<OuterKey>& to, const OuterMap<OuterKey>& from) {
97   FieldInfo empty_info;
98   for (const std::pair<OuterKey, InnerMap>& to_map : to) {
99     InnerMap to_map_copy(to_map.second);
100     OuterKey key = to_map.first;
101     InnerMap current_map = from.Get(key);
102     for (std::pair<Node*, FieldInfo> info : to_map.second) {
103       if (current_map.Get(info.first) != info.second) {
104         to_map_copy.Set(info.first, empty_info);
105       }
106     }
107     to.Set(key, to_map_copy);
108   }
109 }
110 
IntersectWith(AbstractState const * that)111 void CsaLoadElimination::AbstractState::IntersectWith(
112     AbstractState const* that) {
113   IntersectWith(fresh_entries_, that->fresh_entries_);
114   IntersectWith(constant_entries_, that->constant_entries_);
115   IntersectWith(arbitrary_entries_, that->arbitrary_entries_);
116   IntersectWith(fresh_unknown_entries_, that->fresh_unknown_entries_);
117   IntersectWith(constant_unknown_entries_, that->constant_unknown_entries_);
118   IntersectWith(arbitrary_unknown_entries_, that->arbitrary_unknown_entries_);
119 }
120 
121 CsaLoadElimination::AbstractState const*
KillField(Node * object,Node * offset,MachineRepresentation repr) const122 CsaLoadElimination::AbstractState::KillField(Node* object, Node* offset,
123                                              MachineRepresentation repr) const {
124   AbstractState* result = zone_->New<AbstractState>(*this);
125   UnknownOffsetInfos empty_unknown(zone_, InnerMap(zone_));
126   IntPtrMatcher m(offset);
127   if (m.HasResolvedValue()) {
128     uint32_t num_offset = static_cast<uint32_t>(m.ResolvedValue());
129     if (Helpers::IsFreshObject(object)) {
130       // May alias with:
131       // - The same object/offset
132       // - Arbitrary objects with the same offset
133       // - The same object, unkwown offset
134       // - Arbitrary objects with unkwown offset
135       result->KillOffsetInFresh(object, num_offset, repr);
136       KillOffset(result->arbitrary_entries_, num_offset, repr, zone_);
137       result->fresh_unknown_entries_.Set(object, InnerMap(zone_));
138       result->arbitrary_unknown_entries_ = empty_unknown;
139     } else if (Helpers::IsConstantObject(object)) {
140       // May alias with:
141       // - Constant/arbitrary objects with the same offset
142       // - Constant/arbitrary objects with unkwown offset
143       KillOffset(result->constant_entries_, num_offset, repr, zone_);
144       KillOffset(result->arbitrary_entries_, num_offset, repr, zone_);
145       result->constant_unknown_entries_ = empty_unknown;
146       result->arbitrary_unknown_entries_ = empty_unknown;
147     } else {
148       // May alias with:
149       // - Any object with the same or unknown offset
150       KillOffset(result->fresh_entries_, num_offset, repr, zone_);
151       KillOffset(result->constant_entries_, num_offset, repr, zone_);
152       KillOffset(result->arbitrary_entries_, num_offset, repr, zone_);
153       result->fresh_unknown_entries_ = empty_unknown;
154       result->constant_unknown_entries_ = empty_unknown;
155       result->arbitrary_unknown_entries_ = empty_unknown;
156     }
157   } else {
158     ConstantOffsetInfos empty_constant(zone_, InnerMap(zone_));
159     if (Helpers::IsFreshObject(object)) {
160       // May alias with:
161       // - The same object with any known/unknown offset
162       // - Arbitrary objects with any known/unknown offset
163       for (auto map : result->fresh_entries_) {
164         // TODO(manoskouk): Consider adding a map from fresh objects to offsets
165         // to implement this efficiently.
166         InnerMap map_copy(map.second);
167         map_copy.Set(object, FieldInfo());
168         result->fresh_entries_.Set(map.first, map_copy);
169       }
170       result->fresh_unknown_entries_.Set(object, InnerMap(zone_));
171       result->arbitrary_entries_ = empty_constant;
172       result->arbitrary_unknown_entries_ = empty_unknown;
173     } else if (Helpers::IsConstantObject(object)) {
174       // May alias with:
175       // - Constant/arbitrary objects with the any known/unknown offset
176       result->constant_entries_ = empty_constant;
177       result->constant_unknown_entries_ = empty_unknown;
178       result->arbitrary_entries_ = empty_constant;
179       result->arbitrary_unknown_entries_ = empty_unknown;
180     } else {
181       // May alias with anything. Clear the state.
182       return zone_->New<AbstractState>(zone_);
183     }
184   }
185 
186   return result;
187 }
188 
189 CsaLoadElimination::AbstractState const*
AddField(Node * object,Node * offset,Node * value,MachineRepresentation repr) const190 CsaLoadElimination::AbstractState::AddField(Node* object, Node* offset,
191                                             Node* value,
192                                             MachineRepresentation repr) const {
193   AbstractState* new_state = zone_->New<AbstractState>(*this);
194   IntPtrMatcher m(offset);
195   if (m.HasResolvedValue()) {
196     uint32_t offset_num = static_cast<uint32_t>(m.ResolvedValue());
197     ConstantOffsetInfos& infos = Helpers::IsFreshObject(object)
198                                      ? new_state->fresh_entries_
199                                      : Helpers::IsConstantObject(object)
200                                            ? new_state->constant_entries_
201                                            : new_state->arbitrary_entries_;
202     Update(infos, offset_num, object, FieldInfo(value, repr));
203   } else {
204     UnknownOffsetInfos& infos =
205         Helpers::IsFreshObject(object)
206             ? new_state->fresh_unknown_entries_
207             : Helpers::IsConstantObject(object)
208                   ? new_state->constant_unknown_entries_
209                   : new_state->arbitrary_unknown_entries_;
210     Update(infos, object, offset, FieldInfo(value, repr));
211   }
212   return new_state;
213 }
214 
Lookup(Node * object,Node * offset) const215 CsaLoadElimination::FieldInfo CsaLoadElimination::AbstractState::Lookup(
216     Node* object, Node* offset) const {
217   IntPtrMatcher m(offset);
218   if (m.HasResolvedValue()) {
219     uint32_t num_offset = static_cast<uint32_t>(m.ResolvedValue());
220     const ConstantOffsetInfos& infos = Helpers::IsFreshObject(object)
221                                            ? fresh_entries_
222                                            : Helpers::IsConstantObject(object)
223                                                  ? constant_entries_
224                                                  : arbitrary_entries_;
225     return infos.Get(num_offset).Get(object);
226   } else {
227     const UnknownOffsetInfos& infos = Helpers::IsFreshObject(object)
228                                           ? fresh_unknown_entries_
229                                           : Helpers::IsConstantObject(object)
230                                                 ? constant_unknown_entries_
231                                                 : arbitrary_unknown_entries_;
232     return infos.Get(object).Get(offset);
233   }
234 }
235 
236 // static
237 // Kill all elements in {infos} that overlap with an element with {offset} and
238 // size {ElementSizeInBytes(repr)}.
KillOffset(ConstantOffsetInfos & infos,uint32_t offset,MachineRepresentation repr,Zone * zone)239 void CsaLoadElimination::AbstractState::KillOffset(ConstantOffsetInfos& infos,
240                                                    uint32_t offset,
241                                                    MachineRepresentation repr,
242                                                    Zone* zone) {
243   // All elements in the range [{offset}, {offset + ElementSizeInBytes(repr)})
244   // are in the killed range. We do not need to traverse the inner maps, we can
245   // just clear them.
246   for (int i = 0; i < ElementSizeInBytes(repr); i++) {
247     infos.Set(offset + i, InnerMap(zone));
248   }
249 
250   // Now we have to remove all elements in earlier offsets that overlap with an
251   // element in {offset}.
252   // The earliest offset that may overlap with {offset} is
253   // {kMaximumReprSizeInBytes - 1} before.
254   uint32_t initial_offset = offset >= kMaximumReprSizeInBytes - 1
255                                 ? offset - (kMaximumReprSizeInBytes - 1)
256                                 : 0;
257   // For all offsets from {initial_offset} to {offset}, we traverse the
258   // respective inner map, and reset all elements that are large enough to
259   // overlap with {offset}.
260   for (uint32_t i = initial_offset; i < offset; i++) {
261     InnerMap map_copy(infos.Get(i));
262     for (const std::pair<Node*, FieldInfo> info : infos.Get(i)) {
263       if (info.second.representation != MachineRepresentation::kNone &&
264           ElementSizeInBytes(info.second.representation) >
265               static_cast<int>(offset - i)) {
266         map_copy.Set(info.first, {});
267       }
268     }
269     infos.Set(i, map_copy);
270   }
271 }
272 
KillOffsetInFresh(Node * const object,uint32_t offset,MachineRepresentation repr)273 void CsaLoadElimination::AbstractState::KillOffsetInFresh(
274     Node* const object, uint32_t offset, MachineRepresentation repr) {
275   for (int i = 0; i < ElementSizeInBytes(repr); i++) {
276     Update(fresh_entries_, offset + i, object, {});
277   }
278   uint32_t initial_offset = offset >= kMaximumReprSizeInBytes - 1
279                                 ? offset - (kMaximumReprSizeInBytes - 1)
280                                 : 0;
281   for (uint32_t i = initial_offset; i < offset; i++) {
282     const FieldInfo& info = fresh_entries_.Get(i).Get(object);
283     if (info.representation != MachineRepresentation::kNone &&
284         ElementSizeInBytes(info.representation) >
285             static_cast<int>(offset - i)) {
286       Update(fresh_entries_, i, object, {});
287     }
288   }
289 }
290 
291 // static
Print(const CsaLoadElimination::AbstractState::ConstantOffsetInfos & infos)292 void CsaLoadElimination::AbstractState::Print(
293     const CsaLoadElimination::AbstractState::ConstantOffsetInfos& infos) {
294   for (const auto outer_entry : infos) {
295     for (const auto inner_entry : outer_entry.second) {
296       Node* object = inner_entry.first;
297       uint32_t offset = outer_entry.first;
298       FieldInfo info = inner_entry.second;
299       PrintF("    #%d+#%d:%s -> #%d:%s [repr=%s]\n", object->id(), offset,
300              object->op()->mnemonic(), info.value->id(),
301              info.value->op()->mnemonic(),
302              MachineReprToString(info.representation));
303     }
304   }
305 }
306 
307 // static
Print(const CsaLoadElimination::AbstractState::UnknownOffsetInfos & infos)308 void CsaLoadElimination::AbstractState::Print(
309     const CsaLoadElimination::AbstractState::UnknownOffsetInfos& infos) {
310   for (const auto outer_entry : infos) {
311     for (const auto inner_entry : outer_entry.second) {
312       Node* object = outer_entry.first;
313       Node* offset = inner_entry.first;
314       FieldInfo info = inner_entry.second;
315       PrintF("    #%d+#%d:%s -> #%d:%s [repr=%s]\n", object->id(), offset->id(),
316              object->op()->mnemonic(), info.value->id(),
317              info.value->op()->mnemonic(),
318              MachineReprToString(info.representation));
319     }
320   }
321 }
322 
Print() const323 void CsaLoadElimination::AbstractState::Print() const {
324   Print(fresh_entries_);
325   Print(constant_entries_);
326   Print(arbitrary_entries_);
327   Print(fresh_unknown_entries_);
328   Print(constant_unknown_entries_);
329   Print(arbitrary_unknown_entries_);
330 }
331 
ReduceLoadFromObject(Node * node,ObjectAccess const & access)332 Reduction CsaLoadElimination::ReduceLoadFromObject(Node* node,
333                                                    ObjectAccess const& access) {
334   Node* object = NodeProperties::GetValueInput(node, 0);
335   Node* offset = NodeProperties::GetValueInput(node, 1);
336   Node* effect = NodeProperties::GetEffectInput(node);
337   AbstractState const* state = node_states_.Get(effect);
338   if (state == nullptr) return NoChange();
339 
340   MachineRepresentation representation = access.machine_type.representation();
341   FieldInfo lookup_result = state->Lookup(object, offset);
342   if (!lookup_result.IsEmpty()) {
343     // Make sure we don't reuse values that were recorded with a different
344     // representation or resurrect dead {replacement} nodes.
345     MachineRepresentation from = lookup_result.representation;
346     if (Helpers::Subsumes(from, representation) &&
347         !lookup_result.value->IsDead()) {
348       Node* replacement =
349           TruncateAndExtend(lookup_result.value, from, access.machine_type);
350       ReplaceWithValue(node, replacement, effect);
351       return Replace(replacement);
352     }
353   }
354   state = state->AddField(object, offset, node, representation);
355 
356   return UpdateState(node, state);
357 }
358 
ReduceStoreToObject(Node * node,ObjectAccess const & access)359 Reduction CsaLoadElimination::ReduceStoreToObject(Node* node,
360                                                   ObjectAccess const& access) {
361   Node* object = NodeProperties::GetValueInput(node, 0);
362   Node* offset = NodeProperties::GetValueInput(node, 1);
363   Node* value = NodeProperties::GetValueInput(node, 2);
364   Node* effect = NodeProperties::GetEffectInput(node);
365   AbstractState const* state = node_states_.Get(effect);
366   if (state == nullptr) return NoChange();
367 
368   MachineRepresentation repr = access.machine_type.representation();
369   state = state->KillField(object, offset, repr);
370   state = state->AddField(object, offset, value, repr);
371 
372   return UpdateState(node, state);
373 }
374 
ReduceEffectPhi(Node * node)375 Reduction CsaLoadElimination::ReduceEffectPhi(Node* node) {
376   Node* const effect0 = NodeProperties::GetEffectInput(node, 0);
377   Node* const control = NodeProperties::GetControlInput(node);
378   AbstractState const* state0 = node_states_.Get(effect0);
379   if (state0 == nullptr) return NoChange();
380   if (control->opcode() == IrOpcode::kLoop) {
381     // Here we rely on having only reducible loops:
382     // The loop entry edge always dominates the header, so we can just take
383     // the state from the first input, and compute the loop state based on it.
384     AbstractState const* state = ComputeLoopState(node, state0);
385     return UpdateState(node, state);
386   }
387   DCHECK_EQ(IrOpcode::kMerge, control->opcode());
388 
389   // Shortcut for the case when we do not know anything about some input.
390   int const input_count = node->op()->EffectInputCount();
391   for (int i = 1; i < input_count; ++i) {
392     Node* const effect = NodeProperties::GetEffectInput(node, i);
393     if (node_states_.Get(effect) == nullptr) return NoChange();
394   }
395 
396   // Make a copy of the first input's state and intersect it with the state
397   // from other inputs.
398   // TODO(manoskouk): Consider computing phis for at least a subset of the
399   // state.
400   AbstractState* state = zone()->New<AbstractState>(*state0);
401   for (int i = 1; i < input_count; ++i) {
402     Node* const input = NodeProperties::GetEffectInput(node, i);
403     state->IntersectWith(node_states_.Get(input));
404   }
405   return UpdateState(node, state);
406 }
407 
ReduceStart(Node * node)408 Reduction CsaLoadElimination::ReduceStart(Node* node) {
409   return UpdateState(node, empty_state());
410 }
411 
ReduceCall(Node * node)412 Reduction CsaLoadElimination::ReduceCall(Node* node) {
413   Node* value = NodeProperties::GetValueInput(node, 0);
414   ExternalReferenceMatcher m(value);
415   if (m.Is(ExternalReference::check_object_type())) {
416     return PropagateInputState(node);
417   }
418   return ReduceOtherNode(node);
419 }
420 
ReduceOtherNode(Node * node)421 Reduction CsaLoadElimination::ReduceOtherNode(Node* node) {
422   if (node->op()->EffectInputCount() == 1 &&
423       node->op()->EffectOutputCount() == 1) {
424     Node* const effect = NodeProperties::GetEffectInput(node);
425     AbstractState const* state = node_states_.Get(effect);
426     // If we do not know anything about the predecessor, do not propagate just
427     // yet because we will have to recompute anyway once we compute the
428     // predecessor.
429     if (state == nullptr) return NoChange();
430     // If this {node} has some uncontrolled side effects, set its state to
431     // {empty_state()}, otherwise to its input state.
432     return UpdateState(node, node->op()->HasProperty(Operator::kNoWrite)
433                                  ? state
434                                  : empty_state());
435   }
436   DCHECK_EQ(0, node->op()->EffectOutputCount());
437   return NoChange();
438 }
439 
UpdateState(Node * node,AbstractState const * state)440 Reduction CsaLoadElimination::UpdateState(Node* node,
441                                           AbstractState const* state) {
442   AbstractState const* original = node_states_.Get(node);
443   // Only signal that the {node} has Changed, if the information about {state}
444   // has changed wrt. the {original}.
445   if (state != original) {
446     if (original == nullptr || !state->Equals(original)) {
447       node_states_.Set(node, state);
448       return Changed(node);
449     }
450   }
451   return NoChange();
452 }
453 
PropagateInputState(Node * node)454 Reduction CsaLoadElimination::PropagateInputState(Node* node) {
455   Node* const effect = NodeProperties::GetEffectInput(node);
456   AbstractState const* state = node_states_.Get(effect);
457   if (state == nullptr) return NoChange();
458   return UpdateState(node, state);
459 }
460 
ComputeLoopState(Node * node,AbstractState const * state) const461 CsaLoadElimination::AbstractState const* CsaLoadElimination::ComputeLoopState(
462     Node* node, AbstractState const* state) const {
463   DCHECK_EQ(node->opcode(), IrOpcode::kEffectPhi);
464   ZoneQueue<Node*> queue(zone());
465   ZoneSet<Node*> visited(zone());
466   visited.insert(node);
467   for (int i = 1; i < node->InputCount() - 1; ++i) {
468     queue.push(node->InputAt(i));
469   }
470   while (!queue.empty()) {
471     Node* const current = queue.front();
472     queue.pop();
473     if (visited.insert(current).second) {
474       if (!current->op()->HasProperty(Operator::kNoWrite)) {
475         return empty_state();
476       }
477       for (int i = 0; i < current->op()->EffectInputCount(); ++i) {
478         queue.push(NodeProperties::GetEffectInput(current, i));
479       }
480     }
481   }
482   return state;
483 }
484 
TruncateAndExtend(Node * node,MachineRepresentation from,MachineType to)485 Node* CsaLoadElimination::TruncateAndExtend(Node* node,
486                                             MachineRepresentation from,
487                                             MachineType to) {
488   DCHECK(Helpers::Subsumes(from, to.representation()));
489   DCHECK_GE(ElementSizeInBytes(from), ElementSizeInBytes(to.representation()));
490 
491   if (to == MachineType::Int8() || to == MachineType::Int16()) {
492     // 1st case: We want to eliminate a signed 8/16-bit load using the value
493     // from a previous subsuming load or store. Since that value might be
494     // outside 8/16-bit range, we first truncate it accordingly. Then we
495     // sign-extend the result to 32-bit.
496     DCHECK_EQ(to.semantic(), MachineSemantic::kInt32);
497     if (from == MachineRepresentation::kWord64) {
498       node = graph()->NewNode(machine()->TruncateInt64ToInt32(), node);
499     }
500     int shift = 32 - 8 * ElementSizeInBytes(to.representation());
501     return graph()->NewNode(machine()->Word32Sar(),
502                             graph()->NewNode(machine()->Word32Shl(), node,
503                                              jsgraph()->Int32Constant(shift)),
504                             jsgraph()->Int32Constant(shift));
505   } else if (to == MachineType::Uint8() || to == MachineType::Uint16()) {
506     // 2nd case: We want to eliminate an unsigned 8/16-bit load using the value
507     // from a previous subsuming load or store. Since that value might be
508     // outside 8/16-bit range, we first truncate it accordingly.
509     if (from == MachineRepresentation::kWord64) {
510       node = graph()->NewNode(machine()->TruncateInt64ToInt32(), node);
511     }
512     int mask = (1 << 8 * ElementSizeInBytes(to.representation())) - 1;
513     return graph()->NewNode(machine()->Word32And(), node,
514                             jsgraph()->Int32Constant(mask));
515   } else if (from == MachineRepresentation::kWord64 &&
516              to.representation() == MachineRepresentation::kWord32) {
517     // 3rd case: Truncate 64-bits into 32-bits.
518     return graph()->NewNode(machine()->TruncateInt64ToInt32(), node);
519   } else {
520     // 4th case: No need for truncation.
521     DCHECK((from == to.representation() &&
522             (from == MachineRepresentation::kWord32 ||
523              from == MachineRepresentation::kWord64 || !IsIntegral(from))) ||
524            (IsAnyTagged(from) && IsAnyTagged(to.representation())));
525     return node;
526   }
527 }
528 
common() const529 CommonOperatorBuilder* CsaLoadElimination::common() const {
530   return jsgraph()->common();
531 }
532 
machine() const533 MachineOperatorBuilder* CsaLoadElimination::machine() const {
534   return jsgraph()->machine();
535 }
536 
graph() const537 Graph* CsaLoadElimination::graph() const { return jsgraph()->graph(); }
538 
isolate() const539 Isolate* CsaLoadElimination::isolate() const { return jsgraph()->isolate(); }
540 
541 }  // namespace compiler
542 }  // namespace internal
543 }  // namespace v8
544