diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
commit | 881da28418d380042aa95a97f0cbd42560a64f7c (patch) | |
tree | a794dff3274695e99c651902dde93d934ea7a5af /Source/JavaScriptCore/b3/B3Value.h | |
parent | 7e104c57a70fdf551bb3d22a5d637cdcbc69dbea (diff) | |
parent | 0fcedcd17cc00d3dd44c718b3cb36c1033319671 (diff) | |
download | qtwebkit-881da28418d380042aa95a97f0cbd42560a64f7c.tar.gz |
Merge 'wip/next' into dev
Change-Id: Iff9ee5e23bb326c4371ec8ed81d56f2f05d680e9
Diffstat (limited to 'Source/JavaScriptCore/b3/B3Value.h')
-rw-r--r-- | Source/JavaScriptCore/b3/B3Value.h | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/b3/B3Value.h b/Source/JavaScriptCore/b3/B3Value.h new file mode 100644 index 000000000..981f007af --- /dev/null +++ b/Source/JavaScriptCore/b3/B3Value.h @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef B3Value_h +#define B3Value_h + +#if ENABLE(B3_JIT) + +#include "AirArg.h" +#include "B3Effects.h" +#include "B3Opcode.h" +#include "B3Origin.h" +#include "B3SparseCollection.h" +#include "B3Type.h" +#include "B3ValueKey.h" +#include <wtf/CommaPrinter.h> +#include <wtf/FastMalloc.h> +#include <wtf/Noncopyable.h> + +namespace JSC { namespace B3 { + +class BasicBlock; +class CheckValue; +class PhiChildren; +class Procedure; + +class JS_EXPORT_PRIVATE Value { + WTF_MAKE_FAST_ALLOCATED; +public: + typedef Vector<Value*, 3> AdjacencyList; + + static const char* const dumpPrefix; + + static bool accepts(Opcode) { return true; } + + virtual ~Value(); + + unsigned index() const { return m_index; } + + // Note that the opcode is immutable, except for replacing values with Identity or Nop. + Opcode opcode() const { return m_opcode; } + + Origin origin() const { return m_origin; } + void setOrigin(Origin origin) { m_origin = origin; } + + Value*& child(unsigned index) { return m_children[index]; } + Value* child(unsigned index) const { return m_children[index]; } + + Value*& lastChild() { return m_children.last(); } + Value* lastChild() const { return m_children.last(); } + + unsigned numChildren() const { return m_children.size(); } + + Type type() const { return m_type; } + void setType(Type type) { m_type = type; } + + // This is useful when lowering. Note that this is only valid for non-void values. + Air::Arg::Type airType() const { return Air::Arg::typeForB3Type(type()); } + Air::Arg::Width airWidth() const { return Air::Arg::widthForB3Type(type()); } + + AdjacencyList& children() { return m_children; } + const AdjacencyList& children() const { return m_children; } + + void replaceWithIdentity(Value*); + void replaceWithNop(); + void replaceWithPhi(); + + void dump(PrintStream&) const; + void deepDump(const Procedure*, PrintStream&) const; + + // This is how you cast Values. For example, if you want to do something provided that we have a + // ArgumentRegValue, you can do: + // + // if (ArgumentRegValue* argumentReg = value->as<ArgumentRegValue>()) { + // things + // } + // + // This will return null if this opcode() != ArgumentReg. This works because this returns nullptr + // if T::accepts(opcode()) returns false. + template<typename T> + T* as(); + template<typename T> + const T* as() const; + + // What follows are a bunch of helpers for inspecting and modifying values. Note that we have a + // bunch of different idioms for implementing such helpers. You can use virtual methods, and + // override from the various Value subclasses. You can put the method inside Value and make it + // non-virtual, and the implementation can switch on opcode. The method could be inline or not. + // If a method is specific to some Value subclass, you could put it in the subclass, or you could + // put it on Value anyway. It's fine to pick whatever feels right, and we shouldn't restrict + // ourselves to any particular idiom. + + bool isConstant() const; + bool isInteger() const; + + virtual Value* negConstant(Procedure&) const; + virtual Value* addConstant(Procedure&, int32_t other) const; + virtual Value* addConstant(Procedure&, const Value* other) const; + virtual Value* subConstant(Procedure&, const Value* other) const; + virtual Value* mulConstant(Procedure&, const Value* other) const; + virtual Value* checkAddConstant(Procedure&, const Value* other) const; + virtual Value* checkSubConstant(Procedure&, const Value* other) const; + virtual Value* checkMulConstant(Procedure&, const Value* other) const; + virtual Value* checkNegConstant(Procedure&) const; + virtual Value* divConstant(Procedure&, const Value* other) const; // This chooses ChillDiv semantics for integers. + virtual Value* modConstant(Procedure&, const Value* other) const; // This chooses ChillMod semantics. + virtual Value* bitAndConstant(Procedure&, const Value* other) const; + virtual Value* bitOrConstant(Procedure&, const Value* other) const; + virtual Value* bitXorConstant(Procedure&, const Value* other) const; + virtual Value* shlConstant(Procedure&, const Value* other) const; + virtual Value* sShrConstant(Procedure&, const Value* other) const; + virtual Value* zShrConstant(Procedure&, const Value* other) const; + virtual Value* bitwiseCastConstant(Procedure&) const; + virtual Value* doubleToFloatConstant(Procedure&) const; + virtual Value* floatToDoubleConstant(Procedure&) const; + virtual Value* absConstant(Procedure&) const; + virtual Value* ceilConstant(Procedure&) const; + virtual Value* floorConstant(Procedure&) const; + virtual Value* sqrtConstant(Procedure&) const; + + virtual TriState equalConstant(const Value* other) const; + virtual TriState notEqualConstant(const Value* other) const; + virtual TriState lessThanConstant(const Value* other) const; + virtual TriState greaterThanConstant(const Value* other) const; + virtual TriState lessEqualConstant(const Value* other) const; + virtual TriState greaterEqualConstant(const Value* other) const; + virtual TriState aboveConstant(const Value* other) const; + virtual TriState belowConstant(const Value* other) const; + virtual TriState aboveEqualConstant(const Value* other) const; + virtual TriState belowEqualConstant(const Value* other) const; + virtual TriState equalOrUnorderedConstant(const Value* other) const; + + // If the value is a comparison then this returns the inverted form of that comparison, if + // possible. It can be impossible for double comparisons, where for example LessThan and + // GreaterEqual behave differently. If this returns a value, it is a new value, which must be + // either inserted into some block or deleted. + Value* invertedCompare(Procedure&) const; + + bool hasInt32() const; + int32_t asInt32() const; + bool isInt32(int32_t) const; + + bool hasInt64() const; + int64_t asInt64() const; + bool isInt64(int64_t) const; + + bool hasInt() const; + int64_t asInt() const; + bool isInt(int64_t value) const; + + bool hasIntPtr() const; + intptr_t asIntPtr() const; + bool isIntPtr(intptr_t) const; + + bool hasDouble() const; + double asDouble() const; + bool isEqualToDouble(double) const; // We say "isEqualToDouble" because "isDouble" would be a bit equality. + + bool hasFloat() const; + float asFloat() const; + + bool hasNumber() const; + template<typename T> bool representableAs() const; + template<typename T> T asNumber() const; + + // Booleans in B3 are Const32(0) or Const32(1). So this is true if the type is Int32 and the only + // possible return values are 0 or 1. It's OK for this method to conservatively return false. + bool returnsBool() const; + + bool isNegativeZero() const; + + bool isRounded() const; + + TriState asTriState() const; + bool isLikeZero() const { return asTriState() == FalseTriState; } + bool isLikeNonZero() const { return asTriState() == TrueTriState; } + + Effects effects() const; + + // This returns a ValueKey that describes that this Value returns when it executes. Returns an + // empty ValueKey if this Value is impure. Note that an operation that returns Void could still + // have a non-empty ValueKey. This happens for example with Check operations. + ValueKey key() const; + + // Makes sure that none of the children are Identity's. If a child points to Identity, this will + // repoint it at the Identity's child. For simplicity, this will follow arbitrarily long chains + // of Identity's. + void performSubstitution(); + + // Walk the ancestors of this value (i.e. the graph of things it transitively uses). This + // either walks phis or not, depending on whether PhiChildren is null. Your callback gets + // called with the signature: + // + // (Value*) -> WalkStatus + enum WalkStatus { + Continue, + IgnoreChildren, + Stop + }; + template<typename Functor> + void walk(const Functor& functor, PhiChildren* = nullptr); + +protected: + virtual Value* cloneImpl() const; + + virtual void dumpChildren(CommaPrinter&, PrintStream&) const; + virtual void dumpMeta(CommaPrinter&, PrintStream&) const; + +private: + friend class Procedure; + friend class SparseCollection<Value>; + + // Checks that this opcode is valid for use with B3::Value. +#if ASSERT_DISABLED + static void checkOpcode(Opcode) { } +#else + static void checkOpcode(Opcode); +#endif + +protected: + enum CheckedOpcodeTag { CheckedOpcode }; + + Value(const Value&) = default; + Value& operator=(const Value&) = default; + + // Instantiate values via Procedure. + // This form requires specifying the type explicitly: + template<typename... Arguments> + explicit Value(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, Value* firstChild, Arguments... arguments) + : m_opcode(opcode) + , m_type(type) + , m_origin(origin) + , m_children{ firstChild, arguments... } + { + } + // This form is for specifying the type explicitly when the opcode has no children: + explicit Value(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin) + : m_opcode(opcode) + , m_type(type) + , m_origin(origin) + { + } + // This form is for those opcodes that can infer their type from the opcode and first child: + template<typename... Arguments> + explicit Value(CheckedOpcodeTag, Opcode opcode, Origin origin, Value* firstChild) + : m_opcode(opcode) + , m_type(typeFor(opcode, firstChild)) + , m_origin(origin) + , m_children{ firstChild } + { + } + // This form is for those opcodes that can infer their type from the opcode and first and second child: + template<typename... Arguments> + explicit Value(CheckedOpcodeTag, Opcode opcode, Origin origin, Value* firstChild, Value* secondChild, Arguments... arguments) + : m_opcode(opcode) + , m_type(typeFor(opcode, firstChild, secondChild)) + , m_origin(origin) + , m_children{ firstChild, secondChild, arguments... } + { + } + // This form is for those opcodes that can infer their type from the opcode alone, and that don't + // take any arguments: + explicit Value(CheckedOpcodeTag, Opcode opcode, Origin origin) + : m_opcode(opcode) + , m_type(typeFor(opcode, nullptr)) + , m_origin(origin) + { + } + // Use this form for varargs. + explicit Value(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, const AdjacencyList& children) + : m_opcode(opcode) + , m_type(type) + , m_origin(origin) + , m_children(children) + { + } + explicit Value(CheckedOpcodeTag, Opcode opcode, Type type, Origin origin, AdjacencyList&& children) + : m_opcode(opcode) + , m_type(type) + , m_origin(origin) + , m_children(WTFMove(children)) + { + } + + // This is the constructor you end up actually calling, if you're instantiating Value + // directly. + template<typename... Arguments> + explicit Value(Opcode opcode, Arguments&&... arguments) + : Value(CheckedOpcode, opcode, std::forward<Arguments>(arguments)...) + { + checkOpcode(opcode); + } + +private: + friend class CheckValue; // CheckValue::convertToAdd() modifies m_opcode. + + static Type typeFor(Opcode, Value* firstChild, Value* secondChild = nullptr); + + // This group of fields is arranged to fit in 64 bits. +protected: + unsigned m_index { UINT_MAX }; +private: + Opcode m_opcode; + Type m_type; + + Origin m_origin; + AdjacencyList m_children; + +public: + BasicBlock* owner { nullptr }; // computed by Procedure::resetValueOwners(). +}; + +class DeepValueDump { +public: + DeepValueDump(const Procedure* proc, const Value* value) + : m_proc(proc) + , m_value(value) + { + } + + void dump(PrintStream& out) const + { + if (m_value) + m_value->deepDump(m_proc, out); + else + out.print("<null>"); + } + +private: + const Procedure* m_proc; + const Value* m_value; +}; + +inline DeepValueDump deepDump(const Procedure& proc, const Value* value) +{ + return DeepValueDump(&proc, value); +} +inline DeepValueDump deepDump(const Value* value) +{ + return DeepValueDump(nullptr, value); +} + +} } // namespace JSC::B3 + +#endif // ENABLE(B3_JIT) + +#endif // B3Value_h + |