1// Copyright 2014 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#ifndef V8_COMPILER_REPRESENTATION_CHANGE_H_
6#define V8_COMPILER_REPRESENTATION_CHANGE_H_
7
8#include "src/compiler/feedback-source.h"
9#include "src/compiler/js-graph.h"
10#include "src/compiler/simplified-operator.h"
11
12namespace v8 {
13namespace internal {
14namespace compiler {
15
16// Foward declarations.
17class SimplifiedLoweringVerifier;
18class TypeCache;
19
20enum IdentifyZeros : uint8_t { kIdentifyZeros, kDistinguishZeros };
21
22class Truncation final {
23 public:
24  // Constructors.
25  static Truncation None() {
26    return Truncation(TruncationKind::kNone, kIdentifyZeros);
27  }
28  static Truncation Bool() {
29    return Truncation(TruncationKind::kBool, kIdentifyZeros);
30  }
31  static Truncation Word32() {
32    return Truncation(TruncationKind::kWord32, kIdentifyZeros);
33  }
34  static Truncation Word64() {
35    return Truncation(TruncationKind::kWord64, kIdentifyZeros);
36  }
37  static Truncation OddballAndBigIntToNumber(
38      IdentifyZeros identify_zeros = kDistinguishZeros) {
39    return Truncation(TruncationKind::kOddballAndBigIntToNumber,
40                      identify_zeros);
41  }
42  static Truncation Any(IdentifyZeros identify_zeros = kDistinguishZeros) {
43    return Truncation(TruncationKind::kAny, identify_zeros);
44  }
45
46  static Truncation Generalize(Truncation t1, Truncation t2) {
47    return Truncation(
48        Generalize(t1.kind(), t2.kind()),
49        GeneralizeIdentifyZeros(t1.identify_zeros(), t2.identify_zeros()));
50  }
51
52  // Queries.
53  bool IsUnused() const { return kind_ == TruncationKind::kNone; }
54  bool IsUsedAsBool() const {
55    return LessGeneral(kind_, TruncationKind::kBool);
56  }
57  bool IsUsedAsWord32() const {
58    return LessGeneral(kind_, TruncationKind::kWord32);
59  }
60  bool IsUsedAsWord64() const {
61    return LessGeneral(kind_, TruncationKind::kWord64);
62  }
63  bool TruncatesOddballAndBigIntToNumber() const {
64    return LessGeneral(kind_, TruncationKind::kOddballAndBigIntToNumber);
65  }
66  bool IdentifiesUndefinedAndZero() {
67    return LessGeneral(kind_, TruncationKind::kWord32) ||
68           LessGeneral(kind_, TruncationKind::kBool);
69  }
70  bool IdentifiesZeroAndMinusZero() const {
71    return identify_zeros() == kIdentifyZeros;
72  }
73
74  // Operators.
75  bool operator==(Truncation other) const {
76    return kind() == other.kind() && identify_zeros() == other.identify_zeros();
77  }
78  bool operator!=(Truncation other) const { return !(*this == other); }
79
80  // Debug utilities.
81  const char* description() const;
82  bool IsLessGeneralThan(Truncation other) const {
83    return LessGeneral(kind(), other.kind()) &&
84           LessGeneralIdentifyZeros(identify_zeros(), other.identify_zeros());
85  }
86
87  IdentifyZeros identify_zeros() const { return identify_zeros_; }
88
89 private:
90  enum class TruncationKind : uint8_t {
91    kNone,
92    kBool,
93    kWord32,
94    kWord64,
95    kOddballAndBigIntToNumber,
96    kAny
97  };
98
99  explicit Truncation(TruncationKind kind, IdentifyZeros identify_zeros)
100      : kind_(kind), identify_zeros_(identify_zeros) {}
101
102  TruncationKind kind() const { return kind_; }
103
104  friend class SimplifiedLoweringVerifier;
105  TruncationKind kind_;
106  IdentifyZeros identify_zeros_;
107
108  static TruncationKind Generalize(TruncationKind rep1, TruncationKind rep2);
109  static IdentifyZeros GeneralizeIdentifyZeros(IdentifyZeros i1,
110                                               IdentifyZeros i2);
111  static bool LessGeneral(TruncationKind rep1, TruncationKind rep2);
112  static bool LessGeneralIdentifyZeros(IdentifyZeros u1, IdentifyZeros u2);
113};
114
115enum class TypeCheckKind : uint8_t {
116  kNone,
117  kSignedSmall,
118  kSigned32,
119  kSigned64,
120  kNumber,
121  kNumberOrBoolean,
122  kNumberOrOddball,
123  kHeapObject,
124  kBigInt,
125  kArrayIndex
126};
127
128inline std::ostream& operator<<(std::ostream& os, TypeCheckKind type_check) {
129  switch (type_check) {
130    case TypeCheckKind::kNone:
131      return os << "None";
132    case TypeCheckKind::kSignedSmall:
133      return os << "SignedSmall";
134    case TypeCheckKind::kSigned32:
135      return os << "Signed32";
136    case TypeCheckKind::kSigned64:
137      return os << "Signed64";
138    case TypeCheckKind::kNumber:
139      return os << "Number";
140    case TypeCheckKind::kNumberOrBoolean:
141      return os << "NumberOrBoolean";
142    case TypeCheckKind::kNumberOrOddball:
143      return os << "NumberOrOddball";
144    case TypeCheckKind::kHeapObject:
145      return os << "HeapObject";
146    case TypeCheckKind::kBigInt:
147      return os << "BigInt";
148    case TypeCheckKind::kArrayIndex:
149      return os << "ArrayIndex";
150  }
151  UNREACHABLE();
152}
153
154// The {UseInfo} class is used to describe a use of an input of a node.
155//
156// This information is used in two different ways, based on the phase:
157//
158// 1. During propagation, the use info is used to inform the input node
159//    about what part of the input is used (we call this truncation) and what
160//    is the preferred representation. For conversions that will require
161//    checks, we also keep track of whether a minus zero check is needed.
162//
163// 2. During lowering, the use info is used to properly convert the input
164//    to the preferred representation. The preferred representation might be
165//    insufficient to do the conversion (e.g. word32->float64 conv), so we also
166//    need the signedness information to produce the correct value.
167//    Additionally, use info may contain {CheckParameters} which contains
168//    information for the deoptimizer such as a CallIC on which speculation
169//    should be disallowed if the check fails.
170class UseInfo {
171 public:
172  UseInfo(MachineRepresentation representation, Truncation truncation,
173          TypeCheckKind type_check = TypeCheckKind::kNone,
174          const FeedbackSource& feedback = FeedbackSource())
175      : representation_(representation),
176        truncation_(truncation),
177        type_check_(type_check),
178        feedback_(feedback) {}
179  static UseInfo TruncatingWord32() {
180    return UseInfo(MachineRepresentation::kWord32, Truncation::Word32());
181  }
182  static UseInfo CheckedBigIntTruncatingWord64(const FeedbackSource& feedback) {
183    // Note that Trunction::Word64() can safely use kIdentifyZero, because
184    // TypeCheckKind::kBigInt will make sure we deopt for anything other than
185    // type BigInt anyway.
186    return UseInfo(MachineRepresentation::kWord64, Truncation::Word64(),
187                   TypeCheckKind::kBigInt, feedback);
188  }
189  static UseInfo Word64() {
190    return UseInfo(MachineRepresentation::kWord64, Truncation::Any());
191  }
192  static UseInfo Word() {
193    return UseInfo(MachineType::PointerRepresentation(), Truncation::Any());
194  }
195  static UseInfo Bool() {
196    return UseInfo(MachineRepresentation::kBit, Truncation::Bool());
197  }
198  static UseInfo Float32() {
199    return UseInfo(MachineRepresentation::kFloat32, Truncation::Any());
200  }
201  static UseInfo Float64() {
202    return UseInfo(MachineRepresentation::kFloat64, Truncation::Any());
203  }
204  static UseInfo TruncatingFloat64(
205      IdentifyZeros identify_zeros = kDistinguishZeros) {
206    return UseInfo(MachineRepresentation::kFloat64,
207                   Truncation::OddballAndBigIntToNumber(identify_zeros));
208  }
209  static UseInfo AnyTagged() {
210    return UseInfo(MachineRepresentation::kTagged, Truncation::Any());
211  }
212  static UseInfo TaggedSigned() {
213    return UseInfo(MachineRepresentation::kTaggedSigned, Truncation::Any());
214  }
215  static UseInfo TaggedPointer() {
216    return UseInfo(MachineRepresentation::kTaggedPointer, Truncation::Any());
217  }
218
219  // Possibly deoptimizing conversions.
220  static UseInfo CheckedTaggedAsArrayIndex(const FeedbackSource& feedback) {
221    return UseInfo(MachineType::PointerRepresentation(),
222                   Truncation::Any(kIdentifyZeros), TypeCheckKind::kArrayIndex,
223                   feedback);
224  }
225  static UseInfo CheckedHeapObjectAsTaggedPointer(
226      const FeedbackSource& feedback) {
227    return UseInfo(MachineRepresentation::kTaggedPointer, Truncation::Any(),
228                   TypeCheckKind::kHeapObject, feedback);
229  }
230
231  static UseInfo CheckedBigIntAsTaggedPointer(const FeedbackSource& feedback) {
232    return UseInfo(MachineRepresentation::kTaggedPointer, Truncation::Any(),
233                   TypeCheckKind::kBigInt, feedback);
234  }
235
236  static UseInfo CheckedSignedSmallAsTaggedSigned(
237      const FeedbackSource& feedback,
238      IdentifyZeros identify_zeros = kDistinguishZeros) {
239    return UseInfo(MachineRepresentation::kTaggedSigned,
240                   Truncation::Any(identify_zeros), TypeCheckKind::kSignedSmall,
241                   feedback);
242  }
243  static UseInfo CheckedSignedSmallAsWord32(IdentifyZeros identify_zeros,
244                                            const FeedbackSource& feedback) {
245    return UseInfo(MachineRepresentation::kWord32,
246                   Truncation::Any(identify_zeros), TypeCheckKind::kSignedSmall,
247                   feedback);
248  }
249  static UseInfo CheckedSigned32AsWord32(IdentifyZeros identify_zeros,
250                                         const FeedbackSource& feedback) {
251    return UseInfo(MachineRepresentation::kWord32,
252                   Truncation::Any(identify_zeros), TypeCheckKind::kSigned32,
253                   feedback);
254  }
255  static UseInfo CheckedSigned64AsWord64(IdentifyZeros identify_zeros,
256                                         const FeedbackSource& feedback) {
257    return UseInfo(MachineRepresentation::kWord64,
258                   Truncation::Any(identify_zeros), TypeCheckKind::kSigned64,
259                   feedback);
260  }
261  static UseInfo CheckedNumberAsFloat64(IdentifyZeros identify_zeros,
262                                        const FeedbackSource& feedback) {
263    return UseInfo(MachineRepresentation::kFloat64,
264                   Truncation::Any(identify_zeros), TypeCheckKind::kNumber,
265                   feedback);
266  }
267  static UseInfo CheckedNumberAsWord32(const FeedbackSource& feedback) {
268    return UseInfo(MachineRepresentation::kWord32, Truncation::Word32(),
269                   TypeCheckKind::kNumber, feedback);
270  }
271  static UseInfo CheckedNumberOrBooleanAsFloat64(
272      IdentifyZeros identify_zeros, const FeedbackSource& feedback) {
273    return UseInfo(MachineRepresentation::kFloat64,
274                   Truncation::Any(identify_zeros),
275                   TypeCheckKind::kNumberOrBoolean, feedback);
276  }
277  static UseInfo CheckedNumberOrOddballAsFloat64(
278      IdentifyZeros identify_zeros, const FeedbackSource& feedback) {
279    return UseInfo(MachineRepresentation::kFloat64,
280                   Truncation::Any(identify_zeros),
281                   TypeCheckKind::kNumberOrOddball, feedback);
282  }
283  static UseInfo CheckedNumberOrOddballAsWord32(
284      const FeedbackSource& feedback) {
285    return UseInfo(MachineRepresentation::kWord32, Truncation::Word32(),
286                   TypeCheckKind::kNumberOrOddball, feedback);
287  }
288
289  // Undetermined representation.
290  static UseInfo Any() {
291    return UseInfo(MachineRepresentation::kNone, Truncation::Any());
292  }
293  static UseInfo AnyTruncatingToBool() {
294    return UseInfo(MachineRepresentation::kNone, Truncation::Bool());
295  }
296
297  // Value not used.
298  static UseInfo None() {
299    return UseInfo(MachineRepresentation::kNone, Truncation::None());
300  }
301
302  MachineRepresentation representation() const { return representation_; }
303  Truncation truncation() const { return truncation_; }
304  TypeCheckKind type_check() const { return type_check_; }
305  CheckForMinusZeroMode minus_zero_check() const {
306    return truncation().IdentifiesZeroAndMinusZero()
307               ? CheckForMinusZeroMode::kDontCheckForMinusZero
308               : CheckForMinusZeroMode::kCheckForMinusZero;
309  }
310  const FeedbackSource& feedback() const { return feedback_; }
311
312 private:
313  MachineRepresentation representation_;
314  Truncation truncation_;
315  TypeCheckKind type_check_;
316  FeedbackSource feedback_;
317};
318
319// Contains logic related to changing the representation of values for constants
320// and other nodes, as well as lowering Simplified->Machine operators.
321// Eagerly folds any representation changes for constants.
322class V8_EXPORT_PRIVATE RepresentationChanger final {
323 public:
324  RepresentationChanger(JSGraph* jsgraph, JSHeapBroker* broker,
325                        SimplifiedLoweringVerifier* verifier);
326
327  // Changes representation from {output_type} to {use_rep}. The {truncation}
328  // parameter is only used for checking - if the changer cannot figure
329  // out signedness for the word32->float64 conversion, then we check that the
330  // uses truncate to word32 (so they do not care about signedness).
331  Node* GetRepresentationFor(Node* node, MachineRepresentation output_rep,
332                             Type output_type, Node* use_node,
333                             UseInfo use_info);
334  const Operator* Int32OperatorFor(IrOpcode::Value opcode);
335  const Operator* Int32OverflowOperatorFor(IrOpcode::Value opcode);
336  const Operator* Int64OperatorFor(IrOpcode::Value opcode);
337  const Operator* TaggedSignedOperatorFor(IrOpcode::Value opcode);
338  const Operator* Uint32OperatorFor(IrOpcode::Value opcode);
339  const Operator* Uint32OverflowOperatorFor(IrOpcode::Value opcode);
340  const Operator* Float64OperatorFor(IrOpcode::Value opcode);
341
342  MachineType TypeForBasePointer(const FieldAccess& access) {
343    return access.tag() != 0 ? MachineType::AnyTagged()
344                             : MachineType::Pointer();
345  }
346
347  MachineType TypeForBasePointer(const ElementAccess& access) {
348    return access.tag() != 0 ? MachineType::AnyTagged()
349                             : MachineType::Pointer();
350  }
351
352  bool verification_enabled() const { return verifier_ != nullptr; }
353
354 private:
355  TypeCache const* cache_;
356  JSGraph* jsgraph_;
357  JSHeapBroker* broker_;
358  SimplifiedLoweringVerifier* verifier_;
359
360  friend class RepresentationChangerTester;  // accesses the below fields.
361
362  bool testing_type_errors_;  // If {true}, don't abort on a type error.
363  bool type_error_;           // Set when a type error is detected.
364
365  Node* GetTaggedSignedRepresentationFor(Node* node,
366                                         MachineRepresentation output_rep,
367                                         Type output_type, Node* use_node,
368                                         UseInfo use_info);
369  Node* GetTaggedPointerRepresentationFor(Node* node,
370                                          MachineRepresentation output_rep,
371                                          Type output_type, Node* use_node,
372                                          UseInfo use_info);
373  Node* GetTaggedRepresentationFor(Node* node, MachineRepresentation output_rep,
374                                   Type output_type, Truncation truncation);
375  Node* GetFloat32RepresentationFor(Node* node,
376                                    MachineRepresentation output_rep,
377                                    Type output_type, Truncation truncation);
378  Node* GetFloat64RepresentationFor(Node* node,
379                                    MachineRepresentation output_rep,
380                                    Type output_type, Node* use_node,
381                                    UseInfo use_info);
382  Node* GetWord32RepresentationFor(Node* node, MachineRepresentation output_rep,
383                                   Type output_type, Node* use_node,
384                                   UseInfo use_info);
385  Node* GetBitRepresentationFor(Node* node, MachineRepresentation output_rep,
386                                Type output_type);
387  Node* GetWord64RepresentationFor(Node* node, MachineRepresentation output_rep,
388                                   Type output_type, Node* use_node,
389                                   UseInfo use_info);
390  Node* TypeError(Node* node, MachineRepresentation output_rep,
391                  Type output_type, MachineRepresentation use);
392  Node* MakeTruncatedInt32Constant(double value);
393  Node* InsertChangeBitToTagged(Node* node);
394  Node* InsertChangeFloat32ToFloat64(Node* node);
395  Node* InsertChangeFloat64ToInt32(Node* node);
396  Node* InsertChangeFloat64ToUint32(Node* node);
397  Node* InsertChangeInt32ToFloat64(Node* node);
398  Node* InsertChangeTaggedSignedToInt32(Node* node);
399  Node* InsertChangeTaggedToFloat64(Node* node);
400  Node* InsertChangeUint32ToFloat64(Node* node);
401  Node* InsertCheckedFloat64ToInt32(Node* node, CheckForMinusZeroMode check,
402                                    const FeedbackSource& feedback,
403                                    Node* use_node);
404  Node* InsertConversion(Node* node, const Operator* op, Node* use_node);
405  Node* InsertTruncateInt64ToInt32(Node* node);
406  Node* InsertUnconditionalDeopt(Node* node, DeoptimizeReason reason,
407                                 const FeedbackSource& feedback = {});
408  Node* InsertTypeOverrideForVerifier(const Type& type, Node* node);
409
410  JSGraph* jsgraph() const { return jsgraph_; }
411  Isolate* isolate() const;
412  Factory* factory() const { return isolate()->factory(); }
413  SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); }
414  MachineOperatorBuilder* machine() { return jsgraph()->machine(); }
415};
416
417}  // namespace compiler
418}  // namespace internal
419}  // namespace v8
420
421#endif  // V8_COMPILER_REPRESENTATION_CHANGE_H_
422