| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2013 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_OPERATOR_H_ |
| 6 #define V8_COMPILER_OPERATOR_H_ |
| 7 |
| 8 #include "src/v8.h" |
| 9 |
| 10 #include "src/assembler.h" |
| 11 #include "src/ostreams.h" |
| 12 #include "src/unique.h" |
| 13 |
| 14 namespace v8 { |
| 15 namespace internal { |
| 16 namespace compiler { |
| 17 |
| 18 // An operator represents description of the "computation" of a node in the |
| 19 // compiler IR. A computation takes values (i.e. data) as input and produces |
| 20 // zero or more values as output. The side-effects of a computation must be |
| 21 // captured by additional control and data dependencies which are part of the |
| 22 // IR graph. |
| 23 // Operators are immutable and describe the statically-known parts of a |
| 24 // computation. Thus they can be safely shared by many different nodes in the |
| 25 // IR graph, or even globally between graphs. Operators can have "static |
| 26 // parameters" which are compile-time constant parameters to the operator, such |
| 27 // as the name for a named field access, the ID of a runtime function, etc. |
| 28 // Static parameters are private to the operator and only semantically |
| 29 // meaningful to the operator itself. |
| 30 class Operator : public ZoneObject { |
| 31 public: |
| 32 Operator(uint8_t opcode, uint16_t properties) |
| 33 : opcode_(opcode), properties_(properties) {} |
| 34 virtual ~Operator() {} |
| 35 |
| 36 // Properties inform the operator-independent optimizer about legal |
| 37 // transformations for nodes that have this operator. |
| 38 enum Property { |
| 39 kNoProperties = 0, |
| 40 kReducible = 1 << 0, // Participates in strength reduction. |
| 41 kCommutative = 1 << 1, // OP(a, b) == OP(b, a) for all inputs. |
| 42 kAssociative = 1 << 2, // OP(a, OP(b,c)) == OP(OP(a,b), c) for all inputs. |
| 43 kIdempotent = 1 << 3, // OP(a); OP(a) == OP(a). |
| 44 |
| 45 kNoRead = 1 << 4, // Has no scheduling dependency on Effects |
| 46 kNoWrite = 1 << 5, // Does not modify any Effects and thereby |
| 47 // create new scheduling dependencies. |
| 48 kNoThrow = 1 << 6, // Can never generate an exception. |
| 49 |
| 50 kFoldable = kNoRead | kNoWrite, |
| 51 kEliminatable = kNoWrite | kNoThrow, |
| 52 kPure = kNoRead | kNoWrite | kNoThrow | kIdempotent |
| 53 }; |
| 54 |
| 55 // A small integer unique to all instances of a particular kind of operator, |
| 56 // useful for quick matching for specific kinds of operators. For fast access |
| 57 // the opcode is stored directly in the operator object. |
| 58 inline uint8_t opcode() const { return opcode_; } |
| 59 |
| 60 // Returns a constant string representing the mnemonic of the operator, |
| 61 // without the static parameters. Useful for debugging. |
| 62 virtual const char* mnemonic() = 0; |
| 63 |
| 64 // Check if this operator equals another operator. Equivalent operators can |
| 65 // be merged, and nodes with equivalent operators and equivalent inputs |
| 66 // can be merged. |
| 67 virtual bool Equals(Operator* other) = 0; |
| 68 |
| 69 // Compute a hashcode to speed up equivalence-set checking. |
| 70 // Equal operators should always have equal hashcodes, and unequal operators |
| 71 // should have unequal hashcodes with high probability. |
| 72 virtual int HashCode() = 0; |
| 73 |
| 74 // Check whether this operator has the given property. |
| 75 inline bool HasProperty(Property property) const { |
| 76 return (properties_ & static_cast<int>(property)) == property; |
| 77 } |
| 78 |
| 79 // Number of data inputs to the operator, for verifying graph structure. |
| 80 virtual int InputCount() = 0; |
| 81 |
| 82 // Number of data outputs from the operator, for verifying graph structure. |
| 83 virtual int OutputCount() = 0; |
| 84 |
| 85 inline Property properties() { return static_cast<Property>(properties_); } |
| 86 |
| 87 // TODO(titzer): API for input and output types, for typechecking graph. |
| 88 private: |
| 89 // Print the full operator into the given stream, including any |
| 90 // static parameters. Useful for debugging and visualizing the IR. |
| 91 virtual OStream& PrintTo(OStream& os) const = 0; // NOLINT |
| 92 friend OStream& operator<<(OStream& os, const Operator& op); |
| 93 |
| 94 uint8_t opcode_; |
| 95 uint16_t properties_; |
| 96 }; |
| 97 |
| 98 OStream& operator<<(OStream& os, const Operator& op); |
| 99 |
| 100 // An implementation of Operator that has no static parameters. Such operators |
| 101 // have just a name, an opcode, and a fixed number of inputs and outputs. |
| 102 // They can represented by singletons and shared globally. |
| 103 class SimpleOperator : public Operator { |
| 104 public: |
| 105 SimpleOperator(uint8_t opcode, |
| 106 uint16_t properties, |
| 107 int input_count, |
| 108 int output_count, |
| 109 const char* mnemonic) |
| 110 : Operator(opcode, properties), |
| 111 input_count_(input_count), |
| 112 output_count_(output_count), |
| 113 mnemonic_(mnemonic) { } |
| 114 |
| 115 virtual const char* mnemonic() { return mnemonic_; } |
| 116 virtual bool Equals(Operator* that) { return opcode() == that->opcode(); } |
| 117 virtual int HashCode() { return opcode(); } |
| 118 virtual int InputCount() { return input_count_; } |
| 119 virtual int OutputCount() { return output_count_; } |
| 120 |
| 121 private: |
| 122 virtual OStream& PrintTo(OStream& os) const { // NOLINT |
| 123 return os << mnemonic_; |
| 124 } |
| 125 |
| 126 int input_count_; |
| 127 int output_count_; |
| 128 const char* mnemonic_; |
| 129 }; |
| 130 |
| 131 // Template specialization implements a kind of type class for dealing with the |
| 132 // static parameters of Operator1 automatically. |
| 133 template <typename T> |
| 134 struct StaticParameterTraits { |
| 135 static OStream& PrintTo(OStream& os, T val) { // NOLINT |
| 136 return os << "??"; |
| 137 } |
| 138 static int HashCode(T a) { return 0; } |
| 139 static bool Equals(T a, T b) { |
| 140 return false; // Not every T has a ==. By default, be conservative. |
| 141 } |
| 142 }; |
| 143 |
| 144 template <> |
| 145 struct StaticParameterTraits<ExternalReference> { |
| 146 static OStream& PrintTo(OStream& os, ExternalReference val) { // NOLINT |
| 147 os << val.address(); |
| 148 const Runtime::Function* function = |
| 149 Runtime::FunctionForEntry(val.address()); |
| 150 if (function != NULL) { |
| 151 os << " <" << function->name << ".entry>"; |
| 152 } |
| 153 return os; |
| 154 } |
| 155 static int HashCode(ExternalReference a) { |
| 156 return reinterpret_cast<intptr_t>(a.address()) & 0xFFFFFFFF; |
| 157 } |
| 158 static bool Equals(ExternalReference a, ExternalReference b) { |
| 159 return a == b; |
| 160 } |
| 161 }; |
| 162 |
| 163 // Specialization for static parameters of type {int}. |
| 164 template <> |
| 165 struct StaticParameterTraits<int> { |
| 166 static OStream& PrintTo(OStream& os, int val) { // NOLINT |
| 167 return os << val; |
| 168 } |
| 169 static int HashCode(int a) { return a; } |
| 170 static bool Equals(int a, int b) { |
| 171 return a == b; |
| 172 } |
| 173 }; |
| 174 |
| 175 // Specialization for static parameters of type {double}. |
| 176 template <> |
| 177 struct StaticParameterTraits<double> { |
| 178 static OStream& PrintTo(OStream& os, double val) { // NOLINT |
| 179 return os << val; |
| 180 } |
| 181 static int HashCode(double a) { |
| 182 return static_cast<int>(BitCast<int64_t>(a)); |
| 183 } |
| 184 static bool Equals(double a, double b) { |
| 185 return BitCast<int64_t>(a) == BitCast<int64_t>(b); |
| 186 } |
| 187 }; |
| 188 |
| 189 // Specialization for static parameters of type {PrintableUnique<Object>}. |
| 190 template <> |
| 191 struct StaticParameterTraits<PrintableUnique<Object> > { |
| 192 static OStream& PrintTo(OStream& os, PrintableUnique<Object> val) { // NOLINT |
| 193 return os << val.string(); |
| 194 } |
| 195 static int HashCode(PrintableUnique<Object> a) { return a.Hashcode(); } |
| 196 static bool Equals(PrintableUnique<Object> a, PrintableUnique<Object> b) { |
| 197 return a == b; |
| 198 } |
| 199 }; |
| 200 |
| 201 // Specialization for static parameters of type {PrintableUnique<Name>}. |
| 202 template <> |
| 203 struct StaticParameterTraits<PrintableUnique<Name> > { |
| 204 static OStream& PrintTo(OStream& os, PrintableUnique<Name> val) { // NOLINT |
| 205 return os << val.string(); |
| 206 } |
| 207 static int HashCode(PrintableUnique<Name> a) { return a.Hashcode(); } |
| 208 static bool Equals(PrintableUnique<Name> a, PrintableUnique<Name> b) { |
| 209 return a == b; |
| 210 } |
| 211 }; |
| 212 |
| 213 #if DEBUG |
| 214 // Specialization for static parameters of type {Handle<Object>} to prevent any |
| 215 // direct usage of Handles in constants. |
| 216 template <> |
| 217 struct StaticParameterTraits<Handle<Object> > { |
| 218 static OStream& PrintTo(OStream& os, Handle<Object> val) { // NOLINT |
| 219 UNREACHABLE(); // Should use PrintableUnique<Object> instead |
| 220 return os; |
| 221 } |
| 222 static int HashCode(Handle<Object> a) { |
| 223 UNREACHABLE(); // Should use PrintableUnique<Object> instead |
| 224 return 0; |
| 225 } |
| 226 static bool Equals(Handle<Object> a, Handle<Object> b) { |
| 227 UNREACHABLE(); // Should use PrintableUnique<Object> instead |
| 228 return false; |
| 229 } |
| 230 }; |
| 231 #endif |
| 232 |
| 233 // A templatized implementation of Operator that has one static parameter of |
| 234 // type {T}. If a specialization of StaticParameterTraits<{T}> exists, then |
| 235 // operators of this kind can automatically be hashed, compared, and printed. |
| 236 template <typename T> |
| 237 class Operator1 : public Operator { |
| 238 public: |
| 239 Operator1(uint8_t opcode, |
| 240 uint16_t properties, |
| 241 int input_count, |
| 242 int output_count, |
| 243 const char* mnemonic, |
| 244 T parameter) |
| 245 : Operator(opcode, properties), |
| 246 input_count_(input_count), |
| 247 output_count_(output_count), |
| 248 mnemonic_(mnemonic), |
| 249 parameter_(parameter) {} |
| 250 |
| 251 const T& parameter() const { return parameter_; } |
| 252 |
| 253 virtual const char* mnemonic() { return mnemonic_; } |
| 254 virtual bool Equals(Operator* other) { |
| 255 if (opcode() != other->opcode()) return false; |
| 256 Operator1<T>* that = static_cast<Operator1<T>* >(other); |
| 257 T temp1 = this->parameter_; |
| 258 T temp2 = that->parameter_; |
| 259 return StaticParameterTraits<T>::Equals(temp1, temp2); |
| 260 } |
| 261 virtual int HashCode() { |
| 262 return opcode() + 33 * StaticParameterTraits<T>::HashCode(this->parameter_); |
| 263 } |
| 264 virtual int InputCount() { return input_count_; } |
| 265 virtual int OutputCount() { return output_count_; } |
| 266 virtual OStream& PrintParameter(OStream& os) const { // NOLINT |
| 267 return StaticParameterTraits<T>::PrintTo(os << "[", parameter_) << "]"; |
| 268 } |
| 269 |
| 270 private: |
| 271 virtual OStream& PrintTo(OStream& os) const { // NOLINT |
| 272 return PrintParameter(os << mnemonic_); |
| 273 } |
| 274 |
| 275 int input_count_; |
| 276 int output_count_; |
| 277 const char* mnemonic_; |
| 278 T parameter_; |
| 279 }; |
| 280 |
| 281 // Type definitions for operators with specific types of parameters. |
| 282 typedef Operator1<PrintableUnique<Name> > NameOperator; |
| 283 |
| 284 } } } // namespace v8::internal::compiler |
| 285 |
| 286 #endif // V8_COMPILER_OPERATOR_H_ |
| OLD | NEW |