blob: 5dab60f6a32bd06a101d7e6e380f184498b7115c [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
100// TODO(titzer): should Word64 also be implicitly convertable to others?
101bool 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).
114Node* 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
157Node* 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
212Node* 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
266Node* 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
316Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) {
317 return jsgraph()->Int32Constant(DoubleToInt32(value));
318}
319
320
321Node* 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
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 default:
469 UNREACHABLE();
470 return nullptr;
471 }
472}
473
474
475const 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
501Node* 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
525Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) {
526 return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node);
527}
528
529
530Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) {
531 return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(),
532 node);
533}
534
535} // namespace compiler
536} // namespace internal
537} // namespace v8