1// Copyright 2013 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/hydrogen-representation-changes.h"
6
7namespace v8 {
8namespace internal {
9
10void HRepresentationChangesPhase::InsertRepresentationChangeForUse(
11    HValue* value, HValue* use_value, int use_index, Representation to) {
12  // Insert the representation change right before its use. For phi-uses we
13  // insert at the end of the corresponding predecessor.
14  HInstruction* next = NULL;
15  if (use_value->IsPhi()) {
16    next = use_value->block()->predecessors()->at(use_index)->end();
17  } else {
18    next = HInstruction::cast(use_value);
19  }
20  // For constants we try to make the representation change at compile
21  // time. When a representation change is not possible without loss of
22  // information we treat constants like normal instructions and insert the
23  // change instructions for them.
24  HInstruction* new_value = NULL;
25  bool is_truncating_to_smi = use_value->CheckFlag(HValue::kTruncatingToSmi);
26  bool is_truncating_to_int = use_value->CheckFlag(HValue::kTruncatingToInt32);
27  if (value->IsConstant()) {
28    HConstant* constant = HConstant::cast(value);
29    // Try to create a new copy of the constant with the new representation.
30    if (is_truncating_to_int && to.IsInteger32()) {
31      Maybe<HConstant*> res = constant->CopyToTruncatedInt32(graph()->zone());
32      if (res.has_value) new_value = res.value;
33    } else {
34      new_value = constant->CopyToRepresentation(to, graph()->zone());
35    }
36  }
37
38  if (new_value == NULL) {
39    new_value = new(graph()->zone()) HChange(
40        value, to, is_truncating_to_smi, is_truncating_to_int);
41    if (!use_value->operand_position(use_index).IsUnknown()) {
42      new_value->set_position(use_value->operand_position(use_index));
43    } else {
44      DCHECK(!FLAG_hydrogen_track_positions ||
45             !graph()->info()->IsOptimizing());
46    }
47  }
48
49  new_value->InsertBefore(next);
50  use_value->SetOperandAt(use_index, new_value);
51}
52
53
54static bool IsNonDeoptingIntToSmiChange(HChange* change) {
55  Representation from_rep = change->from();
56  Representation to_rep = change->to();
57  // Flags indicating Uint32 operations are set in a later Hydrogen phase.
58  DCHECK(!change->CheckFlag(HValue::kUint32));
59  return from_rep.IsInteger32() && to_rep.IsSmi() && SmiValuesAre32Bits();
60}
61
62
63void HRepresentationChangesPhase::InsertRepresentationChangesForValue(
64    HValue* value) {
65  Representation r = value->representation();
66  if (r.IsNone()) return;
67  if (value->HasNoUses()) {
68    if (value->IsForceRepresentation()) value->DeleteAndReplaceWith(NULL);
69    return;
70  }
71
72  for (HUseIterator it(value->uses()); !it.Done(); it.Advance()) {
73    HValue* use_value = it.value();
74    int use_index = it.index();
75    Representation req = use_value->RequiredInputRepresentation(use_index);
76    if (req.IsNone() || req.Equals(r)) continue;
77
78    // If this is an HForceRepresentation instruction, and an HChange has been
79    // inserted above it, examine the input representation of the HChange. If
80    // that's int32, and this HForceRepresentation use is int32, and int32 to
81    // smi changes can't cause deoptimisation, set the input of the use to the
82    // input of the HChange.
83    if (value->IsForceRepresentation()) {
84      HValue* input = HForceRepresentation::cast(value)->value();
85      if (input->IsChange()) {
86        HChange* change = HChange::cast(input);
87        if (change->from().Equals(req) && IsNonDeoptingIntToSmiChange(change)) {
88          use_value->SetOperandAt(use_index, change->value());
89          continue;
90        }
91      }
92    }
93    InsertRepresentationChangeForUse(value, use_value, use_index, req);
94  }
95  if (value->HasNoUses()) {
96    DCHECK(value->IsConstant() || value->IsForceRepresentation());
97    value->DeleteAndReplaceWith(NULL);
98  } else {
99    // The only purpose of a HForceRepresentation is to represent the value
100    // after the (possible) HChange instruction.  We make it disappear.
101    if (value->IsForceRepresentation()) {
102      value->DeleteAndReplaceWith(HForceRepresentation::cast(value)->value());
103    }
104  }
105}
106
107
108void HRepresentationChangesPhase::Run() {
109  // Compute truncation flag for phis: Initially assume that all
110  // int32-phis allow truncation and iteratively remove the ones that
111  // are used in an operation that does not allow a truncating
112  // conversion.
113  ZoneList<HPhi*> int_worklist(8, zone());
114  ZoneList<HPhi*> smi_worklist(8, zone());
115
116  const ZoneList<HPhi*>* phi_list(graph()->phi_list());
117  for (int i = 0; i < phi_list->length(); i++) {
118    HPhi* phi = phi_list->at(i);
119    if (phi->representation().IsInteger32()) {
120      phi->SetFlag(HValue::kTruncatingToInt32);
121    } else if (phi->representation().IsSmi()) {
122      phi->SetFlag(HValue::kTruncatingToSmi);
123      phi->SetFlag(HValue::kTruncatingToInt32);
124    }
125  }
126
127  for (int i = 0; i < phi_list->length(); i++) {
128    HPhi* phi = phi_list->at(i);
129    HValue* value = NULL;
130    if (phi->representation().IsSmiOrInteger32() &&
131        !phi->CheckUsesForFlag(HValue::kTruncatingToInt32, &value)) {
132      int_worklist.Add(phi, zone());
133      phi->ClearFlag(HValue::kTruncatingToInt32);
134      if (FLAG_trace_representation) {
135        PrintF("#%d Phi is not truncating Int32 because of #%d %s\n",
136               phi->id(), value->id(), value->Mnemonic());
137      }
138    }
139
140    if (phi->representation().IsSmi() &&
141        !phi->CheckUsesForFlag(HValue::kTruncatingToSmi, &value)) {
142      smi_worklist.Add(phi, zone());
143      phi->ClearFlag(HValue::kTruncatingToSmi);
144      if (FLAG_trace_representation) {
145        PrintF("#%d Phi is not truncating Smi because of #%d %s\n",
146               phi->id(), value->id(), value->Mnemonic());
147      }
148    }
149  }
150
151  while (!int_worklist.is_empty()) {
152    HPhi* current = int_worklist.RemoveLast();
153    for (int i = 0; i < current->OperandCount(); ++i) {
154      HValue* input = current->OperandAt(i);
155      if (input->IsPhi() &&
156          input->representation().IsSmiOrInteger32() &&
157          input->CheckFlag(HValue::kTruncatingToInt32)) {
158        if (FLAG_trace_representation) {
159          PrintF("#%d Phi is not truncating Int32 because of #%d %s\n",
160                 input->id(), current->id(), current->Mnemonic());
161        }
162        input->ClearFlag(HValue::kTruncatingToInt32);
163        int_worklist.Add(HPhi::cast(input), zone());
164      }
165    }
166  }
167
168  while (!smi_worklist.is_empty()) {
169    HPhi* current = smi_worklist.RemoveLast();
170    for (int i = 0; i < current->OperandCount(); ++i) {
171      HValue* input = current->OperandAt(i);
172      if (input->IsPhi() &&
173          input->representation().IsSmi() &&
174          input->CheckFlag(HValue::kTruncatingToSmi)) {
175        if (FLAG_trace_representation) {
176          PrintF("#%d Phi is not truncating Smi because of #%d %s\n",
177                 input->id(), current->id(), current->Mnemonic());
178        }
179        input->ClearFlag(HValue::kTruncatingToSmi);
180        smi_worklist.Add(HPhi::cast(input), zone());
181      }
182    }
183  }
184
185  const ZoneList<HBasicBlock*>* blocks(graph()->blocks());
186  for (int i = 0; i < blocks->length(); ++i) {
187    // Process phi instructions first.
188    const HBasicBlock* block(blocks->at(i));
189    const ZoneList<HPhi*>* phis = block->phis();
190    for (int j = 0; j < phis->length(); j++) {
191      InsertRepresentationChangesForValue(phis->at(j));
192    }
193
194    // Process normal instructions.
195    for (HInstruction* current = block->first(); current != NULL; ) {
196      HInstruction* next = current->next();
197      InsertRepresentationChangesForValue(current);
198      current = next;
199    }
200  }
201}
202
203} }  // namespace v8::internal
204