OLD | NEW |
---|---|
(Empty) | |
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 | |
17 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 | |
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 representations"); | |
68 return TruncationKind::kNone; | |
69 } | |
70 | |
71 | |
72 // static | |
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? | |
101 bool IsWord(MachineTypeUnion type) { | |
102 return (type & (kRepWord8 | kRepWord16 | kRepWord32)) != 0; | |
103 } | |
104 | |
105 } // namespace | |
106 | |
107 | |
108 // Changes representation from {output_type} to {use_rep}. The {truncation} | |
109 // parameter is only used for sanity checking - if the changer cannot figure | |
110 // out signedness for the word32->float64 conversion, then we check that the | |
111 // uses truncate to word32 (so they do not care about signedness). | |
112 Node* RepresentationChanger::GetRepresentationFor(Node* node, | |
113 MachineTypeUnion output_type, | |
114 MachineTypeUnion use_rep, | |
115 Truncation truncation) { | |
116 DCHECK((use_rep & kRepMask) == use_rep); | |
117 if (!base::bits::IsPowerOfTwo32(output_type & kRepMask)) { | |
118 // There should be only one output representation. | |
119 return TypeError(node, output_type, use_rep); | |
120 } | |
121 if (use_rep == (output_type & kRepMask)) { | |
122 // Representations are the same. That's a no-op. | |
123 return node; | |
124 } | |
125 if (IsWord(use_rep) && IsWord(output_type)) { | |
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 if (use_rep & kRepTagged) { | |
133 return GetTaggedRepresentationFor(node, output_type); | |
134 } else if (use_rep & kRepFloat32) { | |
135 return GetFloat32RepresentationFor(node, output_type, truncation); | |
136 } else if (use_rep & kRepFloat64) { | |
137 return GetFloat64RepresentationFor(node, output_type, truncation); | |
138 } else if (use_rep & kRepBit) { | |
139 return GetBitRepresentationFor(node, output_type); | |
140 } else if (IsWord(use_rep)) { | |
141 return GetWord32RepresentationFor(node, output_type); | |
142 } else if (use_rep & kRepWord64) { | |
143 return GetWord64RepresentationFor(node, output_type); | |
144 } else { | |
145 return node; | |
146 } | |
147 } | |
148 | |
149 | |
150 Node* RepresentationChanger::GetTaggedRepresentationFor( | |
151 Node* node, MachineTypeUnion output_type) { | |
152 // Eagerly fold representation changes for constants. | |
153 switch (node->opcode()) { | |
154 case IrOpcode::kNumberConstant: | |
155 case IrOpcode::kHeapConstant: | |
156 return node; // No change necessary. | |
157 case IrOpcode::kInt32Constant: | |
158 if (output_type & kTypeUint32) { | |
159 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); | |
160 return jsgraph()->Constant(static_cast<double>(value)); | |
161 } else if (output_type & kTypeInt32) { | |
162 int32_t value = OpParameter<int32_t>(node); | |
163 return jsgraph()->Constant(value); | |
164 } else if (output_type & kRepBit) { | |
165 return OpParameter<int32_t>(node) == 0 ? jsgraph()->FalseConstant() | |
166 : jsgraph()->TrueConstant(); | |
167 } else { | |
168 return TypeError(node, output_type, kRepTagged); | |
169 } | |
170 case IrOpcode::kFloat64Constant: | |
171 return jsgraph()->Constant(OpParameter<double>(node)); | |
172 case IrOpcode::kFloat32Constant: | |
173 return jsgraph()->Constant(OpParameter<float>(node)); | |
174 default: | |
175 break; | |
176 } | |
177 // Select the correct X -> Tagged operator. | |
178 const Operator* op; | |
179 if (output_type & kRepBit) { | |
180 op = simplified()->ChangeBitToBool(); | |
181 } else if (IsWord(output_type)) { | |
182 if (output_type & kTypeUint32) { | |
183 op = simplified()->ChangeUint32ToTagged(); | |
184 } else if (output_type & kTypeInt32) { | |
185 op = simplified()->ChangeInt32ToTagged(); | |
186 } else { | |
187 return TypeError(node, output_type, kRepTagged); | |
188 } | |
189 } else if (output_type & kRepFloat32) { // float32 -> float64 -> tagged | |
190 node = InsertChangeFloat32ToFloat64(node); | |
191 op = simplified()->ChangeFloat64ToTagged(); | |
192 } else if (output_type & kRepFloat64) { | |
193 op = simplified()->ChangeFloat64ToTagged(); | |
194 } else { | |
195 return TypeError(node, output_type, kRepTagged); | |
196 } | |
197 return jsgraph()->graph()->NewNode(op, node); | |
198 } | |
199 | |
200 | |
201 Node* RepresentationChanger::GetFloat32RepresentationFor( | |
202 Node* node, MachineTypeUnion output_type, Truncation truncation) { | |
203 // Eagerly fold representation changes for constants. | |
204 switch (node->opcode()) { | |
205 case IrOpcode::kFloat64Constant: | |
206 case IrOpcode::kNumberConstant: | |
207 return jsgraph()->Float32Constant( | |
208 DoubleToFloat32(OpParameter<double>(node))); | |
209 case IrOpcode::kInt32Constant: | |
210 if (output_type & kTypeUint32) { | |
211 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); | |
212 return jsgraph()->Float32Constant(static_cast<float>(value)); | |
213 } else { | |
214 int32_t value = OpParameter<int32_t>(node); | |
215 return jsgraph()->Float32Constant(static_cast<float>(value)); | |
216 } | |
217 case IrOpcode::kFloat32Constant: | |
218 return node; // No change necessary. | |
219 default: | |
220 break; | |
221 } | |
222 // Select the correct X -> Float32 operator. | |
223 const Operator* op; | |
224 if (output_type & kRepBit) { | |
225 return TypeError(node, output_type, kRepFloat32); | |
226 } else if (IsWord(output_type)) { | |
227 if (output_type & kTypeUint32) { | |
228 op = machine()->ChangeUint32ToFloat64(); | |
229 } else { | |
230 // Either the output is int32 or the uses only care about the | |
231 // low 32 bits (so we can pick int32 safely). | |
232 DCHECK(output_type & kTypeInt32 || truncation.TruncatesToWord32()); | |
233 op = machine()->ChangeInt32ToFloat64(); | |
234 } | |
235 // int32 -> float64 -> float32 | |
236 node = jsgraph()->graph()->NewNode(op, node); | |
237 op = machine()->TruncateFloat64ToFloat32(); | |
238 } else if (output_type & kRepTagged) { | |
239 op = simplified()->ChangeTaggedToFloat64(); // tagged -> float64 -> float32 | |
240 node = jsgraph()->graph()->NewNode(op, node); | |
241 op = machine()->TruncateFloat64ToFloat32(); | |
242 } else if (output_type & kRepFloat64) { | |
243 op = machine()->TruncateFloat64ToFloat32(); | |
244 } else { | |
245 return TypeError(node, output_type, kRepFloat32); | |
246 } | |
247 return jsgraph()->graph()->NewNode(op, node); | |
248 } | |
249 | |
250 | |
251 Node* RepresentationChanger::GetFloat64RepresentationFor( | |
252 Node* node, MachineTypeUnion output_type, Truncation truncation) { | |
253 // Eagerly fold representation changes for constants. | |
254 switch (node->opcode()) { | |
255 case IrOpcode::kNumberConstant: | |
256 return jsgraph()->Float64Constant(OpParameter<double>(node)); | |
257 case IrOpcode::kInt32Constant: | |
258 if (output_type & kTypeUint32) { | |
259 uint32_t value = static_cast<uint32_t>(OpParameter<int32_t>(node)); | |
260 return jsgraph()->Float64Constant(static_cast<double>(value)); | |
261 } else { | |
262 int32_t value = OpParameter<int32_t>(node); | |
263 return jsgraph()->Float64Constant(value); | |
264 } | |
265 case IrOpcode::kFloat64Constant: | |
266 return node; // No change necessary. | |
267 case IrOpcode::kFloat32Constant: | |
268 return jsgraph()->Float64Constant(OpParameter<float>(node)); | |
269 default: | |
270 break; | |
271 } | |
272 // Select the correct X -> Float64 operator. | |
273 const Operator* op; | |
274 if (output_type & kRepBit) { | |
275 return TypeError(node, output_type, kRepFloat64); | |
276 } else if (IsWord(output_type)) { | |
277 if (output_type & kTypeUint32) { | |
278 op = machine()->ChangeUint32ToFloat64(); | |
279 } else { | |
280 // Either the output is int32 or the uses only care about the | |
281 // low 32 bits (so we can pick int32 safely). | |
282 DCHECK(output_type & kTypeInt32 || truncation.TruncatesToWord32()); | |
283 op = machine()->ChangeInt32ToFloat64(); | |
284 } | |
285 } else if (output_type & kRepTagged) { | |
286 op = simplified()->ChangeTaggedToFloat64(); | |
287 } else if (output_type & kRepFloat32) { | |
288 op = machine()->ChangeFloat32ToFloat64(); | |
289 } else { | |
290 return TypeError(node, output_type, kRepFloat64); | |
291 } | |
292 return jsgraph()->graph()->NewNode(op, node); | |
293 } | |
294 | |
295 | |
296 Node* RepresentationChanger::MakeTruncatedInt32Constant(double value) { | |
297 return jsgraph()->Int32Constant(DoubleToInt32(value)); | |
298 } | |
299 | |
300 | |
301 Node* RepresentationChanger::GetWord32RepresentationFor( | |
302 Node* node, MachineTypeUnion output_type) { | |
303 // Eagerly fold representation changes for constants. | |
304 switch (node->opcode()) { | |
305 case IrOpcode::kInt32Constant: | |
306 return node; // No change necessary. | |
307 case IrOpcode::kFloat32Constant: | |
308 return MakeTruncatedInt32Constant(OpParameter<float>(node)); | |
309 case IrOpcode::kNumberConstant: | |
310 case IrOpcode::kFloat64Constant: | |
311 return MakeTruncatedInt32Constant(OpParameter<double>(node)); | |
312 default: | |
313 break; | |
314 } | |
315 // Select the correct X -> Word32 operator. | |
316 const Operator* op; | |
317 Type* type = NodeProperties::GetType(node); | |
318 | |
319 if (output_type & kRepBit) { | |
320 return node; // Sloppy comparison -> word32 | |
321 } else if (output_type & kRepFloat64) { | |
322 if (output_type & kTypeUint32 || type->Is(Type::Unsigned32())) { | |
323 op = machine()->ChangeFloat64ToUint32(); | |
324 } else if (output_type & kTypeInt32 || type->Is(Type::Signed32())) { | |
325 op = machine()->ChangeFloat64ToInt32(); | |
326 } else { | |
327 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); | |
328 } | |
329 } else if (output_type & kRepFloat32) { | |
330 node = InsertChangeFloat32ToFloat64(node); // float32 -> float64 -> int32 | |
331 if (output_type & kTypeUint32 || type->Is(Type::Unsigned32())) { | |
332 op = machine()->ChangeFloat64ToUint32(); | |
333 } else if (output_type & kTypeInt32 || type->Is(Type::Signed32())) { | |
334 op = machine()->ChangeFloat64ToInt32(); | |
335 } else { | |
336 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); | |
337 } | |
338 } else if (output_type & kRepTagged) { | |
339 if (output_type & kTypeUint32 || type->Is(Type::Unsigned32())) { | |
340 op = simplified()->ChangeTaggedToUint32(); | |
341 } else if (output_type & kTypeInt32 || type->Is(Type::Signed32())) { | |
342 op = simplified()->ChangeTaggedToInt32(); | |
343 } else { | |
344 node = InsertChangeTaggedToFloat64(node); | |
345 op = machine()->TruncateFloat64ToInt32(TruncationMode::kJavaScript); | |
346 } | |
347 } else { | |
348 return TypeError(node, output_type, kRepWord32); | |
349 } | |
350 return jsgraph()->graph()->NewNode(op, node); | |
351 } | |
352 | |
353 | |
354 Node* RepresentationChanger::GetBitRepresentationFor( | |
355 Node* node, MachineTypeUnion output_type) { | |
356 // Eagerly fold representation changes for constants. | |
357 switch (node->opcode()) { | |
358 case IrOpcode::kHeapConstant: { | |
359 Handle<HeapObject> value = OpParameter<Handle<HeapObject>>(node); | |
360 DCHECK(value.is_identical_to(factory()->true_value()) || | |
361 value.is_identical_to(factory()->false_value())); | |
362 return jsgraph()->Int32Constant( | |
363 value.is_identical_to(factory()->true_value()) ? 1 : 0); | |
364 } | |
365 default: | |
366 break; | |
367 } | |
368 // Select the correct X -> Bit operator. | |
369 const Operator* op; | |
370 if (output_type & kRepTagged) { | |
371 op = simplified()->ChangeBoolToBit(); | |
372 } else { | |
373 return TypeError(node, output_type, kRepBit); | |
374 } | |
375 return jsgraph()->graph()->NewNode(op, node); | |
376 } | |
377 | |
378 | |
379 Node* RepresentationChanger::GetWord64RepresentationFor( | |
380 Node* node, MachineTypeUnion output_type) { | |
381 if (output_type & kRepBit) { | |
382 return node; // Sloppy comparison -> word64 | |
383 } | |
384 // Can't really convert Word64 to anything else. Purported to be internal. | |
385 return TypeError(node, output_type, kRepWord64); | |
386 } | |
387 | |
388 | |
389 const Operator* RepresentationChanger::Int32OperatorFor( | |
390 IrOpcode::Value opcode) { | |
391 switch (opcode) { | |
392 case IrOpcode::kNumberAdd: | |
393 return machine()->Int32Add(); | |
394 case IrOpcode::kNumberSubtract: | |
395 return machine()->Int32Sub(); | |
396 case IrOpcode::kNumberMultiply: | |
397 return machine()->Int32Mul(); | |
398 case IrOpcode::kNumberDivide: | |
399 return machine()->Int32Div(); | |
400 case IrOpcode::kNumberModulus: | |
401 return machine()->Int32Mod(); | |
402 case IrOpcode::kNumberBitwiseOr: | |
403 return machine()->Word32Or(); | |
404 case IrOpcode::kNumberBitwiseXor: | |
405 return machine()->Word32Xor(); | |
406 case IrOpcode::kNumberBitwiseAnd: | |
407 return machine()->Word32And(); | |
408 case IrOpcode::kNumberEqual: | |
409 return machine()->Word32Equal(); | |
410 case IrOpcode::kNumberLessThan: | |
411 return machine()->Int32LessThan(); | |
412 case IrOpcode::kNumberLessThanOrEqual: | |
413 return machine()->Int32LessThanOrEqual(); | |
414 default: | |
415 UNREACHABLE(); | |
416 return NULL; | |
417 } | |
418 } | |
419 | |
420 | |
421 const Operator* RepresentationChanger::Uint32OperatorFor( | |
422 IrOpcode::Value opcode) { | |
423 switch (opcode) { | |
424 case IrOpcode::kNumberAdd: | |
425 return machine()->Int32Add(); | |
426 case IrOpcode::kNumberSubtract: | |
427 return machine()->Int32Sub(); | |
428 case IrOpcode::kNumberMultiply: | |
429 return machine()->Int32Mul(); | |
430 case IrOpcode::kNumberDivide: | |
431 return machine()->Uint32Div(); | |
432 case IrOpcode::kNumberModulus: | |
433 return machine()->Uint32Mod(); | |
434 case IrOpcode::kNumberEqual: | |
435 return machine()->Word32Equal(); | |
436 case IrOpcode::kNumberLessThan: | |
437 return machine()->Uint32LessThan(); | |
438 case IrOpcode::kNumberLessThanOrEqual: | |
439 return machine()->Uint32LessThanOrEqual(); | |
440 default: | |
441 UNREACHABLE(); | |
442 return NULL; | |
443 } | |
444 } | |
445 | |
446 | |
447 const Operator* RepresentationChanger::Float64OperatorFor( | |
448 IrOpcode::Value opcode) { | |
449 switch (opcode) { | |
450 case IrOpcode::kNumberAdd: | |
451 return machine()->Float64Add(); | |
452 case IrOpcode::kNumberSubtract: | |
453 return machine()->Float64Sub(); | |
454 case IrOpcode::kNumberMultiply: | |
455 return machine()->Float64Mul(); | |
456 case IrOpcode::kNumberDivide: | |
457 return machine()->Float64Div(); | |
458 case IrOpcode::kNumberModulus: | |
459 return machine()->Float64Mod(); | |
460 case IrOpcode::kNumberEqual: | |
461 return machine()->Float64Equal(); | |
462 case IrOpcode::kNumberLessThan: | |
463 return machine()->Float64LessThan(); | |
464 case IrOpcode::kNumberLessThanOrEqual: | |
465 return machine()->Float64LessThanOrEqual(); | |
466 default: | |
467 UNREACHABLE(); | |
468 return NULL; | |
469 } | |
470 } | |
471 | |
472 | |
473 MachineType RepresentationChanger::TypeFromUpperBound(Type* type) { | |
474 if (type->Is(Type::None())) | |
475 return kTypeAny; // TODO(titzer): should be an error | |
Benedikt Meurer
2015/11/25 09:31:05
Nit: Can we fix that? In follow up CL?
| |
476 if (type->Is(Type::Signed32())) return kTypeInt32; | |
477 if (type->Is(Type::Unsigned32())) return kTypeUint32; | |
478 if (type->Is(Type::Number())) return kTypeNumber; | |
479 if (type->Is(Type::Boolean())) return kTypeBool; | |
480 return kTypeAny; | |
481 } | |
482 | |
483 | |
484 Node* RepresentationChanger::TypeError(Node* node, MachineTypeUnion output_type, | |
485 MachineTypeUnion use) { | |
486 type_error_ = true; | |
487 if (!testing_type_errors_) { | |
488 std::ostringstream out_str; | |
489 out_str << static_cast<MachineType>(output_type); | |
490 | |
491 std::ostringstream use_str; | |
492 use_str << static_cast<MachineType>(use); | |
493 | |
494 V8_Fatal(__FILE__, __LINE__, | |
495 "RepresentationChangerError: node #%d:%s of " | |
496 "%s cannot be changed to %s", | |
497 node->id(), node->op()->mnemonic(), out_str.str().c_str(), | |
498 use_str.str().c_str()); | |
499 } | |
500 return node; | |
501 } | |
502 | |
503 | |
504 Node* RepresentationChanger::InsertChangeFloat32ToFloat64(Node* node) { | |
505 return jsgraph()->graph()->NewNode(machine()->ChangeFloat32ToFloat64(), node); | |
506 } | |
507 | |
508 | |
509 Node* RepresentationChanger::InsertChangeTaggedToFloat64(Node* node) { | |
510 return jsgraph()->graph()->NewNode(simplified()->ChangeTaggedToFloat64(), | |
511 node); | |
512 } | |
513 | |
514 } // namespace compiler | |
515 } // namespace internal | |
516 } // namespace v8 | |
OLD | NEW |