|  | /* | 
|  | * Copyright (C) 2015-2018 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. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #if ENABLE(B3_JIT) | 
|  |  | 
|  | #include "B3ConstrainedValue.h" | 
|  | #include "B3Value.h" | 
|  | #include "B3ValueRep.h" | 
|  | #include "CCallHelpers.h" | 
|  | #include "RegisterSet.h" | 
|  | #include <wtf/SharedTask.h> | 
|  |  | 
|  | namespace JSC { namespace B3 { | 
|  |  | 
|  | class StackmapGenerationParams; | 
|  |  | 
|  | typedef void StackmapGeneratorFunction(CCallHelpers&, const StackmapGenerationParams&); | 
|  | typedef SharedTask<StackmapGeneratorFunction> StackmapGenerator; | 
|  |  | 
|  | class JS_EXPORT_PRIVATE StackmapValue : public Value { | 
|  | public: | 
|  | static bool accepts(Kind kind) | 
|  | { | 
|  | // This needs to include opcodes of all subclasses. | 
|  | switch (kind.opcode()) { | 
|  | case CheckAdd: | 
|  | case CheckSub: | 
|  | case CheckMul: | 
|  | case Check: | 
|  | case Patchpoint: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | ~StackmapValue(); | 
|  |  | 
|  | // Use this to add children. | 
|  | void append(const ConstrainedValue& value) | 
|  | { | 
|  | ASSERT(value.value()->type().isNumeric()); | 
|  | append(value.value(), value.rep()); | 
|  | } | 
|  |  | 
|  | void append(Value*, const ValueRep&); | 
|  |  | 
|  | template<typename VectorType> | 
|  | void appendVector(const VectorType& vector) | 
|  | { | 
|  | for (const auto& value : vector) | 
|  | append(value); | 
|  | } | 
|  |  | 
|  | // Helper for appending a bunch of values with some ValueRep. | 
|  | template<typename VectorType> | 
|  | void appendVectorWithRep(const VectorType& vector, const ValueRep& rep) | 
|  | { | 
|  | for (Value* value : vector) | 
|  | append(value, rep); | 
|  | } | 
|  |  | 
|  | // Helper for appending cold any's. This often used by clients to implement OSR. | 
|  | template<typename VectorType> | 
|  | void appendColdAnys(const VectorType& vector) | 
|  | { | 
|  | appendVectorWithRep(vector, ValueRep::ColdAny); | 
|  | } | 
|  | template<typename VectorType> | 
|  | void appendLateColdAnys(const VectorType& vector) | 
|  | { | 
|  | appendVectorWithRep(vector, ValueRep::LateColdAny); | 
|  | } | 
|  |  | 
|  | // This is a helper for something you might do a lot of: append a value that should be constrained | 
|  | // to SomeRegister. | 
|  | void appendSomeRegister(Value*); | 
|  | void appendSomeRegisterWithClobber(Value*); | 
|  |  | 
|  | const Vector<ValueRep>& reps() const { return m_reps; } | 
|  |  | 
|  | // Stackmaps allow you to specify that the operation may clobber some registers. Clobbering a register | 
|  | // means that the operation appears to store a value into the register, but the compiler doesn't | 
|  | // assume to know anything about what kind of value might have been stored. In B3's model of | 
|  | // execution, registers are read or written at instruction boundaries rather than inside the | 
|  | // instructions themselves. A register could be read or written immediately before the instruction | 
|  | // executes, or immediately after. Note that at a boundary between instruction A and instruction B we | 
|  | // simultaneously look at what A does after it executes and what B does before it executes. This is | 
|  | // because when the compiler considers what happens to registers, it views the boundary between two | 
|  | // instructions as a kind of atomic point where the late effects of A happen at the same time as the | 
|  | // early effects of B. | 
|  | // | 
|  | // The compiler views a stackmap as a single instruction, even though of course the stackmap may be | 
|  | // composed of any number of instructions (if it's a Patchpoint). You can claim that a stackmap value | 
|  | // clobbers a set of registers before the stackmap's instruction or after. Clobbering before is called | 
|  | // early clobber, while clobbering after is called late clobber. | 
|  | // | 
|  | // This is quite flexible but it has its limitations. Any register listed as an early clobber will | 
|  | // interfere with all uses of the stackmap. Any register listed as a late clobber will interfere with | 
|  | // all defs of the stackmap (i.e. the result). This means that it's currently not possible to claim | 
|  | // to clobber a register while still allowing that register to be used for both an input and an output | 
|  | // of the instruction. It just so happens that B3's sole client (the FTL) currently never wants to | 
|  | // convey such a constraint, but it will want it eventually (FIXME: | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=151823). | 
|  | // | 
|  | // Note that a common use case of early clobber sets is to indicate that this is the set of registers | 
|  | // that shall not be used for inputs to the value. But B3 supports two different ways of specifying | 
|  | // this, the other being LateUse in combination with late clobber (not yet available to stackmaps | 
|  | // directly, FIXME: https://bugs.webkit.org/show_bug.cgi?id=151335). A late use makes the use of that | 
|  | // value appear to happen after the instruction. This means that a late use cannot use the same | 
|  | // register as the result and it cannot use the same register as either early or late clobbered | 
|  | // registers. Late uses are usually a better way of saying that a clobbered register cannot be used | 
|  | // for an input. Early clobber means that some register(s) interfere with *all* inputs, while LateUse | 
|  | // means that some value interferes with whatever is live after the instruction. Below is a list of | 
|  | // examples of how the FTL can handle its various kinds of scenarios using a combination of early | 
|  | // clobber, late clobber, and late use. These examples are for X86_64, w.l.o.g. | 
|  | // | 
|  | // Basic ById patchpoint: Early and late clobber of r11. Early clobber prevents any inputs from using | 
|  | // r11 since that would mess with the MacroAssembler's assumptions when we | 
|  | // AllowMacroScratchRegisterUsage. Late clobber tells B3 that the patchpoint may overwrite r11. | 
|  | // | 
|  | // ById patchpoint in a try block with some live state: This might throw an exception after already | 
|  | // assigning to the result. So, this should LateUse all stackmap values to ensure that the stackmap | 
|  | // values don't interfere with the result. Note that we do not LateUse the non-OSR inputs of the ById | 
|  | // since LateUse implies that the use is cold: the register allocator will assume that the use is not | 
|  | // important for the critical path. Also, early and late clobber of r11. | 
|  | // | 
|  | // Basic ByIdFlush patchpoint: We could do Flush the same way we did it with LLVM: ignore it and let | 
|  | // PolymorphicAccess figure it out. Or, we could add internal clobber support (FIXME: | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=151823). Or, we could do it by early clobbering r11, late | 
|  | // clobbering all volatile registers, and constraining the result to some register. Or, we could do | 
|  | // that but leave the result constrained to SomeRegister, which will cause it to use a callee-save | 
|  | // register. Internal clobber support would allow us to use SomeRegister while getting the result into | 
|  | // a volatile register. | 
|  | // | 
|  | // ByIdFlush patchpoint in a try block with some live state: LateUse all for-OSR stackmap values, | 
|  | // early clobber of r11 to prevent the other inputs from using r11, and late clobber of all volatile | 
|  | // registers to make way for the call. To handle the result, we could do any of what is listed in the | 
|  | // previous paragraph. | 
|  | // | 
|  | // Basic JS call: Force all non-OSR inputs into specific locations (register, stack, whatever). | 
|  | // All volatile registers are late-clobbered. The output is constrained to a register as well. | 
|  | // | 
|  | // JS call in a try block with some live state: LateUse all for-OSR stackmap values, fully constrain | 
|  | // all non-OSR inputs and the result, and late clobber all volatile registers. | 
|  | // | 
|  | // JS tail call: Pass all inputs as a warm variant of Any (FIXME: | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=151811). | 
|  | // | 
|  | // Note that we cannot yet do all of these things because although Air already supports all of these | 
|  | // various forms of uses (LateUse and warm unconstrained use), B3 doesn't yet expose all of it. The | 
|  | // bugs are: | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=151335 (LateUse) | 
|  | // https://bugs.webkit.org/show_bug.cgi?id=151811 (warm Any) | 
|  | void clobberEarly(const RegisterSet& set) | 
|  | { | 
|  | m_earlyClobbered.merge(set); | 
|  | } | 
|  |  | 
|  | void clobberLate(const RegisterSet& set) | 
|  | { | 
|  | m_lateClobbered.merge(set); | 
|  | } | 
|  |  | 
|  | void clobber(const RegisterSet& set) | 
|  | { | 
|  | clobberEarly(set); | 
|  | clobberLate(set); | 
|  | } | 
|  |  | 
|  | RegisterSet& earlyClobbered() { return m_earlyClobbered; } | 
|  | RegisterSet& lateClobbered() { return m_lateClobbered; } | 
|  | const RegisterSet& earlyClobbered() const { return m_earlyClobbered; } | 
|  | const RegisterSet& lateClobbered() const { return m_lateClobbered; } | 
|  |  | 
|  | void setGenerator(RefPtr<StackmapGenerator> generator) | 
|  | { | 
|  | m_generator = generator; | 
|  | } | 
|  |  | 
|  | template<typename Functor> | 
|  | void setGenerator(const Functor& functor) | 
|  | { | 
|  | m_generator = createSharedTask<StackmapGeneratorFunction>(functor); | 
|  | } | 
|  |  | 
|  | RefPtr<StackmapGenerator> generator() const { return m_generator; } | 
|  |  | 
|  | ConstrainedValue constrainedChild(unsigned index) const | 
|  | { | 
|  | return ConstrainedValue(child(index), index < m_reps.size() ? m_reps[index] : ValueRep::ColdAny); | 
|  | } | 
|  |  | 
|  | void setConstrainedChild(unsigned index, const ConstrainedValue&); | 
|  |  | 
|  | void setConstraint(unsigned index, const ValueRep&); | 
|  |  | 
|  | class ConstrainedValueCollection { | 
|  | public: | 
|  |  | 
|  | ConstrainedValueCollection(const StackmapValue& value) | 
|  | : m_value(value) | 
|  | { | 
|  | } | 
|  |  | 
|  | unsigned size() const { return m_value.numChildren(); } | 
|  |  | 
|  | ConstrainedValue at(unsigned index) const { return m_value.constrainedChild(index); } | 
|  |  | 
|  | ConstrainedValue operator[](unsigned index) const { return at(index); } | 
|  |  | 
|  | class iterator { | 
|  | public: | 
|  | using iterator_category = std::forward_iterator_tag; | 
|  | using value_type = ConstrainedValue; | 
|  | using difference_type = int; | 
|  | using pointer = void; | 
|  | using reference = ConstrainedValue; | 
|  |  | 
|  | iterator() | 
|  | : m_collection(nullptr) | 
|  | , m_index(0) | 
|  | { | 
|  | } | 
|  |  | 
|  | iterator(const ConstrainedValueCollection& collection, unsigned index) | 
|  | : m_collection(&collection) | 
|  | , m_index(index) | 
|  | { | 
|  | } | 
|  |  | 
|  | ConstrainedValue operator*() const | 
|  | { | 
|  | return m_collection->at(m_index); | 
|  | } | 
|  |  | 
|  | iterator& operator++() | 
|  | { | 
|  | m_index++; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | bool operator==(const iterator& other) const | 
|  | { | 
|  | ASSERT(m_collection == other.m_collection); | 
|  | return m_index == other.m_index; | 
|  | } | 
|  |  | 
|  | bool operator!=(const iterator& other) const | 
|  | { | 
|  | return !(*this == other); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const ConstrainedValueCollection* m_collection; | 
|  | unsigned m_index; | 
|  | }; | 
|  |  | 
|  | iterator begin() const { return iterator(*this, 0); } | 
|  | iterator end() const { return iterator(*this, size()); } | 
|  |  | 
|  | private: | 
|  | const StackmapValue& m_value; | 
|  | }; | 
|  |  | 
|  | ConstrainedValueCollection constrainedChildren() const | 
|  | { | 
|  | return ConstrainedValueCollection(*this); | 
|  | } | 
|  |  | 
|  | B3_SPECIALIZE_VALUE_FOR_VARARGS_CHILDREN | 
|  |  | 
|  | protected: | 
|  | void dumpChildren(CommaPrinter&, PrintStream&) const override; | 
|  | void dumpMeta(CommaPrinter&, PrintStream&) const override; | 
|  |  | 
|  | StackmapValue(CheckedOpcodeTag, Kind, Type, Origin); | 
|  |  | 
|  | private: | 
|  | friend class CheckSpecial; | 
|  | friend class PatchpointSpecial; | 
|  | friend class StackmapGenerationParams; | 
|  | friend class StackmapSpecial; | 
|  |  | 
|  | Vector<ValueRep> m_reps; | 
|  | RefPtr<StackmapGenerator> m_generator; | 
|  | RegisterSet m_earlyClobbered; | 
|  | RegisterSet m_lateClobbered; | 
|  | RegisterSet m_usedRegisters; // Stackmaps could be further duplicated by Air, but that's unlikely, so we just merge the used registers sets if that were to happen. | 
|  | }; | 
|  |  | 
|  | } } // namespace JSC::B3 | 
|  |  | 
|  | #endif // ENABLE(B3_JIT) |