• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
13 namespace v8 {
14 namespace internal {
15 namespace compiler {
16 
description() const17 const 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
Generalize(TruncationKind rep1,TruncationKind rep2)57 Truncation::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
LessGeneral(TruncationKind rep1,TruncationKind rep2)73 bool 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 
98 namespace {
99 
100 // TODO(titzer): should Word64 also be implicitly convertable to others?
IsWord(MachineRepresentation rep)101 bool IsWord(MachineRepresentation rep) {
102   return rep == MachineRepresentation::kWord8 ||
103          rep == MachineRepresentation::kWord16 ||
104          rep == MachineRepresentation::kWord32;
105 }
106 
107 }  // namespace
108 
109 
110 // Changes representation from {output_rep} to {use_rep}. The {truncation}
111 // parameter is only used for sanity checking - if the changer cannot figure
112 // out signedness for the word32->float64 conversion, then we check that the
113 // uses truncate to word32 (so they do not care about signedness).
GetRepresentationFor(Node * node,MachineRepresentation output_rep,Type * output_type,MachineRepresentation use_rep,Truncation truncation)114 Node* RepresentationChanger::GetRepresentationFor(
115     Node* node, MachineRepresentation output_rep, Type* output_type,
116     MachineRepresentation use_rep, Truncation truncation) {
117   if (output_rep == MachineRepresentation::kNone) {
118     // The output representation should be set.
119     return TypeError(node, output_rep, output_type, use_rep);
120   }
121   if (use_rep == output_rep) {
122     // Representations are the same. That's a no-op.
123     return node;
124   }
125   if (IsWord(use_rep) && IsWord(output_rep)) {
126     // Both are words less than or equal to 32-bits.
127     // Since loads of integers from memory implicitly sign or zero extend the
128     // value to the full machine word size and stores implicitly truncate,
129     // no representation change is necessary.
130     return node;
131   }
132   switch (use_rep) {
133     case MachineRepresentation::kTagged:
134       return GetTaggedRepresentationFor(node, output_rep, output_type);
135     case MachineRepresentation::kFloat32:
136       return GetFloat32RepresentationFor(node, output_rep, output_type,
137                                          truncation);
138     case MachineRepresentation::kFloat64:
139       return GetFloat64RepresentationFor(node, output_rep, output_type,
140                                          truncation);
141     case MachineRepresentation::kBit:
142       return GetBitRepresentationFor(node, output_rep, output_type);
143     case MachineRepresentation::kWord8:
144     case MachineRepresentation::kWord16:
145     case MachineRepresentation::kWord32:
146       return GetWord32RepresentationFor(node, output_rep, output_type);
147     case MachineRepresentation::kWord64:
148       return GetWord64RepresentationFor(node, output_rep, output_type);
149     case MachineRepresentation::kNone:
150       return node;
151   }
152   UNREACHABLE();
153   return nullptr;
154 }
155 
156 
GetTaggedRepresentationFor(Node * node,MachineRepresentation output_rep,Type * output_type)157 Node* RepresentationChanger::GetTaggedRepresentationFor(
158     Node* node, MachineRepresentation output_rep, Type* output_type) {
159   // Eagerly fold representation changes for constants.
160   switch (node->opcode()) {
161     case IrOpcode::kNumberConstant:
162     case IrOpcode::kHeapConstant:
163       return node;  // No change necessary.
164     case IrOpcode::kInt32Constant:
165       if (output_type->Is(Type::Signed32())) {
166         int32_t value = OpParameter<int32_t>(node);
167         return jsgraph()->Constant(value);
168       } else if (output_type->Is(Type::Unsigned32())) {
169         uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
170         return jsgraph()->Constant(static_cast<double>(value));
171       } else if (output_rep == MachineRepresentation::kBit) {
172         return OpParameter<int32_t>(node) == 0 ? jsgraph()->FalseConstant()
173                                                : jsgraph()->TrueConstant();
174       } else {
175         return TypeError(node, output_rep, output_type,
176                          MachineRepresentation::kTagged);
177       }
178     case IrOpcode::kFloat64Constant:
179       return jsgraph()->Constant(OpParameter<double>(node));
180     case IrOpcode::kFloat32Constant:
181       return jsgraph()->Constant(OpParameter<float>(node));
182     default:
183       break;
184   }
185   // Select the correct X -> Tagged operator.
186   const Operator* op;
187   if (output_rep == MachineRepresentation::kBit) {
188     op = simplified()->ChangeBitToBool();
189   } else if (IsWord(output_rep)) {
190     if (output_type->Is(Type::Unsigned32())) {
191       op = simplified()->ChangeUint32ToTagged();
192     } else if (output_type->Is(Type::Signed32())) {
193       op = simplified()->ChangeInt32ToTagged();
194     } else {
195       return TypeError(node, output_rep, output_type,
196                        MachineRepresentation::kTagged);
197     }
198   } else if (output_rep ==
199              MachineRepresentation::kFloat32) {  // float32 -> float64 -> tagged
200     node = InsertChangeFloat32ToFloat64(node);
201     op = simplified()->ChangeFloat64ToTagged();
202   } else if (output_rep == MachineRepresentation::kFloat64) {
203     op = simplified()->ChangeFloat64ToTagged();
204   } else {
205     return TypeError(node, output_rep, output_type,
206                      MachineRepresentation::kTagged);
207   }
208   return jsgraph()->graph()->NewNode(op, node);
209 }
210 
211 
GetFloat32RepresentationFor(Node * node,MachineRepresentation output_rep,Type * output_type,Truncation truncation)212 Node* RepresentationChanger::GetFloat32RepresentationFor(
213     Node* node, MachineRepresentation output_rep, Type* output_type,
214     Truncation truncation) {
215   // Eagerly fold representation changes for constants.
216   switch (node->opcode()) {
217     case IrOpcode::kFloat64Constant:
218     case IrOpcode::kNumberConstant:
219       return jsgraph()->Float32Constant(
220           DoubleToFloat32(OpParameter<double>(node)));
221     case IrOpcode::kInt32Constant:
222       if (output_type->Is(Type::Unsigned32())) {
223         uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
224         return jsgraph()->Float32Constant(static_cast<float>(value));
225       } else {
226         int32_t value = OpParameter<int32_t>(node);
227         return jsgraph()->Float32Constant(static_cast<float>(value));
228       }
229     case IrOpcode::kFloat32Constant:
230       return node;  // No change necessary.
231     default:
232       break;
233   }
234   // Select the correct X -> Float32 operator.
235   const Operator* op;
236   if (output_rep == MachineRepresentation::kBit) {
237     return TypeError(node, output_rep, output_type,
238                      MachineRepresentation::kFloat32);
239   } else if (IsWord(output_rep)) {
240     if (output_type->Is(Type::Signed32())) {
241       op = machine()->ChangeInt32ToFloat64();
242     } else {
243       // Either the output is int32 or the uses only care about the
244       // low 32 bits (so we can pick int32 safely).
245       DCHECK(output_type->Is(Type::Unsigned32()) ||
246              truncation.TruncatesToWord32());
247       op = machine()->ChangeUint32ToFloat64();
248     }
249     // int32 -> float64 -> float32
250     node = jsgraph()->graph()->NewNode(op, node);
251     op = machine()->TruncateFloat64ToFloat32();
252   } else if (output_rep == MachineRepresentation::kTagged) {
253     op = simplified()->ChangeTaggedToFloat64();  // tagged -> float64 -> float32
254     node = jsgraph()->graph()->NewNode(op, node);
255     op = machine()->TruncateFloat64ToFloat32();
256   } else if (output_rep == MachineRepresentation::kFloat64) {
257     op = machine()->TruncateFloat64ToFloat32();
258   } else {
259     return TypeError(node, output_rep, output_type,
260                      MachineRepresentation::kFloat32);
261   }
262   return jsgraph()->graph()->NewNode(op, node);
263 }
264 
265 
GetFloat64RepresentationFor(Node * node,MachineRepresentation output_rep,Type * output_type,Truncation truncation)266 Node* RepresentationChanger::GetFloat64RepresentationFor(
267     Node* node, MachineRepresentation output_rep, Type* output_type,
268     Truncation truncation) {
269   // Eagerly fold representation changes for constants.
270   switch (node->opcode()) {
271     case IrOpcode::kNumberConstant:
272       return jsgraph()->Float64Constant(OpParameter<double>(node));
273     case IrOpcode::kInt32Constant:
274       if (output_type->Is(Type::Signed32())) {
275         int32_t value = OpParameter<int32_t>(node);
276         return jsgraph()->Float64Constant(value);
277       } else {
278         DCHECK(output_type->Is(Type::Unsigned32()));
279         uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node));
280         return jsgraph()->Float64Constant(static_cast<double>(value));
281       }
282     case IrOpcode::kFloat64Constant:
283       return node;  // No change necessary.
284     case IrOpcode::kFloat32Constant:
285       return jsgraph()->Float64Constant(OpParameter<float>(node));
286     default:
287       break;
288   }
289   // Select the correct X -> Float64 operator.
290   const Operator* op;
291   if (output_rep == MachineRepresentation::kBit) {
292     return TypeError(node, output_rep, output_type,
293                      MachineRepresentation::kFloat64);
294   } else if (IsWord(output_rep)) {
295     if (output_type->Is(Type::Signed32())) {
296       op = machine()->ChangeInt32ToFloat64();
297     } else {
298       // Either the output is int32 or the uses only care about the
299       // low 32 bits (so we can pick int32 safely).
300       DCHECK(output_type->Is(Type::Unsigned32()) ||
301              truncation.TruncatesToWord32());
302       op = machine()->ChangeUint32ToFloat64();
303     }
304   } else if (output_rep == MachineRepresentation::kTagged) {
305     op = simplified()->ChangeTaggedToFloat64();
306   } else if (output_rep == MachineRepresentation::kFloat32) {
307     op = machine()->ChangeFloat32ToFloat64();
308   } else {
309     return TypeError(node, output_rep, output_type,
310                      MachineRepresentation::kFloat64);
311   }
312   return jsgraph()->graph()->NewNode(op, node);
313 }
314 
315 
MakeTruncatedInt32Constant(double value)316 Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) {
317   return jsgraph()->Int32Constant(DoubleToInt32(value));
318 }
319 
320 
GetWord32RepresentationFor(Node * node,MachineRepresentation output_rep,Type * output_type)321 Node* RepresentationChanger::GetWord32RepresentationFor(
322     Node* node, MachineRepresentation output_rep, Type* output_type) {
323   // Eagerly fold representation changes for constants.
324   switch (node->opcode()) {
325     case IrOpcode::kInt32Constant:
326       return node;  // No change necessary.
327     case IrOpcode::kFloat32Constant:
328       return MakeTruncatedInt32Constant(OpParameter<float>(node));
329     case IrOpcode::kNumberConstant:
330     case IrOpcode::kFloat64Constant:
331       return MakeTruncatedInt32Constant(OpParameter<double>(node));
332     default:
333       break;
334   }
335   // Select the correct X -> Word32 operator.
336   const Operator* op;
337   Type* type = NodeProperties::GetType(node);
338 
339   if (output_rep == MachineRepresentation::kBit) {
340     return node;  // Sloppy comparison -> word32
341   } else if (output_rep == MachineRepresentation::kFloat64) {
342     // TODO(jarin) Use only output_type here, once we intersect it with the
343     // type inferred by the typer.
344     if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) {
345       op = machine()->ChangeFloat64ToUint32();
346     } else if (output_type->Is(Type::Signed32()) ||
347                type->Is(Type::Signed32())) {
348       op = machine()->ChangeFloat64ToInt32();
349     } else {
350       op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
351     }
352   } else if (output_rep == MachineRepresentation::kFloat32) {
353     node = InsertChangeFloat32ToFloat64(node);  // float32 -> float64 -> int32
354     if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) {
355       op = machine()->ChangeFloat64ToUint32();
356     } else if (output_type->Is(Type::Signed32()) ||
357                type->Is(Type::Signed32())) {
358       op = machine()->ChangeFloat64ToInt32();
359     } else {
360       op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
361     }
362   } else if (output_rep == MachineRepresentation::kTagged) {
363     if (output_type->Is(Type::Unsigned32()) || type->Is(Type::Unsigned32())) {
364       op = simplified()->ChangeTaggedToUint32();
365     } else if (output_type->Is(Type::Signed32()) ||
366                type->Is(Type::Signed32())) {
367       op = simplified()->ChangeTaggedToInt32();
368     } else {
369       node = InsertChangeTaggedToFloat64(node);
370       op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript);
371     }
372   } else {
373     return TypeError(node, output_rep, output_type,
374                      MachineRepresentation::kWord32);
375   }
376   return jsgraph()->graph()->NewNode(op, node);
377 }
378 
379 
GetBitRepresentationFor(Node * node,MachineRepresentation output_rep,Type * output_type)380 Node* 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 
GetWord64RepresentationFor(Node * node,MachineRepresentation output_rep,Type * output_type)406 Node* 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 
Int32OperatorFor(IrOpcode::Value opcode)417 const 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 
Uint32OperatorFor(IrOpcode::Value opcode)449 const 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     default:
469       UNREACHABLE();
470       return nullptr;
471   }
472 }
473 
474 
Float64OperatorFor(IrOpcode::Value opcode)475 const Operator* RepresentationChanger::Float64OperatorFor(
476     IrOpcode::Value opcode) {
477   switch (opcode) {
478     case IrOpcode::kNumberAdd:
479       return machine()->Float64Add();
480     case IrOpcode::kNumberSubtract:
481       return machine()->Float64Sub();
482     case IrOpcode::kNumberMultiply:
483       return machine()->Float64Mul();
484     case IrOpcode::kNumberDivide:
485       return machine()->Float64Div();
486     case IrOpcode::kNumberModulus:
487       return machine()->Float64Mod();
488     case IrOpcode::kNumberEqual:
489       return machine()->Float64Equal();
490     case IrOpcode::kNumberLessThan:
491       return machine()->Float64LessThan();
492     case IrOpcode::kNumberLessThanOrEqual:
493       return machine()->Float64LessThanOrEqual();
494     default:
495       UNREACHABLE();
496       return nullptr;
497   }
498 }
499 
500 
TypeError(Node * node,MachineRepresentation output_rep,Type * output_type,MachineRepresentation use)501 Node* RepresentationChanger::TypeError(Node* node,
502                                        MachineRepresentation output_rep,
503                                        Type* output_type,
504                                        MachineRepresentation use) {
505   type_error_ = true;
506   if (!testing_type_errors_) {
507     std::ostringstream out_str;
508     out_str << output_rep << " (";
509     output_type->PrintTo(out_str, Type::SEMANTIC_DIM);
510     out_str << ")";
511 
512     std::ostringstream use_str;
513     use_str << use;
514 
515     V8_Fatal(__FILE__, __LINE__,
516              "RepresentationChangerError: node #%d:%s of "
517              "%s cannot be changed to %s",
518              node->id(), node->op()->mnemonic(), out_str.str().c_str(),
519              use_str.str().c_str());
520   }
521   return node;
522 }
523 
524 
InsertChangeFloat32ToFloat64(Node * node)525 Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) {
526   return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node);
527 }
528 
529 
InsertChangeTaggedToFloat64(Node * node)530 Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) {
531   return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(),
532                                      node);
533 }
534 
535 }  // namespace compiler
536 }  // namespace internal
537 }  // namespace v8
538