// Copyright 2013 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_COMPILER_OPERATOR_H_ #define V8_COMPILER_OPERATOR_H_ #include "src/base/flags.h" #include "src/ostreams.h" #include "src/unique.h" namespace v8 { namespace internal { namespace compiler { // An operator represents description of the "computation" of a node in the // compiler IR. A computation takes values (i.e. data) as input and produces // zero or more values as output. The side-effects of a computation must be // captured by additional control and data dependencies which are part of the // IR graph. // Operators are immutable and describe the statically-known parts of a // computation. Thus they can be safely shared by many different nodes in the // IR graph, or even globally between graphs. Operators can have "static // parameters" which are compile-time constant parameters to the operator, such // as the name for a named field access, the ID of a runtime function, etc. // Static parameters are private to the operator and only semantically // meaningful to the operator itself. class Operator : public ZoneObject { public: typedef uint8_t Opcode; // Properties inform the operator-independent optimizer about legal // transformations for nodes that have this operator. enum Property { kNoProperties = 0, kReducible = 1 << 0, // Participates in strength reduction. kCommutative = 1 << 1, // OP(a, b) == OP(b, a) for all inputs. kAssociative = 1 << 2, // OP(a, OP(b,c)) == OP(OP(a,b), c) for all inputs. kIdempotent = 1 << 3, // OP(a); OP(a) == OP(a). kNoRead = 1 << 4, // Has no scheduling dependency on Effects kNoWrite = 1 << 5, // Does not modify any Effects and thereby // create new scheduling dependencies. kNoThrow = 1 << 6, // Can never generate an exception. kFoldable = kNoRead | kNoWrite, kEliminatable = kNoWrite | kNoThrow, kPure = kNoRead | kNoWrite | kNoThrow | kIdempotent }; typedef base::Flags<Property, uint8_t> Properties; Operator(Opcode opcode, Properties properties, const char* mnemonic) : opcode_(opcode), properties_(properties), mnemonic_(mnemonic) {} virtual ~Operator(); // A small integer unique to all instances of a particular kind of operator, // useful for quick matching for specific kinds of operators. For fast access // the opcode is stored directly in the operator object. Opcode opcode() const { return opcode_; } // Returns a constant string representing the mnemonic of the operator, // without the static parameters. Useful for debugging. const char* mnemonic() const { return mnemonic_; } // Check if this operator equals another operator. Equivalent operators can // be merged, and nodes with equivalent operators and equivalent inputs // can be merged. virtual bool Equals(const Operator* other) const = 0; // Compute a hashcode to speed up equivalence-set checking. // Equal operators should always have equal hashcodes, and unequal operators // should have unequal hashcodes with high probability. virtual int HashCode() const = 0; // Check whether this operator has the given property. bool HasProperty(Property property) const { return (properties() & property) == property; } // Number of data inputs to the operator, for verifying graph structure. virtual int InputCount() const = 0; // Number of data outputs from the operator, for verifying graph structure. virtual int OutputCount() const = 0; Properties properties() const { return properties_; } // TODO(titzer): API for input and output types, for typechecking graph. protected: // Print the full operator into the given stream, including any // static parameters. Useful for debugging and visualizing the IR. virtual OStream& PrintTo(OStream& os) const = 0; // NOLINT friend OStream& operator<<(OStream& os, const Operator& op); private: Opcode opcode_; Properties properties_; const char* mnemonic_; DISALLOW_COPY_AND_ASSIGN(Operator); }; DEFINE_OPERATORS_FOR_FLAGS(Operator::Properties) OStream& operator<<(OStream& os, const Operator& op); // An implementation of Operator that has no static parameters. Such operators // have just a name, an opcode, and a fixed number of inputs and outputs. // They can represented by singletons and shared globally. class SimpleOperator : public Operator { public: SimpleOperator(Opcode opcode, Properties properties, int input_count, int output_count, const char* mnemonic); ~SimpleOperator(); virtual bool Equals(const Operator* that) const FINAL { return opcode() == that->opcode(); } virtual int HashCode() const FINAL { return opcode(); } virtual int InputCount() const FINAL { return input_count_; } virtual int OutputCount() const FINAL { return output_count_; } private: virtual OStream& PrintTo(OStream& os) const FINAL { // NOLINT return os << mnemonic(); } int input_count_; int output_count_; DISALLOW_COPY_AND_ASSIGN(SimpleOperator); }; // Template specialization implements a kind of type class for dealing with the // static parameters of Operator1 automatically. template <typename T> struct StaticParameterTraits { static OStream& PrintTo(OStream& os, T val) { // NOLINT return os << "??"; } static int HashCode(T a) { return 0; } static bool Equals(T a, T b) { return false; // Not every T has a ==. By default, be conservative. } }; // Specialization for static parameters of type {int}. template <> struct StaticParameterTraits<int> { static OStream& PrintTo(OStream& os, int val) { // NOLINT return os << val; } static int HashCode(int a) { return a; } static bool Equals(int a, int b) { return a == b; } }; // Specialization for static parameters of type {double}. template <> struct StaticParameterTraits<double> { static OStream& PrintTo(OStream& os, double val) { // NOLINT return os << val; } static int HashCode(double a) { return static_cast<int>(bit_cast<int64_t>(a)); } static bool Equals(double a, double b) { return bit_cast<int64_t>(a) == bit_cast<int64_t>(b); } }; // Specialization for static parameters of type {Unique<Object>}. template <> struct StaticParameterTraits<Unique<Object> > { static OStream& PrintTo(OStream& os, Unique<Object> val) { // NOLINT return os << Brief(*val.handle()); } static int HashCode(Unique<Object> a) { return static_cast<int>(a.Hashcode()); } static bool Equals(Unique<Object> a, Unique<Object> b) { return a == b; } }; // Specialization for static parameters of type {Unique<Name>}. template <> struct StaticParameterTraits<Unique<Name> > { static OStream& PrintTo(OStream& os, Unique<Name> val) { // NOLINT return os << Brief(*val.handle()); } static int HashCode(Unique<Name> a) { return static_cast<int>(a.Hashcode()); } static bool Equals(Unique<Name> a, Unique<Name> b) { return a == b; } }; #if DEBUG // Specialization for static parameters of type {Handle<Object>} to prevent any // direct usage of Handles in constants. template <> struct StaticParameterTraits<Handle<Object> > { static OStream& PrintTo(OStream& os, Handle<Object> val) { // NOLINT UNREACHABLE(); // Should use Unique<Object> instead return os; } static int HashCode(Handle<Object> a) { UNREACHABLE(); // Should use Unique<Object> instead return 0; } static bool Equals(Handle<Object> a, Handle<Object> b) { UNREACHABLE(); // Should use Unique<Object> instead return false; } }; #endif // A templatized implementation of Operator that has one static parameter of // type {T}. If a specialization of StaticParameterTraits<{T}> exists, then // operators of this kind can automatically be hashed, compared, and printed. template <typename T> class Operator1 : public Operator { public: Operator1(Opcode opcode, Properties properties, int input_count, int output_count, const char* mnemonic, T parameter) : Operator(opcode, properties, mnemonic), input_count_(input_count), output_count_(output_count), parameter_(parameter) {} const T& parameter() const { return parameter_; } virtual bool Equals(const Operator* other) const OVERRIDE { if (opcode() != other->opcode()) return false; const Operator1<T>* that = static_cast<const Operator1<T>*>(other); return StaticParameterTraits<T>::Equals(this->parameter_, that->parameter_); } virtual int HashCode() const OVERRIDE { return opcode() + 33 * StaticParameterTraits<T>::HashCode(this->parameter_); } virtual int InputCount() const OVERRIDE { return input_count_; } virtual int OutputCount() const OVERRIDE { return output_count_; } virtual OStream& PrintParameter(OStream& os) const { // NOLINT return StaticParameterTraits<T>::PrintTo(os << "[", parameter_) << "]"; } protected: virtual OStream& PrintTo(OStream& os) const FINAL { // NOLINT return PrintParameter(os << mnemonic()); } private: int input_count_; int output_count_; T parameter_; }; // Helper to extract parameters from Operator1<*> operator. template <typename T> static inline const T& OpParameter(const Operator* op) { return reinterpret_cast<const Operator1<T>*>(op)->parameter(); } } // namespace compiler } // namespace internal } // namespace v8 #endif // V8_COMPILER_OPERATOR_H_