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