/* * Copyright (C) 2011-2013, 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. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 ValueProfile_h #define ValueProfile_h #include "ConcurrentJITLock.h" #include "Heap.h" #include "JSArray.h" #include "SpeculatedType.h" #include "Structure.h" #include "TagRegistersMode.h" #include "WriteBarrier.h" #include #include namespace JSC { class CCallHelpers; template struct ValueProfileBase { static const unsigned numberOfBuckets = numberOfBucketsArgument; static const unsigned numberOfSpecFailBuckets = 1; static const unsigned bucketIndexMask = numberOfBuckets - 1; static const unsigned totalNumberOfBuckets = numberOfBuckets + numberOfSpecFailBuckets; ValueProfileBase() : m_bytecodeOffset(-1) , m_prediction(SpecNone) , m_numberOfSamplesInPrediction(0) { for (unsigned i = 0; i < totalNumberOfBuckets; ++i) m_buckets[i] = JSValue::encode(JSValue()); } ValueProfileBase(int bytecodeOffset) : m_bytecodeOffset(bytecodeOffset) , m_prediction(SpecNone) , m_numberOfSamplesInPrediction(0) { for (unsigned i = 0; i < totalNumberOfBuckets; ++i) m_buckets[i] = JSValue::encode(JSValue()); } EncodedJSValue* specFailBucket(unsigned i) { ASSERT(numberOfBuckets + i < totalNumberOfBuckets); return m_buckets + numberOfBuckets + i; } const ClassInfo* classInfo(unsigned bucket) const { JSValue value = JSValue::decode(m_buckets[bucket]); if (!!value) { if (!value.isCell()) return 0; return value.asCell()->structure()->classInfo(); } return 0; } unsigned numberOfSamples() const { unsigned result = 0; for (unsigned i = 0; i < totalNumberOfBuckets; ++i) { if (!!JSValue::decode(m_buckets[i])) result++; } return result; } unsigned totalNumberOfSamples() const { return numberOfSamples() + m_numberOfSamplesInPrediction; } bool isLive() const { for (unsigned i = 0; i < totalNumberOfBuckets; ++i) { if (!!JSValue::decode(m_buckets[i])) return true; } return false; } CString briefDescription(const ConcurrentJITLocker& locker) { computeUpdatedPrediction(locker); StringPrintStream out; out.print("predicting ", SpeculationDump(m_prediction)); return out.toCString(); } void dump(PrintStream& out) { out.print("samples = ", totalNumberOfSamples(), " prediction = ", SpeculationDump(m_prediction)); bool first = true; for (unsigned i = 0; i < totalNumberOfBuckets; ++i) { JSValue value = JSValue::decode(m_buckets[i]); if (!!value) { if (first) { out.printf(": "); first = false; } else out.printf(", "); out.print(value); } } } // Updates the prediction and returns the new one. Never call this from any thread // that isn't executing the code. SpeculatedType computeUpdatedPrediction(const ConcurrentJITLocker&) { for (unsigned i = 0; i < totalNumberOfBuckets; ++i) { JSValue value = JSValue::decode(m_buckets[i]); if (!value) continue; m_numberOfSamplesInPrediction++; mergeSpeculation(m_prediction, speculationFromValue(value)); m_buckets[i] = JSValue::encode(JSValue()); } return m_prediction; } int m_bytecodeOffset; // -1 for prologue SpeculatedType m_prediction; unsigned m_numberOfSamplesInPrediction; EncodedJSValue m_buckets[totalNumberOfBuckets]; }; struct MinimalValueProfile : public ValueProfileBase<0> { MinimalValueProfile(): ValueProfileBase<0>() { } MinimalValueProfile(int bytecodeOffset): ValueProfileBase<0>(bytecodeOffset) { } }; template struct ValueProfileWithLogNumberOfBuckets : public ValueProfileBase<1 << logNumberOfBucketsArgument> { static const unsigned logNumberOfBuckets = logNumberOfBucketsArgument; ValueProfileWithLogNumberOfBuckets() : ValueProfileBase<1 << logNumberOfBucketsArgument>() { } ValueProfileWithLogNumberOfBuckets(int bytecodeOffset) : ValueProfileBase<1 << logNumberOfBucketsArgument>(bytecodeOffset) { } }; struct ValueProfile : public ValueProfileWithLogNumberOfBuckets<0> { ValueProfile(): ValueProfileWithLogNumberOfBuckets<0>() { } ValueProfile(int bytecodeOffset): ValueProfileWithLogNumberOfBuckets<0>(bytecodeOffset) { } }; template inline int getValueProfileBytecodeOffset(T* valueProfile) { return valueProfile->m_bytecodeOffset; } // This is a mini value profile to catch pathologies. It is a counter that gets // incremented when we take the slow path on any instruction. struct RareCaseProfile { RareCaseProfile(int bytecodeOffset) : m_bytecodeOffset(bytecodeOffset) , m_counter(0) { } int m_bytecodeOffset; uint32_t m_counter; }; inline int getRareCaseProfileBytecodeOffset(RareCaseProfile* rareCaseProfile) { return rareCaseProfile->m_bytecodeOffset; } struct ResultProfile { private: static const int numberOfFlagBits = 5; public: ResultProfile(int bytecodeOffset) : m_bytecodeOffsetAndFlags(bytecodeOffset << numberOfFlagBits) { ASSERT(((bytecodeOffset << numberOfFlagBits) >> numberOfFlagBits) == bytecodeOffset); } enum ObservedResults { NonNegZeroDouble = 1 << 0, NegZeroDouble = 1 << 1, NonNumber = 1 << 2, Int32Overflow = 1 << 3, Int52Overflow = 1 << 4, }; int bytecodeOffset() const { return m_bytecodeOffsetAndFlags >> numberOfFlagBits; } unsigned specialFastPathCount() const { return m_specialFastPathCount; } bool didObserveNonInt32() const { return hasBits(NonNegZeroDouble | NegZeroDouble | NonNumber); } bool didObserveDouble() const { return hasBits(NonNegZeroDouble | NegZeroDouble); } bool didObserveNonNegZeroDouble() const { return hasBits(NonNegZeroDouble); } bool didObserveNegZeroDouble() const { return hasBits(NegZeroDouble); } bool didObserveNonNumber() const { return hasBits(NonNumber); } bool didObserveInt32Overflow() const { return hasBits(Int32Overflow); } bool didObserveInt52Overflow() const { return hasBits(Int52Overflow); } void setObservedNonNegZeroDouble() { setBit(NonNegZeroDouble); } void setObservedNegZeroDouble() { setBit(NegZeroDouble); } void setObservedNonNumber() { setBit(NonNumber); } void setObservedInt32Overflow() { setBit(Int32Overflow); } void setObservedInt52Overflow() { setBit(Int52Overflow); } void* addressOfFlags() { return &m_bytecodeOffsetAndFlags; } void* addressOfSpecialFastPathCount() { return &m_specialFastPathCount; } void detectNumericness(JSValue value) { if (value.isInt32()) return; if (value.isNumber()) { m_bytecodeOffsetAndFlags |= Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble; return; } m_bytecodeOffsetAndFlags |= NonNumber; } #if ENABLE(JIT) // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble) if it sees a // double. Sets NonNumber if it sees a non-number. void emitDetectNumericness(CCallHelpers&, JSValueRegs, TagRegistersMode = HaveTagRegisters); // Sets (Int32Overflow | Int52Overflow | NonNegZeroDouble | NegZeroDouble). void emitSetDouble(CCallHelpers&); // Sets NonNumber. void emitSetNonNumber(CCallHelpers&); #endif // ENABLE(JIT) private: bool hasBits(int mask) const { return m_bytecodeOffsetAndFlags & mask; } void setBit(int mask) { m_bytecodeOffsetAndFlags |= mask; } int m_bytecodeOffsetAndFlags; unsigned m_specialFastPathCount { 0 }; }; inline int getResultProfileBytecodeOffset(ResultProfile* profile) { return profile->bytecodeOffset(); } } // namespace JSC namespace WTF { void printInternal(PrintStream&, const JSC::ResultProfile&); } // namespace WTF #endif // ValueProfile_h