representation-change.cc revision 1b268ca467c924004286c97bac133db489cf43d0
1// Copyright 2015 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/representation-change.h"
6
7#include <sstream>
8
9#include "src/base/bits.h"
10#include "src/code-factory.h"
11#include "src/compiler/machine-operator.h"
12
13namespace v8 {
14namespace internal {
15namespace compiler {
16
17const char* Truncation::description() const {
18  switch (kind()) {
19    case TruncationKind::kNone:
20      return "no-value-use";
21    case TruncationKind::kBool:
22      return "truncate-to-bool";
23    case TruncationKind::kWord32:
24      return "truncate-to-word32";
25    case TruncationKind::kWord64:
26      return "truncate-to-word64";
27    case TruncationKind::kFloat32:
28      return "truncate-to-float32";
29    case TruncationKind::kFloat64:
30      return "truncate-to-float64";
31    case TruncationKind::kAny:
32      return "no-truncation";
33  }
34  UNREACHABLE();
35  return nullptr;
36}
37
38
39// Partial order for truncations:
40//
41//  kWord64       kAny
42//     ^            ^
43//     \            |
44//      \         kFloat64  <--+
45//       \        ^    ^       |
46//        \       /    |       |
47//         kWord32  kFloat32  kBool
48//               ^     ^      ^
49//               \     |      /
50//                \    |     /
51//                 \   |    /
52//                  \  |   /
53//                   \ |  /
54//                   kNone
55
56// static
57Truncation::TruncationKind Truncation::Generalize(TruncationKind rep1,
58                                                  TruncationKind rep2) {
59  if (LessGeneral(rep1, rep2)) return rep2;
60  if (LessGeneral(rep2, rep1)) return rep1;
61  // Handle the generalization of float64-representable values.
62  if (LessGeneral(rep1, TruncationKind::kFloat64) &&
63      LessGeneral(rep2, TruncationKind::kFloat64)) {
64    return TruncationKind::kFloat64;
65  }
66  // All other combinations are illegal.
67  FATAL("Tried to combine incompatible truncations");
68  return TruncationKind::kNone;
69}
70
71
72// static
73bool Truncation::LessGeneral(TruncationKind rep1, TruncationKind rep2) {
74  switch (rep1) {
75    case TruncationKind::kNone:
76      return true;
77    case TruncationKind::kBool:
78      return rep2 == TruncationKind::kBool || rep2 == TruncationKind::kAny;
79    case TruncationKind::kWord32:
80      return rep2 == TruncationKind::kWord32 ||
81             rep2 == TruncationKind::kWord64 ||
82             rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
83    case TruncationKind::kWord64:
84      return rep2 == TruncationKind::kWord64;
85    case TruncationKind::kFloat32:
86      return rep2 == TruncationKind::kFloat32 ||
87             rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
88    case TruncationKind::kFloat64:
89      return rep2 == TruncationKind::kFloat64 || rep2 == TruncationKind::kAny;
90    case TruncationKind::kAny:
91      return rep2 == TruncationKind::kAny;
92  }
93  UNREACHABLE();
94  return false;
95}
96
97
98namespace {
99
100bool IsWord(MachineRepresentation rep) {
101  return rep == MachineRepresentation::kWord8 ||
102         rep == MachineRepresentation::kWord16 ||
103         rep == MachineRepresentation::kWord32;
104}
105
106}  // namespace
107
108
109// Changes representation from {output_rep} to {use_rep}. The {truncation}
110// parameter is only used for sanity checking - if the changer cannot figure
111// out signedness for the word32->float64 conversion, then we check that the
112// uses truncate to word32 (so they do not care about signedness).
113Node* RepresentationChanger::GetRepresentationFor(
114    Node* node, MachineRepresentation output_rep, Type* output_type,
115    MachineRepresentation use_rep, Truncation truncation) {
116  if (output_rep == MachineRepresentation::kNone) {
117    // The output representation should be set.
118    return TypeError(node, output_rep, output_type, use_rep);
119  }
120  if (use_rep == output_rep) {
121    // Representations are the same. That's a no-op.
122    return node;
123  }
124  if (IsWord(use_rep) && IsWord(output_rep)) {
125    // Both are words less than or equal to 32-bits.
126    // Since loads of integers from memory implicitly sign or zero extend the
127    // value to the full machine word size and stores implicitly truncate,
128    // no representation change is necessary.
129    return node;
130  }
131  switch (use_rep) {
132    case MachineRepresentation::kTagged:
133      return GetTaggedRepresentationFor(node, output_rep, output_type);
134    case MachineRepresentation::kFloat32:
135      return GetFloat32RepresentationFor(node, output_rep, output_type,
136                                         truncation);
137    case MachineRepresentation::kFloat64:
138      return GetFloat64RepresentationFor(node, output_rep, output_type,
139                                         truncation);
140    case MachineRepresentation::kBit:
141      return GetBitRepresentationFor(node, output_rep, output_type);
142    case MachineRepresentation::kWord8:
143    case MachineRepresentation::kWord16:
144    case MachineRepresentation::kWord32:
145      return GetWord32RepresentationFor(node, output_rep, output_type,
146                                        truncation);
147    case MachineRepresentation::kWord64:
148      return GetWord64RepresentationFor(node, output_rep, output_type);
149    case MachineRepresentation::kSimd128:  // Fall through.
150      // TODO(bbudge) Handle conversions between tagged and untagged.
151      break;
152    case MachineRepresentation::kNone:
153      return node;
154  }
155  UNREACHABLE();
156  return nullptr;
157}
158
159
160Node* RepresentationChanger::GetTaggedRepresentationFor(
161    Node* node, MachineRepresentation output_rep, Type* output_type) {
162  // Eagerly fold representation changes for constants.
163  switch (node->opcode()) {
164    case IrOpcode::kNumberConstant:
165    case IrOpcode::kHeapConstant:
166      return node;  // No change necessary.
167    case IrOpcode::kInt32Constant:
168      if (output_type->Is(Type::Signed32())) {
169        int32_t value = OpParameter<int32_t>(node);
170        return jsgraph()->Constant(value);
171      } else if (output_type->Is(Type::Unsigned32())) {
172        uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
173        return jsgraph()->Constant(static_cast<double>(value));
174      } else if (output_rep == MachineRepresentation::kBit) {
175        return OpParameter<int32_t>(node) == 0 ? jsgraph()->FalseConstant()
176                                               : jsgraph()->TrueConstant();
177      } else {
178        return TypeError(node, output_rep, output_type,
179                         MachineRepresentation::kTagged);
180      }
181    case IrOpcode::kFloat64Constant:
182      return jsgraph()->Constant(OpParameter<double>(node));
183    case IrOpcode::kFloat32Constant:
184      return jsgraph()->Constant(OpParameter<float>(node));
185    default:
186      break;
187  }
188  // Select the correct X -> Tagged operator.
189  const Operator* op;
190  if (output_rep == MachineRepresentation::kBit) {
191    op = simplified()->ChangeBitToBool();
192  } else if (IsWord(output_rep)) {
193    if (output_type->Is(Type::Unsigned32())) {
194      op = simplified()->ChangeUint32ToTagged();
195    } else if (output_type->Is(Type::Signed32())) {
196      op = simplified()->ChangeInt32ToTagged();
197    } else {
198      return TypeError(node, output_rep, output_type,
199                       MachineRepresentation::kTagged);
200    }
201  } else if (output_rep ==
202             MachineRepresentation::kFloat32) {  // float32 -> float64 -> tagged
203    node = InsertChangeFloat32ToFloat64(node);
204    op = simplified()->ChangeFloat64ToTagged();
205  } else if (output_rep == MachineRepresentation::kFloat64) {
206    op = simplified()->ChangeFloat64ToTagged();
207  } else {
208    return TypeError(node, output_rep, output_type,
209                     MachineRepresentation::kTagged);
210  }
211  return jsgraph()->graph()->NewNode(op, node);
212}
213
214
215Node* RepresentationChanger::GetFloat32RepresentationFor(
216    Node* node, MachineRepresentation output_rep, Type* output_type,
217    Truncation truncation) {
218  // Eagerly fold representation changes for constants.
219  switch (node->opcode()) {
220    case IrOpcode::kFloat64Constant:
221    case IrOpcode::kNumberConstant:
222      return jsgraph()->Float32Constant(
223          DoubleToFloat32(OpParameter<double>(node)));
224    case IrOpcode::kInt32Constant:
225      if (output_type->Is(Type::Unsigned32())) {
226        uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
227        return jsgraph()->Float32Constant(static_cast<float>(value));
228      } else {
229        int32_t value = OpParameter<int32_t>(node);
230        return jsgraph()->Float32Constant(static_cast<float>(value));
231      }
232    case IrOpcode::kFloat32Constant:
233      return node;  // No change necessary.
234    default:
235      break;
236  }
237  // Select the correct X -> Float32 operator.
238  const Operator* op = nullptr;
239  if (IsWord(output_rep)) {
240    if (output_type->Is(Type::Signed32())) {
241      // int32 -> float64 -> float32
242      op = machine()->ChangeInt32ToFloat64();
243      node = jsgraph()->graph()->NewNode(op, node);
244      op = machine()->TruncateFloat64ToFloat32();
245    } else if (output_type->Is(Type::Unsigned32()) ||
246               truncation.TruncatesToWord32()) {
247      // Either the output is uint32 or the uses only care about the
248      // low 32 bits (so we can pick uint32 safely).
249
250      // uint32 -> float64 -> float32
251      op = machine()->ChangeUint32ToFloat64();
252      node = jsgraph()->graph()->NewNode(op, node);
253      op = machine()->TruncateFloat64ToFloat32();
254    }
255  } else if (output_rep == MachineRepresentation::kTagged) {
256    if (output_type->Is(Type::Number())) {
257      op = simplified()
258               ->ChangeTaggedToFloat64();  // tagged -> float64 -> float32
259      node = jsgraph()->graph()->NewNode(op, node);
260      op = machine()->TruncateFloat64ToFloat32();
261    }
262  } else if (output_rep == MachineRepresentation::kFloat64) {
263    op = machine()->TruncateFloat64ToFloat32();
264  }
265  if (op == nullptr) {
266    return TypeError(node, output_rep, output_type,
267                     MachineRepresentation::kFloat32);
268  }
269  return jsgraph()->graph()->NewNode(op, node);
270}
271
272
273Node* RepresentationChanger::GetFloat64RepresentationFor(
274    Node* node, MachineRepresentation output_rep, Type* output_type,
275    Truncation truncation) {
276  // Eagerly fold representation changes for constants.
277  switch (node->opcode()) {
278    case IrOpcode::kNumberConstant:
279      return jsgraph()->Float64Constant(OpParameter<double>(node));
280    case IrOpcode::kInt32Constant:
281      if (output_type->Is(Type::Signed32())) {
282        int32_t value = OpParameter<int32_t>(node);
283        return jsgraph()->Float64Constant(value);
284      } else {
285        DCHECK(output_type->Is(Type::Unsigned32()));
286        uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
287        return jsgraph()->Float64Constant(static_cast<double>(value));
288      }
289    case IrOpcode::kFloat64Constant:
290      return node;  // No change necessary.
291    case IrOpcode::kFloat32Constant:
292      return jsgraph()->Float64Constant(OpParameter<float>(node));
293    default:
294      break;
295  }
296  // Select the correct X -> Float64 operator.
297  const Operator* op = nullptr;
298  if (IsWord(output_rep)) {
299    if (output_type->Is(Type::Signed32())) {
300      op = machine()->ChangeInt32ToFloat64();
301    } else if (output_type->Is(Type::Unsigned32()) ||
302               truncation.TruncatesToWord32()) {
303      // Either the output is uint32 or the uses only care about the
304      // low 32 bits (so we can pick uint32 safely).
305      op = machine()->ChangeUint32ToFloat64();
306    }
307  } else if (output_rep == MachineRepresentation::kTagged) {
308    if (output_type->Is(Type::Number())) {
309      op = simplified()->ChangeTaggedToFloat64();
310    }
311  } else if (output_rep == MachineRepresentation::kFloat32) {
312    op = machine()->ChangeFloat32ToFloat64();
313  }
314  if (op == nullptr) {
315    return TypeError(node, output_rep, output_type,
316                     MachineRepresentation::kFloat64);
317  }
318  return jsgraph()->graph()->NewNode(op, node);
319}
320
321
322Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) {
323  return jsgraph()->Int32Constant(DoubleToInt32(value));
324}
325
326Node* RepresentationChanger::GetWord32RepresentationFor(
327    Node* node, MachineRepresentation output_rep, Type* output_type,
328    Truncation truncation) {
329  // Eagerly fold representation changes for constants.
330  switch (node->opcode()) {
331    case IrOpcode::kInt32Constant:
332      return node;  // No change necessary.
333    case IrOpcode::kFloat32Constant:
334      return MakeTruncatedInt32Constant(OpParameter<float>(node));
335    case IrOpcode::kNumberConstant:
336    case IrOpcode::kFloat64Constant:
337      return MakeTruncatedInt32Constant(OpParameter<double>(node));
338    default:
339      break;
340  }
341  // Select the correct X -> Word32 operator.
342  const Operator* op = nullptr;
343  if (output_rep == MachineRepresentation::kBit) {
344    return node;  // Sloppy comparison -> word32
345  } else if (output_rep == MachineRepresentation::kFloat64) {
346    if (output_type->Is(Type::Unsigned32())) {
347      op = machine()->ChangeFloat64ToUint32();
348    } else if (output_type->Is(Type::Signed32())) {
349      op = machine()->ChangeFloat64ToInt32();
350    } else if (truncation.TruncatesToWord32()) {
351      op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
352    }
353  } else if (output_rep == MachineRepresentation::kFloat32) {
354    node = InsertChangeFloat32ToFloat64(node);  // float32 -> float64 -> int32
355    if (output_type->Is(Type::Unsigned32())) {
356      op = machine()->ChangeFloat64ToUint32();
357    } else if (output_type->Is(Type::Signed32())) {
358      op = machine()->ChangeFloat64ToInt32();
359    } else if (truncation.TruncatesToWord32()) {
360      op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
361    }
362  } else if (output_rep == MachineRepresentation::kTagged) {
363    if (output_type->Is(Type::Unsigned32())) {
364      op = simplified()->ChangeTaggedToUint32();
365    } else if (output_type->Is(Type::Signed32())) {
366      op = simplified()->ChangeTaggedToInt32();
367    } else if (truncation.TruncatesToWord32()) {
368      node = InsertChangeTaggedToFloat64(node);
369      op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
370    }
371  }
372  if (op == nullptr) {
373    return TypeError(node, output_rep, output_type,
374                     MachineRepresentation::kWord32);
375  }
376  return jsgraph()->graph()->NewNode(op, node);
377}
378
379
380Node* RepresentationChanger::GetBitRepresentationFor(
381    Node* node, MachineRepresentation output_rep, Type* output_type) {
382  // Eagerly fold representation changes for constants.
383  switch (node->opcode()) {
384    case IrOpcode::kHeapConstant: {
385      Handle<HeapObject> value = OpParameter<Handle<HeapObject>>(node);
386      DCHECK(value.is_identical_to(factory()->true_value()) ||
387             value.is_identical_to(factory()->false_value()));
388      return jsgraph()->Int32Constant(
389          value.is_identical_to(factory()->true_value()) ? 1 : 0);
390    }
391    default:
392      break;
393  }
394  // Select the correct X -> Bit operator.
395  const Operator* op;
396  if (output_rep == MachineRepresentation::kTagged) {
397    op = simplified()->ChangeBoolToBit();
398  } else {
399    return TypeError(node, output_rep, output_type,
400                     MachineRepresentation::kBit);
401  }
402  return jsgraph()->graph()->NewNode(op, node);
403}
404
405
406Node* RepresentationChanger::GetWord64RepresentationFor(
407    Node* node, MachineRepresentation output_rep, Type* output_type) {
408  if (output_rep == MachineRepresentation::kBit) {
409    return node;  // Sloppy comparison -> word64
410  }
411  // Can't really convert Word64 to anything else. Purported to be internal.
412  return TypeError(node, output_rep, output_type,
413                   MachineRepresentation::kWord64);
414}
415
416
417const Operator* RepresentationChanger::Int32OperatorFor(
418    IrOpcode::Value opcode) {
419  switch (opcode) {
420    case IrOpcode::kNumberAdd:
421      return machine()->Int32Add();
422    case IrOpcode::kNumberSubtract:
423      return machine()->Int32Sub();
424    case IrOpcode::kNumberMultiply:
425      return machine()->Int32Mul();
426    case IrOpcode::kNumberDivide:
427      return machine()->Int32Div();
428    case IrOpcode::kNumberModulus:
429      return machine()->Int32Mod();
430    case IrOpcode::kNumberBitwiseOr:
431      return machine()->Word32Or();
432    case IrOpcode::kNumberBitwiseXor:
433      return machine()->Word32Xor();
434    case IrOpcode::kNumberBitwiseAnd:
435      return machine()->Word32And();
436    case IrOpcode::kNumberEqual:
437      return machine()->Word32Equal();
438    case IrOpcode::kNumberLessThan:
439      return machine()->Int32LessThan();
440    case IrOpcode::kNumberLessThanOrEqual:
441      return machine()->Int32LessThanOrEqual();
442    default:
443      UNREACHABLE();
444      return nullptr;
445  }
446}
447
448
449const Operator* RepresentationChanger::Uint32OperatorFor(
450    IrOpcode::Value opcode) {
451  switch (opcode) {
452    case IrOpcode::kNumberAdd:
453      return machine()->Int32Add();
454    case IrOpcode::kNumberSubtract:
455      return machine()->Int32Sub();
456    case IrOpcode::kNumberMultiply:
457      return machine()->Int32Mul();
458    case IrOpcode::kNumberDivide:
459      return machine()->Uint32Div();
460    case IrOpcode::kNumberModulus:
461      return machine()->Uint32Mod();
462    case IrOpcode::kNumberEqual:
463      return machine()->Word32Equal();
464    case IrOpcode::kNumberLessThan:
465      return machine()->Uint32LessThan();
466    case IrOpcode::kNumberLessThanOrEqual:
467      return machine()->Uint32LessThanOrEqual();
468    case IrOpcode::kNumberClz32:
469      return machine()->Word32Clz();
470    case IrOpcode::kNumberImul:
471      return machine()->Int32Mul();
472    default:
473      UNREACHABLE();
474      return nullptr;
475  }
476}
477
478
479const Operator* RepresentationChanger::Float64OperatorFor(
480    IrOpcode::Value opcode) {
481  switch (opcode) {
482    case IrOpcode::kNumberAdd:
483      return machine()->Float64Add();
484    case IrOpcode::kNumberSubtract:
485      return machine()->Float64Sub();
486    case IrOpcode::kNumberMultiply:
487      return machine()->Float64Mul();
488    case IrOpcode::kNumberDivide:
489      return machine()->Float64Div();
490    case IrOpcode::kNumberModulus:
491      return machine()->Float64Mod();
492    case IrOpcode::kNumberEqual:
493      return machine()->Float64Equal();
494    case IrOpcode::kNumberLessThan:
495      return machine()->Float64LessThan();
496    case IrOpcode::kNumberLessThanOrEqual:
497      return machine()->Float64LessThanOrEqual();
498    default:
499      UNREACHABLE();
500      return nullptr;
501  }
502}
503
504
505Node* RepresentationChanger::TypeError(Node* node,
506                                       MachineRepresentation output_rep,
507                                       Type* output_type,
508                                       MachineRepresentation use) {
509  type_error_ = true;
510  if (!testing_type_errors_) {
511    std::ostringstream out_str;
512    out_str << output_rep << " (";
513    output_type->PrintTo(out_str, Type::SEMANTIC_DIM);
514    out_str << ")";
515
516    std::ostringstream use_str;
517    use_str << use;
518
519    V8_Fatal(__FILE__, __LINE__,
520             "RepresentationChangerError: node #%d:%s of "
521             "%s cannot be changed to %s",
522             node->id(), node->op()->mnemonic(), out_str.str().c_str(),
523             use_str.str().c_str());
524  }
525  return node;
526}
527
528
529Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) {
530  return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node);
531}
532
533
534Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) {
535  return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(),
536                                     node);
537}
538
539}  // namespace compiler
540}  // namespace internal
541}  // namespace v8
542